Use aliases for more constructors and destructors.
With this patch we produce alias for cases like template<typename T> struct foobar { foobar() { } }; template struct foobar<void>; We just have to be careful to produce the same aliases in every TU because of comdats. llvm-svn: 194000
This commit is contained in:
parent
a6775b622f
commit
3f643bd124
|
@ -95,12 +95,16 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
|
|||
return true;
|
||||
|
||||
return TryEmitDefinitionAsAlias(GlobalDecl(D, Dtor_Base),
|
||||
GlobalDecl(BaseD, Dtor_Base));
|
||||
GlobalDecl(BaseD, Dtor_Base),
|
||||
false);
|
||||
}
|
||||
|
||||
/// Try to emit a definition as a global alias for another definition.
|
||||
/// If \p InEveryTU is true, we know that an equivalent alias can be produced
|
||||
/// in every translation unit.
|
||||
bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl,
|
||||
GlobalDecl TargetDecl) {
|
||||
GlobalDecl TargetDecl,
|
||||
bool InEveryTU) {
|
||||
if (!getCodeGenOpts().CXXCtorDtorAliases)
|
||||
return true;
|
||||
|
||||
|
@ -108,34 +112,34 @@ bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl,
|
|||
// support aliases with that linkage, fail.
|
||||
llvm::GlobalValue::LinkageTypes Linkage = getFunctionLinkage(AliasDecl);
|
||||
|
||||
switch (Linkage) {
|
||||
// We can definitely emit aliases to definitions with external linkage.
|
||||
case llvm::GlobalValue::ExternalLinkage:
|
||||
case llvm::GlobalValue::ExternalWeakLinkage:
|
||||
break;
|
||||
|
||||
// Same with local linkage.
|
||||
case llvm::GlobalValue::InternalLinkage:
|
||||
case llvm::GlobalValue::PrivateLinkage:
|
||||
case llvm::GlobalValue::LinkerPrivateLinkage:
|
||||
break;
|
||||
|
||||
// We should try to support linkonce linkages.
|
||||
case llvm::GlobalValue::LinkOnceAnyLinkage:
|
||||
case llvm::GlobalValue::LinkOnceODRLinkage:
|
||||
return true;
|
||||
|
||||
// Other linkages will probably never be supported.
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::GlobalValue::LinkageTypes TargetLinkage
|
||||
= getFunctionLinkage(TargetDecl);
|
||||
|
||||
if (llvm::GlobalValue::isWeakForLinker(TargetLinkage))
|
||||
// Don't create an alias to a linker weak symbol unless we know we can do
|
||||
// that in every TU. This avoids producing different COMDATs in different
|
||||
// TUs.
|
||||
if (llvm::GlobalValue::isWeakForLinker(TargetLinkage)) {
|
||||
if (!InEveryTU)
|
||||
return true;
|
||||
|
||||
// In addition to making sure we produce it in every TU, we have to make
|
||||
// sure llvm keeps it.
|
||||
// FIXME: Instead of outputting an alias we could just replace every use of
|
||||
// AliasDecl with TargetDecl.
|
||||
assert(Linkage == TargetLinkage);
|
||||
Linkage = llvm::GlobalValue::WeakODRLinkage;
|
||||
}
|
||||
|
||||
// We can't use an alias if the linkage is not valid for one.
|
||||
if (!llvm::GlobalAlias::isValidLinkage(Linkage))
|
||||
return true;
|
||||
|
||||
// Check if we have it already.
|
||||
StringRef MangledName = getMangledName(AliasDecl);
|
||||
llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
|
||||
if (Entry && !Entry->isDeclaration())
|
||||
return false;
|
||||
|
||||
// Derive the type for the alias.
|
||||
llvm::PointerType *AliasType
|
||||
= getTypes().GetFunctionType(AliasDecl)->getPointerTo();
|
||||
|
@ -153,10 +157,7 @@ bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl,
|
|||
new llvm::GlobalAlias(AliasType, Linkage, "", Aliasee, &getModule());
|
||||
|
||||
// Switch any previous uses to the alias.
|
||||
StringRef MangledName = getMangledName(AliasDecl);
|
||||
llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
|
||||
if (Entry) {
|
||||
assert(Entry->isDeclaration() && "definition already exists for alias");
|
||||
assert(Entry->getType() == AliasType &&
|
||||
"declaration exists with different type");
|
||||
Alias->takeName(Entry);
|
||||
|
@ -177,11 +178,14 @@ void CodeGenModule::EmitCXXConstructor(const CXXConstructorDecl *ctor,
|
|||
// The complete constructor is equivalent to the base constructor
|
||||
// for classes with no virtual bases. Try to emit it as an alias.
|
||||
if (getTarget().getCXXABI().hasConstructorVariants() &&
|
||||
ctorType == Ctor_Complete &&
|
||||
!ctor->getParent()->getNumVBases() &&
|
||||
!TryEmitDefinitionAsAlias(GlobalDecl(ctor, Ctor_Complete),
|
||||
GlobalDecl(ctor, Ctor_Base)))
|
||||
return;
|
||||
(ctorType == Ctor_Complete || ctorType == Ctor_Base)) {
|
||||
bool ProducedAlias =
|
||||
!TryEmitDefinitionAsAlias(GlobalDecl(ctor, Ctor_Complete),
|
||||
GlobalDecl(ctor, Ctor_Base), true);
|
||||
if (ctorType == Ctor_Complete && ProducedAlias)
|
||||
return;
|
||||
}
|
||||
|
||||
const CGFunctionInfo &fnInfo =
|
||||
getTypes().arrangeCXXConstructorDeclaration(ctor, ctorType);
|
||||
|
@ -218,11 +222,18 @@ void CodeGenModule::EmitCXXDestructor(const CXXDestructorDecl *dtor,
|
|||
CXXDtorType dtorType) {
|
||||
// The complete destructor is equivalent to the base destructor for
|
||||
// classes with no virtual bases, so try to emit it as an alias.
|
||||
if (dtorType == Dtor_Complete &&
|
||||
!dtor->getParent()->getNumVBases() &&
|
||||
!TryEmitDefinitionAsAlias(GlobalDecl(dtor, Dtor_Complete),
|
||||
GlobalDecl(dtor, Dtor_Base)))
|
||||
return;
|
||||
if (!dtor->getParent()->getNumVBases() &&
|
||||
(dtorType == Dtor_Complete || dtorType == Dtor_Base)) {
|
||||
bool ProducedAlias =
|
||||
!TryEmitDefinitionAsAlias(GlobalDecl(dtor, Dtor_Complete),
|
||||
GlobalDecl(dtor, Dtor_Base), true);
|
||||
if (ProducedAlias) {
|
||||
if (dtorType == Dtor_Complete)
|
||||
return;
|
||||
if (dtor->isVirtual())
|
||||
getVTables().EmitThunks(GlobalDecl(dtor, Dtor_Complete));
|
||||
}
|
||||
}
|
||||
|
||||
// The base destructor is equivalent to the base destructor of its
|
||||
// base class if there is exactly one non-virtual base class with a
|
||||
|
|
|
@ -387,10 +387,6 @@ void CodeGenVTables::emitThunk(GlobalDecl GD, const ThunkInfo &Thunk,
|
|||
return;
|
||||
}
|
||||
|
||||
// If a function has a body, it should have available_externally linkage.
|
||||
assert(ThunkFn->hasAvailableExternallyLinkage() &&
|
||||
"Function should have available_externally linkage!");
|
||||
|
||||
// Change the linkage.
|
||||
CGM.setFunctionLinkage(GD, ThunkFn);
|
||||
return;
|
||||
|
|
|
@ -1034,7 +1034,8 @@ private:
|
|||
|
||||
// C++ related functions.
|
||||
|
||||
bool TryEmitDefinitionAsAlias(GlobalDecl Alias, GlobalDecl Target);
|
||||
bool TryEmitDefinitionAsAlias(GlobalDecl Alias, GlobalDecl Target,
|
||||
bool InEveryTU);
|
||||
bool TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D);
|
||||
|
||||
void EmitNamespace(const NamespaceDecl *D);
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// RUN: %clang_cc1 %s -triple x86_64-linux -emit-llvm -o - -mconstructor-aliases | FileCheck %s
|
||||
|
||||
namespace test1 {
|
||||
// test that we produce an alias when the destructor is weak_odr
|
||||
|
||||
// CHECK-DAG: @_ZN5test16foobarIvEC1Ev = alias weak_odr void (%"struct.test1::foobar"*)* @_ZN5test16foobarIvEC2Ev
|
||||
// CHECK-DAG: define weak_odr void @_ZN5test16foobarIvEC2Ev(
|
||||
template <typename T> struct foobar {
|
||||
foobar() {}
|
||||
};
|
||||
|
||||
template struct foobar<void>;
|
||||
}
|
||||
|
||||
namespace test2 {
|
||||
// test that we produce an alias when the destrucor is linkonce_odr. Note that
|
||||
// the alias itself is weak_odr to make sure we don't get a translation unit
|
||||
// with just _ZN5test26foobarIvEC2Ev in it.
|
||||
|
||||
// CHECK-DAG: @_ZN5test26foobarIvEC1Ev = alias weak_odr void (%"struct.test2::foobar"*)* @_ZN5test26foobarIvEC2Ev
|
||||
// CHECK-DAG: define linkonce_odr void @_ZN5test26foobarIvEC2Ev(
|
||||
void g();
|
||||
template <typename T> struct foobar {
|
||||
foobar() { g(); }
|
||||
};
|
||||
foobar<void> x;
|
||||
}
|
||||
|
||||
namespace test3 {
|
||||
// test that these alias are internal.
|
||||
|
||||
// CHECK-DAG: @_ZN5test312_GLOBAL__N_11AD1Ev = alias internal void (%"struct.test3::<anonymous namespace>::A"*)* @_ZN5test312_GLOBAL__N_11AD2Ev
|
||||
// CHECK-DAG: @_ZN5test312_GLOBAL__N_11BD2Ev = alias internal bitcast (void (%"struct.test3::<anonymous namespace>::A"*)* @_ZN5test312_GLOBAL__N_11AD2Ev to void (%"struct.test3::<anonymous namespace>::B"*)*)
|
||||
// CHECK-DAG: @_ZN5test312_GLOBAL__N_11BD1Ev = alias internal void (%"struct.test3::<anonymous namespace>::B"*)* @_ZN5test312_GLOBAL__N_11BD2Ev
|
||||
// CHECK-DAG: define internal void @_ZN5test312_GLOBAL__N_11AD2Ev(
|
||||
namespace {
|
||||
struct A {
|
||||
~A() {}
|
||||
};
|
||||
|
||||
struct B : public A {};
|
||||
}
|
||||
|
||||
B x;
|
||||
}
|
||||
|
||||
namespace test4 {
|
||||
// Test that we don't produce aliases from B to A. We cannot because we cannot
|
||||
// guarantee that they will be present in every TU.
|
||||
|
||||
// CHECK-DAG: @_ZN5test41BD1Ev = alias weak_odr void (%"struct.test4::B"*)* @_ZN5test41BD2Ev
|
||||
// CHECK-DAG: define linkonce_odr void @_ZN5test41BD2Ev(
|
||||
// CHECK-DAG: @_ZN5test41AD1Ev = alias weak_odr void (%"struct.test4::A"*)* @_ZN5test41AD2Ev
|
||||
// CHECK-DAG: define linkonce_odr void @_ZN5test41AD2Ev(
|
||||
struct A {
|
||||
virtual ~A() {}
|
||||
};
|
||||
struct B : public A{
|
||||
~B() {}
|
||||
};
|
||||
B X;
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
// CHECK-DAG: @_ZN5test312_GLOBAL__N_11DD1Ev = alias internal {{.*}} @_ZN5test312_GLOBAL__N_11DD2Ev
|
||||
// CHECK-DAG: @_ZN5test312_GLOBAL__N_11DD2Ev = alias internal bitcast {{.*}} @_ZN5test312_GLOBAL__N_11CD2Ev
|
||||
// CHECK-DAG: @_ZN5test312_GLOBAL__N_11CD1Ev = alias internal {{.*}} @_ZN5test312_GLOBAL__N_11CD2Ev
|
||||
// CHECK-DAG: @_ZN6PR752617allocator_derivedD1Ev = alias weak_odr void (%"struct.PR7526::allocator_derived"*)* @_ZN6PR752617allocator_derivedD2Ev
|
||||
|
||||
struct A {
|
||||
int a;
|
||||
|
@ -44,9 +45,6 @@ namespace PR7526 {
|
|||
// CHECK: call void @__cxa_call_unexpected
|
||||
allocator::~allocator() throw() { foo(); }
|
||||
|
||||
// CHECK-LABEL: define linkonce_odr void @_ZN6PR752617allocator_derivedD1Ev(%"struct.PR7526::allocator_derived"* %this) unnamed_addr
|
||||
// CHECK-NOT: call void @__cxa_call_unexpected
|
||||
// CHECK: }
|
||||
void foo() {
|
||||
allocator_derived ad;
|
||||
}
|
||||
|
@ -396,6 +394,11 @@ namespace test9 {
|
|||
// CHECK: call void @_ZN5test31AD2Ev(
|
||||
// CHECK: ret void
|
||||
|
||||
// CHECK-LABEL: define internal void @_ZThn8_N5test312_GLOBAL__N_11CD1Ev(
|
||||
// CHECK: getelementptr inbounds i8* {{.*}}, i64 -8
|
||||
// CHECK: call void @_ZN5test312_GLOBAL__N_11CD1Ev(
|
||||
// CHECK: ret void
|
||||
|
||||
// CHECK: declare void @_ZN5test31BD2Ev(
|
||||
// CHECK: declare void @_ZN5test31AD2Ev(
|
||||
|
||||
|
@ -408,11 +411,6 @@ namespace test9 {
|
|||
// CHECK: call void @_ZdlPv({{.*}}) [[NUW]]
|
||||
// CHECK: resume { i8*, i32 }
|
||||
|
||||
// CHECK-LABEL: define internal void @_ZThn8_N5test312_GLOBAL__N_11CD1Ev(
|
||||
// CHECK: getelementptr inbounds i8* {{.*}}, i64 -8
|
||||
// CHECK: call void @_ZN5test312_GLOBAL__N_11CD1Ev(
|
||||
// CHECK: ret void
|
||||
|
||||
// CHECK-LABEL: define internal void @_ZThn8_N5test312_GLOBAL__N_11CD0Ev(
|
||||
// CHECK: getelementptr inbounds i8* {{.*}}, i64 -8
|
||||
// CHECK: call void @_ZN5test312_GLOBAL__N_11CD0Ev(
|
||||
|
|
Loading…
Reference in New Issue