[llvm-branch-commits] [clang] [CIR] Add static_local attribute to GlobalOp and GetGlobalOp (PR #179826)

Bruno Cardoso Lopes via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Feb 10 18:10:42 PST 2026


https://github.com/bcardosolopes updated https://github.com/llvm/llvm-project/pull/179826

>From 5056543d687bb31ed2c09db32988838e3f8d233f Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Fri, 30 Jan 2026 22:47:52 -0800
Subject: [PATCH] [CIR] Add static_local attribute to GlobalOp and GetGlobalOp

This attribute marks function-local static variables that require
guarded initialization (e.g., C++ static local variables with
non-constant initializers). It is used by CIRGen to communicate
to LoweringPrepare which globals need guard variable emission.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td | 16 +++++++++-
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp      |  4 +++
 clang/test/CIR/IR/invalid-static-local.cir   | 33 ++++++++++++++++++++
 clang/test/CIR/IR/static-local.cir           | 19 +++++++++++
 4 files changed, 71 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CIR/IR/invalid-static-local.cir
 create mode 100644 clang/test/CIR/IR/static-local.cir

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 20b85dd877d00..ace8246a91ab3 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2335,6 +2335,10 @@ def CIR_GlobalOp : CIR_Op<"global", [
     The `linkage` tracks C/C++ linkage types, currently very similar to LLVM's.
     Symbol visibility in `sym_visibility` is defined in terms of MLIR's visibility
     and verified to be in accordance to `linkage`.
+
+    The `static_local` attribute indicates that this global represents a
+    function-local static variable that requires guarded initialization
+    (e.g., C++ static local variables with non-constant initializers).
   }];
 
   // Note that both sym_name and sym_visibility are tied to Symbol trait.
@@ -2353,6 +2357,7 @@ def CIR_GlobalOp : CIR_Op<"global", [
                        UnitAttr:$comdat,
                        UnitAttr:$constant,
                        UnitAttr:$dso_local,
+                       UnitAttr:$static_local,
                        OptionalAttr<I64Attr>:$alignment);
 
   let regions = (region MaxSizedRegion<1>:$ctorRegion,
@@ -2366,6 +2371,7 @@ def CIR_GlobalOp : CIR_Op<"global", [
     (`comdat` $comdat^)?
     ($tls_model^)?
     (`dso_local` $dso_local^)?
+    (`static_local` $static_local^)?
     $sym_name
     custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value,
                                         $ctorRegion, $dtorRegion)
@@ -2433,19 +2439,27 @@ def CIR_GetGlobalOp : CIR_Op<"get_global", [
     Addresses of thread local globals can only be retrieved if this operation
     is marked `thread_local`, which indicates the address isn't constant.
 
+    The `static_local` attribute indicates that this global is a function-local
+    static variable that requires guarded initialization (e.g., C++ static
+    local variables with non-constant initializers).
+
     Example:
     ```mlir
     %x = cir.get_global @gv : !cir.ptr<i32>
     ...
     %y = cir.get_global thread_local @tls_gv : !cir.ptr<i32>
+    ...
+    %w = cir.get_global static_local @func_static : !cir.ptr<i32>
     ```
   }];
 
-  let arguments = (ins FlatSymbolRefAttr:$name, UnitAttr:$tls);
+  let arguments = (ins FlatSymbolRefAttr:$name, UnitAttr:$tls,
+                       UnitAttr:$static_local);
   let results = (outs Res<CIR_PointerType, "", []>:$addr);
 
   let assemblyFormat = [{
     (`thread_local` $tls^)?
+    (`static_local` $static_local^)?
     $name `:` qualified(type($addr)) attr-dict
   }];
 }
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 9574851a0ae2c..2fe84873c5cdd 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1854,6 +1854,10 @@ cir::GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
     // be marked with tls bits.
     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())
+      return emitOpError("static_local attribute mismatch");
   } else if (auto f = dyn_cast<FuncOp>(op)) {
     symTy = f.getFunctionType();
   } else {
diff --git a/clang/test/CIR/IR/invalid-static-local.cir b/clang/test/CIR/IR/invalid-static-local.cir
new file mode 100644
index 0000000000000..13ef4f63a8288
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-static-local.cir
@@ -0,0 +1,33 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+// Global is not marked static_local, but get_global is
+cir.global "private" internal @_ZZ1fvE1x : !s32i
+
+cir.func @test_static_local_mismatch() {
+  // expected-error @below {{static_local attribute mismatch}}
+  %0 = cir.get_global static_local @_ZZ1fvE1x : !cir.ptr<!s32i>
+  cir.return
+}
+
+}
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+// Global is marked static_local, but get_global is not
+cir.global "private" internal static_local @_ZZ1fvE1y : !s32i
+
+cir.func @test_static_local_mismatch_reverse() {
+  // expected-error @below {{static_local attribute mismatch}}
+  %0 = cir.get_global @_ZZ1fvE1y : !cir.ptr<!s32i>
+  cir.return
+}
+
+}
diff --git a/clang/test/CIR/IR/static-local.cir b/clang/test/CIR/IR/static-local.cir
new file mode 100644
index 0000000000000..d4c5a743358de
--- /dev/null
+++ b/clang/test/CIR/IR/static-local.cir
@@ -0,0 +1,19 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+!s32i = !cir.int<s, 32>
+
+module {
+
+// Test static_local attribute on global and get_global
+cir.global "private" internal static_local @_ZZ1fvE1x : !s32i
+// CHECK: cir.global "private" internal static_local @_ZZ1fvE1x : !s32i
+
+cir.func @test_static_local() {
+  %0 = cir.get_global static_local @_ZZ1fvE1x : !cir.ptr<!s32i>
+  cir.return
+}
+// CHECK: cir.func @test_static_local()
+// CHECK:   %{{.*}} = cir.get_global static_local @_ZZ1fvE1x : !cir.ptr<!s32i>
+// CHECK:   cir.return
+
+}



More information about the llvm-branch-commits mailing list