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

Reply via email to