[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