https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/179827
>From 1bdd0ea3166fff48964de4517efb6fb1e3e28921 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 | 49 +++++++++++++++++++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 3 +- clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 3 +- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 7 +++ clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 16 ++++-- clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp | 14 ++++++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 5 ++ clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 15 ++++++ 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 ++++++++++++ 12 files changed, 140 insertions(+), 7 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..0d2648e8c82d7 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,52 @@ 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 appropriated + CIR operation is delegated to the operation verifier. + + This always implies a non-null AST reference (verified). + }]; + let parameters = (ins clang_name:$ast); + + // Printing and parsing available in CIRDialect.cpp + let hasCustomAssemblyFormat = 1; + + // Enable verifier. + let genVerifyDecl = 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. + } + + llvm::LogicalResult $cppClass::verify( + ::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError, + }] # clang_name # [{ decl) { + return mlir::success(); + } + }]; +} + +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 c37764ed70202..282733f0045bf 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2358,7 +2358,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/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 27d48bfabeb38..3d6db471f0c99 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -188,6 +188,13 @@ class CIRGenCXXABI { bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) = 0; + /// Emit a guarded initializer for a static local variable or a static + /// data member. This handles: + /// - a static local variable + /// - a static data member of a class template instantiation + virtual void emitGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl, + cir::GlobalOp globalOp, bool performInit) = 0; + /// Emit code to force the execution of a destructor during global /// teardown. The default implementation of this uses atexit. /// diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 42a7d70677b61..8fa38b093e958 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -568,13 +568,23 @@ 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.errorUnsupported(d.getInit(), "constant l-value expression"); + else if (d.hasFlexibleArrayInit(getContext())) + cgm.errorUnsupported(d.getInit(), "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..dcc27006cb87e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDeclCXX.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "CIRGenCXXABI.h" +#include "CIRGenFunction.h" #include "CIRGenModule.h" #include "clang/AST/Attr.h" #include "clang/Basic/LangOptions.h" @@ -17,6 +19,18 @@ 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.errorNYI(varDecl.getLocation(), "guard variables are forbidden"); + + cgm.getCXXABI().emitGuardedInit(*this, varDecl, globalOp, performInit); +} + 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 adcf4d56e3892..551dc6303d723 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1842,6 +1842,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/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index aca2278c3876c..1c1a87a673e25 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -74,6 +74,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { QualType thisTy) override; void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor, mlir::Value addr) override; + void emitGuardedInit(CIRGenFunction &cgf, const VarDecl &varDecl, + cir::GlobalOp globalOp, bool performInit) override; void emitVirtualObjectDelete(CIRGenFunction &cgf, const CXXDeleteExpr *de, Address ptr, QualType elementType, const CXXDestructorDecl *dtor) override; @@ -1592,6 +1594,19 @@ void CIRGenItaniumCXXABI::registerGlobalDtor(const VarDecl *vd, // prepare. Nothing to be done for CIR here. } +void CIRGenItaniumCXXABI::emitGuardedInit(CIRGenFunction &cgf, + const VarDecl &varDecl, + cir::GlobalOp globalOp, + bool performInit) { + // Emit the initializer and add a global destructor if appropriate. + cgf.cgm.emitCXXGlobalVarDeclInit(&varDecl, globalOp, performInit); + + // CIR diverges from IRGen here by emitting the init into the ctor region and + // marking the global as static local. The emission of the guard/acquire walk + // is done during LoweringPrepare. + globalOp.setStaticLocal(true); +} + mlir::Value CIRGenItaniumCXXABI::getCXXDestructorImplicitParam( CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type, bool forVirtualBase, bool delegating) { diff --git a/clang/test/CIR/CodeGen/global-array-dtor.cpp b/clang/test/CIR/CodeGen/global-array-dtor.cpp index 01277a3f34015..b39373c0d61bb 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> {alignment = 16 : i64{{.*}}} // 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..51246cbe1c86a 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> {alignment = 16 : i64{{.*}}} // 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 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
