Takashi Idobe cbe9891b44
[Clang] fix bad codegen from constexpr structured bindings (#186594)
Resolves: https://github.com/llvm/llvm-project/issues/164150

C++26 allows for constexpr packs in structured bindings. This is a new
feature (the code doesn't compile on lower the -std=c++26) and so was
previously unhandled in clang.

This makes clang aware of packs and handle them as one constant unit
instead of materializing them as separate mutable reference temporaries
allowing llvm to optimize them.

This turns the example code from the issue into this as you would expect
without compiling for zen 5 (the good codegen described).

```asm
  movq    %rdi, %rax
  movups  (%rsi), %xmm0
  movups  %xmm0, (%rdi)
  movups  (%rdx), %xmm0
  movups  %xmm0, 16(%rdi)
  retq
```
2026-03-27 12:07:23 +08:00

923 lines
28 KiB
C++

// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 | FileCheck %s -check-prefixes=CHECK,NULL-INVALID,CHECK-CXX11
// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++17 | FileCheck %s -check-prefixes=CHECK,NULL-INVALID,CHECK-CXX17
// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 -fno-delete-null-pointer-checks | FileCheck %s -check-prefixes=CHECK,NULL-VALID,CHECK-CXX11
namespace PR16263 {
const unsigned int n = 1234;
extern const int &r = (const int&)n;
// CHECK: @_ZGRN7PR162631rE_ = internal constant i32 1234,
// CHECK: @_ZN7PR162631rE ={{.*}} constant ptr @_ZGRN7PR162631rE_,
extern const int &s = reinterpret_cast<const int&>(n);
// CHECK: @_ZN7PR16263L1nE = internal constant i32 1234, align 4
// CHECK: @_ZN7PR162631sE ={{.*}} constant ptr @_ZN7PR16263L1nE, align 8
struct A { int n; };
struct B { int n; };
struct C : A, B {};
extern const A &&a = (A&&)(A&&)(C&&)(C{});
// CHECK: @_ZGRN7PR162631aE_ = internal global {{.*}} zeroinitializer,
// CHECK: @_ZN7PR162631aE ={{.*}} constant {{.*}} @_ZGRN7PR162631aE_
extern const int &&t = ((B&&)C{}).n;
// CHECK: @_ZGRN7PR162631tE_ = internal global {{.*}} zeroinitializer,
// CHECK: @_ZN7PR162631tE ={{.*}} constant ptr {{.*}} @_ZGRN7PR162631tE_, {{.*}} 4
struct D { double d; C c; };
extern const int &&u = (123, static_cast<B&&>(0, ((D&&)D{}).*&D::c).n);
// CHECK: @_ZGRN7PR162631uE_ = internal global {{.*}} zeroinitializer
// CHECK: @_ZN7PR162631uE ={{.*}} constant ptr {{.*}} @_ZGRN7PR162631uE_, {{.*}} 12
}
namespace PR20227 {
struct A { ~A(); };
struct B { virtual ~B(); };
struct C : B {};
A &&a = dynamic_cast<A&&>(A{});
// CHECK: @_ZGRN7PR202271aE_ = internal global
B &&b = dynamic_cast<C&&>(dynamic_cast<B&&>(C{}));
// CHECK: @_ZGRN7PR202271bE_ = internal global
B &&c = static_cast<C&&>(static_cast<B&&>(C{}));
// CHECK: @_ZGRN7PR202271cE_ = internal global
}
namespace BraceInit {
typedef const int &CIR;
CIR x = CIR{3};
// CHECK-CXX11: @_ZGRN9BraceInit1xE_ = internal constant i32 3
// CHECK-CXX17: @_ZGRN9BraceInit1xE_ = internal constant i32 3
// CHECK: @_ZN9BraceInit1xE ={{.*}} constant ptr @_ZGRN9BraceInit1xE_
}
namespace RefTempSubobject {
struct SelfReferential {
int *p = ints;
int ints[3] = {1, 2, 3};
};
// CHECK: @_ZGRN16RefTempSubobject2srE_ = internal constant { ptr, [3 x i32] } { {{.*}} getelementptr {{.*}} @_ZGRN16RefTempSubobject2srE_, {{.*}}, [3 x i32] [i32 1, i32 2, i32 3] }
// CHECK: @_ZN16RefTempSubobject2srE = constant {{.*}} @_ZGRN16RefTempSubobject2srE_
constexpr const SelfReferential &sr = SelfReferential();
}
namespace Vector {
typedef __attribute__((vector_size(16))) int vi4a;
typedef __attribute__((ext_vector_type(4))) int vi4b;
struct S {
vi4a v;
vi4b w;
};
int &&r = S().v[1];
// CHECK: @_ZGRN6Vector1rE_ = internal global i32 0, align 4
// CHECK: @_ZN6Vector1rE = constant ptr @_ZGRN6Vector1rE_, align 8
int &&s = S().w[1];
// CHECK: @_ZGRN6Vector1sE_ = internal global i32 0, align 4
// CHECK: @_ZN6Vector1sE = constant ptr @_ZGRN6Vector1sE_, align 8
int &&t = S().w.y;
// CHECK: @_ZGRN6Vector1tE_ = internal global i32 0, align 4
// CHECK: @_ZN6Vector1tE = constant ptr @_ZGRN6Vector1tE_, align 8
}
struct A {
A();
~A();
void f();
};
void f1() {
// CHECK: call void @_ZN1AC1Ev
// CHECK: call void @_ZN1AD1Ev
(void)A();
// CHECK: call void @_ZN1AC1Ev
// CHECK: call void @_ZN1AD1Ev
A().f();
}
// Function calls
struct B {
B();
~B();
};
B g();
void f2() {
// CHECK-NOT: call void @_ZN1BC1Ev
// CHECK: call void @_ZN1BD1Ev
(void)g();
}
// Member function calls
struct C {
C();
~C();
C f();
};
void f3() {
// CHECK: call void @_ZN1CC1Ev
// CHECK: call void @_ZN1CD1Ev
// CHECK: call void @_ZN1CD1Ev
C().f();
}
// Function call operator
struct D {
D();
~D();
D operator()();
};
void f4() {
// CHECK: call void @_ZN1DC1Ev
// CHECK: call void @_ZN1DD1Ev
// CHECK: call void @_ZN1DD1Ev
D()();
}
// Overloaded operators
struct E {
E();
~E();
E operator+(const E&);
E operator!();
};
void f5() {
// CHECK: call void @_ZN1EC1Ev
// CHECK: call void @_ZN1EC1Ev
// CHECK: call void @_ZN1ED1Ev
// CHECK: call void @_ZN1ED1Ev
// CHECK: call void @_ZN1ED1Ev
E() + E();
// CHECK: call void @_ZN1EC1Ev
// CHECK: call void @_ZN1ED1Ev
// CHECK: call void @_ZN1ED1Ev
!E();
}
struct F {
F();
~F();
F& f();
};
void f6() {
// CHECK: call void @_ZN1FC1Ev
// CHECK: call void @_ZN1FD1Ev
F().f();
}
struct G {
G();
G(A);
~G();
operator A();
};
void a(const A&);
void f7() {
// CHECK: call void @_ZN1AC1Ev
// CHECK: call void @_Z1aRK1A
// CHECK: call void @_ZN1AD1Ev
a(A());
// CHECK: call void @_ZN1GC1Ev
// CHECK: call void @_ZN1Gcv1AEv
// CHECK: call void @_Z1aRK1A
// CHECK: call void @_ZN1AD1Ev
// CHECK: call void @_ZN1GD1Ev
a(G());
}
namespace PR5077 {
struct A {
A();
~A();
int f();
};
void f();
int g(const A&);
struct B {
int a1;
int a2;
B();
~B();
};
B::B()
// CHECK: call void @_ZN6PR50771AC1Ev
// CHECK: call noundef i32 @_ZN6PR50771A1fEv
// CHECK: call void @_ZN6PR50771AD1Ev
: a1(A().f())
// CHECK: call void @_ZN6PR50771AC1Ev
// CHECK: call noundef i32 @_ZN6PR50771gERKNS_1AE
// CHECK: call void @_ZN6PR50771AD1Ev
, a2(g(A()))
{
// CHECK: call void @_ZN6PR50771fEv
f();
}
}
A f8() {
// CHECK: call void @_ZN1AC1Ev
// CHECK-NOT: call void @_ZN1AD1Ev
return A();
// CHECK: ret void
}
struct H {
H();
~H();
H(const H&);
};
void f9(H h) {
// CHECK: call void @_ZN1HC1Ev
// CHECK: call void @_Z2f91H
// CHECK: call void @_ZN1HD1Ev
f9(H());
// CHECK: call void @_ZN1HC1ERKS_
// CHECK: call void @_Z2f91H
// CHECK: call void @_ZN1HD1Ev
f9(h);
}
void f10(const H&);
void f11(H h) {
// CHECK: call void @_ZN1HC1Ev
// CHECK: call void @_Z3f10RK1H
// CHECK: call void @_ZN1HD1Ev
f10(H());
// CHECK: call void @_Z3f10RK1H
// CHECK-NOT: call void @_ZN1HD1Ev
// CHECK: ret void
f10(h);
}
// PR5808
struct I {
I(const char *);
~I();
};
// CHECK: _Z3f12v
I f12() {
// CHECK: call void @_ZN1IC1EPKc
// CHECK-NOT: call void @_ZN1ID1Ev
// CHECK: ret void
return "Hello";
}
// PR5867
namespace PR5867 {
struct S {
S();
S(const S &);
~S();
};
void f(S, int);
// CHECK-LABEL: define{{.*}} void @_ZN6PR58671gEv
void g() {
// CHECK: call void @_ZN6PR58671SC1Ev
// CHECK-NEXT: call void @_ZN6PR58671fENS_1SEi
// CHECK-NEXT: call void @_ZN6PR58671SD1Ev
// CHECK-NEXT: ret void
(f)(S(), 0);
}
// CHECK-LABEL: define linkonce_odr void @_ZN6PR58672g2IiEEvT_
template<typename T>
void g2(T) {
// CHECK: call void @_ZN6PR58671SC1Ev
// CHECK-NEXT: call void @_ZN6PR58671fENS_1SEi
// CHECK-NEXT: call void @_ZN6PR58671SD1Ev
// CHECK-NEXT: ret void
(f)(S(), 0);
}
void h() {
g2(17);
}
}
// PR6199
namespace PR6199 {
struct A { ~A(); };
struct B { operator A(); };
// CHECK-LABEL: define weak_odr void @_ZN6PR61992f2IiEENS_1AET_
template<typename T> A f2(T) {
B b;
// CHECK: call void @_ZN6PR61991BcvNS_1AEEv
// CHECK-NEXT: ret void
return b;
}
template A f2<int>(int);
}
namespace T12 {
struct A {
A();
~A();
int f();
};
int& f(int);
// CHECK-LABEL: define{{.*}} void @_ZN3T121gEv
void g() {
// CHECK: call void @_ZN3T121AC1Ev
// CHECK-NEXT: call noundef i32 @_ZN3T121A1fEv(
// CHECK-NEXT: call noundef {{(nonnull )?}}align {{[0-9]+}} dereferenceable({{[0-9]+}}) ptr @_ZN3T121fEi(
// CHECK-NEXT: call void @_ZN3T121AD1Ev(
int& i = f(A().f());
}
}
namespace PR6648 {
struct B {
~B();
};
B foo;
struct D;
D& zed(B);
void foobar() {
// NULL-INVALID: call noundef nonnull align 1 ptr @_ZN6PR66483zedENS_1BE
// NULL-VALID: call noundef align 1 ptr @_ZN6PR66483zedENS_1BE
zed(foo);
}
}
namespace UserConvertToValue {
struct X {
X(int);
X(const X&);
~X();
};
void f(X);
// CHECK: void @_ZN18UserConvertToValue1gEv()
void g() {
// CHECK: call void @_ZN18UserConvertToValue1XC1Ei
// CHECK: call void @_ZN18UserConvertToValue1fENS_1XE
// CHECK: call void @_ZN18UserConvertToValue1XD1Ev
// CHECK: ret void
f(1);
}
}
namespace PR7556 {
struct A { ~A(); };
struct B { int i; ~B(); };
struct C { int C::*pm; ~C(); };
// CHECK-LABEL: define{{.*}} void @_ZN6PR75563fooEv()
void foo() {
// CHECK: call void @_ZN6PR75561AD1Ev
A();
// CHECK: call void @llvm.memset.p0.i64
// CHECK: call void @_ZN6PR75561BD1Ev
B();
// CHECK: call void @llvm.memcpy.p0.p0.i64
// CHECK: call void @_ZN6PR75561CD1Ev
C();
// CHECK-NEXT: ret void
}
}
namespace Elision {
struct A {
A(); A(const A &); ~A();
void *p;
void foo() const;
};
void foo();
A fooA();
void takeA(A a);
// CHECK-LABEL: define{{.*}} void @_ZN7Elision5test0Ev()
void test0() {
// CHECK: [[I:%.*]] = alloca [[A:%.*]], align 8
// CHECK-NEXT: [[J:%.*]] = alloca [[A]], align 8
// CHECK-NEXT: [[T0:%.*]] = alloca [[A]], align 8
// CHECK-NEXT: [[K:%.*]] = alloca [[A]], align 8
// CHECK-NEXT: [[T1:%.*]] = alloca [[A]], align 8
// CHECK-NEXT: call void @_ZN7Elision3fooEv()
// CHECK-NEXT: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[I]])
A i = (foo(), A());
// CHECK-NEXT: call void @_ZN7Elision4fooAEv(ptr dead_on_unwind writable sret([[A]]) align 8 [[T0]])
// CHECK-NEXT: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[J]])
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[T0]])
A j = (fooA(), A());
// CHECK-NEXT: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[T1]])
// CHECK-NEXT: call void @_ZN7Elision4fooAEv(ptr dead_on_unwind writable sret([[A]]) align 8 [[K]])
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[T1]])
A k = (A(), fooA());
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[K]])
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[J]])
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[I]])
}
// CHECK-LABEL: define{{.*}} void @_ZN7Elision5test1EbNS_1AE(
void test1(bool c, A x) {
// CHECK: [[I:%.*]] = alloca [[A]], align 8
// CHECK-NEXT: [[J:%.*]] = alloca [[A]], align 8
// CHECK: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[I]])
// CHECK: call void @_ZN7Elision1AC1ERKS0_(ptr {{[^,]*}} [[I]], ptr noundef {{(nonnull )?}}align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[X:%.*]])
A i = (c ? A() : x);
// CHECK: call void @_ZN7Elision1AC1ERKS0_(ptr {{[^,]*}} [[J]], ptr noundef {{(nonnull )?}}align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[X]])
// CHECK: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[J]])
A j = (c ? x : A());
// CHECK: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[J]])
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[I]])
}
// CHECK: define{{.*}} void @_ZN7Elision5test2Ev(ptr dead_on_unwind noalias writable sret([[A]]) align 8
A test2() {
// CHECK: call void @_ZN7Elision3fooEv()
// CHECK-NEXT: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[RET:%.*]])
// CHECK-NEXT: ret void
return (foo(), A());
}
// CHECK: define{{.*}} void @_ZN7Elision5test3EiNS_1AE(ptr dead_on_unwind noalias writable sret([[A]]) align 8
A test3(int v, A x) {
if (v < 5)
// CHECK: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[RET:%.*]])
// CHECK: call void @_ZN7Elision1AC1ERKS0_(ptr {{[^,]*}} [[RET]], ptr noundef {{(nonnull )?}}align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[X:%.*]])
return (v < 0 ? A() : x);
else
// CHECK: call void @_ZN7Elision1AC1ERKS0_(ptr {{[^,]*}} [[RET]], ptr noundef {{(nonnull )?}}align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[X]])
// CHECK: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[RET]])
return (v > 10 ? x : A());
// CHECK: ret void
}
// CHECK-LABEL: define{{.*}} void @_ZN7Elision5test4Ev()
void test4() {
// CHECK: [[X:%.*]] = alloca [[A]], align 8
// CHECK-NEXT: [[XS:%.*]] = alloca [2 x [[A]]], align 16
// CHECK-NEXT: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[X]])
A x;
// CHECK-NEXT: call void @_ZN7Elision1AC1Ev(ptr {{[^,]*}} [[XS]])
// CHECK-NEXT: [[XS1:%.*]] = getelementptr inbounds [[A]], ptr [[XS]], i64 1
// CHECK-NEXT: call void @_ZN7Elision1AC1ERKS0_(ptr {{[^,]*}} [[XS1]], ptr noundef {{(nonnull )?}}align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[X]])
A xs[] = { A(), x };
// CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [2 x [[A]]], ptr [[XS]], i32 0, i32 0
// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]], ptr [[BEGIN]], i64 2
// CHECK-NEXT: br label
// CHECK: [[AFTER:%.*]] = phi ptr
// CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]], ptr [[AFTER]], i64 -1
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[CUR]])
// CHECK-NEXT: [[T0:%.*]] = icmp eq ptr [[CUR]], [[BEGIN]]
// CHECK-NEXT: br i1 [[T0]],
// CHECK: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[X]])
}
// CHECK: define{{.*}} void @_ZN7Elision5test5Ev(ptr dead_on_unwind noalias writable sret([[A]]) align 8
struct B { A a; B(); };
A test5() {
// CHECK: [[AT0:%.*]] = alloca [[A]], align 8
// CHECK-NEXT: [[BT0:%.*]] = alloca [[B:%.*]], align 8
// CHECK-NEXT: [[X:%.*]] = alloca [[A]], align 8
// CHECK-NEXT: [[BT1:%.*]] = alloca [[B]], align 8
// CHECK-NEXT: [[BT2:%.*]] = alloca [[B]], align 8
// CHECK: call void @_ZN7Elision1BC1Ev(ptr {{[^,]*}} [[BT0]])
// CHECK-NEXT: [[AM:%.*]] = getelementptr inbounds nuw [[B]], ptr [[BT0]], i32 0, i32 0
// CHECK-NEXT: call void @_ZN7Elision1AC1ERKS0_(ptr {{[^,]*}} [[AT0]], ptr noundef {{(nonnull )?}}align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[AM]])
// CHECK-NEXT: call void @_ZN7Elision5takeAENS_1AE(ptr noundef [[AT0]])
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[AT0]])
// CHECK-NEXT: call void @_ZN7Elision1BD1Ev(ptr {{[^,]*}} [[BT0]])
takeA(B().a);
// CHECK-NEXT: call void @_ZN7Elision1BC1Ev(ptr {{[^,]*}} [[BT1]])
// CHECK-NEXT: [[AM:%.*]] = getelementptr inbounds nuw [[B]], ptr [[BT1]], i32 0, i32 0
// CHECK-NEXT: call void @_ZN7Elision1AC1ERKS0_(ptr {{[^,]*}} [[X]], ptr noundef {{(nonnull )?}}align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[AM]])
// CHECK-NEXT: call void @_ZN7Elision1BD1Ev(ptr {{[^,]*}} [[BT1]])
A x = B().a;
// CHECK-NEXT: call void @_ZN7Elision1BC1Ev(ptr {{[^,]*}} [[BT2]])
// CHECK-NEXT: [[AM:%.*]] = getelementptr inbounds nuw [[B]], ptr [[BT2]], i32 0, i32 0
// CHECK-NEXT: call void @_ZN7Elision1AC1ERKS0_(ptr {{[^,]*}} [[RET:%.*]], ptr noundef {{(nonnull )?}}align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[AM]])
// CHECK-NEXT: call void @_ZN7Elision1BD1Ev(ptr {{[^,]*}} [[BT2]])
return B().a;
// CHECK: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[X]])
}
// Reduced from webkit.
// CHECK: define{{.*}} void @_ZN7Elision5test6EPKNS_1CE(ptr
struct C { operator A() const; };
void test6(const C *x) {
// CHECK: [[T0:%.*]] = alloca [[A]], align 8
// CHECK: [[X:%.*]] = load ptr, ptr {{%.*}}, align 8
// CHECK-NEXT: call void @_ZNK7Elision1CcvNS_1AEEv(ptr dead_on_unwind writable sret([[A]]) align 8 [[T0]], ptr {{[^,]*}} [[X]])
// CHECK-NEXT: call void @_ZNK7Elision1A3fooEv(ptr {{[^,]*}} [[T0]])
// CHECK-NEXT: call void @_ZN7Elision1AD1Ev(ptr {{[^,]*}} [[T0]])
// CHECK-NEXT: ret void
A(*x).foo();
}
}
namespace PR8623 {
struct A { A(int); ~A(); };
// CHECK-LABEL: define{{.*}} void @_ZN6PR86233fooEb(
void foo(bool b) {
// CHECK: [[TMP:%.*]] = alloca [[A:%.*]], align 1
// CHECK-NEXT: [[LCONS:%.*]] = alloca i1
// CHECK-NEXT: [[RCONS:%.*]] = alloca i1
// CHECK: store i1 false, ptr [[LCONS]]
// CHECK-NEXT: store i1 false, ptr [[RCONS]]
// CHECK-NEXT: br i1
// CHECK: call void @_ZN6PR86231AC1Ei(ptr {{[^,]*}} [[TMP]], i32 noundef 2)
// CHECK-NEXT: store i1 true, ptr [[LCONS]]
// CHECK-NEXT: br label
// CHECK: call void @_ZN6PR86231AC1Ei(ptr {{[^,]*}} [[TMP]], i32 noundef 3)
// CHECK-NEXT: store i1 true, ptr [[RCONS]]
// CHECK-NEXT: br label
// CHECK: load i1, ptr [[RCONS]]
// CHECK-NEXT: br i1
// CHECK: call void @_ZN6PR86231AD1Ev(ptr {{[^,]*}} [[TMP]])
// CHECK-NEXT: br label
// CHECK: load i1, ptr [[LCONS]]
// CHECK-NEXT: br i1
// CHECK: call void @_ZN6PR86231AD1Ev(ptr {{[^,]*}} [[TMP]])
// CHECK-NEXT: br label
// CHECK: ret void
b ? A(2) : A(3);
}
}
namespace PR11365 {
struct A { A(); ~A(); };
// CHECK-LABEL: define{{.*}} void @_ZN7PR113653fooEv(
void foo() {
// CHECK: [[BEGIN:%.*]] = getelementptr inbounds [3 x [[A:%.*]]], ptr {{.*}}, i32 0, i32 0
// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]], ptr [[BEGIN]], i64 3
// CHECK-NEXT: br label
// CHECK: [[PHI:%.*]] = phi
// CHECK-NEXT: [[ELEM:%.*]] = getelementptr inbounds [[A]], ptr [[PHI]], i64 -1
// CHECK-NEXT: call void @_ZN7PR113651AD1Ev(ptr {{[^,]*}} [[ELEM]])
// CHECK-NEXT: icmp eq ptr [[ELEM]], [[BEGIN]]
// CHECK-NEXT: br i1
(void) (A [3]) {};
}
}
namespace AssignmentOp {
struct A { ~A(); };
struct B { A operator=(const B&); };
struct C : B { B b1, b2; };
// CHECK-LABEL: define{{.*}} void @_ZN12AssignmentOp1fE
void f(C &c1, const C &c2) {
// CHECK: call {{.*}} @_ZN12AssignmentOp1CaSERKS0_(
c1 = c2;
}
// Ensure that each 'A' temporary is destroyed before the next subobject is
// copied.
// CHECK: define {{.*}} @_ZN12AssignmentOp1CaSERKS0_(
// CHECK: call {{.*}} @_ZN12AssignmentOp1BaSERKS
// CHECK: call {{.*}} @_ZN12AssignmentOp1AD1Ev(
// CHECK: call {{.*}} @_ZN12AssignmentOp1BaSERKS
// CHECK: call {{.*}} @_ZN12AssignmentOp1AD1Ev(
// CHECK: call {{.*}} @_ZN12AssignmentOp1BaSERKS
// CHECK: call {{.*}} @_ZN12AssignmentOp1AD1Ev(
}
namespace BindToSubobject {
struct A {
A();
~A();
int a;
};
void f(), g();
// CHECK: call void @_ZN15BindToSubobject1AC1Ev({{.*}} @_ZGRN15BindToSubobject1aE_)
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN15BindToSubobject1AD1Ev, ptr @_ZGRN15BindToSubobject1aE_, ptr @__dso_handle)
// CHECK: store ptr @_ZGRN15BindToSubobject1aE_, ptr @_ZN15BindToSubobject1aE, align 8
int &&a = A().a;
// CHECK: call void @_ZN15BindToSubobject1fEv()
// CHECK: call void @_ZN15BindToSubobject1AC1Ev({{.*}} @_ZGRN15BindToSubobject1bE_)
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN15BindToSubobject1AD1Ev, ptr @_ZGRN15BindToSubobject1bE_, ptr @__dso_handle)
// CHECK: store ptr @_ZGRN15BindToSubobject1bE_, ptr @_ZN15BindToSubobject1bE, align 8
int &&b = (f(), A().a);
int A::*h();
// CHECK: call void @_ZN15BindToSubobject1fEv()
// CHECK: call void @_ZN15BindToSubobject1gEv()
// CHECK: call void @_ZN15BindToSubobject1AC1Ev({{.*}} @_ZGRN15BindToSubobject1cE_)
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN15BindToSubobject1AD1Ev, ptr @_ZGRN15BindToSubobject1cE_, ptr @__dso_handle)
// CHECK: call {{.*}} @_ZN15BindToSubobject1hE
// CHECK: getelementptr
// CHECK: store ptr {{.*}}, ptr @_ZN15BindToSubobject1cE, align 8
int &&c = (f(), (g(), A().*h()));
struct B {
int padding;
A a;
};
// CHECK: call void @_ZN15BindToSubobject1BC1Ev({{.*}} @_ZGRN15BindToSubobject1dE_)
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN15BindToSubobject1BD1Ev, ptr @_ZGRN15BindToSubobject1dE_, ptr @__dso_handle)
// CHECK: call {{.*}} @_ZN15BindToSubobject1hE
// CHECK: getelementptr {{.*}} getelementptr
// CHECK: store ptr {{.*}}, ptr @_ZN15BindToSubobject1dE, align 8
int &&d = (B().a).*h();
}
namespace Bitfield {
struct S { int a : 5; ~S(); };
// Do not lifetime extend the S() temporary here.
// CHECK: alloca
// CHECK: call {{.*}}memset
// CHECK: store i32 {{.*}}, ptr @_ZGRN8Bitfield1rE_
// CHECK: call void @_ZN8Bitfield1SD1
// CHECK: store ptr @_ZGRN8Bitfield1rE_, ptr @_ZN8Bitfield1rE, align 8
int &&r = S().a;
}
namespace ImplicitTemporaryCleanup {
struct A { A(int); ~A(); };
void g();
// CHECK-LABEL: define{{.*}} void @_ZN24ImplicitTemporaryCleanup1fEv(
void f() {
// CHECK: call {{.*}} @_ZN24ImplicitTemporaryCleanup1AC1Ei(
A &&a = 0;
// CHECK: call {{.*}} @_ZN24ImplicitTemporaryCleanup1gEv(
g();
// CHECK: call {{.*}} @_ZN24ImplicitTemporaryCleanup1AD1Ev(
}
}
namespace MultipleExtension {
struct A { A(); ~A(); };
struct B { B(); ~B(); };
struct C { C(); ~C(); };
struct D { D(); ~D(); int n; C c; };
struct E { const A &a; B b; const C &c; ~E(); };
E &&e1 = { A(), B(), D().c };
// CHECK: call void @_ZN17MultipleExtension1AC1Ev({{.*}} @[[TEMPA:_ZGRN17MultipleExtension2e1E.*]])
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1AD1Ev, {{.*}} @[[TEMPA]]
// CHECK: store {{.*}} @[[TEMPA]], {{.*}} @[[TEMPE:_ZGRN17MultipleExtension2e1E.*]],
// CHECK: call void @_ZN17MultipleExtension1BC1Ev({{.*}} getelementptr inbounds nuw (i8, ptr @[[TEMPE]], i64 8))
// CHECK: call void @_ZN17MultipleExtension1DC1Ev({{.*}} @[[TEMPD:_ZGRN17MultipleExtension2e1E.*]])
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1DD1Ev, {{.*}} @[[TEMPD]]
// CHECK: store {{.*}} getelementptr inbounds nuw (i8, ptr @[[TEMPD]], i64 4), {{.*}} getelementptr inbounds nuw (i8, ptr @[[TEMPE]], i64 16)
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1ED1Ev, {{.*}} @[[TEMPE]]
// CHECK: store {{.*}} @[[TEMPE]], ptr @_ZN17MultipleExtension2e1E, align 8
E e2 = { A(), B(), D().c };
// CHECK: call void @_ZN17MultipleExtension1AC1Ev({{.*}} @[[TEMPA:_ZGRN17MultipleExtension2e2E.*]])
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1AD1Ev, {{.*}} @[[TEMPA]]
// CHECK: store {{.*}} @[[TEMPA]], {{.*}} @[[E:_ZN17MultipleExtension2e2E]]
// CHECK: call void @_ZN17MultipleExtension1BC1Ev({{.*}} getelementptr inbounds nuw (i8, ptr @[[E]], i64 8))
// CHECK: call void @_ZN17MultipleExtension1DC1Ev({{.*}} @[[TEMPD:_ZGRN17MultipleExtension2e2E.*]])
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1DD1Ev, {{.*}} @[[TEMPD]]
// CHECK: store {{.*}} getelementptr inbounds nuw (i8, ptr @[[TEMPD]], i64 4), {{.*}} getelementptr inbounds nuw (i8, ptr @[[E]], i64 16)
// CHECK: call i32 @__cxa_atexit({{.*}} @_ZN17MultipleExtension1ED1Ev, {{.*}} @[[E]]
void g();
// CHECK: define{{.*}} void @[[NS:_ZN17MultipleExtension]]1fEv(
void f() {
E &&e1 = { A(), B(), D().c };
// CHECK: %[[TEMPE1_A:.*]] = getelementptr inbounds {{.*}} %[[TEMPE1:.*]], i32 0, i32 0
// CHECK: call void @[[NS]]1AC1Ev({{.*}} %[[TEMPA1:.*]])
// CHECK: store {{.*}} %[[TEMPA1]], {{.*}} %[[TEMPE1_A]]
// CHECK: %[[TEMPE1_B:.*]] = getelementptr inbounds {{.*}} %[[TEMPE1]], i64 8
// CHECK: call void @[[NS]]1BC1Ev({{.*}} %[[TEMPE1_B]])
// CHECK: %[[TEMPE1_C:.*]] = getelementptr inbounds {{.*}} %[[TEMPE1]], i32 0, i32 2
// CHECK: call void @[[NS]]1DC1Ev({{.*}} %[[TEMPD1:.*]])
// CHECK: %[[TEMPD1_C:.*]] = getelementptr inbounds {{.*}} %[[TEMPD1]], i64 4
// CHECK: store {{.*}} %[[TEMPD1_C]], {{.*}} %[[TEMPE1_C]]
// CHECK: store {{.*}} %[[TEMPE1]], {{.*}} %[[E1:.*]]
g();
// CHECK: call void @[[NS]]1gEv()
E e2 = { A(), B(), D().c };
// CHECK: %[[TEMPE2_A:.*]] = getelementptr inbounds {{.*}} %[[E2:.*]], i32 0, i32 0
// CHECK: call void @[[NS]]1AC1Ev({{.*}} %[[TEMPA2:.*]])
// CHECK: store {{.*}} %[[TEMPA2]], {{.*}} %[[TEMPE2_A]]
// CHECK: %[[TEMPE2_B:.*]] = getelementptr inbounds {{.*}} %[[E2]], i64 8
// CHECK: call void @[[NS]]1BC1Ev({{.*}} %[[TEMPE2_B]])
// CHECK: %[[TEMPE2_C:.*]] = getelementptr inbounds {{.*}} %[[E2]], i32 0, i32 2
// CHECK: call void @[[NS]]1DC1Ev({{.*}} %[[TEMPD2:.*]])
// CHECK: %[[TEMPD2_C:.*]] = getelementptr inbounds {{.*}} %[[TEMPD2]], i64 4
// CHECK: store {{.*}} %[[TEMPD2_C]], ptr %[[TEMPE2_C]]
g();
// CHECK: call void @[[NS]]1gEv()
// CHECK: call void @[[NS]]1ED1Ev({{.*}} %[[E2]])
// CHECK: call void @[[NS]]1DD1Ev({{.*}} %[[TEMPD2]])
// CHECK: call void @[[NS]]1AD1Ev({{.*}} %[[TEMPA2]])
// CHECK: call void @[[NS]]1ED1Ev({{.*}} %[[TEMPE1]])
// CHECK: call void @[[NS]]1DD1Ev({{.*}} %[[TEMPD1]])
// CHECK: call void @[[NS]]1AD1Ev({{.*}} %[[TEMPA1]])
}
}
namespace ArrayAccess {
struct A { A(int); ~A(); };
void g();
void f() {
using T = A[3];
// CHECK: call void @_ZN11ArrayAccess1AC1Ei({{.*}}, i32 noundef 1
// CHECK-NOT: @_ZN11ArrayAccess1AD
// CHECK: call void @_ZN11ArrayAccess1AC1Ei({{.*}}, i32 noundef 2
// CHECK-NOT: @_ZN11ArrayAccess1AD
// CHECK: call void @_ZN11ArrayAccess1AC1Ei({{.*}}, i32 noundef 3
// CHECK-NOT: @_ZN11ArrayAccess1AD
A &&a = T{1, 2, 3}[1];
// CHECK: call void @_ZN11ArrayAccess1AC1Ei({{.*}}, i32 noundef 4
// CHECK-NOT: @_ZN11ArrayAccess1AD
// CHECK: call void @_ZN11ArrayAccess1AC1Ei({{.*}}, i32 noundef 5
// CHECK-NOT: @_ZN11ArrayAccess1AD
// CHECK: call void @_ZN11ArrayAccess1AC1Ei({{.*}}, i32 noundef 6
// CHECK-NOT: @_ZN11ArrayAccess1AD
A &&b = 2[T{4, 5, 6}];
// CHECK: call void @_ZN11ArrayAccess1gEv(
g();
// CHECK: call void @_ZN11ArrayAccess1AD
// CHECK: call void @_ZN11ArrayAccess1AD
}
}
namespace PR14130 {
struct S { S(int); };
struct U { S &&s; };
U v { { 0 } };
// CHECK: call void @_ZN7PR141301SC1Ei({{.*}} @_ZGRN7PR141301vE_, i32 noundef 0)
// CHECK: store {{.*}} @_ZGRN7PR141301vE_, {{.*}} @_ZN7PR141301vE
}
namespace Conditional {
struct A {};
struct B : A { B(); ~B(); };
struct C : A { C(); ~C(); };
void g();
// CHECK-LABEL: define {{.*}} @_ZN11Conditional1fEb(
void f(bool b) {
// CHECK: store i1 false, ptr %[[CLEANUP_B:.*]],
// CHECK: store i1 false, ptr %[[CLEANUP_C:.*]],
// CHECK: br i1
//
// CHECK: call {{.*}} @_ZN11Conditional1BC1Ev(
// CHECK: store i1 true, ptr %[[CLEANUP_B]],
// CHECK: br label
//
// CHECK: call {{.*}} @_ZN11Conditional1CC1Ev(
// CHECK: store i1 true, ptr %[[CLEANUP_C]],
// CHECK: br label
A &&r = b ? static_cast<A&&>(B()) : static_cast<A&&>(C());
// CHECK: call {{.*}} @_ZN11Conditional1gEv(
g();
// CHECK: load {{.*}} %[[CLEANUP_C]]
// CHECK: br i1
// CHECK: call {{.*}} @_ZN11Conditional1CD1Ev(
// CHECK: br label
// CHECK: load {{.*}} %[[CLEANUP_B]]
// CHECK: br i1
// CHECK: call {{.*}} @_ZN11Conditional1BD1Ev(
// CHECK: br label
}
struct D { A &&a; };
// CHECK-LABEL: define {{.*}} @_ZN11Conditional10f_indirectEb(
void f_indirect(bool b) {
// CHECK: store i1 false, ptr %[[CLEANUP_B:.*]],
// CHECK: store i1 false, ptr %[[CLEANUP_C:.*]],
// CHECK: br i1
//
// CHECK: call {{.*}} @_ZN11Conditional1BC1Ev(
// CHECK: store i1 true, ptr %[[CLEANUP_B]],
// CHECK: br label
//
// CHECK: call {{.*}} @_ZN11Conditional1CC1Ev(
// CHECK: store i1 true, ptr %[[CLEANUP_C]],
// CHECK: br label
D d = b ? D{B()} : D{C()};
// In C++17, the expression D{...} directly initializes the 'd' object, so
// lifetime-extending the temporaries to the lifetime of the D object
// extends them past the call to g().
//
// In C++14 and before, D is move-constructed from the result of the
// conditional expression, so no lifetime extension occurs.
// CHECK-CXX17: call {{.*}} @_ZN11Conditional1gEv(
// CHECK: load {{.*}} %[[CLEANUP_C]]
// CHECK: br i1
// CHECK: call {{.*}} @_ZN11Conditional1CD1Ev(
// CHECK: br label
// CHECK: load {{.*}} %[[CLEANUP_B]]
// CHECK: br i1
// CHECK: call {{.*}} @_ZN11Conditional1BD1Ev(
// CHECK: br label
// CHECK-CXX11: call {{.*}} @_ZN11Conditional1gEv(
g();
}
extern bool b;
// CHECK: load {{.*}} @_ZN11Conditional1b
// CHECK: br i1
//
// CHECK: call {{.*}} @_ZN11Conditional1BC1Ev({{.*}} @_ZGRN11Conditional1rE_)
// CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN11Conditional1BD1Ev, ptr @_ZGRN11Conditional1rE_,
// CHECK: br label
//
// CHECK: call {{.*}} @_ZN11Conditional1CC1Ev({{.*}} @_ZGRN11Conditional1rE0_)
// CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN11Conditional1CD1Ev, ptr @_ZGRN11Conditional1rE0_,
// CHECK: br label
A &&r = b ? static_cast<A&&>(B()) : static_cast<A&&>(C());
}
#if __cplusplus >= 201703L
namespace PR42220 {
struct X { X(); ~X(); };
struct A { X &&x; };
struct B : A {};
void g() noexcept;
// CHECK-CXX17-LABEL: define{{.*}} @_ZN7PR422201fEv(
void f() {
// CHECK-CXX17: call{{.*}} @_ZN7PR422201XC1Ev(
B &&b = {X()};
// CHECK-CXX17-NOT: call{{.*}} @_ZN7PR422201XD1Ev(
// CHECK-CXX17: call{{.*}} @_ZN7PR422201gEv(
g();
// CHECK-CXX17: call{{.*}} @_ZN7PR422201XD1Ev(
}
}
#endif