https://github.com/Fznamznon updated https://github.com/llvm/llvm-project/pull/156933
>From 3885133c5a2bafb8654f7b60e016c058705a21ee Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Thu, 4 Sep 2025 10:17:23 -0700 Subject: [PATCH 1/3] [clang] Emit static const/constexpr variables once they're met Do not wait until the body of the function is met. This helps with initializing static variables in conseval functions. Fixes https://github.com/llvm/llvm-project/issues/82994 --- clang/lib/CodeGen/CGDecl.cpp | 21 +++++- clang/test/CodeGenCXX/pr47636.cpp | 2 +- clang/test/CodeGenCXX/reference-init.cpp | 2 +- .../static-constexpr-in-consteval.cpp | 73 +++++++++++++++++++ 4 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 29193e0c541b9..da030f4c73779 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -275,18 +275,31 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl( LangAS AS = GetGlobalVarAddressSpace(&D); unsigned TargetAS = getContext().getTargetAddressSpace(AS); + Expr::EvalResult EvalResult; + llvm::Constant *Init = nullptr; + std::optional<ConstantEmitter> Emitter; // OpenCL variables in local address space and CUDA shared // variables cannot have an initializer. - llvm::Constant *Init = nullptr; if (Ty.getAddressSpace() == LangAS::opencl_local || - D.hasAttr<CUDASharedAttr>() || D.hasAttr<LoaderUninitializedAttr>()) + D.hasAttr<CUDASharedAttr>() || D.hasAttr<LoaderUninitializedAttr>()) { Init = llvm::UndefValue::get(LTy); - else + } else if (D.isUsableInConstantExpressions(getContext())) { + const Expr *InitExpr = D.getInit(); + assert(InitExpr && "Usable in constant expressions without initializer?"); + Emitter.emplace(*this); + llvm::Constant *Initializer = Emitter->tryEmitForInitializer(D); + Init = Initializer; + } else { Init = EmitNullConstant(Ty); + } llvm::GlobalVariable *GV = new llvm::GlobalVariable( - getModule(), LTy, Ty.isConstant(getContext()), Linkage, Init, Name, + getModule(), Init->getType(), Ty.isConstant(getContext()), Linkage, Init, Name, nullptr, llvm::GlobalVariable::NotThreadLocal, TargetAS); + + if (Emitter) + Emitter->finalize(GV); + GV->setAlignment(getContext().getDeclAlign(&D).getAsAlign()); if (supportsCOMDAT() && GV->isWeakForLinker()) diff --git a/clang/test/CodeGenCXX/pr47636.cpp b/clang/test/CodeGenCXX/pr47636.cpp index 597e94695ca5f..022e085e28845 100644 --- a/clang/test/CodeGenCXX/pr47636.cpp +++ b/clang/test/CodeGenCXX/pr47636.cpp @@ -5,8 +5,8 @@ int(&&intu_rvref)[] {1,2,3,4}; void foo() { static const int(&&intu_rvref)[] {1,2,3,4}; - // CHECK: @_ZZ3foovE10intu_rvref = internal constant ptr @_ZGRZ3foovE10intu_rvref_ // CHECK: @_ZGRZ3foovE10intu_rvref_ = internal constant [4 x i32] [i32 1, i32 2, i32 3, i32 4] + // CHECK: @_ZZ3foovE10intu_rvref = internal constant ptr @_ZGRZ3foovE10intu_rvref_ } // Example given on review, ensure this doesn't crash as well. diff --git a/clang/test/CodeGenCXX/reference-init.cpp b/clang/test/CodeGenCXX/reference-init.cpp index a98d400eb17a2..67a901dfe6fa3 100644 --- a/clang/test/CodeGenCXX/reference-init.cpp +++ b/clang/test/CodeGenCXX/reference-init.cpp @@ -4,8 +4,8 @@ // expected-no-diagnostics #if __cplusplus >= 201103L -// CHECK-CXX11: @_ZZ15InitRefWithListvE1r = internal constant ptr @_ZGRZ15InitRefWithListvE1r_ // CHECK-CXX11: @_ZGRZ15InitRefWithListvE1r_ = internal constant i32 123 +// CHECK-CXX11: @_ZZ15InitRefWithListvE1r = internal constant ptr @_ZGRZ15InitRefWithListvE1r_ int InitRefWithList() { static const int &r = {123}; return r; } #endif 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..d818f9725f196 --- /dev/null +++ b/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp @@ -0,0 +1,73 @@ +// 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 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; +} + +// CHECK: @_ZZ1avE1b = linkonce_odr constant i32 3, comdat, align 4 +// CHECK: @_ZZ12returnsarrayvE1a = linkonce_odr constant [3 x i32] [i32 10, i32 20, i32 30], comdat, align 4 + +// 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: %ref.tmp = alloca %struct.S, align 4 +// CHECK-NEXT: %0 = getelementptr inbounds nuw %struct.S, ptr %ref.tmp, i32 0, i32 0 +// CHECK-NEXT: store i32 48, ptr %0, align 4 +// CHECK-NEXT: ret i32 48 +// CHECK-NEXT: } + +int f() { return why(); } +// CHECK: define dso_local noundef i32 @_Z1fv() +// CHECK-NEXT: entry: +// CHECK-NEXT: ret i32 20 +// 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: } >From 995b57487b44dd13933c646275072ad82ce0c482 Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Wed, 10 Sep 2025 07:05:13 -0700 Subject: [PATCH 2/3] [clang] Fix static local variables in consteval functions Because the initializers for static locals were only emitted once the body of the parent functions were emitted, we were always missing initializers for static locals defined inside of consteval functions because their bodies are never handled by CodeGen. This patch attempts to fix that by fitting such variables into existing lazy emission system. --- clang/docs/ReleaseNotes.rst | 3 ++ clang/lib/CodeGen/CGDecl.cpp | 27 ++++++------- clang/lib/CodeGen/CodeGenModule.cpp | 8 +++- clang/test/CodeGenCXX/pr47636.cpp | 2 +- clang/test/CodeGenCXX/reference-init.cpp | 2 +- .../static-constexpr-in-consteval.cpp | 40 ++++++++++++++----- 6 files changed, 55 insertions(+), 27 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 7311610a05396..df5faa4b40618 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -303,6 +303,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 da030f4c73779..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,31 +276,21 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl( LangAS AS = GetGlobalVarAddressSpace(&D); unsigned TargetAS = getContext().getTargetAddressSpace(AS); - Expr::EvalResult EvalResult; llvm::Constant *Init = nullptr; - std::optional<ConstantEmitter> Emitter; // OpenCL variables in local address space and CUDA shared // variables cannot have an initializer. + // Initializers for constant-initialized static locals are deferred. if (Ty.getAddressSpace() == LangAS::opencl_local || D.hasAttr<CUDASharedAttr>() || D.hasAttr<LoaderUninitializedAttr>()) { Init = llvm::UndefValue::get(LTy); - } else if (D.isUsableInConstantExpressions(getContext())) { - const Expr *InitExpr = D.getInit(); - assert(InitExpr && "Usable in constant expressions without initializer?"); - Emitter.emplace(*this); - llvm::Constant *Initializer = Emitter->tryEmitForInitializer(D); - Init = Initializer; - } else { + } else if (!HasConstantInit) { Init = EmitNullConstant(Ty); } llvm::GlobalVariable *GV = new llvm::GlobalVariable( - getModule(), Init->getType(), Ty.isConstant(getContext()), Linkage, Init, Name, + getModule(), LTy, Ty.isConstant(getContext()), Linkage, Init, Name, nullptr, llvm::GlobalVariable::NotThreadLocal, TargetAS); - if (Emitter) - Emitter->finalize(GV); - GV->setAlignment(getContext().getDeclAlign(&D).getAsAlign()); if (supportsCOMDAT() && GV->isWeakForLinker()) @@ -322,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. @@ -349,6 +342,10 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl( assert(isa<ObjCMethodDecl>(DC) && "unexpected parent code decl"); } if (GD.getDecl()) { + // Avoid producing declarations of consteval functions. + if (auto FD = dyn_cast<FunctionDecl>(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); @@ -419,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 @@ -445,7 +443,8 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D, bool isCudaSharedVar = getLangOpts().CUDA && getLangOpts().CUDAIsDevice && D.hasAttr<CUDASharedAttr>(); // 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 f1ddaa875f3e4..124c4459eb92e 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<VarDecl>(D)) + if (const auto *VD = dyn_cast<VarDecl>(D)) { + if (VD->isStaticLocal() && !getContext().shouldExternalize(D)) { + CodeGenFunction(*this).AddInitializerToStaticVarDecl( + *VD, cast<llvm::GlobalVariable>(GV)); + return; + } return EmitGlobalVarDefinition(VD, !VD->hasDefinition()); + } llvm_unreachable("Invalid argument to EmitGlobalDefinition()"); } diff --git a/clang/test/CodeGenCXX/pr47636.cpp b/clang/test/CodeGenCXX/pr47636.cpp index 022e085e28845..597e94695ca5f 100644 --- a/clang/test/CodeGenCXX/pr47636.cpp +++ b/clang/test/CodeGenCXX/pr47636.cpp @@ -5,8 +5,8 @@ int(&&intu_rvref)[] {1,2,3,4}; void foo() { static const int(&&intu_rvref)[] {1,2,3,4}; - // CHECK: @_ZGRZ3foovE10intu_rvref_ = internal constant [4 x i32] [i32 1, i32 2, i32 3, i32 4] // CHECK: @_ZZ3foovE10intu_rvref = internal constant ptr @_ZGRZ3foovE10intu_rvref_ + // CHECK: @_ZGRZ3foovE10intu_rvref_ = internal constant [4 x i32] [i32 1, i32 2, i32 3, i32 4] } // Example given on review, ensure this doesn't crash as well. diff --git a/clang/test/CodeGenCXX/reference-init.cpp b/clang/test/CodeGenCXX/reference-init.cpp index 67a901dfe6fa3..c376145b31ea3 100644 --- a/clang/test/CodeGenCXX/reference-init.cpp +++ b/clang/test/CodeGenCXX/reference-init.cpp @@ -4,7 +4,6 @@ // expected-no-diagnostics #if __cplusplus >= 201103L -// CHECK-CXX11: @_ZGRZ15InitRefWithListvE1r_ = internal constant i32 123 // CHECK-CXX11: @_ZZ15InitRefWithListvE1r = internal constant ptr @_ZGRZ15InitRefWithListvE1r_ 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 index d818f9725f196..a496900672270 100644 --- a/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp +++ b/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp @@ -17,14 +17,14 @@ struct S { consteval auto returns48viastruct() { static constexpr S s{ 48 }; - return s; + return &s; } -consteval int why() { +consteval const int *why() { static constexpr int a = 10; { static constexpr int a = 20; - return a; + return &a; } } @@ -34,8 +34,16 @@ consteval const int* returnsarray() { 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: @@ -50,19 +58,17 @@ int c() { return *a(); } // CHECK-NEXT: } int d() { return returns42(); } -int e() { return returns48viastruct().a; } +int e() { return returns48viastruct()->a; } // CHECK: define dso_local noundef i32 @_Z1ev() // CHECK-NEXT: entry: -// CHECK-NEXT: %ref.tmp = alloca %struct.S, align 4 -// CHECK-NEXT: %0 = getelementptr inbounds nuw %struct.S, ptr %ref.tmp, i32 0, i32 0 -// CHECK-NEXT: store i32 48, ptr %0, align 4 -// CHECK-NEXT: ret i32 48 +// CHECK-NEXT: ret i32 48 // CHECK-NEXT: } -int f() { return why(); } +int f() { return *why(); } // CHECK: define dso_local noundef i32 @_Z1fv() // CHECK-NEXT: entry: -// CHECK-NEXT: ret i32 20 +// CHECK-NEXT: %0 = load i32, ptr @_ZZ3whyvE1a_0, align 4 +// CHECK-NEXT: ret i32 %0 // CHECK-NEXT: } int g() { return returnsarray()[2]; } @@ -71,3 +77,17 @@ int g() { return returnsarray()[2]; } // 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() #0 { +// 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: } >From fedad4ac1c20504efa49f31427ab204151aa0956 Mon Sep 17 00:00:00 2001 From: "Podchishchaeva, Mariya" <mariya.podchishcha...@intel.com> Date: Wed, 10 Sep 2025 08:02:05 -0700 Subject: [PATCH 3/3] Check that there is no decl for consteval function --- clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp b/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp index a496900672270..e8c3143af8c09 100644 --- a/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp +++ b/clang/test/CodeGenCXX/static-constexpr-in-consteval.cpp @@ -83,7 +83,7 @@ int usesself_ref() { size_t b = (size_t)self_ref(); return (int)b; } -// CHECK: define dso_local noundef i32 @_Z12usesself_refv() #0 { +// 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 @@ -91,3 +91,5 @@ int usesself_ref() { // CHECK-NEXT: %conv = trunc i64 %0 to i32 // CHECK-NEXT: ret i32 %conv // CHECK-NEXT: } + +// CHECK-NOT: declare noundef ptr @_Z8self_refv() _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits