Reapply r231508 "CodeGen: Emit constant temporaries into read-only globals."

I disabled putting the new global into the same COMDAT as the function for now.
There's a fundamental problem when we inline references to the global but still
have the global in a COMDAT linked to the inlined function. Since this is only
an optimization there may be other versions of the COMDAT around that are
missing the new global and hell breaks loose at link time.

I hope the chromium build doesn't break this time :)

llvm-svn: 231564
This commit is contained in:
Benjamin Kramer 2015-03-07 13:37:13 +00:00
parent c9d79e8103
commit f8b86964ca
5 changed files with 66 additions and 22 deletions

View File

@ -301,6 +301,23 @@ createReferenceTemporary(CodeGenFunction &CGF,
switch (M->getStorageDuration()) { switch (M->getStorageDuration()) {
case SD_FullExpression: case SD_FullExpression:
case SD_Automatic: case SD_Automatic:
// If we have a constant temporary array or record try to promote it into a
// constant global under the same rules a normal constant would've been
// promoted. This is easier on the optimizer and generally emits fewer
// instructions.
if (CGF.CGM.getCodeGenOpts().MergeAllConstants &&
(M->getType()->isArrayType() || M->getType()->isRecordType()) &&
CGF.CGM.isTypeConstant(M->getType(), true))
if (llvm::Constant *Init =
CGF.CGM.EmitConstantExpr(Inner, M->getType(), &CGF)) {
auto *GV = new llvm::GlobalVariable(
CGF.CGM.getModule(), Init->getType(), /*isConstant=*/true,
llvm::GlobalValue::PrivateLinkage, Init, ".ref.tmp");
GV->setAlignment(
CGF.getContext().getTypeAlignInChars(M->getType()).getQuantity());
// FIXME: Should we put the new global into a COMDAT?
return GV;
}
return CGF.CreateMemTemp(Inner->getType(), "ref.tmp"); return CGF.CreateMemTemp(Inner->getType(), "ref.tmp");
case SD_Thread: case SD_Thread:
@ -370,8 +387,9 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
// Create and initialize the reference temporary. // Create and initialize the reference temporary.
llvm::Value *Object = createReferenceTemporary(*this, M, E); llvm::Value *Object = createReferenceTemporary(*this, M, E);
if (auto *Var = dyn_cast<llvm::GlobalVariable>(Object)) { if (auto *Var = dyn_cast<llvm::GlobalVariable>(Object)) {
// If the temporary is a global and has a constant initializer, we may // If the temporary is a global and has a constant initializer or is a
// have already initialized it. // constant temporary that we promoted to a global, we may have already
// initialized it.
if (!Var->hasInitializer()) { if (!Var->hasInitializer()) {
Var->setInitializer(CGM.EmitNullConstant(E->getType())); Var->setInitializer(CGM.EmitNullConstant(E->getType()));
EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true); EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);

View File

@ -28,7 +28,7 @@ int f() {
// CHECK-LABEL: define i32 @_Z1gv() // CHECK-LABEL: define i32 @_Z1gv()
int g() { int g() {
// CHECK: store [2 x i32]* %{{[a-z0-9.]+}}, [2 x i32]** [[V:%[a-z0-9.]+]] // CHECK: store [2 x i32]* @{{.*}}, [2 x i32]** [[V:%[a-z0-9.]+]]
const int (&v)[2] = (int [2]) {1,2}; const int (&v)[2] = (int [2]) {1,2};
// CHECK: [[A:%[a-z0-9.]+]] = load [2 x i32]*, [2 x i32]** [[V]] // CHECK: [[A:%[a-z0-9.]+]] = load [2 x i32]*, [2 x i32]** [[V]]

View File

@ -42,10 +42,11 @@ namespace ValueInitArrayOfMemPtr {
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %{{.*}}, i8* bitcast ([3 x i32]* @[[THREE_NULL_MEMPTRS]] to i8*), i32 12, i32 4, i1 false) // CHECK: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %{{.*}}, i8* bitcast ([3 x i32]* @[[THREE_NULL_MEMPTRS]] to i8*), i32 12, i32 4, i1 false)
} }
// CHECK-LABEL: define void @_ZN22ValueInitArrayOfMemPtr1gEv // Test dynamic initialization.
void g() { // CHECK-LABEL: define void @_ZN22ValueInitArrayOfMemPtr1gEMNS_1SEi
void g(p ptr) {
// CHECK: store i32 -1, // CHECK: store i32 -1,
f(a{}); f(a{ptr});
} }
} }

View File

@ -35,22 +35,30 @@ namespace reference {
// CHECK-NEXT: ret // CHECK-NEXT: ret
} }
void reference_to_aggregate() { void reference_to_aggregate(int i) {
// CHECK: getelementptr {{.*}}, i32 0, i32 0 // CHECK: getelementptr {{.*}}, i32 0, i32 0
// CHECK-NEXT: store i32 1 // CHECK-NEXT: store i32 1
// CHECK-NEXT: getelementptr {{.*}}, i32 0, i32 1 // CHECK-NEXT: getelementptr {{.*}}, i32 0, i32 1
// CHECK-NEXT: store i32 2 // CHECK-NEXT: %[[I1:.*]] = load i32, i32*
// CHECK-NEXT: store i32 %[[I1]]
// CHECK-NEXT: store %{{.*}}* %{{.*}}, %{{.*}}** %{{.*}}, align // CHECK-NEXT: store %{{.*}}* %{{.*}}, %{{.*}}** %{{.*}}, align
const A &ra1{1, 2}; const A &ra1{1, i};
// CHECK-NEXT: getelementptr inbounds [3 x i32], [3 x i32]* %{{.*}}, i{{32|64}} 0, i{{32|64}} 0 // CHECK-NEXT: getelementptr inbounds [3 x i32], [3 x i32]* %{{.*}}, i{{32|64}} 0, i{{32|64}} 0
// CHECK-NEXT: store i32 1 // CHECK-NEXT: store i32 1
// CHECK-NEXT: getelementptr inbounds i32, i32* %{{.*}}, i{{32|64}} 1 // CHECK-NEXT: getelementptr inbounds i32, i32* %{{.*}}, i{{32|64}} 1
// CHECK-NEXT: store i32 2 // CHECK-NEXT: store i32 2
// CHECK-NEXT: getelementptr inbounds i32, i32* %{{.*}}, i{{32|64}} 1 // CHECK-NEXT: getelementptr inbounds i32, i32* %{{.*}}, i{{32|64}} 1
// CHECK-NEXT: store i32 3 // CHECK-NEXT: %[[I2:.*]] = load i32, i32*
// CHECK-NEXT: store i32 %[[I2]]
// CHECK-NEXT: store [3 x i32]* %{{.*}}, [3 x i32]** %{{.*}}, align // CHECK-NEXT: store [3 x i32]* %{{.*}}, [3 x i32]** %{{.*}}, align
const int (&arrayRef)[] = {1, 2, 3}; const int (&arrayRef)[] = {1, 2, i};
// CHECK: store %{{.*}}* @{{.*}}, %{{.*}}** %{{.*}}, align
const A &constra1{1, 2};
// CHECK-NEXT: store [3 x i32]* @{{.*}}, [3 x i32]** %{{.*}}, align
const int (&constarrayRef)[] = {1, 2, 3};
// CHECK-NEXT: ret // CHECK-NEXT: ret
} }

View File

@ -72,6 +72,9 @@ namespace thread_local_global_array {
// CHECK: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE.*]] = internal global [2 x i32] zeroinitializer, align 4 // CHECK: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE.*]] = internal global [2 x i32] zeroinitializer, align 4
// CHECK: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE.*]] = internal constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4 // CHECK: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE.*]] = internal constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4
// CHECK: @[[REFTMP1:.*]] = private constant [2 x i32] [i32 42, i32 43], align 4
// CHECK: @[[REFTMP2:.*]] = private constant [3 x %{{.*}}] [%{{.*}} { i32 1 }, %{{.*}} { i32 2 }, %{{.*}} { i32 3 }], align 4
// CHECK: appending global // CHECK: appending global
@ -215,17 +218,16 @@ void fn9() {
struct haslist1 { struct haslist1 {
std::initializer_list<int> il; std::initializer_list<int> il;
haslist1(); haslist1(int i);
}; };
// CHECK-LABEL: define void @_ZN8haslist1C2Ev // CHECK-LABEL: define void @_ZN8haslist1C2Ei
haslist1::haslist1() haslist1::haslist1(int i)
// CHECK: alloca [3 x i32] // CHECK: alloca [3 x i32]
// CHECK: store i32 1 // CHECK: store i32 %
// CHECK: store i32 2 // CHECK: store i32 2
// CHECK: store i32 3 // CHECK: store i32 3
// CHECK: store i{{32|64}} 3 : il{i, 2, 3}
: il{1, 2, 3}
{ {
destroyme2 dm2; destroyme2 dm2;
} }
@ -244,16 +246,15 @@ haslist2::haslist2()
// CHECK: call void @_ZN10destroyme1D1Ev // CHECK: call void @_ZN10destroyme1D1Ev
} }
void fn10() { void fn10(int i) {
// CHECK-LABEL: define void @_Z4fn10v // CHECK-LABEL: define void @_Z4fn10i
// CHECK: alloca [3 x i32] // CHECK: alloca [3 x i32]
// CHECK: call noalias i8* @_Znw{{[jm]}} // CHECK: call noalias i8* @_Znw{{[jm]}}
// CHECK: store i32 1 // CHECK: store i32 %
// CHECK: store i32 2 // CHECK: store i32 2
// CHECK: store i32 3 // CHECK: store i32 3
// CHECK: store i32* // CHECK: store i32*
// CHECK: store i{{32|64}} 3 (void) new std::initializer_list<int> {i, 2, 3};
(void) new std::initializer_list<int> {1, 2, 3};
} }
void fn11() { void fn11() {
@ -462,6 +463,22 @@ namespace PR20445 {
template<int x> void f() { new MyClass({42, 43}); } template<int x> void f() { new MyClass({42, 43}); }
template void f<0>(); template void f<0>();
// CHECK-LABEL: define {{.*}} @_ZN7PR204451fILi0EEEvv( // CHECK-LABEL: define {{.*}} @_ZN7PR204451fILi0EEEvv(
// CHECK: store i32* getelementptr inbounds ([2 x i32]* @[[REFTMP1]], i64 0, i64 0)
// CHECK: call void @_ZN7PR204456vectorC1ESt16initializer_listIiE( // CHECK: call void @_ZN7PR204456vectorC1ESt16initializer_listIiE(
// CHECK: call void @_ZN7PR204457MyClassC1ERKNS_6vectorE( // CHECK: call void @_ZN7PR204457MyClassC1ERKNS_6vectorE(
} }
namespace ConstExpr {
class C {
int x;
public:
constexpr C(int x) : x(x) {}
};
void f(std::initializer_list<C>);
void g() {
// CHECK-LABEL: _ZN9ConstExpr1gEv
// CHECK: store %"class.ConstExpr::C"* getelementptr inbounds ([3 x %"class.ConstExpr::C"]* @[[REFTMP2]], i64 0, i64 0)
// CHECK: call void @_ZN9ConstExpr1fESt16initializer_listINS_1CEE
f({C(1), C(2), C(3)});
}
}