https://github.com/erichkeane updated 
https://github.com/llvm/llvm-project/pull/216845

>From 726d837234753bd1cc6bcc886f95f03550b45a2d Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Mon, 17 Aug 2026 14:01:55 -0700
Subject: [PATCH] [CIR] Implement 'init_priority' attribute

This attribute sets the order of initialization of a global variable.
The test is pulled from classic codegen, and the rest is pretty rote.
All variables of the same 'priority' are initialized in the same init
function, generated based on the priority number.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |   9 +-
 clang/lib/CIR/CodeGen/CIRGenCXX.cpp           |   3 +
 .../Dialect/Transforms/LoweringPrepare.cpp    |  84 ++++++++---
 .../CIR/CodeGenCXX/init-priority-attr.cpp     | 133 ++++++++++++++++++
 4 files changed, 210 insertions(+), 19 deletions(-)
 create mode 100644 clang/test/CIR/CodeGenCXX/init-priority-attr.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index ccf0a21d56e04..7430b4f0526b6 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3343,6 +3343,12 @@ def CIR_GlobalOp : CIR_Op<"global", [
     into the `ctorRegion`/`dtorRegion` runs under a constrained floating-point
     environment. LoweringPrepare forwards it to the `strictfp` attribute of the
     generated `__cxx_global_var_init` function.
+
+    The `init_priority` attribute records the priority specified by a
+    C++ `init_priority` attribute on the source variable declaration.
+    LoweringPrepare uses it to place the dynamic initializer emitted from
+    `ctorRegion` into a priority-specific `_GLOBAL__I_<priority>` function
+    instead of the default `_GLOBAL__sub_I_*` function.
   }];
 
   // Note that both sym_name and sym_visibility are tied to Symbol trait.
@@ -3369,7 +3375,8 @@ def CIR_GlobalOp : CIR_Op<"global", [
                        OptionalAttr<StrAttr>:$section,
                        OptionalAttr<CIR_AnnotationArrayAttr>:$annotations,
                        OptionalAttr<FlatSymbolRefAttr>:$aliasee,
-                       UnitAttr:$strictfp
+                       UnitAttr:$strictfp,
+                       OptionalAttr<I32Attr>:$init_priority
                        );
 
   let regions = (region AnyRegion:$ctorRegion,
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
index 812ecc1016dcc..3b9d60e4b11be 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp
@@ -364,6 +364,9 @@ void CIRGenModule::emitCXXGlobalVarDeclInit(const VarDecl 
*varDecl,
     addr.setStrictfp(true);
   }
 
+  if (const auto *ipa = varDecl->getAttr<InitPriorityAttr>())
+    addr.setInitPriority(ipa->getPriority());
+
   emitCXXSpecialVarDeclInit(varDecl, addr, performInit, addr.getCtorRegion(),
                             addr.getDtorRegion());
 }
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 42ab14aba338e..f2e316aef1cdc 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -37,6 +37,7 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/VirtualFileSystem.h"
 
+#include <map>
 #include <memory>
 #include <optional>
 
@@ -141,8 +142,21 @@ struct LoweringPreparePass
                                   mlir::Region &dtorRegion,
                                   cir::CallOp &dtorCall);
 
+  /// Build a function named `fnName` with the given linkage that calls each
+  /// of `initializers` in order, then returns, and register it in
+  /// `globalCtorList` under `priority`. Shared by the default
+  /// `_GLOBAL__sub_I_*` initializer function and the per-priority
+  /// `_GLOBAL__I_<priority>` initializer functions.
+  cir::FuncOp buildGlobalInitCallerFunc(
+      llvm::StringRef fnName, cir::GlobalLinkageKind linkage,
+      llvm::ArrayRef<cir::FuncOp> initializers, uint32_t priority);
+
   /// Build a module init function that calls all the dynamic initializers.
   void buildCXXGlobalInitFunc();
+  /// Build one `_GLOBAL__I_<priority>` function per distinct priority found
+  /// in `prioritizedDynamicInitializers`, in ascending priority order, and
+  /// register each with `globalCtorList`.
+  void buildCXXGlobalPriorityInitFuncs();
   // Build an init function for all of the ordered global thread local storage
   // variables.
   void buildCXXGlobalTlsFunc();
@@ -308,6 +322,12 @@ struct LoweringPreparePass
   /// Tracks existing dynamic initializers.
   llvm::StringMap<uint32_t> dynamicInitializerNames;
   llvm::SmallVector<cir::FuncOp> dynamicInitializers;
+  /// Dynamic initializers with an explicit `init_priority` attribute,
+  /// grouped by priority (keys kept in ascending order). Each group is
+  /// emitted into its own `_GLOBAL__I_<priority>` function instead of being
+  /// folded into the single default-priority `_GLOBAL__sub_I_*` function.
+  std::map<unsigned, llvm::SmallVector<cir::FuncOp, 4>>
+      prioritizedDynamicInitializers;
   llvm::SmallVector<cir::FuncOp> globalThreadLocalInitializers;
   llvm::StringMap<cir::FuncOp> threadLocalWrappers;
   llvm::StringMap<cir::FuncOp> threadLocalInitAliases;
@@ -1638,6 +1658,8 @@ void LoweringPreparePass::lowerGlobalOp(GlobalOp op) {
         // it later.
         globalThreadLocalInitializers.push_back(f);
       }
+    } else if (std::optional<uint32_t> priority = op.getInitPriority()) {
+      prioritizedDynamicInitializers[*priority].push_back(f);
     } else {
       dynamicInitializers.push_back(f);
     }
@@ -1857,14 +1879,52 @@ void LoweringPreparePass::buildCXXGlobalTlsFunc() {
   cir::ReturnOp::create(builder, loc);
 }
 
+/// Compute the zero-padded priority suffix used to name priority-specific
+/// global init functions, so that the function names also sort in priority
+/// order (e.g. 200 -> "000200").
+static std::string getPrioritySuffix(unsigned priority) {
+  assert(priority <= 65535 && "Priority should always be <= 65535.");
+  std::string prioritySuffix = llvm::utostr(priority);
+  prioritySuffix = std::string(6 - prioritySuffix.size(), '0') + 
prioritySuffix;
+  return prioritySuffix;
+}
+
+cir::FuncOp LoweringPreparePass::buildGlobalInitCallerFunc(
+    llvm::StringRef fnName, cir::GlobalLinkageKind linkage,
+    llvm::ArrayRef<cir::FuncOp> initializers, uint32_t priority) {
+  CIRBaseBuilderTy builder(getContext());
+  builder.setInsertionPointToEnd(&mlirModule.getBodyRegion().back());
+  auto fnType = cir::FuncType::get({}, builder.getVoidTy());
+  cir::FuncOp fn = buildRuntimeFunction(builder, fnName, mlirModule.getLoc(),
+                                        fnType, linkage);
+  builder.setInsertionPointToStart(fn.addEntryBlock());
+  for (cir::FuncOp init : initializers)
+    builder.createCallOp(init.getLoc(), init, {});
+  cir::ReturnOp::create(builder, fn.getLoc());
+  globalCtorList.emplace_back(fnName, priority);
+  return fn;
+}
+
+void LoweringPreparePass::buildCXXGlobalPriorityInitFuncs() {
+  // std::map keeps priorities in ascending order, so each group is already
+  // ready to emit into its own function, named after its priority so that
+  // the functions are naturally ordered relative to one another.
+  for (const auto &[priority, initializers] : prioritizedDynamicInitializers) {
+    SmallString<256> fnName;
+    fnName += "_GLOBAL__I_";
+    fnName += getPrioritySuffix(priority);
+
+    buildGlobalInitCallerFunc(fnName, cir::GlobalLinkageKind::InternalLinkage,
+                              initializers, priority);
+  }
+}
+
 void LoweringPreparePass::buildCXXGlobalInitFunc() {
+  buildCXXGlobalPriorityInitFuncs();
+
   if (dynamicInitializers.empty())
     return;
 
-  // TODO: handle globals with a user-specified initialzation priority.
-  // TODO: handle default priority more nicely.
-  assert(!cir::MissingFeatures::opGlobalCtorPriority());
-
   SmallString<256> fnName;
   cir::GlobalLinkageKind linkage;
   // Include the filename in the symbol name. Including "sub_" matches gcc
@@ -1886,20 +1946,8 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() {
     linkage = cir::GlobalLinkageKind::InternalLinkage;
   }
 
-  CIRBaseBuilderTy builder(getContext());
-  builder.setInsertionPointToEnd(&mlirModule.getBodyRegion().back());
-  auto fnType = cir::FuncType::get({}, builder.getVoidTy());
-  cir::FuncOp f = buildRuntimeFunction(builder, fnName, mlirModule.getLoc(),
-                                       fnType, linkage);
-  builder.setInsertionPointToStart(f.addEntryBlock());
-  for (cir::FuncOp &f : dynamicInitializers)
-    builder.createCallOp(f.getLoc(), f, {});
-  // Add the global init function (not the individual ctor functions) to the
-  // global ctor list.
-  globalCtorList.emplace_back(fnName,
-                              cir::GlobalCtorAttr::getDefaultPriority());
-
-  cir::ReturnOp::create(builder, f.getLoc());
+  buildGlobalInitCallerFunc(fnName, linkage, dynamicInitializers,
+                            cir::GlobalCtorAttr::getDefaultPriority());
 }
 
 /// Lower a cir.array.ctor or cir.array.dtor into a do-while loop that
diff --git a/clang/test/CIR/CodeGenCXX/init-priority-attr.cpp 
b/clang/test/CIR/CodeGenCXX/init-priority-attr.cpp
new file mode 100644
index 0000000000000..d5a45357f8d27
--- /dev/null
+++ b/clang/test/CIR/CodeGenCXX/init-priority-attr.cpp
@@ -0,0 +1,133 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir 
--mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir
+// RUN: FileCheck --input-file=%t-before.cir %s --check-prefix=CIR-BEFORE-LPP
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
- | FileCheck %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | 
FileCheck %s -check-prefix=LLVM,OGCG
+
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fclangir -emit-cir -mmlir 
--mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir
+// RUN: FileCheck --input-file=%t-before.cir %s --check-prefix=CIR-BEFORE-LPP
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fclangir -emit-llvm %s -o - 
| FileCheck %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm %s -o - | 
FileCheck %s -check-prefix=LLVM,OGCG
+
+void foo(int);
+
+class A {
+public:
+  A() { foo(1); }
+};
+
+class A1 {
+public:
+  A1() { foo(2); }
+};
+
+class B {
+public:
+  B() { foo(3); }
+};
+
+class C {
+public:
+  static A a;
+  C() { foo(4); }
+};
+
+
+A C::a = A();
+
+
+// Static inside of C.
+// CIR-BEFORE-LPP: cir.global external @_ZN1C1aE = ctor : !rec_A {
+// CIR-BEFORE-LPP: } {alignment = 1 : i64, ast = #cir.var.decl.ast}
+// CIR-BEFORE-LPP: cir.global external @c = ctor : !rec_C {
+// CIR-BEFORE-LPP: } {alignment = 1 : i64, ast = #cir.var.decl.ast}
+
+// CIR-BEFORE-LPP: cir.global external @a1 = ctor : !rec_A1 {
+// CIR-BEFORE-LPP: } {alignment = 1 : i64, ast = #cir.var.decl.ast, 
init_priority = 300 : i32}
+// CIR-BEFORE-LPP: cir.global external @a = ctor : !rec_A {
+// CIR-BEFORE-LPP: } {alignment = 1 : i64, ast = #cir.var.decl.ast, 
init_priority = 300 : i32}
+// CIR-BEFORE-LPP: cir.global external @b = ctor : !rec_B {
+// CIR-BEFORE-LPP: } {alignment = 1 : i64, ast = #cir.var.decl.ast, 
init_priority = 200 : i32}
+
+// CIR: cir.global_ctors = [#cir.global_ctor<"_GLOBAL__I_000200", 200>, 
#cir.global_ctor<"_GLOBAL__I_000300", 300>, 
#cir.global_ctor<"_GLOBAL__sub_I_[[FILENAME:.*]]", 65535>]
+// CIR-LABEL: cir.func internal private @__cxx_global_var_init() {
+// CIR-NEXT:    cir.get_global @_ZN1C1aE : !cir.ptr<!rec_A>
+// CIR-NEXT:    cir.call @_ZN1AC1Ev(
+// CIR-NEXT:    cir.return
+//
+// CIR-LABEL: cir.func internal private @__cxx_global_var_init.1() {
+// CIR-NEXT:    cir.get_global @c : !cir.ptr<!rec_C>
+// CIR-NEXT:    cir.call @_ZN1CC1Ev(
+// CIR-NEXT:    cir.return
+
+// CIR-LABEL: cir.func internal private @__cxx_global_var_init.2() {
+// CIR-NEXT:    cir.get_global @a1 : !cir.ptr<!rec_A1> loc(#loc40)
+// CIR-NEXT:    cir.call @_ZN2A1C1Ev(
+// CIR-NEXT:    cir.return
+
+// CIR-LABEL: cir.func internal private @__cxx_global_var_init.4() {
+// CIR-NEXT:    cir.get_global @b : !cir.ptr<!rec_B> loc(#loc43)
+// CIR-NEXT:    cir.call @_ZN1BC1Ev(
+// CIR-NEXT:    cir.return
+
+// CIR-LABEL: cir.func internal private @_GLOBAL__I_000200() {
+// CIR-NEXT:   cir.call @__cxx_global_var_init.4() : () -> ()
+// CIR-NEXT:   cir.return
+
+// CIR-LABEL: cir.func internal private @_GLOBAL__I_000300() {
+// CIR-NEXT:   cir.call @__cxx_global_var_init.2() : () -> ()
+// CIR-NEXT:   cir.call @__cxx_global_var_init.3() : () -> ()
+// CIR-NEXT:   cir.return
+
+// CIR-LABEL: cir.func internal private @_GLOBAL__sub_I_
+// CIR-SAME: [[FILENAME]]() {
+// CIR-NEXT:   cir.call @__cxx_global_var_init() : () -> ()
+// CIR-NEXT:   cir.call @__cxx_global_var_init.1() : () -> ()
+// CIR-NEXT:   cir.return
+
+// LLVM: @llvm.global_ctors = appending global [3 x { i32, ptr, ptr }] [{ i32, 
ptr, ptr } { i32 200, ptr @_GLOBAL__I_000200, ptr null }, { i32, ptr, ptr } { 
i32 300, ptr @_GLOBAL__I_000300, ptr null }, { i32, ptr, ptr } { i32 65535, ptr 
@_GLOBAL__sub_I_[[FILENAME:.*]], ptr null }]
+
+// LLVM-LABEL: define internal void @__cxx_global_var_init()
+// OGCG-NEXT: entry:
+// LLVM-NEXT:   call void @_ZN1AC1Ev({{.*}}@_ZN1C1aE)
+
+// LLVM-LABEL: define internal void @__cxx_global_var_init.1()
+// OGCG-NEXT: entry:
+// LLVM-NEXT:   call void @_ZN1CC1Ev({{.*}}@c)
+
+// LLVM-LABEL: define internal void @__cxx_global_var_init.2()
+// OGCG-NEXT: entry:
+// LLVM-NEXT:   call void @_ZN2A1C1Ev({{.*}}@a1)
+
+// LLVM-LABEL: define internal void @__cxx_global_var_init.3()
+// OGCG-NEXT: entry:
+// LLVM-NEXT:   call void @_ZN1AC1Ev({{.*}}@a)
+
+// LLVM-LABEL: define internal void @__cxx_global_var_init.4()
+// OGCG-NEXT: entry:
+// LLVM-NEXT:   call void @_ZN1BC1Ev({{.*}}@b)
+
+// LLVM-LABEL: define internal void @_GLOBAL__I_000200()
+// OGCG-NEXT: entry:
+// LLVM-NEXT:   call void @__cxx_global_var_init.4()
+// LLVM-NEXT:   ret void
+
+// LLVM-LABEL: define internal void @_GLOBAL__I_000300()
+// OGCG-NEXT: entry:
+// LLVM-NEXT:   call void @__cxx_global_var_init.2()
+// LLVM-NEXT:   call void @__cxx_global_var_init.3()
+// LLVM-NEXT:   ret void
+
+// LLVM-LABEL: define internal void @_GLOBAL__sub_I_
+// LLVM-SAME: [[FILENAME]]()
+// OGCG-NEXT: entry:
+// LLVM-NEXT:   call void @__cxx_global_var_init()
+// LLVM-NEXT:   call void @__cxx_global_var_init.1()
+// LLVM-NEXT:   ret void
+
+
+C c;
+A1 a1 __attribute__((init_priority (300)));
+A a __attribute__((init_priority (300)));
+B b __attribute__((init_priority (200)));

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to