diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -394,13 +394,15 @@ OldGV->eraseFromParent(); } - GV->setConstant(CGM.isTypeConstant(D.getType(), true)); + bool NeedsDtor = + D.needsDestruction(getContext()) == QualType::DK_cxx_destructor; + + GV->setConstant(CGM.isTypeConstant(D.getType(), true, !NeedsDtor)); GV->setInitializer(Init); emitter.finalize(GV); - if (D.needsDestruction(getContext()) == QualType::DK_cxx_destructor && - HaveInsertPoint()) { + if (NeedsDtor && HaveInsertPoint()) { // We have a constant initializer, but a nontrivial destructor. We still // need to perform a guarded "initialization" in order to register the // destructor. @@ -1481,10 +1483,12 @@ // emit it as a global instead. // Exception is if a variable is located in non-constant address space // in OpenCL. + bool NeedsDtor = + D.needsDestruction(getContext()) == QualType::DK_cxx_destructor; if ((!getLangOpts().OpenCL || Ty.getAddressSpace() == LangAS::opencl_constant) && (CGM.getCodeGenOpts().MergeAllConstants && !NRVO && - !isEscapingByRef && CGM.isTypeConstant(Ty, true))) { + !isEscapingByRef && CGM.isTypeConstant(Ty, true, !NeedsDtor))) { EmitStaticVarDecl(D, llvm::GlobalValue::InternalLinkage); // Signal this condition to later callbacks. diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -214,9 +214,11 @@ &D, DeclAddr, D.getAttr()->getLocation(), PerformInit, this); } + bool NeedsDtor = + D.needsDestruction(getContext()) == QualType::DK_cxx_destructor; if (PerformInit) EmitDeclInit(*this, D, DeclAddr); - if (CGM.isTypeConstant(D.getType(), true)) + if (CGM.isTypeConstant(D.getType(), true, !NeedsDtor)) EmitDeclInvariant(*this, D, DeclPtr); else EmitDeclDestroy(*this, D, DeclAddr); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -401,7 +401,7 @@ QualType Ty = Inner->getType(); if (CGF.CGM.getCodeGenOpts().MergeAllConstants && (Ty->isArrayType() || Ty->isRecordType()) && - CGF.CGM.isTypeConstant(Ty, true)) + CGF.CGM.isTypeConstant(Ty, true, false)) if (auto Init = ConstantEmitter(CGF).tryEmitAbstract(Inner, Ty)) { auto AS = CGF.CGM.GetGlobalConstantAddressSpace(); auto *GV = new llvm::GlobalVariable( diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -532,7 +532,8 @@ Emitter.tryEmitForInitializer(ExprToVisit, AS, ArrayQTy)) { auto GV = new llvm::GlobalVariable( CGM.getModule(), C->getType(), - CGM.isTypeConstant(ArrayQTy, /* ExcludeCtorDtor= */ true), + CGM.isTypeConstant(ArrayQTy, /* ExcludeCtor= */ true, + /* ExcludeDtor= */ false), llvm::GlobalValue::PrivateLinkage, C, "constinit", /* InsertBefore= */ nullptr, llvm::GlobalVariable::NotThreadLocal, CGM.getContext().getTargetAddressSpace(AS)); diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -932,12 +932,12 @@ return ConstantAddress::invalid(); } - auto GV = new llvm::GlobalVariable(CGM.getModule(), C->getType(), - CGM.isTypeConstant(E->getType(), true), - llvm::GlobalValue::InternalLinkage, - C, ".compoundliteral", nullptr, - llvm::GlobalVariable::NotThreadLocal, - CGM.getContext().getTargetAddressSpace(addressSpace)); + auto GV = new llvm::GlobalVariable( + CGM.getModule(), C->getType(), + CGM.isTypeConstant(E->getType(), true, false), + llvm::GlobalValue::InternalLinkage, C, ".compoundliteral", nullptr, + llvm::GlobalVariable::NotThreadLocal, + CGM.getContext().getTargetAddressSpace(addressSpace)); emitter.finalize(GV); GV->setAlignment(Align.getAsAlign()); CGM.setAddrOfConstantCompoundLiteral(E, GV); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -816,7 +816,7 @@ return getTBAAAccessInfo(AccessType); } - bool isTypeConstant(QualType QTy, bool ExcludeCtorDtor); + bool isTypeConstant(QualType QTy, bool ExcludeCtor, bool ExcludeDtor); bool isPaddedAtomicType(QualType type); bool isPaddedAtomicType(const AtomicType *type); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3130,7 +3130,7 @@ // codegen for global variables, because they may be marked as threadprivate. if (LangOpts.OpenMP && LangOpts.OpenMPUseTLS && getContext().getTargetInfo().isTLSSupported() && isa(Global) && - !isTypeConstant(Global->getType(), false) && + !isTypeConstant(Global->getType(), false, false) && !OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(Global)) return false; @@ -4335,8 +4335,9 @@ /// /// If ExcludeCtor is true, the duration when the object's constructor runs /// will not be considered. The caller will need to verify that the object is -/// not written to during its construction. -bool CodeGenModule::isTypeConstant(QualType Ty, bool ExcludeCtor) { +/// not written to during its construction. ExcludeDtor works similarly. +bool CodeGenModule::isTypeConstant(QualType Ty, bool ExcludeCtor, + bool ExcludeDtor) { if (!Ty.isConstant(Context) && !Ty->isReferenceType()) return false; @@ -4344,7 +4345,7 @@ if (const CXXRecordDecl *Record = Context.getBaseElementType(Ty)->getAsCXXRecordDecl()) return ExcludeCtor && !Record->hasMutableFields() && - Record->hasTrivialDestructor(); + (Record->hasTrivialDestructor() || ExcludeDtor); } return true; @@ -4457,7 +4458,7 @@ // FIXME: This code is overly simple and should be merged with other global // handling. - GV->setConstant(isTypeConstant(D->getType(), false)); + GV->setConstant(isTypeConstant(D->getType(), false, false)); GV->setAlignment(getContext().getDeclAlign(D).getAsAlign()); @@ -5013,7 +5014,7 @@ // If it is safe to mark the global 'constant', do so now. GV->setConstant(!NeedsGlobalCtor && !NeedsGlobalDtor && - isTypeConstant(D->getType(), true)); + isTypeConstant(D->getType(), true, true)); // If it is in a read-only section, mark it 'constant'. if (const SectionAttr *SA = D->getAttr()) { @@ -6087,7 +6088,8 @@ emitter.emplace(*this); InitialValue = emitter->emitForInitializer(*Value, AddrSpace, MaterializedType); - Constant = isTypeConstant(MaterializedType, /*ExcludeCtor*/Value); + Constant = isTypeConstant(MaterializedType, /*ExcludeCtor*/ Value, + /*ExcludeDtor*/ false); Type = InitialValue->getType(); } else { // No initializer, the initialization will be provided when we diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -9632,7 +9632,7 @@ return AddrSpace; // Only promote to address space 4 if VarDecl has constant initialization. - if (CGM.isTypeConstant(D->getType(), false) && + if (CGM.isTypeConstant(D->getType(), false, false) && D->hasConstantInitialization()) { if (auto ConstAS = CGM.getTarget().getConstantAddressSpace()) return *ConstAS; diff --git a/clang/test/CodeGenCXX/const-init-cxx11.cpp b/clang/test/CodeGenCXX/const-init-cxx11.cpp --- a/clang/test/CodeGenCXX/const-init-cxx11.cpp +++ b/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 { diff --git a/clang/test/CodeGenCXX/const-init-cxx2a.cpp b/clang/test/CodeGenCXX/const-init-cxx2a.cpp --- a/clang/test/CodeGenCXX/const-init-cxx2a.cpp +++ b/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; } diff --git a/clang/test/CodeGenCXX/init-invariant.cpp b/clang/test/CodeGenCXX/init-invariant.cpp --- a/clang/test/CodeGenCXX/init-invariant.cpp +++ b/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) diff --git a/clang/test/CodeGenCXX/static-init.cpp b/clang/test/CodeGenCXX/static-init.cpp --- a/clang/test/CodeGenCXX/static-init.cpp +++ b/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