diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a20b1ab298f9c..f95df0ccc9f3c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -308,6 +308,9 @@ Bug Fixes in This Version - Builtin elementwise operators now accept vector arguments that have different qualifiers on their elements. For example, vector of 4 ``const float`` values and vector of 4 ``float`` values. (#GH155405) +- ``static constexpr`` variables defined in ``consteval`` functions now + correctly preserve their initializers even if leaked out of the parent + function. (#GH82994) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 29193e0c541b9..54929b396a7e0 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -261,6 +261,7 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl( if (llvm::Constant *ExistingGV = StaticLocalDeclMap[&D]) return ExistingGV; + bool HasConstantInit = D.isUsableInConstantExpressions(getContext()); QualType Ty = D.getType(); assert(Ty->isConstantSizeType() && "VLAs can't be static"); @@ -275,18 +276,21 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl( LangAS AS = GetGlobalVarAddressSpace(&D); unsigned TargetAS = getContext().getTargetAddressSpace(AS); + llvm::Constant *Init = nullptr; // OpenCL variables in local address space and CUDA shared // variables cannot have an initializer. - llvm::Constant *Init = nullptr; + // Initializers for constant-initialized static locals are deferred. if (Ty.getAddressSpace() == LangAS::opencl_local || - D.hasAttr() || D.hasAttr()) + D.hasAttr() || D.hasAttr()) { Init = llvm::UndefValue::get(LTy); - else + } else if (!HasConstantInit) { Init = EmitNullConstant(Ty); + } llvm::GlobalVariable *GV = new llvm::GlobalVariable( getModule(), LTy, Ty.isConstant(getContext()), Linkage, Init, Name, nullptr, llvm::GlobalVariable::NotThreadLocal, TargetAS); + GV->setAlignment(getContext().getDeclAlign(&D).getAsAlign()); if (supportsCOMDAT() && GV->isWeakForLinker()) @@ -309,6 +313,8 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl( } setStaticLocalDeclAddress(&D, Addr); + if (HasConstantInit && D.isStaticLocal()) + addDeferredDeclToEmit(GlobalDecl(&D)); // Ensure that the static local gets initialized by making sure the parent // function gets emitted eventually. @@ -336,6 +342,10 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl( assert(isa(DC) && "unexpected parent code decl"); } if (GD.getDecl()) { + // Avoid producing declarations of consteval functions. + if (auto FD = dyn_cast(GD.getDecl()); + FD && FD->isImmediateFunction()) + return Addr; // Disable emission of the parent function for the OpenMP device codegen. CGOpenMPRuntime::DisableAutoDeclareTargetRAII NoDeclTarget(*this); (void)GetAddrOfGlobal(GD); @@ -406,6 +416,7 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D, // declaration. This can happen when double-emitting function // bodies, e.g. with complete and base constructors. llvm::Constant *addr = CGM.getOrCreateStaticVarDecl(D, Linkage); + CharUnits alignment = getContext().getDeclAlign(&D); // Store into LocalDeclMap before generating initializer to handle @@ -432,7 +443,8 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D, bool isCudaSharedVar = getLangOpts().CUDA && getLangOpts().CUDAIsDevice && D.hasAttr(); // If this value has an initializer, emit it. - if (D.getInit() && !isCudaSharedVar) { + if (D.getInit() && !isCudaSharedVar && + !(D.isUsableInConstantExpressions(getContext()) && D.isStaticLocal())) { ApplyAtomGroup Grp(getDebugInfo()); var = AddInitializerToStaticVarDecl(D, var); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 5e96f5bd6e5f8..630029def6ea9 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4461,8 +4461,14 @@ void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { return EmitGlobalFunctionDefinition(GD, GV); } - if (const auto *VD = dyn_cast(D)) + if (const auto *VD = dyn_cast(D)) { + if (VD->isStaticLocal() && !getContext().shouldExternalize(D)) { + CodeGenFunction(*this).AddInitializerToStaticVarDecl( + *VD, cast(GV)); + return; + } return EmitGlobalVarDefinition(VD, !VD->hasDefinition()); + } llvm_unreachable("Invalid argument to EmitGlobalDefinition()"); } diff --git a/clang/test/CodeGenCXX/reference-init.cpp b/clang/test/CodeGenCXX/reference-init.cpp index a98d400eb17a2..c376145b31ea3 100644 --- a/clang/test/CodeGenCXX/reference-init.cpp +++ b/clang/test/CodeGenCXX/reference-init.cpp @@ -5,7 +5,6 @@ #if __cplusplus >= 201103L // CHECK-CXX11: @_ZZ15InitRefWithListvE1r = internal constant ptr @_ZGRZ15InitRefWithListvE1r_ -// CHECK-CXX11: @_ZGRZ15InitRefWithListvE1r_ = internal constant i32 123 int InitRefWithList() { static const int &r = {123}; return r; } #endif @@ -38,4 +37,5 @@ extern SelfReference self_reference_1; SelfReference self_reference_2 = {self_reference_1}; // CHECK-CXX98: @self_reference_2 = {{.*}}global %[[SELF_REF:.*]] { ptr @self_reference_1 } // CHECK-CXX11: @self_reference_2 = {{(dso_local )?}}global %[[SELF_REF:.*]] zeroinitializer +// CHECK-CXX11: @_ZGRZ15InitRefWithListvE1r_ = internal constant i32 123 // CHECK-CXX11: call {{.*}}memcpy{{.*}} @self_reference_2, {{.*}} @self_reference_1 diff --git a/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp b/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp new file mode 100644 index 0000000000000..e8c3143af8c09 --- /dev/null +++ b/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp @@ -0,0 +1,95 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++23 %s -emit-llvm -o - | FileCheck %s + +consteval const int* a() { + static constexpr int b = 3; + return &b; +} + +consteval const int returns42() { + static constexpr long c = 42; + return c; +} + +struct S { + int a; + constexpr S(int _a) : a(_a){}; +}; + +consteval auto returns48viastruct() { + static constexpr S s{ 48 }; + return &s; +} + +consteval const int *why() { + static constexpr int a = 10; + { + static constexpr int a = 20; + return &a; + } + +} + +consteval const int* returnsarray() { + static constexpr int a[] = {10, 20, 30}; + return a; +} + +consteval const void * self_ref() { + static constexpr const void* b = &b; + return b; +} + +// CHECK: @_ZZ1avE1b = linkonce_odr constant i32 3, comdat, align 4 +// CHECK: @_ZZ18returns48viastructvE1s = linkonce_odr constant %struct.S { i32 48 }, comdat, align 4 +// CHECK: @_ZZ3whyvE1a_0 = linkonce_odr constant i32 20, comdat, align 4 +// CHECK: @_ZZ12returnsarrayvE1a = linkonce_odr constant [3 x i32] [i32 10, i32 20, i32 30], comdat, align 4 +// CHECK: @_ZZ8self_refvE1b = linkonce_odr constant ptr @_ZZ8self_refvE1b, comdat, align 8 + +// CHECK: define dso_local noundef i32 @_Z1cv() +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = load i32, ptr @_ZZ1avE1b, align 4 +// CHECK-NEXT: ret i32 %0 +// CHECK-NEXT: } +int c() { return *a(); } + +// CHECK: define dso_local noundef i32 @_Z1dv() +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 42 +// CHECK-NEXT: } +int d() { return returns42(); } + +int e() { return returns48viastruct()->a; } +// CHECK: define dso_local noundef i32 @_Z1ev() +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 48 +// CHECK-NEXT: } + +int f() { return *why(); } +// CHECK: define dso_local noundef i32 @_Z1fv() +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = load i32, ptr @_ZZ3whyvE1a_0, align 4 +// CHECK-NEXT: ret i32 %0 +// CHECK-NEXT: } + +int g() { return returnsarray()[2]; } +// CHECK: define dso_local noundef i32 @_Z1gv() +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = load i32, ptr getelementptr inbounds (i32, ptr @_ZZ12returnsarrayvE1a, i64 2), align 4 +// CHECK-NEXT: ret i32 %0 +// CHECK-NEXT: } + +using size_t = decltype(sizeof(void*)); +int usesself_ref() { + size_t b = (size_t)self_ref(); + return (int)b; +} +// CHECK: define dso_local noundef i32 @_Z12usesself_refv() +// CHECK-NEXT: entry: +// CHECK-NEXT: %b = alloca i64, align 8 +// CHECK-NEXT: store i64 ptrtoint (ptr @_ZZ8self_refvE1b to i64), ptr %b, align 8 +// CHECK-NEXT: %0 = load i64, ptr %b, align 8 +// CHECK-NEXT: %conv = trunc i64 %0 to i32 +// CHECK-NEXT: ret i32 %conv +// CHECK-NEXT: } + +// CHECK-NOT: declare noundef ptr @_Z8self_refv()