Skip to content

Commit

Permalink
C++14: Disable sized deallocation by default due to ABI breakage
Browse files Browse the repository at this point in the history
There are no widely deployed standard libraries providing sized
deallocation functions, so we have to punt and ask the user if they want
us to use sized deallocation. In the future, when such libraries are
deployed, we can teach the driver to detect them and enable this
feature.

N3536 claimed that a weak thunk from sized to unsized deallocation could
be emitted to avoid breaking backwards compatibility with standard
libraries not providing sized deallocation. However, this approach and
other variations don't work in practice.

With the weak function approach, the thunk has to have default
visibility in order to ensure that it is overridden by other DSOs
providing sized deallocation. Weak, default visibility symbols are
particularly expensive on MachO, so John McCall was considering
disabling this feature by default on Darwin. It also changes behavior
ELF linking behavior, causing certain otherwise unreferenced object
files from an archive to be pulled into the link.

Our second approach was to use an extern_weak function declaration and
do an inline conditional branch at the deletion call site. This doesn't
work because extern_weak only works on MachO if you have some archive
providing the default value of the extern_weak symbol. Arranging to
provide such an archive has the same challenges as providing the symbol
in the standard library. Not to mention that extern_weak doesn't really
work on COFF.

Reviewers: rsmith, rjmccall

Differential Revision: http://reviews.llvm.org/D8467

llvm-svn: 232788
  • Loading branch information
rnk committed Mar 20, 2015
1 parent 6fe5fcb commit 7ffc3fb
Show file tree
Hide file tree
Showing 17 changed files with 34 additions and 200 deletions.
5 changes: 5 additions & 0 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -62,6 +62,11 @@ about them. The improvements since the 3.5 release include:
New Compiler Flags
------------------

The sized deallocation feature of C++14 is now controlled by the
``-fsized-deallocation`` flag. This feature relies on library support that
isn't yet widely deployed, so the user must supply an extra flag to get the
extra functionality.

The option ....


Expand Down
5 changes: 0 additions & 5 deletions clang/include/clang/AST/Decl.h
Expand Up @@ -1861,11 +1861,6 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
/// allocation function. [...]
bool isReplaceableGlobalAllocationFunction() const;

/// \brief Determine whether this function is a sized global deallocation
/// function in C++1y. If so, find and return the corresponding unsized
/// deallocation function.
FunctionDecl *getCorrespondingUnsizedGlobalDeallocationFunction() const;

/// Compute the language linkage.
LanguageLinkage getLanguageLinkage() const;

Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/Basic/LangOptions.def
Expand Up @@ -165,7 +165,6 @@ LANGOPT(CUDAAllowHostCallsFromHostDevice, 1, 0, "Allow host device functions to

LANGOPT(AssumeSaneOperatorNew , 1, 1, "implicit __attribute__((malloc)) for C++'s new operators")
LANGOPT(SizedDeallocation , 1, 0, "enable sized deallocation functions")
LANGOPT(DefineSizedDeallocation , 1, 0, "generate weak definitions of sized delete")
BENIGN_LANGOPT(ElideConstructors , 1, 1, "C++ copy constructor elision")
BENIGN_LANGOPT(DumpRecordLayouts , 1, 0, "dumping the layout of IRgen'd records")
BENIGN_LANGOPT(DumpRecordLayoutsSimple , 1, 0, "dumping the layout of IRgen'd records in a simple form")
Expand Down
6 changes: 0 additions & 6 deletions clang/include/clang/Driver/CC1Options.td
Expand Up @@ -518,12 +518,6 @@ def fdeprecated_macro : Flag<["-"], "fdeprecated-macro">,
HelpText<"Defines the __DEPRECATED macro">;
def fno_deprecated_macro : Flag<["-"], "fno-deprecated-macro">,
HelpText<"Undefines the __DEPRECATED macro">;
def fsized_deallocation : Flag<["-"], "fsized-deallocation">,
HelpText<"Enable C++14 sized global deallocation functions">;
def fno_sized_deallocation: Flag<["-"], "fno-sized-deallocation">,
HelpText<"Disable sized deallocation functions">;
def fdefine_sized_deallocation: Flag<["-"], "fdefine-sized-deallocation">,
HelpText<"Allow compiler-generated definition of sized deallocation functions">;
def fobjc_subscripting_legacy_runtime : Flag<["-"], "fobjc-subscripting-legacy-runtime">,
HelpText<"Allow Objective-C array and dictionary subscripting in legacy runtime">;
def vtordisp_mode_EQ : Joined<["-"], "vtordisp-mode=">,
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -830,6 +830,9 @@ def fapplication_extension : Flag<["-"], "fapplication-extension">,
HelpText<"Restrict code to those available for App Extensions">;
def fno_application_extension : Flag<["-"], "fno-application-extension">,
Group<f_Group>;
def fsized_deallocation : Flag<["-"], "fsized-deallocation">, Flags<[CC1Option]>,
HelpText<"Enable C++14 sized global deallocation functions">, Group<f_Group>;
def fno_sized_deallocation: Flag<["-"], "fno-sized-deallocation">, Group<f_Group>;

def fobjc_gc_only : Flag<["-"], "fobjc-gc-only">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Use GC exclusively for Objective-C related memory management">;
Expand Down
33 changes: 0 additions & 33 deletions clang/lib/AST/Decl.cpp
Expand Up @@ -2513,39 +2513,6 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction() const {
return RD && isNamed(RD, "nothrow_t") && RD->isInStdNamespace();
}

FunctionDecl *
FunctionDecl::getCorrespondingUnsizedGlobalDeallocationFunction() const {
ASTContext &Ctx = getASTContext();
if (!Ctx.getLangOpts().SizedDeallocation)
return nullptr;

if (getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
return nullptr;
if (getDeclName().getCXXOverloadedOperator() != OO_Delete &&
getDeclName().getCXXOverloadedOperator() != OO_Array_Delete)
return nullptr;
if (isa<CXXRecordDecl>(getDeclContext()))
return nullptr;

if (!getDeclContext()->getRedeclContext()->isTranslationUnit())
return nullptr;

if (getNumParams() != 2 || isVariadic() ||
!Ctx.hasSameType(getType()->castAs<FunctionProtoType>()->getParamType(1),
Ctx.getSizeType()))
return nullptr;

// This is a sized deallocation function. Find the corresponding unsized
// deallocation function.
lookup_result R = getDeclContext()->lookup(getDeclName());
for (lookup_result::iterator RI = R.begin(), RE = R.end(); RI != RE;
++RI)
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(*RI))
if (FD->getNumParams() == 1 && !FD->isVariadic())
return FD;
return nullptr;
}

LanguageLinkage FunctionDecl::getLanguageLinkage() const {
return getDeclLanguageLinkage(*this);
}
Expand Down
22 changes: 0 additions & 22 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Expand Up @@ -802,20 +802,6 @@ static void TryMarkNoThrow(llvm::Function *F) {
F->setDoesNotThrow();
}

static void EmitSizedDeallocationFunction(CodeGenFunction &CGF,
const FunctionDecl *UnsizedDealloc) {
// This is a weak discardable definition of the sized deallocation function.
CGF.CurFn->setLinkage(llvm::Function::LinkOnceAnyLinkage);
if (CGF.CGM.supportsCOMDAT())
CGF.CurFn->setComdat(
CGF.CGM.getModule().getOrInsertComdat(CGF.CurFn->getName()));

// Call the unsized deallocation function and forward the first argument
// unchanged.
llvm::Constant *Unsized = CGF.CGM.GetAddrOfFunction(UnsizedDealloc);
CGF.Builder.CreateCall(Unsized, &*CGF.CurFn->arg_begin());
}

void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
const CGFunctionInfo &FnInfo) {
const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl());
Expand Down Expand Up @@ -891,14 +877,6 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
emitImplicitAssignmentOperatorBody(Args);
} else if (Stmt *Body = FD->getBody()) {
EmitFunctionBody(Args, Body);
} else if (FunctionDecl *UnsizedDealloc =
FD->getCorrespondingUnsizedGlobalDeallocationFunction()) {
// Global sized deallocation functions get an implicit weak definition if
// they don't have an explicit definition, if allowed.
assert(getLangOpts().DefineSizedDeallocation &&
"Can't emit unallowed definition.");
EmitSizedDeallocationFunction(*this, UnsizedDealloc);

} else
llvm_unreachable("no definition for emitted function");

Expand Down
10 changes: 0 additions & 10 deletions clang/lib/CodeGen/CodeGenModule.cpp
Expand Up @@ -1620,16 +1620,6 @@ CodeGenModule::GetOrCreateLLVMFunction(StringRef MangledName,
// don't need it anymore).
addDeferredDeclToEmit(F, DDI->second);
DeferredDecls.erase(DDI);

// Otherwise, if this is a sized deallocation function, emit a weak
// definition for it at the end of the translation unit (if allowed),
// unless the sized deallocation function is aliased.
} else if (D &&
cast<FunctionDecl>(D)
->getCorrespondingUnsizedGlobalDeallocationFunction() &&
getLangOpts().DefineSizedDeallocation &&
!D->hasAttr<AliasAttr>()) {
addDeferredDeclToEmit(F, GD);

// Otherwise, there are cases we have to worry about where we're
// using a declaration for which we must emit a definition but where
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Driver/Tools.cpp
Expand Up @@ -4250,6 +4250,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
options::OPT_fno_assume_sane_operator_new))
CmdArgs.push_back("-fno-assume-sane-operator-new");

// -fsized-deallocation is off by default, as it is an ABI-breaking change for
// most platforms.
if (Args.hasFlag(options::OPT_fsized_deallocation,
options::OPT_fno_sized_deallocation, false))
CmdArgs.push_back("-fsized-deallocation");

// -fconstant-cfstrings is default, and may be subject to argument translation
// on Darwin.
if (!Args.hasFlag(options::OPT_fconstant_cfstrings,
Expand Down
8 changes: 1 addition & 7 deletions clang/lib/Frontend/CompilerInvocation.cpp
Expand Up @@ -1262,9 +1262,6 @@ void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
Opts.CXXOperatorNames = Opts.CPlusPlus;

Opts.DollarIdents = !Opts.AsmPreprocessor;

// C++14 onwards has sized global deallocation functions.
Opts.SizedDeallocation = Opts.CPlusPlus14;
}

/// Attempt to parse a visibility value out of the given argument.
Expand Down Expand Up @@ -1543,10 +1540,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding;
Opts.NoMathBuiltin = Args.hasArg(OPT_fno_math_builtin);
Opts.AssumeSaneOperatorNew = !Args.hasArg(OPT_fno_assume_sane_operator_new);
Opts.SizedDeallocation |= Args.hasArg(OPT_fsized_deallocation);
Opts.SizedDeallocation &= !Args.hasArg(OPT_fno_sized_deallocation);
Opts.DefineSizedDeallocation = Opts.SizedDeallocation &&
Args.hasArg(OPT_fdefine_sized_deallocation);
Opts.SizedDeallocation = Args.hasArg(OPT_fsized_deallocation);
Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions);
Opts.AccessControl = !Args.hasArg(OPT_fno_access_control);
Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors);
Expand Down
9 changes: 0 additions & 9 deletions clang/lib/Sema/SemaExprCXX.cpp
Expand Up @@ -2168,15 +2168,6 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
}
}
}

// If the function is sized operator delete and has not already been
// declared, and weak definitions have been disabled, do not declare
// it implicitly. Instead, let deallocation function lookup pick up
// unsized delete.
// FIXME: We should remove this guard once backward compatibility is
// no longer an issue
if (NumParams == 2 && !getLangOpts().DefineSizedDeallocation)
return;

FunctionProtoType::ExtProtoInfo EPI;

Expand Down
2 changes: 1 addition & 1 deletion clang/test/CXX/drs/dr412.cpp
Expand Up @@ -15,7 +15,7 @@ inline void* operator new(size_t) BAD_ALLOC; // expected-error {{cannot be decla
inline void* operator new[](size_t) BAD_ALLOC; // expected-error {{cannot be declared 'inline'}}
inline void operator delete(void*) NOEXCEPT; // expected-error {{cannot be declared 'inline'}}
inline void operator delete[](void*) NOEXCEPT; // expected-error {{cannot be declared 'inline'}}
#if __cplusplus >= 201402L
#ifdef __cpp_sized_deallocation
inline void operator delete(void*, size_t) NOEXCEPT; // expected-error {{cannot be declared 'inline'}}
inline void operator delete[](void*, size_t) NOEXCEPT; // expected-error {{cannot be declared 'inline'}}
#endif
28 changes: 10 additions & 18 deletions clang/test/CodeGenCXX/cxx1y-sized-deallocation.cpp
@@ -1,22 +1,18 @@
// RUN: %clang_cc1 -std=c++1y %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK-UNSIZED
// RUN: %clang_cc1 -std=c++1y %s -emit-llvm -triple x86_64-linux-gnu -o - -DINLIB | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKUND
// RUN: %clang_cc1 -std=c++1y %s -emit-llvm -triple x86_64-linux-gnu -fdefine-sized-deallocation -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKDEF
// RUN: %clang_cc1 -std=c++11 -fsized-deallocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK-UNSIZED
// RUN: %clang_cc1 -std=c++11 -fsized-deallocation %s -emit-llvm -triple x86_64-linux-gnu -o - -DINLIB | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKUND
// RUN: %clang_cc1 -std=c++11 -fsized-deallocation -fdefine-sized-deallocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECKDEF
// Check that delete exprs call the sized deallocation function if
// -fsized-deallocation is passed in both C++11 and C++14.
// RUN: %clang_cc1 -std=c++11 -fsized-deallocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s
// RUN: %clang_cc1 -std=c++14 -fsized-deallocation %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s

// Check that we don't used sized deallocation without -fsized-deallocation and
// C++14.
// RUN: %clang_cc1 -std=c++11 %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK-UNSIZED
// RUN: %clang_cc1 -std=c++1y %s -emit-llvm -triple x86_64-linux-gnu -fno-sized-deallocation -o - | FileCheck %s --check-prefix=CHECK-UNSIZED
// RUN: %clang_cc1 -std=c++14 %s -emit-llvm -triple x86_64-linux-gnu -o - | FileCheck %s --check-prefix=CHECK-UNSIZED

// CHECK-UNSIZED-NOT: _ZdlPvm
// CHECK-UNSIZED-NOT: _ZdaPvm

typedef decltype(sizeof(0)) size_t;

#ifdef INLIB
void operator delete(void *, size_t) noexcept;
void operator delete[](void *, size_t) noexcept;
#endif

typedef int A;
struct B { int n; };
struct C { ~C() {} };
Expand Down Expand Up @@ -60,9 +56,7 @@ D::D() {}
// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 4)
// CHECK: call void @_ZdaPv(i8* %{{[^ ]*}})

// CHECKDEF-LABEL: define linkonce void @_ZdlPvm(i8*, i64) #{{[0-9]+}} comdat
// CHECKDEF: call void @_ZdlPv(i8* %0)
// CHECKUND-LABEL: declare void @_ZdlPvm(i8*
// CHECK-LABEL: declare void @_ZdlPvm(i8*

// CHECK-LABEL: define weak_odr void @_Z3delI1BEvv()
// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 4)
Expand All @@ -82,9 +76,7 @@ D::D() {}
// CHECK: add i64 %{{[^ ]*}}, 8
// CHECK: call void @_ZdaPvm(i8* %{{[^ ]*}}, i64 %{{[^ ]*}})

// CHECKDEF-LABEL: define linkonce void @_ZdaPvm(i8*, i64) #{{[0-9]+}} comdat
// CHECKDEF: call void @_ZdaPv(i8* %0)
// CHECKUND-LABEL: declare void @_ZdaPvm(i8*
// CHECK-LABEL: declare void @_ZdaPvm(i8*

// CHECK-LABEL: define weak_odr void @_Z3delI1DEvv()
// CHECK: call void @_ZdlPvm(i8* %{{[^ ]*}}, i64 8)
Expand Down
69 changes: 0 additions & 69 deletions clang/test/CodeGenCXX/implicit-allocation-functions.cpp

This file was deleted.

11 changes: 0 additions & 11 deletions clang/test/CodeGenCXX/pr21754.cpp

This file was deleted.

2 changes: 1 addition & 1 deletion clang/test/Lexer/cxx-features.cpp
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -std=c++98 -verify %s
// RUN: %clang_cc1 -std=c++11 -verify %s
// RUN: %clang_cc1 -std=c++1y -verify %s
// RUN: %clang_cc1 -std=c++1y -fsized-deallocation -verify %s

// expected-no-diagnostics

Expand Down
14 changes: 7 additions & 7 deletions clang/www/cxx_status.html
Expand Up @@ -504,14 +504,14 @@ <h2 id="cxx14">C++14 implementation status</h2>
<td class="full" align="center">Clang 3.4 <a href="#n3778">(6)</a></td>
</tr>
</table>

<p>
<span id="n3778">(6): In Clang SVN, using sized deallocation in C++14 mode requires linking either
libc++ 3.7 or later, or libstdc++ 5 or later. Alternatively, you can use the
<code>-Xclang -fdefine-sized-deallocation</code> compiler option to enable the implementation from
Clang 3.4 through Clang 3.6, where weak definitions of sized deallocation functions are implicitly
generated (by the compiler). You can also use the <code>-Xclang -fno-sized-deallocation</code> option
to disable sized deallocation. Note that both of these flags are temporary provisions and may go away
soon.
<span id="n3778">(6): In Clang 3.7 and later, sized deallocation is only enabled
if the user passes the <code>-fsized-deallocation</code> flag. The user must
supply definitions of the sized deallocation functions, either by providing them
explicitly or by using a C++ standard library that does. <code>libstdc++</code>
added these functions in version 5.0, and <code>libc++</code> added them in
version 3.7.
</span>
</p>

Expand Down

0 comments on commit 7ffc3fb

Please sign in to comment.