[Mlir-commits] [mlir] [mlir][LLVM] Preserve !associated and !absolute_symbol on llvm.mlir.global (PR #218839)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Aug 25 22:34:31 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-llvm
Author: Tanishq Khurana (ktanishqk)
<details>
<summary>Changes</summary>
Add optional `associated` and `absolute_symbol` attributes on `llvm.mlir.global` so the corresponding LLVM IR metadata is preserved through import and export.
`associated` is a symbol ref to another global, alias, or function (used for ELF `SHF_LINK_ORDER`). `absolute_symbol` is an array of `[lower, upper)` integer range pairs, with `[-1, -1]` for the full set.
Verifier rejects a self-associated global and malformed range lists. Translation attaches the metadata after all globals, aliases, and function signatures exist so symbol refs can be resolved.
Fixes #<!-- -->215549
---
Full diff: https://github.com/llvm/llvm-project/pull/218839.diff
7 Files Affected:
- (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td (+21)
- (modified) mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp (+21)
- (modified) mlir/lib/Target/LLVMIR/ModuleImport.cpp (+44)
- (modified) mlir/lib/Target/LLVMIR/ModuleTranslation.cpp (+32)
- (modified) mlir/test/Dialect/LLVMIR/global.mlir (+34)
- (modified) mlir/test/Target/LLVMIR/Import/global-variables.ll (+42)
- (modified) mlir/test/Target/LLVMIR/llvmir.mlir (+41)
``````````diff
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 84f71054994f4..83bc0dad5d137 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1393,6 +1393,8 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global",
DefaultValuedAttr<ConfinedAttr<I32Attr, [IntNonNegative]>, "0">:$addr_space,
OptionalAttr<UnnamedAddr>:$unnamed_addr,
OptionalAttr<StrAttr>:$section,
+ OptionalAttr<FlatSymbolRefAttr>:$associated,
+ OptionalAttr<ArrayAttr>:$absolute_symbol,
OptionalAttr<SymbolRefAttr>:$comdat,
OptionalAttr<DIGlobalVariableExpressionArrayAttr>:$dbg_exprs,
DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_,
@@ -1494,6 +1496,25 @@ def LLVM_GlobalOp : LLVM_Op<"mlir.global",
llvm.mlir.global private constant @y(dense<1.0> : tensor<8xf32>) { alignment = 32 : i64 } : !llvm.array<8 x f32>
```
+ The optional `associated` attribute models LLVM IR `!associated` metadata
+ on a global. It is a symbol reference to another global object (or an
+ alias of one) and is used to emit `SHF_LINK_ORDER` on ELF.
+
+ The optional `absolute_symbol` attribute models LLVM IR `!absolute_symbol`
+ metadata. It is an array of integer attributes forming one or more
+ `[lower, upper)` range pairs for the global's address, using the same
+ encoding as LLVM `range` metadata. The pair `[-1, -1]` represents the
+ full set.
+
+ Examples:
+
+ ```mlir
+ llvm.mlir.global external @b(0 : i32) : i32
+ llvm.mlir.global external @a(0 : i32) {associated = @b} : i32
+
+ llvm.mlir.global external @abs_sym() {absolute_symbol = [0 : i64, 42 : i64]} : i8
+ ```
+
The `target_specific_attrs` attribute provides a mechanism to preserve
target-specific LLVM IR attributes that are not explicitly modeled in the
LLVM dialect.
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 6382dfcbc8ad2..3b2f7d129fb3a 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -2671,6 +2671,27 @@ LogicalResult GlobalOp::verify() {
return emitError() << "alignment attribute is not a power of 2";
}
+ if (FlatSymbolRefAttr associated = getAssociatedAttr()) {
+ if (associated.getValue() == getSymName())
+ return emitOpError("associated cannot refer to the global itself");
+ }
+
+ if (ArrayAttr absSym = getAbsoluteSymbolAttr()) {
+ if (absSym.empty() || absSym.size() % 2 != 0)
+ return emitOpError(
+ "absolute_symbol must contain one or more integer range pairs");
+ Type pairType;
+ for (Attribute attr : absSym) {
+ auto intAttr = dyn_cast<IntegerAttr>(attr);
+ if (!intAttr)
+ return emitOpError("absolute_symbol operands must be integers");
+ if (!pairType)
+ pairType = intAttr.getType();
+ else if (intAttr.getType() != pairType)
+ return emitOpError("absolute_symbol range pair types must match");
+ }
+ }
+
return success();
}
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 725d7cf6c8413..8730b5d67b901 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1648,6 +1648,50 @@ LogicalResult ModuleImport::convertGlobal(llvm::GlobalVariable *globalVar) {
if (globalVar->hasComdat())
globalOp.setComdatAttr(comdatMapping.lookup(globalVar->getComdat()));
+ if (llvm::MDNode *associatedMD =
+ globalVar->getMetadata(llvm::LLVMContext::MD_associated)) {
+ if (associatedMD->getNumOperands() == 1) {
+ if (auto *valueAsMD = dyn_cast_or_null<llvm::ValueAsMetadata>(
+ associatedMD->getOperand(0).get())) {
+ llvm::Value *value = valueAsMD->getValue();
+ llvm::GlobalValue *gv = dyn_cast<llvm::GlobalValue>(value);
+ if (!gv)
+ gv = dyn_cast<llvm::GlobalValue>(value->stripPointerCastsAndAliases());
+ if (gv) {
+ StringRef name = gv->getName();
+ FlatSymbolRefAttr symbolRef;
+ if (name.empty()) {
+ if (auto *namelessVar = dyn_cast<llvm::GlobalVariable>(gv))
+ symbolRef = getOrCreateNamelessSymbolName(namelessVar);
+ } else {
+ symbolRef = FlatSymbolRefAttr::get(context, name);
+ }
+ if (symbolRef)
+ globalOp.setAssociatedAttr(symbolRef);
+ }
+ }
+ }
+ }
+
+ if (llvm::MDNode *absSymMD =
+ globalVar->getMetadata(llvm::LLVMContext::MD_absolute_symbol)) {
+ SmallVector<Attribute> rangeAttrs;
+ rangeAttrs.reserve(absSymMD->getNumOperands());
+ bool valid = absSymMD->getNumOperands() >= 2 &&
+ absSymMD->getNumOperands() % 2 == 0;
+ for (const llvm::MDOperand &op : absSymMD->operands()) {
+ auto *constInt = llvm::mdconst::dyn_extract<llvm::ConstantInt>(op);
+ if (!constInt) {
+ valid = false;
+ break;
+ }
+ auto intType = IntegerType::get(context, constInt->getBitWidth());
+ rangeAttrs.push_back(IntegerAttr::get(intType, constInt->getValue()));
+ }
+ if (valid)
+ globalOp.setAbsoluteSymbolAttr(ArrayAttr::get(context, rangeAttrs));
+ }
+
processTargetSpecificAttrs(globalVar, globalOp);
return success();
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 47e63e2aa279a..d6b5287d78bf2 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -45,6 +45,7 @@
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/MDBuilder.h"
+#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/Debug.h"
@@ -1383,6 +1384,37 @@ LogicalResult ModuleTranslation::convertGlobalsAndAliases() {
aliasesMapping.try_emplace(op, var);
}
+ // Attach global metadata after all globals, aliases, and function signatures
+ // exist so symbol references can be resolved.
+ for (auto op : getModuleBody(mlirModule).getOps<LLVM::GlobalOp>()) {
+ auto *var = cast<llvm::GlobalVariable>(lookupGlobal(op));
+ if (FlatSymbolRefAttr associated = op.getAssociatedAttr()) {
+ auto mdAttr = MDGlobalValueAttr::get(op.getContext(), associated);
+ FailureOr<llvm::Metadata *> md = convertMetadataAttr(mdAttr, [&]() {
+ return op.emitError("failed to convert associated metadata");
+ });
+ if (failed(md))
+ return failure();
+ var->setMetadata(llvm::LLVMContext::MD_associated,
+ llvm::MDNode::get(var->getContext(), *md));
+ }
+
+ if (ArrayAttr absSym = op.getAbsoluteSymbolAttr()) {
+ SmallVector<llvm::Metadata *> mdOps;
+ llvm::LLVMContext &ctx = var->getContext();
+ mdOps.reserve(absSym.size());
+ for (Attribute attr : absSym) {
+ auto intAttr = llvm::cast<IntegerAttr>(attr);
+ llvm::IntegerType *ty = llvm::IntegerType::get(
+ ctx, intAttr.getType().getIntOrFloatBitWidth());
+ mdOps.push_back(llvm::ConstantAsMetadata::get(
+ llvm::ConstantInt::get(ty, intAttr.getValue())));
+ }
+ var->setMetadata(llvm::LLVMContext::MD_absolute_symbol,
+ llvm::MDNode::get(ctx, mdOps));
+ }
+ }
+
// Convert global variable bodies.
for (auto op : getModuleBody(mlirModule).getOps<LLVM::GlobalOp>()) {
if (Block *initializer = op.getInitializerBlock()) {
diff --git a/mlir/test/Dialect/LLVMIR/global.mlir b/mlir/test/Dialect/LLVMIR/global.mlir
index 193ab7987a2b6..a72dad1f796a5 100644
--- a/mlir/test/Dialect/LLVMIR/global.mlir
+++ b/mlir/test/Dialect/LLVMIR/global.mlir
@@ -292,3 +292,37 @@ llvm.mlir.global external @global_with_expr1() {addr_space = 0 : i32, dbg_expr =
llvm.mlir.global external @global_with_expr2() {addr_space = 0 : i32, dbg_expr = [#llvm.di_global_variable_expression<var = <scope = #di_compile_unit, name = "global_with_expr_2", linkageName = "global_with_expr_2", file = #di_file, line = 371, type = #di_basic_type, isLocalToUnit = true, isDefined = true, alignInBits = 8>, expr = <[DW_OP_push_object_address, DW_OP_deref]>>]} : i64
llvm.mlir.global external @global_with_expr3() {addr_space = 0 : i32, dbg_expr = [#llvm.di_global_variable_expression<var = <scope = #di_compile_unit, name = "global_with_expr_3", linkageName = "global_with_expr_3", file = #di_file, line = 372, type = #di_basic_type, isLocalToUnit = true, isDefined = true, alignInBits = 8>, expr = <[DW_OP_LLVM_arg(0), DW_OP_LLVM_arg(1), DW_OP_plus]>>]} : i64
llvm.mlir.global external @global_with_expr4() {addr_space = 0 : i32, dbg_expr = [#llvm.di_global_variable_expression<var = <scope = #di_compile_unit, name = "global_with_expr_4", linkageName = "global_with_expr_4", file = #di_file, line = 373, type = #di_basic_type, isLocalToUnit = true, isDefined = true, alignInBits = 8>, expr = <[DW_OP_LLVM_convert(16, DW_ATE_signed)]>>]} : i64
+
+// -----
+
+// CHECK: llvm.mlir.global external @associated_target(0 : i32) {addr_space = 0 : i32} : i32
+// CHECK: llvm.mlir.global external @associated_global(0 : i32) {addr_space = 0 : i32, associated = @associated_target} : i32
+llvm.mlir.global external @associated_target(0 : i32) : i32
+llvm.mlir.global external @associated_global(0 : i32) {associated = @associated_target} : i32
+
+// CHECK: llvm.mlir.global external @absolute_symbol_global() {absolute_symbol = [0, 42], addr_space = 0 : i32} : i8
+llvm.mlir.global external @absolute_symbol_global() {absolute_symbol = [0 : i64, 42 : i64]} : i8
+
+// CHECK: llvm.mlir.global external @absolute_symbol_full() {absolute_symbol = [-1, -1], addr_space = 0 : i32} : i8
+llvm.mlir.global external @absolute_symbol_full() {absolute_symbol = [-1 : i64, -1 : i64]} : i8
+
+// -----
+
+// expected-error @+1 {{associated cannot refer to the global itself}}
+llvm.mlir.global @self_associated(0 : i32) {associated = @self_associated} : i32
+
+// -----
+
+// expected-error @+1 {{absolute_symbol must contain one or more integer range pairs}}
+llvm.mlir.global @odd_absolute_symbol() {absolute_symbol = [0 : i64]} : i8
+
+// -----
+
+// expected-error @+1 {{absolute_symbol operands must be integers}}
+llvm.mlir.global @non_int_absolute_symbol() {absolute_symbol = ["foo", "bar"]} : i8
+
+// -----
+
+// expected-error @+1 {{absolute_symbol range pair types must match}}
+llvm.mlir.global @mixed_absolute_symbol() {absolute_symbol = [0 : i32, 42 : i64]} : i8
+
diff --git a/mlir/test/Target/LLVMIR/Import/global-variables.ll b/mlir/test/Target/LLVMIR/Import/global-variables.ll
index 2be36c4366304..1d49726fb8966 100644
--- a/mlir/test/Target/LLVMIR/Import/global-variables.ll
+++ b/mlir/test/Target/LLVMIR/Import/global-variables.ll
@@ -373,3 +373,45 @@ attributes #0 = { readnone "int-attr"="4" "no-enum-attr" "string-attr"="string"
; CHECK-SAME: target_specific_attrs = ["norecurse", ["bss-section", "my_bss.1"]]}
@target_specific_attrs_combined = global i32 2, align 4, section "mysection" #0
attributes #0 = { norecurse "bss-section"="my_bss.1" }
+
+; // -----
+
+; CHECK: llvm.mlir.global external @a
+; CHECK-SAME: {addr_space = 0 : i32, associated = @b} : i32
+; CHECK: llvm.mlir.global external @b
+; CHECK-SAME: {addr_space = 0 : i32} : i32
+ at a = global i32 0, !associated !0
+ at b = global i32 0
+!0 = !{ptr @b}
+
+; // -----
+
+; CHECK: llvm.mlir.global external @a
+; CHECK-SAME: {addr_space = 0 : i32, associated = @f} : i32
+ at a = global i32 0, !associated !0
+declare void @f()
+!0 = !{ptr @f}
+
+; // -----
+
+; CHECK: llvm.mlir.global external @associated_via_alias
+; CHECK-SAME: associated = @alias_of_target
+ at alias_target = global i32 1
+ at alias_of_target = alias i32, ptr @alias_target
+ at associated_via_alias = global i32 2, !associated !0
+!0 = !{ptr @alias_of_target}
+
+; // -----
+
+; CHECK: llvm.mlir.global external @a()
+; CHECK-SAME: {absolute_symbol = [0 : i64, 42 : i64], addr_space = 0 : i32} : i8
+ at a = external global i8, !absolute_symbol !0
+!0 = !{i64 0, i64 42}
+
+; // -----
+
+; CHECK: llvm.mlir.global external @a()
+; CHECK-SAME: {absolute_symbol = [-1 : i64, -1 : i64], addr_space = 0 : i32} : i8
+ at a = external global i8, !absolute_symbol !0
+!0 = !{i64 -1, i64 -1}
+
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 5ffbb3eef7bb0..c00b69bdbeb3b 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -3633,6 +3633,47 @@ llvm.mlir.global external @target_specific_attrs_combined(2 : i32) {alignment =
// -----
+// CHECK: @associated_target = global i32 0
+// CHECK: @associated_global = global i32 0, !associated ![[ASSOC:[0-9]+]]
+// CHECK: ![[ASSOC]] = !{ptr @associated_target}
+llvm.mlir.global external @associated_target(0 : i32) {addr_space = 0 : i32} : i32
+llvm.mlir.global external @associated_global(0 : i32) {addr_space = 0 : i32, associated = @associated_target} : i32
+
+// -----
+
+// CHECK: @associated_fn_global = global i32 0, !associated ![[ASSOC_FN:[0-9]+]]
+// CHECK: declare void @associated_fn()
+// CHECK: ![[ASSOC_FN]] = !{ptr @associated_fn}
+llvm.mlir.global external @associated_fn_global(0 : i32) {associated = @associated_fn} : i32
+llvm.func @associated_fn()
+
+// -----
+
+// CHECK: @alias_target = global i32 1
+// CHECK: @associated_via_alias = global i32 2, !associated ![[ASSOC_ALIAS:[0-9]+]]
+// CHECK: @alias_of_target = alias i32, ptr @alias_target
+// CHECK: ![[ASSOC_ALIAS]] = !{ptr @alias_of_target}
+llvm.mlir.global @alias_target(1 : i32) : i32
+llvm.mlir.alias external @alias_of_target : i32 {
+ %0 = llvm.mlir.addressof @alias_target : !llvm.ptr
+ llvm.return %0 : !llvm.ptr
+}
+llvm.mlir.global @associated_via_alias(2 : i32) {associated = @alias_of_target} : i32
+
+// -----
+
+// CHECK: @absolute_symbol_global = external global i8, !absolute_symbol ![[ABS:[0-9]+]]
+// CHECK: ![[ABS]] = !{i64 0, i64 42}
+llvm.mlir.global external @absolute_symbol_global() {absolute_symbol = [0 : i64, 42 : i64]} : i8
+
+// -----
+
+// CHECK: @absolute_symbol_full = external global i8, !absolute_symbol ![[ABS_FULL:[0-9]+]]
+// CHECK: ![[ABS_FULL]] = !{i64 -1, i64 -1}
+llvm.mlir.global external @absolute_symbol_full() {absolute_symbol = [-1 : i64, -1 : i64]} : i8
+
+// -----
+
// CHECK-LABEL: define b8 @byte_type(b8 %0)
llvm.func @byte_type(%arg0: !llvm.byte<8>) -> !llvm.byte<8> {
llvm.return %arg0 : !llvm.byte<8>
``````````
</details>
https://github.com/llvm/llvm-project/pull/218839
More information about the Mlir-commits
mailing list