mostly in avoiding unnecessary work at compile time but also in producing more sensible block orderings. Move the destructor cleanups for local variables over to use lazy cleanups. Eventually all cleanups will do this; for now we have some awkward code duplication. Tell IR generation just to never produce landing pads in -fno-exceptions. This is a much more comprehensive solution to a problem which previously was half-solved by checks in most cleanup-generation spots. llvm-svn: 108270
318 lines
6.5 KiB
C++
318 lines
6.5 KiB
C++
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
|
|
void *f();
|
|
|
|
template <typename T> T* g() {
|
|
if (T* t = f())
|
|
return t;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void h() {
|
|
void *a = g<void>();
|
|
}
|
|
|
|
struct X {
|
|
X();
|
|
X(const X&);
|
|
~X();
|
|
operator bool();
|
|
};
|
|
|
|
struct Y {
|
|
Y();
|
|
~Y();
|
|
};
|
|
|
|
X getX();
|
|
|
|
// CHECK: define void @_Z11if_destructi(
|
|
void if_destruct(int z) {
|
|
// Verify that the condition variable is destroyed at the end of the
|
|
// "if" statement.
|
|
// CHECK: call void @_ZN1XC1Ev
|
|
// CHECK: call zeroext i1 @_ZN1XcvbEv
|
|
if (X x = X()) {
|
|
// CHECK: store i32 18
|
|
z = 18;
|
|
}
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: store i32 17
|
|
z = 17;
|
|
|
|
// CHECK: call void @_ZN1XC1Ev
|
|
if (X x = X())
|
|
Y y;
|
|
// CHECK: br
|
|
// CHECK: call void @_ZN1YC1Ev
|
|
// CHECK: call void @_ZN1YD1Ev
|
|
// CHECK: br
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
|
|
// CHECK: call void @_Z4getXv
|
|
// CHECK: call zeroext i1 @_ZN1XcvbEv
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: br
|
|
if (getX()) { }
|
|
|
|
// CHECK: ret
|
|
}
|
|
|
|
struct ConvertibleToInt {
|
|
ConvertibleToInt();
|
|
~ConvertibleToInt();
|
|
operator int();
|
|
};
|
|
|
|
ConvertibleToInt getConvToInt();
|
|
|
|
void switch_destruct(int z) {
|
|
// CHECK: call void @_ZN16ConvertibleToIntC1Ev
|
|
switch (ConvertibleToInt conv = ConvertibleToInt()) {
|
|
case 0:
|
|
break;
|
|
|
|
default:
|
|
// CHECK: store i32 19
|
|
z = 19;
|
|
break;
|
|
}
|
|
// CHECK: call void @_ZN16ConvertibleToIntD1Ev
|
|
// CHECK: store i32 20
|
|
z = 20;
|
|
|
|
// CHECK: call void @_Z12getConvToIntv
|
|
// CHECK: call i32 @_ZN16ConvertibleToIntcviEv
|
|
// CHECK: call void @_ZN16ConvertibleToIntD1Ev
|
|
switch(getConvToInt()) {
|
|
case 0:
|
|
break;
|
|
}
|
|
// CHECK: store i32 27
|
|
z = 27;
|
|
// CHECK: ret
|
|
}
|
|
|
|
int foo();
|
|
|
|
// CHECK: define void @_Z14while_destructi
|
|
void while_destruct(int z) {
|
|
// CHECK: [[Z:%.*]] = alloca i32
|
|
// CHECK: [[CLEANUPDEST:%.*]] = alloca i32
|
|
while (X x = X()) {
|
|
// CHECK: call void @_ZN1XC1Ev
|
|
// CHECK-NEXT: [[COND:%.*]] = call zeroext i1 @_ZN1XcvbEv
|
|
// CHECK-NEXT: br i1 [[COND]]
|
|
|
|
// Loop-exit staging block.
|
|
// CHECK: store i32 1, i32* [[CLEANUPDEST]]
|
|
// CHECK-NEXT: br
|
|
|
|
// While body.
|
|
// CHECK: store i32 21, i32* [[Z]]
|
|
// CHECK: store i32 2, i32* [[CLEANUPDEST]]
|
|
// CHECK-NEXT: br
|
|
z = 21;
|
|
|
|
// Cleanup.
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK-NEXT: [[DEST:%.*]] = load i32* [[CLEANUPDEST]]
|
|
// CHECK-NEXT: switch i32 [[DEST]]
|
|
}
|
|
|
|
// CHECK: store i32 22, i32* [[Z]]
|
|
z = 22;
|
|
|
|
// CHECK: call void @_Z4getXv
|
|
// CHECK-NEXT: call zeroext i1 @_ZN1XcvbEv
|
|
// CHECK-NEXT: call void @_ZN1XD1Ev
|
|
// CHECK-NEXT: br
|
|
while(getX()) { }
|
|
|
|
// CHECK: store i32 25, i32* [[Z]]
|
|
z = 25;
|
|
|
|
// CHECK: ret
|
|
}
|
|
|
|
// CHECK: define void @_Z12for_destructi(
|
|
void for_destruct(int z) {
|
|
// CHECK: [[Z:%.*]] = alloca i32
|
|
// CHECK: [[XDEST:%.*]] = alloca i32
|
|
// CHECK: [[I:%.*]] = alloca i32
|
|
// CHECK: call void @_ZN1YC1Ev
|
|
// CHECK-NEXT: br
|
|
// -> %for.cond
|
|
|
|
for(Y y = Y(); X x = X(); ++z) {
|
|
// %for.cond: The loop condition.
|
|
// CHECK: call void @_ZN1XC1Ev
|
|
// CHECK-NEXT: [[COND:%.*]] = call zeroext i1 @_ZN1XcvbEv(
|
|
// CHECK-NEXT: br i1 [[COND]]
|
|
// -> %for.body, %for.cond.cleanup
|
|
|
|
// %for.cond.cleanup: Exit cleanup staging.
|
|
// CHECK: store i32 1, i32* [[XDEST]]
|
|
// CHECK-NEXT: br
|
|
// -> %cleanup
|
|
|
|
// %for.body:
|
|
// CHECK: store i32 23, i32* [[Z]]
|
|
// CHECK-NEXT: br
|
|
// -> %for.inc
|
|
z = 23;
|
|
|
|
// %for.inc:
|
|
// CHECK: [[TMP:%.*]] = load i32* [[Z]]
|
|
// CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP]], 1
|
|
// CHECK-NEXT: store i32 [[INC]], i32* [[Z]]
|
|
// CHECK-NEXT: store i32 2, i32* [[XDEST]]
|
|
// CHECK-NEXT: br
|
|
// -> %cleanup
|
|
|
|
// %cleanup: Destroys X.
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK-NEXT: [[YDESTTMP:%.*]] = load i32* [[XDEST]]
|
|
// CHECK-NEXT: switch i32 [[YDESTTMP]]
|
|
// 1 -> %cleanup4, 2 -> %cleanup.cont
|
|
|
|
// %cleanup.cont: (eliminable)
|
|
// CHECK: br
|
|
// -> %for.cond
|
|
|
|
// %cleanup4: Destroys Y.
|
|
// CHECK: call void @_ZN1YD1Ev(
|
|
// CHECK-NEXT: br
|
|
// -> %for.end
|
|
}
|
|
|
|
// %for.end:
|
|
// CHECK: store i32 24
|
|
z = 24;
|
|
|
|
// CHECK-NEXT: store i32 0, i32* [[I]]
|
|
// CHECK-NEXT: br
|
|
// -> %for.cond6
|
|
|
|
// %for.cond6:
|
|
// CHECK: call void @_Z4getXv
|
|
// CHECK-NEXT: call zeroext i1 @_ZN1XcvbEv
|
|
// CHECK-NEXT: call void @_ZN1XD1Ev
|
|
// CHECK-NEXT: br
|
|
// -> %for.body10, %for.end16
|
|
|
|
// %for.body10:
|
|
// CHECK: br
|
|
// -> %for.inc11
|
|
|
|
// %for.inc11:
|
|
// CHECK: call void @_Z4getXv
|
|
// CHECK-NEXT: load i32* [[I]]
|
|
// CHECK-NEXT: add
|
|
// CHECK-NEXT: store
|
|
// CHECK-NEXT: call void @_ZN1XD1Ev
|
|
// CHECK-NEXT: br
|
|
// -> %for.cond6
|
|
int i = 0;
|
|
for(; getX(); getX(), ++i) { }
|
|
|
|
// %for.end16
|
|
// CHECK: store i32 26
|
|
z = 26;
|
|
|
|
// CHECK-NEXT: ret void
|
|
}
|
|
|
|
void do_destruct(int z) {
|
|
// CHECK: define void @_Z11do_destruct
|
|
do {
|
|
// CHECK: store i32 77
|
|
z = 77;
|
|
// CHECK: call void @_Z4getXv
|
|
// CHECK: call zeroext i1 @_ZN1XcvbEv
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: br
|
|
} while (getX());
|
|
// CHECK: store i32 99
|
|
z = 99;
|
|
// CHECK: ret
|
|
}
|
|
|
|
int f(X);
|
|
|
|
template<typename T>
|
|
int instantiated(T x) {
|
|
int result;
|
|
|
|
// CHECK: call void @_ZN1XC1ERKS_
|
|
// CHECK: call i32 @_Z1f1X
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: br
|
|
// CHECK: store i32 2
|
|
// CHECK: br
|
|
// CHECK: store i32 3
|
|
if (f(x)) { result = 2; } else { result = 3; }
|
|
|
|
// CHECK: call void @_ZN1XC1ERKS_
|
|
// CHECK: call i32 @_Z1f1X
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: br
|
|
// CHECK: store i32 4
|
|
// CHECK: br
|
|
while (f(x)) { result = 4; }
|
|
|
|
// CHECK: call void @_ZN1XC1ERKS_
|
|
// CHECK: call i32 @_Z1f1X
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: br
|
|
// CHECK: store i32 6
|
|
// CHECK: br
|
|
// CHECK: call void @_ZN1XC1ERKS_
|
|
// CHECK: call i32 @_Z1f1X
|
|
// CHECK: store i32 5
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: br
|
|
for (; f(x); f(x), result = 5) {
|
|
result = 6;
|
|
}
|
|
|
|
// CHECK: call void @_ZN1XC1ERKS_
|
|
// CHECK: call i32 @_Z1f1X
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: switch i32
|
|
// CHECK: store i32 7
|
|
// CHECK: store i32 8
|
|
switch (f(x)) {
|
|
case 0:
|
|
result = 7;
|
|
break;
|
|
|
|
case 1:
|
|
result = 8;
|
|
}
|
|
|
|
// CHECK: store i32 9
|
|
// CHECK: br
|
|
// CHECK: call void @_ZN1XC1ERKS_
|
|
// CHECK: call i32 @_Z1f1X
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: br
|
|
do {
|
|
result = 9;
|
|
} while (f(x));
|
|
|
|
// CHECK: store i32 10
|
|
// CHECK: call void @_ZN1XC1ERKS_
|
|
// CHECK: call zeroext i1 @_ZN1XcvbEv
|
|
// CHECK: call void @_ZN1XD1Ev
|
|
// CHECK: br
|
|
do {
|
|
result = 10;
|
|
} while (X(x));
|
|
|
|
// CHECK: ret i32
|
|
return result;
|
|
}
|
|
|
|
template int instantiated(X);
|