[Mlir-commits] [mlir] 7c3ecff - [MLIR][LLVMIR] Add support for the full form of global_{ctor, dtor} (#133176)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Mar 27 14:11:09 PDT 2025


Author: Bruno Cardoso Lopes
Date: 2025-03-27T14:11:05-07:00
New Revision: 7c3ecffe9b23411463919742ad8399f6aa48af4b

URL: https://github.com/llvm/llvm-project/commit/7c3ecffe9b23411463919742ad8399f6aa48af4b
DIFF: https://github.com/llvm/llvm-project/commit/7c3ecffe9b23411463919742ad8399f6aa48af4b.diff

LOG: [MLIR][LLVMIR] Add support for the full form of global_{ctor,dtor} (#133176)

Currently only ctor/dtor list and their priorities are supported. This
PR adds support for the missing data field.

Few implementation notes:
- The assembly printer has a fixed form because previous `attr_dict`
will sort the dict by key name, making global_dtor and global_ctor
differ in the order of printed arguments.
- LLVM's `ptr null` is being converted to `#llvm.zero` otherwise we'd
have to create a region to use the default operation conversion from
`ptr null`, which is silly given that the field only support null or a
symbol.

Added: 
    

Modified: 
    flang/lib/Optimizer/Transforms/CUFAddConstructor.cpp
    flang/test/Fir/CUDA/cuda-constructor.f90
    mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
    mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
    mlir/lib/Target/LLVMIR/ModuleImport.cpp
    mlir/test/Dialect/LLVMIR/global.mlir
    mlir/test/Dialect/LLVMIR/invalid.mlir
    mlir/test/Target/LLVMIR/Import/global-variables.ll
    mlir/test/Target/LLVMIR/Import/import-failure.ll
    mlir/test/Target/LLVMIR/llvmir.mlir
    mlir/test/mlir-runner/global-constructors.mlir

Removed: 
    


################################################################################
diff  --git a/flang/lib/Optimizer/Transforms/CUFAddConstructor.cpp b/flang/lib/Optimizer/Transforms/CUFAddConstructor.cpp
index 43ef6822de302..ad39640235e91 100644
--- a/flang/lib/Optimizer/Transforms/CUFAddConstructor.cpp
+++ b/flang/lib/Optimizer/Transforms/CUFAddConstructor.cpp
@@ -23,6 +23,7 @@
 #include "flang/Runtime/CUDA/registration.h"
 #include "flang/Runtime/entry-names.h"
 #include "mlir/Dialect/GPU/IR/GPUDialect.h"
+#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
 #include "mlir/IR/Value.h"
 #include "mlir/Pass/Pass.h"
@@ -157,10 +158,12 @@ struct CUFAddConstructor
     funcs.push_back(
         mlir::FlatSymbolRefAttr::get(mod.getContext(), func.getSymName()));
     llvm::SmallVector<int> priorities;
+    llvm::SmallVector<mlir::Attribute> data;
     priorities.push_back(0);
+    data.push_back(mlir::LLVM::ZeroAttr::get(mod.getContext()));
     builder.create<mlir::LLVM::GlobalCtorsOp>(
         mod.getLoc(), builder.getArrayAttr(funcs),
-        builder.getI32ArrayAttr(priorities));
+        builder.getI32ArrayAttr(priorities), builder.getArrayAttr(data));
   }
 };
 

diff  --git a/flang/test/Fir/CUDA/cuda-constructor.f90 b/flang/test/Fir/CUDA/cuda-constructor.f90
index d02350b4f4198..1ba3fdc4a6b73 100644
--- a/flang/test/Fir/CUDA/cuda-constructor.f90
+++ b/flang/test/Fir/CUDA/cuda-constructor.f90
@@ -9,4 +9,4 @@ program main
 ! CHECK:   llvm.call @_FortranACUFRegisterAllocator() : () -> ()
 ! CHECK:   llvm.return
 ! CHECK: }
-! CHECK: llvm.mlir.global_ctors {ctors = [@__cudaFortranConstructor], priorities = [0 : i32]}
+! CHECK: llvm.mlir.global_ctors ctors = [@__cudaFortranConstructor], priorities = [0 : i32], data = [#llvm.zero]

diff  --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 5e1ab32e0a53d..707e23194061b 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1438,32 +1438,37 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global",
 
 def LLVM_GlobalCtorsOp : LLVM_Op<"mlir.global_ctors", [
                            DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
-  let arguments = (ins FlatSymbolRefArrayAttr
-                   : $ctors, I32ArrayAttr
-                   : $priorities);
+  let arguments = (ins FlatSymbolRefArrayAttr:$ctors,
+                       I32ArrayAttr:$priorities,
+                       ArrayAttr:$data);
   let summary = "LLVM dialect global_ctors.";
   let description = [{
-    Specifies a list of constructor functions and priorities. The functions
-    referenced by this array will be called in ascending order of priority (i.e.
-    lowest first) when the module is loaded. The order of functions with the
-    same priority is not defined. This operation is translated to LLVM's
-    global_ctors global variable. The initializer functions are run at load
-    time. The `data` field present in LLVM's global_ctors variable is not
-    modeled here.
+    Specifies a list of constructor functions, priorities, and associated data.
+    The functions referenced by this array will be called in ascending order
+    of priority (i.e. lowest first) when the module is loaded. The order of
+    functions with the same priority is not defined. This operation is
+    translated to LLVM's global_ctors global variable. The initializer
+    functions are run at load time. However, if the associated data is not
+    `#llvm.zero`, functions only run if the data is not discarded.
 
     Examples:
 
     ```mlir
-    llvm.mlir.global_ctors {@ctor}
-
     llvm.func @ctor() {
       ...
       llvm.return
     }
+    llvm.mlir.global_ctors ctors = [@ctor], priorities = [0],
+                                   data = [#llvm.zero]
     ```
 
   }];
-  let assemblyFormat = "attr-dict";
+  let assemblyFormat = [{
+    `ctors` `=` $ctors
+    `,` `priorities` `=` $priorities
+    `,` `data` `=` $data
+    attr-dict
+  }];
   let hasVerifier = 1;
 }
 
@@ -1471,16 +1476,18 @@ def LLVM_GlobalDtorsOp : LLVM_Op<"mlir.global_dtors", [
                            DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
   let arguments = (ins
     FlatSymbolRefArrayAttr:$dtors,
-    I32ArrayAttr:$priorities
+    I32ArrayAttr:$priorities,
+    ArrayAttr:$data
   );
   let summary = "LLVM dialect global_dtors.";
   let description = [{
     Specifies a list of destructor functions and priorities. The functions
-    referenced by this array will be called in descending order of priority (i.e.
-    highest first) when the module is unloaded. The order of functions with the
-    same priority is not defined. This operation is translated to LLVM's
-    global_dtors global variable. The `data` field present in LLVM's
-    global_dtors variable is not modeled here.
+    referenced by this array will be called in descending order of priority
+    (i.e. highest first) when the module is unloaded. The order of functions
+    with the same priority is not defined. This operation is translated to
+    LLVM's global_dtors global variable. The destruction functions are run at
+    load time. However, if the associated data is not `#llvm.zero`, functions
+    only run if the data is not discarded.
 
     Examples:
 
@@ -1488,11 +1495,16 @@ def LLVM_GlobalDtorsOp : LLVM_Op<"mlir.global_dtors", [
     llvm.func @dtor() {
       llvm.return
     }
-    llvm.mlir.global_dtors {@dtor}
+    llvm.mlir.global_dtors dtors = [@dtor], priorities = [0],
+                                   data = [#llvm.zero]
     ```
-
   }];
-  let assemblyFormat = "attr-dict";
+  let assemblyFormat = [{
+    `dtors` `=` $dtors
+    `,` `priorities` `=` $priorities
+    `,` `data` `=` $data
+    attr-dict
+  }];
   let hasVerifier = 1;
 }
 

diff  --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 32b5e8bdcebeb..e5970574f0e93 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -2508,6 +2508,17 @@ LogicalResult GlobalOp::verifyRegions() {
 // LLVM::GlobalCtorsOp
 //===----------------------------------------------------------------------===//
 
+LogicalResult checkGlobalXtorData(Operation *op, ArrayAttr data) {
+  if (data.empty())
+    return success();
+
+  if (llvm::all_of(data.getAsRange<Attribute>(), [](Attribute v) {
+        return isa<FlatSymbolRefAttr, ZeroAttr>(v);
+      }))
+    return success();
+  return op->emitError("data element must be symbol or #llvm.zero");
+}
+
 LogicalResult
 GlobalCtorsOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
   for (Attribute ctor : getCtors()) {
@@ -2519,10 +2530,14 @@ GlobalCtorsOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 }
 
 LogicalResult GlobalCtorsOp::verify() {
-  if (getCtors().size() != getPriorities().size())
-    return emitError(
-        "mismatch between the number of ctors and the number of priorities");
-  return success();
+  if (checkGlobalXtorData(*this, getData()).failed())
+    return failure();
+
+  if (getCtors().size() == getPriorities().size() &&
+      getCtors().size() == getData().size())
+    return success();
+  return emitError(
+      "ctors, priorities, and data must have the same number of elements");
 }
 
 //===----------------------------------------------------------------------===//
@@ -2540,10 +2555,14 @@ GlobalDtorsOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
 }
 
 LogicalResult GlobalDtorsOp::verify() {
-  if (getDtors().size() != getPriorities().size())
-    return emitError(
-        "mismatch between the number of dtors and the number of priorities");
-  return success();
+  if (checkGlobalXtorData(*this, getData()).failed())
+    return failure();
+
+  if (getDtors().size() == getPriorities().size() &&
+      getDtors().size() == getData().size())
+    return success();
+  return emitError(
+      "dtors, priorities, and data must have the same number of elements");
 }
 
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 00b16c86157e9..c0711f7dded71 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1115,6 +1115,7 @@ ModuleImport::convertGlobalCtorsAndDtors(llvm::GlobalVariable *globalVar) {
 
   SmallVector<Attribute> funcs;
   SmallVector<int32_t> priorities;
+  SmallVector<Attribute> dataList;
   for (llvm::Value *operand : initializer->operands()) {
     auto *aggregate = dyn_cast<llvm::ConstantAggregate>(operand);
     if (!aggregate || aggregate->getNumOperands() != 3)
@@ -1126,12 +1127,18 @@ ModuleImport::convertGlobalCtorsAndDtors(llvm::GlobalVariable *globalVar) {
     if (!priority || !func || !data)
       return failure();
 
-    // GlobalCtorsOps and GlobalDtorsOps do not support non-null data fields.
-    if (!data->isNullValue())
+    auto *gv = dyn_cast_or_null<llvm::GlobalValue>(data);
+    Attribute dataAttr;
+    if (gv)
+      dataAttr = FlatSymbolRefAttr::get(context, gv->getName());
+    else if (data->isNullValue())
+      dataAttr = ZeroAttr::get(context);
+    else
       return failure();
 
     funcs.push_back(FlatSymbolRefAttr::get(context, func->getName()));
     priorities.push_back(priority->getValue().getZExtValue());
+    dataList.push_back(dataAttr);
   }
 
   // Insert the global after the last one or at the start of the module.
@@ -1140,12 +1147,12 @@ ModuleImport::convertGlobalCtorsAndDtors(llvm::GlobalVariable *globalVar) {
   if (globalVar->getName() == getGlobalCtorsVarName()) {
     globalInsertionOp = builder.create<LLVM::GlobalCtorsOp>(
         mlirModule.getLoc(), builder.getArrayAttr(funcs),
-        builder.getI32ArrayAttr(priorities));
+        builder.getI32ArrayAttr(priorities), builder.getArrayAttr(dataList));
     return success();
   }
   globalInsertionOp = builder.create<LLVM::GlobalDtorsOp>(
       mlirModule.getLoc(), builder.getArrayAttr(funcs),
-      builder.getI32ArrayAttr(priorities));
+      builder.getI32ArrayAttr(priorities), builder.getArrayAttr(dataList));
   return success();
 }
 

diff  --git a/mlir/test/Dialect/LLVMIR/global.mlir b/mlir/test/Dialect/LLVMIR/global.mlir
index bd3584de9a405..193ab7987a2b6 100644
--- a/mlir/test/Dialect/LLVMIR/global.mlir
+++ b/mlir/test/Dialect/LLVMIR/global.mlir
@@ -228,16 +228,16 @@ llvm.func @ctor() {
   llvm.return
 }
 
-// CHECK: llvm.mlir.global_ctors {ctors = [@ctor], priorities = [0 : i32]}
-llvm.mlir.global_ctors { ctors = [@ctor], priorities = [0 : i32]}
+// CHECK: llvm.mlir.global_ctors ctors = [@ctor], priorities = [0 : i32], data = [#llvm.zero]
+llvm.mlir.global_ctors ctors = [@ctor], priorities = [0 : i32], data = [#llvm.zero]
 
 // -----
 
-// CHECK: llvm.mlir.global_ctors {ctors = [], priorities = []}
-llvm.mlir.global_ctors {ctors = [], priorities = []}
+// CHECK: llvm.mlir.global_ctors ctors = [], priorities = [], data = []
+llvm.mlir.global_ctors ctors = [], priorities = [], data = []
 
-// CHECK: llvm.mlir.global_dtors {dtors = [], priorities = []}
-llvm.mlir.global_dtors {dtors = [], priorities = []}
+// CHECK: llvm.mlir.global_dtors dtors = [], priorities = [], data = []
+llvm.mlir.global_dtors dtors = [], priorities = [], data = []
 
 // -----
 
@@ -245,8 +245,8 @@ llvm.func @dtor() {
   llvm.return
 }
 
-// CHECK: llvm.mlir.global_dtors {dtors = [@dtor], priorities = [0 : i32]}
-llvm.mlir.global_dtors { dtors = [@dtor], priorities = [0 : i32]}
+// CHECK: llvm.mlir.global_dtors dtors = [@dtor], priorities = [0 : i32], data = [#llvm.zero]
+llvm.mlir.global_dtors dtors = [@dtor], priorities = [0 : i32], data = [#llvm.zero]
 
 // -----
 

diff  --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index 6d3d3937b651c..fb9631d99b91a 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -9,8 +9,8 @@ llvm.func @ctor() {
   llvm.return
 }
 
-// expected-error at +1{{mismatch between the number of ctors and the number of priorities}}
-llvm.mlir.global_ctors {ctors = [@ctor], priorities = []}
+// expected-error at +1{{ctors, priorities, and data must have the same number of elements}}
+llvm.mlir.global_ctors ctors = [@ctor], priorities = [], data = [#llvm.zero]
 
 // -----
 
@@ -18,20 +18,29 @@ llvm.func @dtor() {
   llvm.return
 }
 
-// expected-error at +1{{mismatch between the number of dtors and the number of priorities}}
-llvm.mlir.global_dtors {dtors = [@dtor], priorities = [0 : i32, 32767 : i32]}
+// expected-error at +1{{dtors, priorities, and data must have the same number of elements}}
+llvm.mlir.global_dtors dtors = [@dtor], priorities = [0 : i32, 32767 : i32], data = [#llvm.zero]
 
 // -----
 
 // expected-error at +1{{'ctor' does not reference a valid LLVM function}}
-llvm.mlir.global_ctors {ctors = [@ctor], priorities = [0 : i32]}
+llvm.mlir.global_ctors ctors = [@ctor], priorities = [0 : i32], data = [#llvm.zero]
 
 // -----
 
 llvm.func @dtor()
 
 // expected-error at +1{{'dtor' does not have a definition}}
-llvm.mlir.global_dtors {dtors = [@dtor], priorities = [0 : i32]}
+llvm.mlir.global_dtors dtors = [@dtor], priorities = [0 : i32], data = [#llvm.zero]
+
+// -----
+
+llvm.func @dtor() {
+  llvm.return
+}
+
+// expected-error at +1{{data element must be symbol or #llvm.zero}}
+llvm.mlir.global_dtors dtors = [@dtor], priorities = [0 : i32], data = [0 : i32]
 
 ////////////////////////////////////////////////////////////////////////////////
 

diff  --git a/mlir/test/Target/LLVMIR/Import/global-variables.ll b/mlir/test/Target/LLVMIR/Import/global-variables.ll
index b809c93d772f5..b8bbdbab2e2ca 100644
--- a/mlir/test/Target/LLVMIR/Import/global-variables.ll
+++ b/mlir/test/Target/LLVMIR/Import/global-variables.ll
@@ -241,8 +241,8 @@
 
 ; // -----
 
-; CHECK: llvm.mlir.global_ctors {ctors = [@foo, @bar], priorities = [0 : i32, 42 : i32]}
-; CHECK: llvm.mlir.global_dtors {dtors = [@foo], priorities = [0 : i32]}
+; CHECK: llvm.mlir.global_ctors ctors = [@foo, @bar], priorities = [0 : i32, 42 : i32], data = [#llvm.zero, #llvm.zero]
+; CHECK: llvm.mlir.global_dtors dtors = [@foo], priorities = [0 : i32], data = [#llvm.zero]
 @llvm.global_ctors = appending global [2 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @foo, ptr null }, { i32, ptr, ptr } { i32 42, ptr @bar, ptr null }]
 @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @foo, ptr null }]
 
@@ -256,14 +256,23 @@ define void @bar() {
 
 ; // -----
 
-; CHECK: llvm.mlir.global_ctors {ctors = [], priorities = []}
+; CHECK: llvm.mlir.global_ctors ctors = [], priorities = [], data = []
 @llvm.global_ctors = appending global [0 x { i32, ptr, ptr }] zeroinitializer
 
-; CHECK: llvm.mlir.global_dtors {dtors = [], priorities = []}
+; CHECK: llvm.mlir.global_dtors dtors = [], priorities = [], data = []
 @llvm.global_dtors = appending global [0 x { i32, ptr, ptr }] zeroinitializer
 
 ; // -----
 
+; llvm.mlir.global_dtors dtors = [@foo], priorities = [0 : i32], data = [@foo]
+ at llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @foo, ptr @foo }]
+
+define void @foo() {
+  ret void
+}
+
+; // -----
+
 ; Visibility attribute.
 
 ; CHECK: llvm.mlir.global external hidden constant @hidden("string")

diff  --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll
index 4ef67b7190aab..d3ea3a510d7f8 100644
--- a/mlir/test/Target/LLVMIR/Import/import-failure.ll
+++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll
@@ -64,17 +64,6 @@ define void @unsupported_argument(i64 %arg1) {
 
 ; // -----
 
-; global_dtors with non-null data fields cannot be represented in MLIR.
-; CHECK:      <unknown>
-; CHECK-SAME: error: unhandled global variable: @llvm.global_dtors
- at llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @foo, ptr @foo }]
-
-define void @foo() {
-  ret void
-}
-
-; // -----
-
 ; CHECK:      import-failure.ll
 ; CHECK-SAME: error: unsupported TBAA node format: !{{.*}} = !{!{{.*}}, i64 1, !"omnipotent char"}
 define dso_local void @tbaa(ptr %0) {

diff  --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 89faa6abb6cf7..0238c95835a0f 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -1851,7 +1851,7 @@ llvm.mlir.global linkonce @take_self_address() : !llvm.struct<(i32, !llvm.ptr)>
 // -----
 
 // CHECK: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @foo, ptr null }]
-llvm.mlir.global_ctors { ctors = [@foo], priorities = [0 : i32]}
+llvm.mlir.global_ctors ctors = [@foo], priorities = [0 : i32], data = [#llvm.zero]
 
 llvm.func @foo() {
   llvm.return
@@ -1860,15 +1860,15 @@ llvm.func @foo() {
 // -----
 
 // CHECK: @llvm.global_ctors = appending global [0 x { i32, ptr, ptr }] zeroinitializer
-llvm.mlir.global_ctors {ctors = [], priorities = []}
+llvm.mlir.global_ctors ctors = [], priorities = [], data = []
 
 // CHECK: @llvm.global_dtors = appending global [0 x { i32, ptr, ptr }] zeroinitializer
-llvm.mlir.global_dtors {dtors = [], priorities = []}
+llvm.mlir.global_dtors dtors = [], priorities = [], data = []
 
 // -----
 
 // CHECK: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @foo, ptr null }]
-llvm.mlir.global_dtors { dtors = [@foo], priorities = [0 : i32]}
+llvm.mlir.global_dtors dtors = [@foo], priorities = [0 : i32], data = [#llvm.zero]
 
 llvm.func @foo() {
   llvm.return

diff  --git a/mlir/test/mlir-runner/global-constructors.mlir b/mlir/test/mlir-runner/global-constructors.mlir
index 593d6aa4945ef..8b19d9873b8b0 100644
--- a/mlir/test/mlir-runner/global-constructors.mlir
+++ b/mlir/test/mlir-runner/global-constructors.mlir
@@ -7,8 +7,8 @@
 module {
   llvm.func @printNewline()
   llvm.func @printI64(i64)
-  llvm.mlir.global_ctors {ctors = [@ctor], priorities = [0 : i32]}
-  llvm.mlir.global_dtors {dtors = [@dtor], priorities = [0 : i32]}
+  llvm.mlir.global_ctors ctors = [@ctor], priorities = [0 : i32], data = [#llvm.zero]
+  llvm.mlir.global_dtors dtors = [@dtor], priorities = [0 : i32], data = [#llvm.zero]
   llvm.func @ctor() {
     %0 = llvm.mlir.constant(1 : i64) : i64
     llvm.call @printI64(%0) : (i64) -> ()


        


More information about the Mlir-commits mailing list