https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/179827
>From c9509827e8cde0afe8181ba382bd59eb99f38a96 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes <[email protected]> Date: Fri, 30 Jan 2026 22:48:48 -0800 Subject: [PATCH] [CIR] Add CIRGen support for static local variables with non-constant initializers This adds CIRGen infrastructure for C++ function-local static variables that require guarded initialization (Itanium C++ ABI). Changes: - Add ASTVarDeclAttr to carry VarDecl AST through the pipeline - Add emitGuardedInit() to CIRGenCXXABI for guarded initialization - Add emitCXXGuardedInit() to CIRGenFunction - Replace NYI in addInitializerToStaticVarDecl() with ctor region emission - Set static_local attribute on GlobalOp and GetGlobalOp The global's ctor region contains the initialization code, which will be lowered by LoweringPrepare to emit the actual guard variable pattern with __cxa_guard_acquire/__cxa_guard_release calls. --- clang/include/clang/CIR/Dialect/IR/CIRAttrs.h | 1 + .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 41 +++++++++++++++++++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 3 +- clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 3 +- clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 17 ++++++-- clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp | 18 ++++++++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 5 +++ clang/lib/CIR/CodeGen/CIRGenModule.cpp | 3 +- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 5 ++- .../Dialect/Transforms/LoweringPrepare.cpp | 4 ++ clang/test/CIR/CodeGen/global-array-dtor.cpp | 2 +- clang/test/CIR/CodeGen/global-init.cpp | 2 +- clang/test/CIR/CodeGen/static-local.cpp | 30 ++++++++++++++ 13 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 clang/test/CIR/CodeGen/static-local.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h index 858d4d6350bed..eb87dc083b0f5 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.h @@ -19,6 +19,7 @@ #include "clang/CIR/Dialect/IR/CIROpsEnums.h" +#include "clang/CIR/Interfaces/ASTAttrInterfaces.h" #include "clang/CIR/Interfaces/CIRTypeInterfaces.h" //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index d7938bc350925..47a0b3394c48e 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -19,6 +19,8 @@ include "clang/CIR/Dialect/IR/CIRAttrConstraints.td" include "clang/CIR/Dialect/IR/CIRDialect.td" include "clang/CIR/Dialect/IR/CIREnumAttr.td" +include "clang/CIR/Interfaces/ASTAttrInterfaces.td" + //===----------------------------------------------------------------------===// // CIR Attrs //===----------------------------------------------------------------------===// @@ -1286,5 +1288,44 @@ def CIR_SideEffect : CIR_I32EnumAttr< }]; } +//===----------------------------------------------------------------------===// +// AST Wrappers +//===----------------------------------------------------------------------===// + +class CIR_AST<string name, string prefix, list<Trait> traits = []> + : CIR_Attr<!strconcat("AST", name), !strconcat(prefix, ".ast"), traits> { + string clang_name = !strconcat("const clang::", name, " *"); + + let summary = !strconcat("Wraps a '", clang_name, "' AST node."); + let description = [{ + Operations optionally refer to this node, they could be available depending + on the CIR lowering stage. Whether it's attached to the appropriate + CIR operation is delegated to the operation verifier. + + Note: the AST pointer can be null when CIR is parsed from text, since + there is no serialization support for AST nodes yet. + }]; + let parameters = (ins clang_name:$ast); + + // Printing and parsing available in CIRDialect.cpp + let hasCustomAssemblyFormat = 1; + + let extraClassDefinition = [{ + ::mlir::Attribute $cppClass::parse(::mlir::AsmParser &parser, + ::mlir::Type type) { + // We cannot really parse anything AST related at this point + // since we have no serialization/JSON story. + return $cppClass::get(parser.getContext(), nullptr); + } + + void $cppClass::print(::mlir::AsmPrinter &printer) const { + // Nothing to print besides the mnemonics. + } + }]; +} + +def CIR_ASTVarDeclAttr : CIR_AST<"VarDecl", "var.decl", [ + ASTVarDeclInterface +]>; #endif // CLANG_CIR_DIALECT_IR_CIRATTRS_TD diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 0ce7420e4bc9a..25b09c9eb4ed4 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2408,7 +2408,8 @@ def CIR_GlobalOp : CIR_Op<"global", [ UnitAttr:$constant, UnitAttr:$dso_local, UnitAttr:$static_local, - OptionalAttr<I64Attr>:$alignment); + OptionalAttr<I64Attr>:$alignment, + OptionalAttr<ASTVarDeclInterface>:$ast); let regions = (region MaxSizedRegion<1>:$ctorRegion, MaxSizedRegion<1>:$dtorRegion); diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp index f8a058b521c54..c3457e40a9110 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp @@ -15,6 +15,7 @@ #include "CIRGenModule.h" #include "clang/AST/GlobalDecl.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/MissingFeatures.h" #include "llvm/Support/SaveAndRestore.h" @@ -256,7 +257,7 @@ void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl *varDecl, CIRGenFunction::SourceLocRAIIObject fnLoc{cgf, getLoc(varDecl->getLocation())}; - assert(!cir::MissingFeatures::astVarDeclInterface()); + addr.setAstAttr(cir::ASTVarDeclAttr::get(&getMLIRContext(), varDecl)); if (!ty->isReferenceType()) { assert(!cir::MissingFeatures::openMP()); diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 42a7d70677b61..66bc7680965d4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -568,13 +568,24 @@ Address CIRGenModule::createUnnamedGlobalFrom(const VarDecl &d, cir::GlobalOp CIRGenFunction::addInitializerToStaticVarDecl( const VarDecl &d, cir::GlobalOp gv, cir::GetGlobalOp gvAddr) { ConstantEmitter emitter(*this); - mlir::TypedAttr init = - mlir::cast<mlir::TypedAttr>(emitter.tryEmitForInitializer(d)); + mlir::TypedAttr init = mlir::dyn_cast_if_present<mlir::TypedAttr>( + emitter.tryEmitForInitializer(d)); // If constant emission failed, then this should be a C++ static // initializer. if (!init) { - cgm.errorNYI(d.getSourceRange(), "static var without initializer"); + if (!getLangOpts().CPlusPlus) { + cgm.errorNYI(d.getInit()->getSourceRange(), + "constant l-value expression"); + } else if (d.hasFlexibleArrayInit(getContext())) { + cgm.errorNYI(d.getInit()->getSourceRange(), "flexible array initializer"); + } else { + // Since we have a static initializer, this global variable can't + // be constant. + gv.setConstant(false); + emitCXXGuardedInit(d, gv, /*performInit*/ true); + gvAddr.setStaticLocal(true); + } return gv; } diff --git a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp index d1efed80aaf0e..9c2a5fe0cda07 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "CIRGenFunction.h" #include "CIRGenModule.h" #include "clang/AST/Attr.h" #include "clang/Basic/LangOptions.h" @@ -17,6 +18,23 @@ using namespace clang; using namespace clang::CIRGen; +void CIRGenFunction::emitCXXGuardedInit(const VarDecl &varDecl, + cir::GlobalOp globalOp, + bool performInit) { + // If we've been asked to forbid guard variables, emit an error now. + // This diagnostic is hard-coded for Darwin's use case; we can find + // better phrasing if someone else needs it. + if (cgm.getCodeGenOpts().ForbidGuardVariables) + cgm.error(varDecl.getLocation(), "guard variables are forbidden"); + + // Emit the initializer and add a global destructor if appropriate. + cgm.emitCXXGlobalVarDeclInit(&varDecl, globalOp, performInit); + + // Mark the global as static local. The emission of the guard/acquire + // is done during LoweringPrepare. + globalOp.setStaticLocal(true); +} + void CIRGenModule::emitCXXGlobalVarDeclInitFunc(const VarDecl *vd, cir::GlobalOp addr, bool performInit) { diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 3b07273f513e6..786a0f6e9e23c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1849,6 +1849,11 @@ class CIRGenFunction : public CIRGenTypeCache { void emitStaticVarDecl(const VarDecl &d, cir::GlobalLinkageKind linkage); + /// Emit a guarded initializer for a static local variable or a static + /// data member of a class template instantiation. + void emitCXXGuardedInit(const VarDecl &varDecl, cir::GlobalOp globalOp, + bool performInit); + void emitStoreOfComplex(mlir::Location loc, mlir::Value v, LValue dest, bool isInit); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 007d501f25014..133eb0f3dfe7e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -830,7 +830,8 @@ mlir::Value CIRGenModule::getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty, cir::GlobalOp g = getOrCreateCIRGlobal(d, ty, isForDefinition); mlir::Type ptrTy = builder.getPointerTo(g.getSymType()); return cir::GetGlobalOp::create(builder, getLoc(d->getSourceRange()), ptrTy, - g.getSymNameAttr(), tlsAccess); + g.getSymNameAttr(), tlsAccess, + g.getStaticLocal()); } cir::GlobalViewAttr CIRGenModule::getAddrOfGlobalVarAttr(const VarDecl *d) { diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 2fe84873c5cdd..c38a77a7598b8 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1855,8 +1855,9 @@ cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) { if (getTls() && !g.getTlsModel()) return emitOpError("access to global not marked thread local"); // Verify that the static_local attribute matches between get_global and - // global. - if (getStaticLocal() != g.getStaticLocal()) + // global. Skip when inside a GlobalOp region (e.g., ctor/dtor regions). + if (getStaticLocal() != g.getStaticLocal() && + !getOperation()->getParentOfType<cir::GlobalOp>()) return emitOpError("static_local attribute mismatch"); } else if (auto f = dyn_cast<FuncOp>(op)) { symTy = f.getFunctionType(); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index b7cc8775d298f..5c6d40027158f 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -872,6 +872,10 @@ LoweringPreparePass::buildCXXGlobalVarDeclInitFunc(cir::GlobalOp op) { } void LoweringPreparePass::lowerGlobalOp(GlobalOp op) { + // Static locals are handled separately via guard variables. + if (op.getStaticLocal()) + return; + mlir::Region &ctorRegion = op.getCtorRegion(); mlir::Region &dtorRegion = op.getDtorRegion(); diff --git a/clang/test/CIR/CodeGen/global-array-dtor.cpp b/clang/test/CIR/CodeGen/global-array-dtor.cpp index 01277a3f34015..43acb5b79e5f0 100644 --- a/clang/test/CIR/CodeGen/global-array-dtor.cpp +++ b/clang/test/CIR/CodeGen/global-array-dtor.cpp @@ -26,7 +26,7 @@ ArrayDtor arrDtor[16]; // CIR-BEFORE-LPP: } // CIR-BEFORE-LPP: } -// CIR: cir.global external @arrDtor = #cir.zero : !cir.array<!rec_ArrayDtor x 16> {alignment = 16 : i64} +// CIR: cir.global external @arrDtor = #cir.zero : !cir.array<!rec_ArrayDtor x 16> // CIR: cir.func internal private @__cxx_global_array_dtor(%[[ARR_ARG:.*]]: !cir.ptr<!void> {{.*}}) { // CIR: %[[CONST15:.*]] = cir.const #cir.int<15> : !u64i // CIR: %[[BEGIN:.*]] = cir.cast array_to_ptrdecay %[[ARR_ARG]] : !cir.ptr<!void> -> !cir.ptr<!rec_ArrayDtor> diff --git a/clang/test/CIR/CodeGen/global-init.cpp b/clang/test/CIR/CodeGen/global-init.cpp index 3510e3e82f4e8..ecfe25eed28cc 100644 --- a/clang/test/CIR/CodeGen/global-init.cpp +++ b/clang/test/CIR/CodeGen/global-init.cpp @@ -187,7 +187,7 @@ ArrayDtor arrDtor[16]; // CIR-BEFORE-LPP: } // CIR-BEFORE-LPP: } -// CIR: cir.global external @arrDtor = #cir.zero : !cir.array<!rec_ArrayDtor x 16> {alignment = 16 : i64} +// CIR: cir.global external @arrDtor = #cir.zero : !cir.array<!rec_ArrayDtor x 16> // CIR: cir.func internal private @__cxx_global_array_dtor(%[[ARR_ARG:.*]]: !cir.ptr<!void> {{.*}}) { // CIR: %[[CONST15:.*]] = cir.const #cir.int<15> : !u64i // CIR: %[[BEGIN:.*]] = cir.cast array_to_ptrdecay %[[ARR_ARG]] : !cir.ptr<!void> -> !cir.ptr<!rec_ArrayDtor> diff --git a/clang/test/CIR/CodeGen/static-local.cpp b/clang/test/CIR/CodeGen/static-local.cpp new file mode 100644 index 0000000000000..0297028e96438 --- /dev/null +++ b/clang/test/CIR/CodeGen/static-local.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2>&1 | FileCheck %s --check-prefix=CIR-BEFORE-LPP +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=OGCG + +class A { +public: + A(); +}; + +void f() { + static A a; +} + +// CIR-BEFORE-LPP: cir.global "private" internal dso_local static_local @_ZZ1fvE1a = ctor : !rec_A { +// CIR-BEFORE-LPP: %[[ADDR:.*]] = cir.get_global @_ZZ1fvE1a : !cir.ptr<!rec_A> +// CIR-BEFORE-LPP: cir.call @_ZN1AC1Ev(%[[ADDR]]) : (!cir.ptr<!rec_A>) -> () +// CIR-BEFORE-LPP: } {alignment = 1 : i64, ast = #cir.var.decl.ast} + +// CIR-BEFORE-LPP: cir.func no_inline dso_local @_Z1fv() +// CIR-BEFORE-LPP: %[[VAR:.*]] = cir.get_global static_local @_ZZ1fvE1a : !cir.ptr<!rec_A> +// CIR-BEFORE-LPP: cir.return + +// OGCG: @_ZGVZ1fvE1a = internal global i64 0 +// OGCG: define{{.*}}void @_Z1fv() +// OGCG: %[[GUARD:.*]] = load atomic i8, ptr @_ZGVZ1fvE1a acquire +// OGCG: %[[IS_UNINIT:.*]] = icmp eq i8 %[[GUARD]], 0 +// OGCG: br i1 %[[IS_UNINIT]] +// OGCG: call i32 @__cxa_guard_acquire +// OGCG: call void @_ZN1AC1Ev +// OGCG: call void @__cxa_guard_release +// OGCG: ret void _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
