[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