Skip to content

Commit 7a85aa9

Browse files
committed
Emit const globals with constexpr destructor as constant LLVM values
This follows 2b4fa53 which made Clang not emit destructor calls for such objects. However, they would still not get emitted as constants since CodeGenModule::isTypeConstant() returns false if the destructor is constexpr. This change adds a param to make isTypeConstant() ignore the dtor, allowing the caller to check it instead. Fixes Issue #61212 Differential revision: https://reviews.llvm.org/D145369
1 parent 44e03d6 commit 7a85aa9

File tree

12 files changed

+76
-26
lines changed

12 files changed

+76
-26
lines changed

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,13 +394,15 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
394394
OldGV->eraseFromParent();
395395
}
396396

397-
GV->setConstant(CGM.isTypeConstant(D.getType(), true));
397+
bool NeedsDtor =
398+
D.needsDestruction(getContext()) == QualType::DK_cxx_destructor;
399+
400+
GV->setConstant(CGM.isTypeConstant(D.getType(), true, !NeedsDtor));
398401
GV->setInitializer(Init);
399402

400403
emitter.finalize(GV);
401404

402-
if (D.needsDestruction(getContext()) == QualType::DK_cxx_destructor &&
403-
HaveInsertPoint()) {
405+
if (NeedsDtor && HaveInsertPoint()) {
404406
// We have a constant initializer, but a nontrivial destructor. We still
405407
// need to perform a guarded "initialization" in order to register the
406408
// destructor.
@@ -1481,10 +1483,12 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) {
14811483
// emit it as a global instead.
14821484
// Exception is if a variable is located in non-constant address space
14831485
// in OpenCL.
1486+
bool NeedsDtor =
1487+
D.needsDestruction(getContext()) == QualType::DK_cxx_destructor;
14841488
if ((!getLangOpts().OpenCL ||
14851489
Ty.getAddressSpace() == LangAS::opencl_constant) &&
14861490
(CGM.getCodeGenOpts().MergeAllConstants && !NRVO &&
1487-
!isEscapingByRef && CGM.isTypeConstant(Ty, true))) {
1491+
!isEscapingByRef && CGM.isTypeConstant(Ty, true, !NeedsDtor))) {
14881492
EmitStaticVarDecl(D, llvm::GlobalValue::InternalLinkage);
14891493

14901494
// Signal this condition to later callbacks.

clang/lib/CodeGen/CGDeclCXX.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,11 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D,
214214
&D, DeclAddr, D.getAttr<OMPThreadPrivateDeclAttr>()->getLocation(),
215215
PerformInit, this);
216216
}
217+
bool NeedsDtor =
218+
D.needsDestruction(getContext()) == QualType::DK_cxx_destructor;
217219
if (PerformInit)
218220
EmitDeclInit(*this, D, DeclAddr);
219-
if (CGM.isTypeConstant(D.getType(), true))
221+
if (CGM.isTypeConstant(D.getType(), true, !NeedsDtor))
220222
EmitDeclInvariant(*this, D, DeclPtr);
221223
else
222224
EmitDeclDestroy(*this, D, DeclAddr);

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ static Address createReferenceTemporary(CodeGenFunction &CGF,
401401
QualType Ty = Inner->getType();
402402
if (CGF.CGM.getCodeGenOpts().MergeAllConstants &&
403403
(Ty->isArrayType() || Ty->isRecordType()) &&
404-
CGF.CGM.isTypeConstant(Ty, true))
404+
CGF.CGM.isTypeConstant(Ty, true, false))
405405
if (auto Init = ConstantEmitter(CGF).tryEmitAbstract(Inner, Ty)) {
406406
auto AS = CGF.CGM.GetGlobalConstantAddressSpace();
407407
auto *GV = new llvm::GlobalVariable(

clang/lib/CodeGen/CGExprAgg.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,8 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
532532
Emitter.tryEmitForInitializer(ExprToVisit, AS, ArrayQTy)) {
533533
auto GV = new llvm::GlobalVariable(
534534
CGM.getModule(), C->getType(),
535-
CGM.isTypeConstant(ArrayQTy, /* ExcludeCtorDtor= */ true),
535+
CGM.isTypeConstant(ArrayQTy, /* ExcludeCtor= */ true,
536+
/* ExcludeDtor= */ false),
536537
llvm::GlobalValue::PrivateLinkage, C, "constinit",
537538
/* InsertBefore= */ nullptr, llvm::GlobalVariable::NotThreadLocal,
538539
CGM.getContext().getTargetAddressSpace(AS));

clang/lib/CodeGen/CGExprConstant.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -932,12 +932,12 @@ tryEmitGlobalCompoundLiteral(ConstantEmitter &emitter,
932932
return ConstantAddress::invalid();
933933
}
934934

935-
auto GV = new llvm::GlobalVariable(CGM.getModule(), C->getType(),
936-
CGM.isTypeConstant(E->getType(), true),
937-
llvm::GlobalValue::InternalLinkage,
938-
C, ".compoundliteral", nullptr,
939-
llvm::GlobalVariable::NotThreadLocal,
940-
CGM.getContext().getTargetAddressSpace(addressSpace));
935+
auto GV = new llvm::GlobalVariable(
936+
CGM.getModule(), C->getType(),
937+
CGM.isTypeConstant(E->getType(), true, false),
938+
llvm::GlobalValue::InternalLinkage, C, ".compoundliteral", nullptr,
939+
llvm::GlobalVariable::NotThreadLocal,
940+
CGM.getContext().getTargetAddressSpace(addressSpace));
941941
emitter.finalize(GV);
942942
GV->setAlignment(Align.getAsAlign());
943943
CGM.setAddrOfConstantCompoundLiteral(E, GV);

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3130,7 +3130,7 @@ bool CodeGenModule::MayBeEmittedEagerly(const ValueDecl *Global) {
31303130
// codegen for global variables, because they may be marked as threadprivate.
31313131
if (LangOpts.OpenMP && LangOpts.OpenMPUseTLS &&
31323132
getContext().getTargetInfo().isTLSSupported() && isa<VarDecl>(Global) &&
3133-
!isTypeConstant(Global->getType(), false) &&
3133+
!isTypeConstant(Global->getType(), false, false) &&
31343134
!OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(Global))
31353135
return false;
31363136

@@ -4335,16 +4335,17 @@ CodeGenModule::CreateRuntimeFunction(llvm::FunctionType *FTy, StringRef Name,
43354335
///
43364336
/// If ExcludeCtor is true, the duration when the object's constructor runs
43374337
/// will not be considered. The caller will need to verify that the object is
4338-
/// not written to during its construction.
4339-
bool CodeGenModule::isTypeConstant(QualType Ty, bool ExcludeCtor) {
4338+
/// not written to during its construction. ExcludeDtor works similarly.
4339+
bool CodeGenModule::isTypeConstant(QualType Ty, bool ExcludeCtor,
4340+
bool ExcludeDtor) {
43404341
if (!Ty.isConstant(Context) && !Ty->isReferenceType())
43414342
return false;
43424343

43434344
if (Context.getLangOpts().CPlusPlus) {
43444345
if (const CXXRecordDecl *Record
43454346
= Context.getBaseElementType(Ty)->getAsCXXRecordDecl())
43464347
return ExcludeCtor && !Record->hasMutableFields() &&
4347-
Record->hasTrivialDestructor();
4348+
(Record->hasTrivialDestructor() || ExcludeDtor);
43484349
}
43494350

43504351
return true;
@@ -4457,7 +4458,7 @@ CodeGenModule::GetOrCreateLLVMGlobal(StringRef MangledName, llvm::Type *Ty,
44574458

44584459
// FIXME: This code is overly simple and should be merged with other global
44594460
// handling.
4460-
GV->setConstant(isTypeConstant(D->getType(), false));
4461+
GV->setConstant(isTypeConstant(D->getType(), false, false));
44614462

44624463
GV->setAlignment(getContext().getDeclAlign(D).getAsAlign());
44634464

@@ -5013,7 +5014,7 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
50135014

50145015
// If it is safe to mark the global 'constant', do so now.
50155016
GV->setConstant(!NeedsGlobalCtor && !NeedsGlobalDtor &&
5016-
isTypeConstant(D->getType(), true));
5017+
isTypeConstant(D->getType(), true, true));
50175018

50185019
// If it is in a read-only section, mark it 'constant'.
50195020
if (const SectionAttr *SA = D->getAttr<SectionAttr>()) {
@@ -6087,7 +6088,8 @@ ConstantAddress CodeGenModule::GetAddrOfGlobalTemporary(
60876088
emitter.emplace(*this);
60886089
InitialValue = emitter->emitForInitializer(*Value, AddrSpace,
60896090
MaterializedType);
6090-
Constant = isTypeConstant(MaterializedType, /*ExcludeCtor*/Value);
6091+
Constant = isTypeConstant(MaterializedType, /*ExcludeCtor*/ Value,
6092+
/*ExcludeDtor*/ false);
60916093
Type = InitialValue->getType();
60926094
} else {
60936095
// No initializer, the initialization will be provided when we

clang/lib/CodeGen/CodeGenModule.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ class CodeGenModule : public CodeGenTypeCache {
816816
return getTBAAAccessInfo(AccessType);
817817
}
818818

819-
bool isTypeConstant(QualType QTy, bool ExcludeCtorDtor);
819+
bool isTypeConstant(QualType QTy, bool ExcludeCtor, bool ExcludeDtor);
820820

821821
bool isPaddedAtomicType(QualType type);
822822
bool isPaddedAtomicType(const AtomicType *type);

clang/lib/CodeGen/TargetInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9632,7 +9632,7 @@ AMDGPUTargetCodeGenInfo::getGlobalVarAddressSpace(CodeGenModule &CGM,
96329632
return AddrSpace;
96339633

96349634
// Only promote to address space 4 if VarDecl has constant initialization.
9635-
if (CGM.isTypeConstant(D->getType(), false) &&
9635+
if (CGM.isTypeConstant(D->getType(), false, false) &&
96369636
D->hasConstantInitialization()) {
96379637
if (auto ConstAS = CGM.getTarget().getConstantAddressSpace())
96389638
return *ConstAS;

clang/test/CodeGenCXX/const-init-cxx11.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -no-opaque-pointers -w -fmerge-all-constants -triple x86_64-elf-gnu -emit-llvm -o - %s -std=c++11 | FileCheck %s
2+
// 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
23

34
// FIXME: The padding in all these objects should be zero-initialized.
45
namespace StructUnion {
@@ -424,6 +425,7 @@ namespace DR2126 {
424425
// CHECK: @_ZGRN39ClassTemplateWithHiddenStaticDataMember1SIvE1aE_ = linkonce_odr hidden constant i32 5, comdat
425426
// CHECK: @_ZN39ClassTemplateWithHiddenStaticDataMember3useE ={{.*}} constant i32* @_ZGRN39ClassTemplateWithHiddenStaticDataMember1SIvE1aE_
426427
// CHECK: @_ZGRZN20InlineStaticConstRef3funEvE1i_ = linkonce_odr constant i32 10, comdat
428+
// CHECK20: @_ZZN12LocalVarInit4dtorEvE1a = internal constant {{.*}} i32 103
427429

428430
// Constant initialization tests go before this point,
429431
// dynamic initialization tests go after.
@@ -483,6 +485,14 @@ namespace LocalVarInit {
483485
// CHECK-NOT: ret i32 103
484486
// CHECK: }
485487
int mutable_() { constexpr Mutable a = { f(103) }; return a.k; }
488+
489+
#if __cplusplus >= 202002L
490+
// CHECK20: define {{.*}} @_ZN12LocalVarInit4dtorEv
491+
// CHECK20-NOT: call
492+
// CHECK20: ret i32 103
493+
struct Dtor { constexpr Dtor(int n) : k(n) {} constexpr ~Dtor() {} int k; };
494+
int dtor() { constexpr Dtor a = { f(103) }; return a.k; }
495+
#endif
486496
}
487497

488498
namespace CrossFuncLabelDiff {

clang/test/CodeGenCXX/const-init-cxx2a.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ struct B {
1111
constexpr ~B() { n *= 5; }
1212
int n = 123;
1313
};
14-
// CHECK: @b ={{.*}} global {{.*}} i32 123
14+
// CHECK: @b ={{.*}} constant {{.*}} i32 123
1515
extern constexpr B b = B();
1616

17-
// CHECK: @_ZL1c = internal global {{.*}} i32 123
17+
// CHECK: @_ZL1c = internal constant {{.*}} i32 123
1818
const B c;
1919
int use_c() { return c.n; }
2020

clang/test/CodeGenCXX/init-invariant.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -triple i686-linux-gnu -emit-llvm %s -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-O0
2-
// RUN: %clang_cc1 -triple i686-linux-gnu -emit-llvm %s -O1 -disable-llvm-passes -o - | FileCheck %s
1+
// RUN: %clang_cc1 -triple i686-linux-gnu -std=c++20 -emit-llvm %s -disable-llvm-passes -o - | FileCheck %s --check-prefix=CHECK-O0
2+
// RUN: %clang_cc1 -triple i686-linux-gnu -std=c++20 -emit-llvm %s -O1 -disable-llvm-passes -o - | FileCheck %s
33

44
// Check that we add an llvm.invariant.start.p0i8 to mark when a global becomes
55
// read-only. If globalopt can fold the initializer, it will then mark the
@@ -16,6 +16,16 @@ struct A {
1616
// CHECK: @a ={{.*}} global {{.*}} zeroinitializer
1717
extern const A a = A();
1818

19+
struct A2 {
20+
A2();
21+
constexpr ~A2() {}
22+
int n;
23+
};
24+
25+
// CHECK: @a2 ={{.*}} global {{.*}} zeroinitializer
26+
extern const A2 a2 = A2();
27+
28+
1929
struct B {
2030
B();
2131
mutable int n;
@@ -44,6 +54,9 @@ void e() {
4454
// CHECK: call void @_ZN1AC1Ev(ptr noundef {{[^,]*}} @a)
4555
// CHECK: call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a)
4656

57+
// CHECK: call void @_ZN2A2C1Ev(ptr noundef {{[^,]*}} @a2)
58+
// CHECK: call {{.*}}@llvm.invariant.start.p0(i64 4, ptr @a2)
59+
4760
// CHECK: call void @_ZN1BC1Ev(ptr noundef {{[^,]*}} @b)
4861
// CHECK-NOT: call {{.*}}@llvm.invariant.start.p0(i64 noundef 4, ptr @b)
4962

clang/test/CodeGenCXX/static-init.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
// RUN: %clang_cc1 %s -triple=x86_64-pc-linuxs -emit-llvm -std=c++98 -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK98 %s
22
// RUN: %clang_cc1 %s -triple=x86_64-pc-linuxs -emit-llvm -std=c++11 -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK11 %s
3+
// RUN: %clang_cc1 %s -triple=x86_64-pc-linuxs -emit-llvm -std=c++20 -o - | FileCheck -check-prefix=CHECK -check-prefix=CHECK20 %s
34

45
// CHECK: @_ZZ1hvE1i = internal global i32 0, align 4
56
// CHECK: @base_req ={{.*}} global [4 x i8] c"foo\00", align 1
67
// CHECK: @base_req_uchar ={{.*}} global [4 x i8] c"bar\00", align 1
78

89
// CHECK: @_ZZN5test31BC1EvE1u = internal global { i8, [3 x i8] } { i8 97, [3 x i8] undef }, align 4
910

11+
// CHECK20: @_ZZN5test51fEvE1a = internal constant %"struct.test5::A" { i32 42 }
12+
1013
// CHECK: @_ZZ2h2vE1i = linkonce_odr global i32 0, comdat, align 4
1114
// CHECK: @_ZGVZ2h2vE1i = linkonce_odr global i64 0, comdat, align 8{{$}}
1215
// CHECK: @_ZZN5test1L6getvarEiE3var = internal constant [4 x i32] [i32 1, i32 0, i32 2, i32 4], align 16
@@ -173,3 +176,18 @@ void useit() {
173176
// CHECK: define linkonce_odr noundef nonnull align 8 dereferenceable(8) ptr @_ZN5test414useStaticLocalEv()
174177
// CHECK: ret ptr{{.*}} @_ZZN5test414useStaticLocalEvE3obj
175178
}
179+
180+
#if __cplusplus >= 202002L
181+
// A const object with constexpr destructor can be emitted as a constant.
182+
namespace test5 {
183+
struct A {
184+
constexpr A(int x) : x_(x) {}
185+
constexpr ~A() {}
186+
int x_;
187+
};
188+
const int *f() {
189+
static const A a{42};
190+
return &a.x_;
191+
}
192+
}
193+
#endif

0 commit comments

Comments
 (0)