Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -4340,7 +4340,8 @@ if (const CXXRecordDecl *Record = Context.getBaseElementType(Ty)->getAsCXXRecordDecl()) return ExcludeCtor && !Record->hasMutableFields() && - Record->hasTrivialDestructor(); + (Record->hasTrivialDestructor() || + Record->hasConstexprDestructor()); } return true; Index: clang/test/CodeGenCXX/const-init-cxx11.cpp =================================================================== --- clang/test/CodeGenCXX/const-init-cxx11.cpp +++ clang/test/CodeGenCXX/const-init-cxx11.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -no-opaque-pointers -w -fmerge-all-constants -triple x86_64-elf-gnu -emit-llvm -o - %s -std=c++11 | FileCheck %s +// RUN: %clang_cc1 -no-opaque-pointers -w -fmerge-all-constants -triple x86_64-elf-gnu -emit-llvm -o - %s -std=c++20 | FileCheck -check-prefix=CHECK20 %s // FIXME: The padding in all these objects should be zero-initialized. namespace StructUnion { @@ -424,6 +425,7 @@ // CHECK: @_ZGRN39ClassTemplateWithHiddenStaticDataMember1SIvE1aE_ = linkonce_odr hidden constant i32 5, comdat // CHECK: @_ZN39ClassTemplateWithHiddenStaticDataMember3useE ={{.*}} constant i32* @_ZGRN39ClassTemplateWithHiddenStaticDataMember1SIvE1aE_ // CHECK: @_ZGRZN20InlineStaticConstRef3funEvE1i_ = linkonce_odr constant i32 10, comdat +// CHECK20: @_ZZN12LocalVarInit4dtorEvE1a = internal constant {{.*}} i32 103 // Constant initialization tests go before this point, // dynamic initialization tests go after. @@ -483,6 +485,14 @@ // CHECK-NOT: ret i32 103 // CHECK: } int mutable_() { constexpr Mutable a = { f(103) }; return a.k; } + +#if __cplusplus >= 202002L + // CHECK20: define {{.*}} @_ZN12LocalVarInit4dtorEv + // CHECK20-NOT: call + // CHECK20: ret i32 103 + struct Dtor { constexpr Dtor(int n) : k(n) {} constexpr ~Dtor() {} int k; }; + int dtor() { constexpr Dtor a = { f(103) }; return a.k; } +#endif } namespace CrossFuncLabelDiff { Index: clang/test/CodeGenCXX/const-init-cxx2a.cpp =================================================================== --- clang/test/CodeGenCXX/const-init-cxx2a.cpp +++ clang/test/CodeGenCXX/const-init-cxx2a.cpp @@ -11,10 +11,10 @@ constexpr ~B() { n *= 5; } int n = 123; }; -// CHECK: @b ={{.*}} global {{.*}} i32 123 +// CHECK: @b ={{.*}} constant {{.*}} i32 123 extern constexpr B b = B(); -// CHECK: @_ZL1c = internal global {{.*}} i32 123 +// CHECK: @_ZL1c = internal constant {{.*}} i32 123 const B c; int use_c() { return c.n; } Index: clang/test/CodeGenCXX/init-invariant.cpp =================================================================== --- clang/test/CodeGenCXX/init-invariant.cpp +++ clang/test/CodeGenCXX/init-invariant.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple i686-linux-gnu -emit-llvm %s -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-O0 -// RUN: %clang_cc1 -triple i686-linux-gnu -emit-llvm %s -O1 -disable-llvm-passes -o - | FileCheck %s +// RUN: %clang_cc1 -triple i686-linux-gnu -std=c++20 -emit-llvm %s -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-O0 +// RUN: %clang_cc1 -triple i686-linux-gnu -std=c++20 -emit-llvm %s -O1 -disable-llvm-passes -o - | FileCheck %s // Check that we add an llvm.invariant.start.p0i8 to mark when a global becomes // read-only. If globalopt can fold the initializer, it will then mark the @@ -16,6 +16,16 @@ // CHECK: @a ={{.*}} global {{.*}} zeroinitializer extern const A a = A(); +struct A2 { + A2(); + constexpr ~A2() {} + int n; +}; + +// CHECK: @a2 ={{.*}} global {{.*}} zeroinitializer +extern const A2 a2 = A2(); + + struct B { B(); mutable int n; @@ -44,6 +54,9 @@ // CHECK: call void @_ZN1AC1Ev(ptr noundef {{[^,]*}} @a) // CHECK: call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a) +// CHECK: call void @_ZN2A2C1Ev(ptr noundef {{[^,]*}} @a2) +// CHECK: call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a2) + // CHECK: call void @_ZN1BC1Ev(ptr noundef {{[^,]*}} @b) // CHECK-NOT: call {{.*}}@llvm.invariant.start.p0(i64 noundef 4, ptr @b) Index: clang/test/CodeGenCXX/static-init.cpp =================================================================== --- clang/test/CodeGenCXX/static-init.cpp +++ clang/test/CodeGenCXX/static-init.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 %s -triple=x86_64-pc-linuxs -emit-llvm -std=c++98 -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK98 %s // RUN: %clang_cc1 %s -triple=x86_64-pc-linuxs -emit-llvm -std=c++11 -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK11 %s +// RUN: %clang_cc1 %s -triple=x86_64-pc-linuxs -emit-llvm -std=c++20 -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK20 %s // CHECK: @_ZZ1hvE1i = internal global i32 0, align 4 // CHECK: @base_req ={{.*}} global [4 x i8] c"foo\00", align 1 @@ -7,6 +8,8 @@ // CHECK: @_ZZN5test31BC1EvE1u = internal global { i8, [3 x i8] } { i8 97, [3 x i8] undef }, align 4 +// CHECK20: @_ZZN5test51fEvE1a = internal constant %"struct.test5::A" { i32 42 } + // CHECK: @_ZZ2h2vE1i = linkonce_odr global i32 0, comdat, align 4 // CHECK: @_ZGVZ2h2vE1i = linkonce_odr global i64 0, comdat, align 8{{$}} // CHECK: @_ZZN5test1L6getvarEiE3var = internal constant [4 x i32] [i32 1, i32 0, i32 2, i32 4], align 16 @@ -173,3 +176,18 @@ // CHECK: define linkonce_odr noundef nonnull align 8 dereferenceable(8) ptr @_ZN5test414useStaticLocalEv() // CHECK: ret ptr{{.*}} @_ZZN5test414useStaticLocalEvE3obj } + +#if __cplusplus >= 202002L +// A const object with constexpr destructor can be emitted as a constant. +namespace test5 { + struct A { + constexpr A(int x) : x_(x) {} + constexpr ~A() {} + int x_; + }; + const int *f() { + static const A a{42}; + return &a.x_; + } +} +#endif