// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=thumbv7-apple-ios6.0 -fno-use-cxa-atexit -target-abi apcs-gnu -emit-llvm -std=gnu++98 -o - -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK98 %s
// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=thumbv7-apple-ios6.0 -fno-use-cxa-atexit -target-abi apcs-gnu -emit-llvm -std=gnu++11 -o - -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK11 %s
// CHECK: @_ZZN5test74testEvE1x = internal global i32 0, align 4
// CHECK: @_ZGVZN5test74testEvE1x = internal global i32 0
// CHECK: @_ZZN5test84testEvE1x = internal global [[TEST8A:.*]] zeroinitializer, align 1
// CHECK: @_ZGVZN5test84testEvE1x = internal global i32 0
typedef typeof(sizeof(int)) size_t;
class foo {
public:
foo();
virtual ~foo();
};
class bar : public foo {
public:
bar();
};
// The global dtor needs the right calling conv with -fno-use-cxa-atexit
bar baz;
// PR9593
// Make sure atexit(3) is used for global dtors.
// CHECK: call ptr @_ZN3barC1Ev(
// CHECK-NEXT: call i32 @atexit(ptr @__dtor_baz)
// CHECK-NOT: @_GLOBAL__D_a()
// CHECK-LABEL: define internal void @__dtor_baz()
// CHECK: call ptr @_ZN3barD1Ev(ptr @baz)
// Destructors and constructors must return this.
namespace test1 {
void foo();
struct A {
A(int i) { foo(); }
~A() { foo(); }
void bar() { foo(); }
};
// CHECK-LABEL: define{{.*}} void @_ZN5test14testEv()
void test() {
// CHECK: [[AV:%.*]] = alloca [[A:%.*]], align 1
// CHECK: call ptr @_ZN5test11AC1Ei(ptr {{[^,]*}} [[AV]], i32 10)
// CHECK: invoke void @_ZN5test11A3barEv(ptr {{[^,]*}} [[AV]])
// CHECK: call ptr @_ZN5test11AD1Ev(ptr {{[^,]*}} [[AV]])
// CHECK: ret void
A a = 10;
a.bar();
}
// CHECK: define linkonce_odr ptr @_ZN5test11AC1Ei(ptr {{[^,]*}} returned {{[^,]*}} %this, i32 %i) unnamed_addr
// CHECK: [[THIS:%.*]] = alloca ptr, align 4
// CHECK: store ptr {{.*}}, ptr [[THIS]]
// CHECK: [[THIS1:%.*]] = load ptr, ptr [[THIS]]
// CHECK: {{%.*}} = call ptr @_ZN5test11AC2Ei(
// CHECK: ret ptr [[THIS1]]
// CHECK: define linkonce_odr ptr @_ZN5test11AD1Ev(ptr {{[^,]*}} returned {{[^,]*}} %this) unnamed_addr
// CHECK: [[THIS:%.*]] = alloca ptr, align 4
// CHECK: store ptr {{.*}}, ptr [[THIS]]
// CHECK: [[THIS1:%.*]] = load ptr, ptr [[THIS]]
// CHECK: {{%.*}} = call ptr @_ZN5test11AD2Ev(
// CHECK: ret ptr [[THIS1]]
}
// Awkward virtual cases.
namespace test2 {
void foo();
struct A {
int x;
A(int);
virtual ~A() { foo(); }
};
struct B {
int y;
int z;
B(int);
virtual ~B() { foo(); }
};
struct C : A, virtual B {
int q;
C(int i) : A(i), B(i) { foo(); }
~C() { foo(); }
};
void test() {
C c = 10;
}
// Tests at eof
}
namespace test3 {
struct A {
int x;
~A();
};
void a() {
// CHECK-LABEL: define{{.*}} void @_ZN5test31aEv()
// CHECK: call noalias nonnull ptr @_Znam(i32 48)
// CHECK: store i32 4
// CHECK: store i32 10
A *x = new A[10];
}
void b(int n) {
// CHECK-LABEL: define{{.*}} void @_ZN5test31bEi(
// CHECK: [[N:%.*]] = load i32, ptr
// CHECK: @llvm.umul.with.overflow.i32(i32 [[N]], i32 4)
// CHECK: @llvm.uadd.with.overflow.i32(i32 {{.*}}, i32 8)
// CHECK: [[OR:%.*]] = or i1
// CHECK: [[SZ:%.*]] = select i1 [[OR]]
// CHECK: call noalias nonnull ptr @_Znam(i32 [[SZ]])
// CHECK: store i32 4
// CHECK: store i32 [[N]]
A *x = new A[n];
}
void c() {
// CHECK-LABEL: define{{.*}} void @_ZN5test31cEv()
// CHECK: call noalias nonnull ptr @_Znam(i32 808)
// CHECK: store i32 4
// CHECK: store i32 200
A (*x)[20] = new A[10][20];
}
void d(int n) {
// CHECK-LABEL: define{{.*}} void @_ZN5test31dEi(
// CHECK: [[N:%.*]] = load i32, ptr
// CHECK: @llvm.umul.with.overflow.i32(i32 [[N]], i32 80)
// CHECK: [[NE:%.*]] = mul i32 [[N]], 20
// CHECK: @llvm.uadd.with.overflow.i32(i32 {{.*}}, i32 8)
// CHECK: [[SZ:%.*]] = select
// CHECK: call noalias nonnull ptr @_Znam(i32 [[SZ]])
// CHECK: store i32 4
// CHECK: store i32 [[NE]]
A (*x)[20] = new A[n][20];
}
void e(A *x) {
// CHECK-LABEL: define{{.*}} void @_ZN5test31eEPNS_1AE(
// CHECK: icmp eq {{.*}}, null
// CHECK: getelementptr {{.*}}, i32 -8
// CHECK: getelementptr {{.*}}, i32 4
// CHECK: load
// CHECK98: invoke {{.*}} @_ZN5test31AD1Ev
// CHECK11: call {{.*}} @_ZN5test31AD1Ev
// CHECK: call void @_ZdaPv
delete [] x;
}
void f(A (*x)[20]) {
// CHECK-LABEL: define{{.*}} void @_ZN5test31fEPA20_NS_1AE(
// CHECK: icmp eq {{.*}}, null
// CHECK: getelementptr {{.*}}, i32 -8
// CHECK: getelementptr {{.*}}, i32 4
// CHECK: load
// CHECK98: invoke {{.*}} @_ZN5test31AD1Ev
// CHECK11: call {{.*}} @_ZN5test31AD1Ev
// CHECK: call void @_ZdaPv
delete [] x;
}
}
namespace test4 {
struct A {
int x;
void operator delete[](void *, size_t sz);
};
void a() {
// CHECK-LABEL: define{{.*}} void @_ZN5test41aEv()
// CHECK: call noalias nonnull ptr @_Znam(i32 48)
// CHECK: store i32 4
// CHECK: store i32 10
A *x = new A[10];
}
void b(int n) {
// CHECK-LABEL: define{{.*}} void @_ZN5test41bEi(
// CHECK: [[N:%.*]] = load i32, ptr
// CHECK: @llvm.umul.with.overflow.i32(i32 [[N]], i32 4)
// CHECK: @llvm.uadd.with.overflow.i32(i32 {{.*}}, i32 8)
// CHECK: [[SZ:%.*]] = select
// CHECK: call noalias nonnull ptr @_Znam(i32 [[SZ]])
// CHECK: store i32 4
// CHECK: store i32 [[N]]
A *x = new A[n];
}
void c() {
// CHECK-LABEL: define{{.*}} void @_ZN5test41cEv()
// CHECK: call noalias nonnull ptr @_Znam(i32 808)
// CHECK: store i32 4
// CHECK: store i32 200
A (*x)[20] = new A[10][20];
}
void d(int n) {
// CHECK-LABEL: define{{.*}} void @_ZN5test41dEi(
// CHECK: [[N:%.*]] = load i32, ptr
// CHECK: @llvm.umul.with.overflow.i32(i32 [[N]], i32 80)
// CHECK: [[NE:%.*]] = mul i32 [[N]], 20
// CHECK: @llvm.uadd.with.overflow.i32(i32 {{.*}}, i32 8)
// CHECK: [[SZ:%.*]] = select
// CHECK: call noalias nonnull ptr @_Znam(i32 [[SZ]])
// CHECK: store i32 4
// CHECK: store i32 [[NE]]
A (*x)[20] = new A[n][20];
}
void e(A *x) {
// CHECK-LABEL: define{{.*}} void @_ZN5test41eEPNS_1AE(
// CHECK: [[ALLOC:%.*]] = getelementptr inbounds {{.*}}, i32 -8
// CHECK: getelementptr inbounds {{.*}}, i32 4
// CHECK: [[T0:%.*]] = load i32, ptr
// CHECK: [[T1:%.*]] = mul i32 4, [[T0]]
// CHECK: [[T2:%.*]] = add i32 [[T1]], 8
// CHECK: call void @_ZN5test41AdaEPvm(ptr [[ALLOC]], i32 [[T2]])
delete [] x;
}
void f(A (*x)[20]) {
// CHECK-LABEL: define{{.*}} void @_ZN5test41fEPA20_NS_1AE(
// CHECK: [[ALLOC:%.*]] = getelementptr inbounds {{.*}}, i32 -8
// CHECK: getelementptr inbounds {{.*}}, i32 4
// CHECK: [[T0:%.*]] = load i32, ptr
// CHECK: [[T1:%.*]] = mul i32 4, [[T0]]
// CHECK: [[T2:%.*]] = add i32 [[T1]], 8
// CHECK: call void @_ZN5test41AdaEPvm(ptr [[ALLOC]], i32 [[T2]])
delete [] x;
}
}
namespace test5 {
struct A {
~A();
};
// CHECK-LABEL: define{{.*}} void @_ZN5test54testEPNS_1AE
void test(A *a) {
// CHECK: [[PTR:%.*]] = alloca ptr, align 4
// CHECK-NEXT: store ptr {{.*}}, ptr [[PTR]], align 4
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr [[PTR]], align 4
// CHECK-NEXT: call ptr @_ZN5test51AD1Ev(ptr {{[^,]*}} [[TMP]])
// CHECK-NEXT: ret void
a->~A();
}
}
namespace test6 {
struct A {
virtual ~A();
};
// CHECK-LABEL: define{{.*}} void @_ZN5test64testEPNS_1AE
void test(A *a) {
// CHECK: [[AVAR:%.*]] = alloca ptr, align 4
// CHECK-NEXT: store ptr {{.*}}, ptr [[AVAR]], align 4
// CHECK-NEXT: [[V:%.*]] = load ptr, ptr [[AVAR]], align 4
// CHECK-NEXT: [[ISNULL:%.*]] = icmp eq ptr [[V]], null
// CHECK-NEXT: br i1 [[ISNULL]]
// CHECK: [[T1:%.*]] = load ptr, ptr [[V]]
// CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds ptr, ptr [[T1]], i64 1
// CHECK-NEXT: [[T3:%.*]] = load ptr, ptr [[T2]]
// CHECK-NEXT: call void [[T3]](ptr {{[^,]*}} [[V]])
// CHECK-NEXT: br label
// CHECK: ret void
delete a;
}
}
namespace test7 {
int foo();
// Static and guard tested at top of file
// CHECK-LABEL: define{{.*}} void @_ZN5test74testEv() {{.*}} personality ptr @__gxx_personality_v0
void test() {
// CHECK: [[T0:%.*]] = load atomic i8, ptr @_ZGVZN5test74testEvE1x acquire, align 4
// CHECK-NEXT: [[T1:%.*]] = and i8 [[T0]], 1
// CHECK-NEXT: [[T2:%.*]] = icmp eq i8 [[T1]], 0
// CHECK-NEXT: br i1 [[T2]]
// -> fallthrough, end
// CHECK: [[T3:%.*]] = call i32 @__cxa_guard_acquire(ptr @_ZGVZN5test74testEvE1x)
// CHECK-NEXT: [[T4:%.*]] = icmp ne i32 [[T3]], 0
// CHECK-NEXT: br i1 [[T4]]
// -> fallthrough, end
// CHECK: [[INIT:%.*]] = invoke i32 @_ZN5test73fooEv()
// CHECK: store i32 [[INIT]], ptr @_ZZN5test74testEvE1x, align 4
// CHECK-NEXT: call void @__cxa_guard_release(ptr @_ZGVZN5test74testEvE1x)
// CHECK-NEXT: br label
// -> end
// end:
// CHECK: ret void
static int x = foo();
// CHECK: landingpad { ptr, i32 }
// CHECK-NEXT: cleanup
// CHECK: call void @__cxa_guard_abort(ptr @_ZGVZN5test74testEvE1x)
// CHECK: resume { ptr, i32 }
}
}
namespace test8 {
struct A {
A();
~A();
};
// Static and guard tested at top of file
// CHECK-LABEL: define{{.*}} void @_ZN5test84testEv() {{.*}} personality ptr @__gxx_personality_v0
void test() {
// CHECK: [[T0:%.*]] = load atomic i8, ptr @_ZGVZN5test84testEvE1x acquire, align 4
// CHECK-NEXT: [[T1:%.*]] = and i8 [[T0]], 1
// CHECK-NEXT: [[T2:%.*]] = icmp eq i8 [[T1]], 0
// CHECK-NEXT: br i1 [[T2]]
// -> fallthrough, end
// CHECK: [[T3:%.*]] = call i32 @__cxa_guard_acquire(ptr @_ZGVZN5test84testEvE1x)
// CHECK-NEXT: [[T4:%.*]] = icmp ne i32 [[T3]], 0
// CHECK-NEXT: br i1 [[T4]]
// -> fallthrough, end
// CHECK: [[INIT:%.*]] = invoke ptr @_ZN5test81AC1Ev(ptr {{[^,]*}} @_ZZN5test84testEvE1x)
// FIXME: Here we register a global destructor that
// unconditionally calls the destructor. That's what we've always
// done for -fno-use-cxa-atexit here, but that's really not
// semantically correct at all.
// CHECK: call void @__cxa_guard_release(ptr @_ZGVZN5test84testEvE1x)
// CHECK-NEXT: br label
// -> end
// end:
// CHECK: ret void
static A x;
// CHECK: landingpad { ptr, i32 }
// CHECK-NEXT: cleanup
// CHECK: call void @__cxa_guard_abort(ptr @_ZGVZN5test84testEvE1x)
// CHECK: resume { ptr, i32 }
}
}
// Use a larger-than-mandated array cookie when allocating an
// array whose type is overaligned.
namespace test9 {
class __attribute__((aligned(16))) A {
float data[4];
public:
A();
~A();
};
A *testNew(unsigned n) {
return new A[n];
}
// CHECK: define{{.*}} ptr @_ZN5test97testNewEj(i32
// CHECK: [[N_VAR:%.*]] = alloca i32, align 4
// CHECK: [[N:%.*]] = load i32, ptr [[N_VAR]], align 4
// CHECK-NEXT: [[T0:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[N]], i32 16)
// CHECK-NEXT: [[O0:%.*]] = extractvalue { i32, i1 } [[T0]], 1
// CHECK-NEXT: [[T1:%.*]] = extractvalue { i32, i1 } [[T0]], 0
// CHECK-NEXT: [[T2:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[T1]], i32 16)
// CHECK-NEXT: [[O1:%.*]] = extractvalue { i32, i1 } [[T2]], 1
// CHECK-NEXT: [[OVERFLOW:%.*]] = or i1 [[O0]], [[O1]]
// CHECK-NEXT: [[T3:%.*]] = extractvalue { i32, i1 } [[T2]], 0
// CHECK-NEXT: [[T4:%.*]] = select i1 [[OVERFLOW]], i32 -1, i32 [[T3]]
// CHECK-NEXT: [[ALLOC:%.*]] = call noalias nonnull ptr @_Znam(i32 [[T4]])
// CHECK-NEXT: store i32 16, ptr [[ALLOC]]
// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i32, ptr [[ALLOC]], i32 1
// CHECK-NEXT: store i32 [[N]], ptr [[T1]]
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8, ptr [[ALLOC]], i32 16
// Array allocation follows.
void testDelete(A *array) {
delete[] array;
}
// CHECK-LABEL: define{{.*}} void @_ZN5test910testDeleteEPNS_1AE(
// CHECK: [[BEGIN:%.*]] = load ptr, ptr
// CHECK-NEXT: [[T0:%.*]] = icmp eq ptr [[BEGIN]], null
// CHECK-NEXT: br i1 [[T0]],
// CHECK: [[ALLOC:%.*]] = getelementptr inbounds i8, ptr [[BEGIN]], i32 -16
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i8, ptr [[ALLOC]], i32 4
// CHECK-NEXT: [[N:%.*]] = load i32, ptr [[T0]]
// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[TEST9:%.*]], ptr [[BEGIN]], i32 [[N]]
// CHECK-NEXT: [[T0:%.*]] = icmp eq ptr [[BEGIN]], [[END]]
// CHECK-NEXT: br i1 [[T0]],
// Array deallocation follows.
}
// CHECK: define linkonce_odr ptr @_ZTv0_n12_N5test21CD1Ev(
// CHECK: call ptr @_ZN5test21CD1Ev(
// CHECK: ret ptr undef
// CHECK-LABEL: define linkonce_odr void @_ZTv0_n12_N5test21CD0Ev(
// CHECK: call void @_ZN5test21CD0Ev(
// CHECK: ret void