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

Reply via email to