-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[MLIR][LLVMIR] Add support for the full form of global_{ctor,dtor} #133176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MLIR][LLVMIR] Add support for the full form of global_{ctor,dtor} #133176
Conversation
Currently only ctor/dtor list and priorities are supported. This PR adds support for the data field. Few implementation notes: - The assembly printer has a fixed form now because just `attr_dict` will sort the dict by key name, making dtor and ctor differ in the order of arguments. - LLVM's `ptr null` is being converted to `#llvm.zero` otherwise we'd have to create a region to use the operation version of `ptr null`, which is silly given that the field only support null or an actual symbol.
@llvm/pr-subscribers-flang-fir-hlfir @llvm/pr-subscribers-mlir-llvm Author: Bruno Cardoso Lopes (bcardosolopes) ChangesCurrently only ctor/dtor list and their priorities are supported. This PR adds support for the missing data field. Few implementation notes:
Full diff: https://github.com/llvm/llvm-project/pull/133176.diff 9 Files Affected:
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 5e1ab32e0a53d..2830718d93685 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1438,24 +1438,24 @@ 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.mlir.global_ctors {ctors = [@ctor], data = [#llvm.zero],
+ priorities = [0]}
llvm.func @ctor() {
...
llvm.return
@@ -1463,7 +1463,12 @@ def LLVM_GlobalCtorsOp : LLVM_Op<"mlir.global_ctors", [
```
}];
- let assemblyFormat = "attr-dict";
+ let assemblyFormat = [{
+ `ctors` `=` $ctors
+ `,` `priorities` `=` $priorities
+ `,` `data` `=` $data
+ attr-dict
+ }];
let hasVerifier = 1;
}
@@ -1471,7 +1476,8 @@ 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 = [{
@@ -1479,8 +1485,9 @@ def LLVM_GlobalDtorsOp : LLVM_Op<"mlir.global_dtors", [
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.
+ 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], data = [#llvm.zero],
+ priorities = [0]}
```
-
}];
- 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 18a70cc64628f..b038628762c16 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>(v) || isa<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..d739dfce5b6da 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@+1{{mismatch between the number of ctors and the number of priorities}}
-llvm.mlir.global_ctors {ctors = [@ctor], priorities = []}
+// expected-error@+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@+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@+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@+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@+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@+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]
+@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
-@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 2476b1b15faaa..3e60b7cbaf63b 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) -> ()
|
Looks like it breaks Flang in |
CUDA Fortran part looks good to me. Thanks for the update. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the fix!
LGTM
Thanks for the review, addressed! |
…lvm#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.
…op linkage fix (#42) * [MLIR][LLVMIR] Add support for the full form of global_{ctor,dtor} (llvm#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. * [MLIR][LLVM] Add weak_odr to allowed linkage for alias (llvm#132840) I missed this when originally introduced the feature (note the verifier message already contains it), this fixes a small bug. --------- Co-authored-by: Bruno Cardoso Lopes <[email protected]>
Currently only ctor/dtor list and their priorities are supported. This PR adds support for the missing data field.
Few implementation notes:
attr_dict
will sort the dict by key name, making global_dtor and global_ctor differ in the order of printed arguments.ptr null
is being converted to#llvm.zero
otherwise we'd have to create a region to use the default operation conversion fromptr null
, which is silly given that the field only support null or a symbol.