Skip to content

Commit

Permalink
CodeGen: Emit constant temporaries into read-only globals.
Browse files Browse the repository at this point in the history
Instead of creating a copy on the stack just stash them in a private
constant global. This saves both the copying overhead and the stack
space, and gives the optimizer more room to constant fold.

This tries to make array temporaries more similar to regular arrays,
they can't use the same logic because a temporary has no VarDecl to be
bound to so we roll our own version here.

The original use case for this optimization was code like
  for (int i : {1, 2, 3, 4, 5, 6, 7, 8, 10})
    foo(i);
where without this patch (assuming that the loop is not unrolled) we
would alloca an array on the stack, copy the 10 values over and
iterate on that. With this patch we put the array in .text use it
directly. Apart from that case this helps on virtually any passing of
a constant std::initializer_list as a function argument.

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

llvm-svn: 231508
  • Loading branch information
d0k committed Mar 6, 2015
1 parent 898d11e commit 3d8aa5c
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 22 deletions.
24 changes: 22 additions & 2 deletions clang/lib/CodeGen/CGExpr.cpp
Expand Up @@ -301,6 +301,25 @@ createReferenceTemporary(CodeGenFunction &CGF,
switch (M->getStorageDuration()) {
case SD_FullExpression:
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());
// Put the new global in the same COMDAT the function containing the
// temporary is in.
GV->setComdat(CGF.CurFn->getComdat());
return GV;
}
return CGF.CreateMemTemp(Inner->getType(), "ref.tmp");

case SD_Thread:
Expand Down Expand Up @@ -370,8 +389,9 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) {
// Create and initialize the reference temporary.
llvm::Value *Object = createReferenceTemporary(*this, M, E);
if (auto *Var = dyn_cast<llvm::GlobalVariable>(Object)) {
// If the temporary is a global and has a constant initializer, we may
// have already initialized it.
// If the temporary is a global and has a constant initializer or is a
// constant temporary that we promoted to a global, we may have already
// initialized it.
if (!Var->hasInitializer()) {
Var->setInitializer(CGM.EmitNullConstant(E->getType()));
EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true);
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenCXX/compound-literals.cpp
Expand Up @@ -28,7 +28,7 @@ int f() {

// CHECK-LABEL: define i32 @_Z1gv()
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};

// CHECK: [[A:%[a-z0-9.]+]] = load [2 x i32]*, [2 x i32]** [[V]]
Expand Down
7 changes: 4 additions & 3 deletions clang/test/CodeGenCXX/cxx0x-initializer-array.cpp
Expand Up @@ -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-LABEL: define void @_ZN22ValueInitArrayOfMemPtr1gEv
void g() {
// Test dynamic initialization.
// CHECK-LABEL: define void @_ZN22ValueInitArrayOfMemPtr1gEMNS_1SEi
void g(p ptr) {
// CHECK: store i32 -1,
f(a{});
f(a{ptr});
}
}

Expand Down
18 changes: 13 additions & 5 deletions clang/test/CodeGenCXX/cxx0x-initializer-references.cpp
Expand Up @@ -35,22 +35,30 @@ namespace reference {
// CHECK-NEXT: ret
}

void reference_to_aggregate() {
void reference_to_aggregate(int i) {
// CHECK: getelementptr {{.*}}, i32 0, i32 0
// CHECK-NEXT: store 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
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: store i32 1
// CHECK-NEXT: getelementptr inbounds i32, i32* %{{.*}}, i{{32|64}} 1
// CHECK-NEXT: store i32 2
// 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
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
}
Expand Down
39 changes: 28 additions & 11 deletions clang/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp
Expand Up @@ -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_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], comdat($_ZN7PR204451fILi0EEEvv), align 4
// CHECK: @[[REFTMP2:.*]] = private constant [3 x %{{.*}}] [%{{.*}} { i32 1 }, %{{.*}} { i32 2 }, %{{.*}} { i32 3 }], align 4

// CHECK: appending global


Expand Down Expand Up @@ -215,17 +218,16 @@ void fn9() {

struct haslist1 {
std::initializer_list<int> il;
haslist1();
haslist1(int i);
};

// CHECK-LABEL: define void @_ZN8haslist1C2Ev
haslist1::haslist1()
// CHECK-LABEL: define void @_ZN8haslist1C2Ei
haslist1::haslist1(int i)
// CHECK: alloca [3 x i32]
// CHECK: store i32 1
// CHECK: store i32 %
// CHECK: store i32 2
// CHECK: store i32 3
// CHECK: store i{{32|64}} 3
: il{1, 2, 3}
: il{i, 2, 3}
{
destroyme2 dm2;
}
Expand All @@ -244,16 +246,15 @@ haslist2::haslist2()
// CHECK: call void @_ZN10destroyme1D1Ev
}

void fn10() {
// CHECK-LABEL: define void @_Z4fn10v
void fn10(int i) {
// CHECK-LABEL: define void @_Z4fn10i
// CHECK: alloca [3 x i32]
// CHECK: call noalias i8* @_Znw{{[jm]}}
// CHECK: store i32 1
// CHECK: store i32 %
// CHECK: store i32 2
// CHECK: store i32 3
// CHECK: store i32*
// CHECK: store i{{32|64}} 3
(void) new std::initializer_list<int> {1, 2, 3};
(void) new std::initializer_list<int> {i, 2, 3};
}

void fn11() {
Expand Down Expand Up @@ -462,6 +463,22 @@ namespace PR20445 {
template<int x> void f() { new MyClass({42, 43}); }
template void f<0>();
// 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 @_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)});
}
}

0 comments on commit 3d8aa5c

Please sign in to comment.