[Mlir-commits] [mlir] [MLIR][LLVM] Preserve pointer-valued metadata operands on import (PR #215743)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Wed Aug 12 00:11:04 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-llvm
Author: Hamza Qureshi (hamzaqureshi5)
<details>
<summary>Changes</summary>
convertMetadataToAttrImpl only modelled ConstantInt operands wrapped in a ConstantAsMetadata, so any metadata node containing a pointer constant could not be represented and the whole node was rejected.
Add #llvm.md_null and #llvm.md_addrspacecast to model ConstantPointerNull and addrspacecast constant expressions, keeping the address space so that `ptr null` and `ptr addrspace(1) null` stay distinct. MDAddrSpaceCastAttr verifies that its operand is itself pointer-valued metadata.
Global values are constants, so ValueAsMetadata::get wraps them in a ConstantAsMetadata and they never reached the ValueAsMetadata case. Match them in the ConstantAsMetadata case instead, which also generalizes the existing function-only handling to any named global value and makes the addrspacecast operand representable.
Mirror both attributes in ModuleTranslation::convertMetadataAttr so the original LLVM IR is reconstructed on export.
Fixes #<!-- -->215550
---
Full diff: https://github.com/llvm/llvm-project/pull/215743.diff
8 Files Affected:
- (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td (+42-3)
- (modified) mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp (+16)
- (modified) mlir/lib/Target/LLVMIR/ModuleImport.cpp (+37-10)
- (modified) mlir/lib/Target/LLVMIR/ModuleTranslation.cpp (+17)
- (modified) mlir/test/Dialect/LLVMIR/invalid.mlir (+16)
- (modified) mlir/test/Dialect/LLVMIR/roundtrip.mlir (+8)
- (modified) mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll (+86)
- (modified) mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir (+12)
``````````diff
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index a5e4bc52c84a6..7b15bfe37193f 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -1814,12 +1814,50 @@ def LLVM_MDGlobalValueAttr : LLVM_Attr<"MDGlobalValue", "md_global_value"> {
let assemblyFormat = "`<` $name `>`";
}
+def LLVM_MDNullAttr : LLVM_Attr<"MDNull", "md_null"> {
+ let summary = "LLVM null-pointer-as-metadata";
+ let description = [{
+ Wraps a null pointer constant as an LLVM metadata node, corresponding to
+ `llvm::ConstantAsMetadata` wrapping a `llvm::ConstantPointerNull` in LLVM
+ IR. `addressSpace` is the address space of the null pointer, so that
+ `ptr null` and `ptr addrspace(1) null` remain distinguishable.
+
+ Example:
+ ```mlir
+ #llvm.md_null<0>
+ ```
+ }];
+ let parameters = (ins "unsigned":$addressSpace);
+ let assemblyFormat = "`<` $addressSpace `>`";
+}
+
+def LLVM_MDAddrSpaceCastAttr
+ : LLVM_Attr<"MDAddrSpaceCast", "md_addrspacecast"> {
+ let summary = "LLVM addrspacecast-constant-expression-as-metadata";
+ let description = [{
+ Wraps an `addrspacecast` constant expression as an LLVM metadata node,
+ corresponding to `llvm::ConstantAsMetadata` wrapping a `llvm::ConstantExpr`
+ with an `addrspacecast` opcode in LLVM IR. `arg` is the metadata attribute
+ for the pointer being cast and `addressSpace` is the address space of the
+ resulting pointer.
+
+ Example:
+ ```mlir
+ #llvm.md_addrspacecast<#llvm.md_global_value<@my_global>, 0>
+ ```
+ }];
+ let parameters = (ins "Attribute":$arg, "unsigned":$addressSpace);
+ let assemblyFormat = "`<` $arg `,` $addressSpace `>`";
+ let genVerifyDecl = 1;
+}
+
def LLVM_MDNodeAttr : LLVM_Attr<"MDNode", "md_node"> {
let summary = "LLVM metadata node";
let description = [{
Represents an LLVM metadata node. The operands
can be any combination of metadata attributes: `#llvm.md_string`,
- `#llvm.md_const`, `#llvm.md_global_value`, or nested `#llvm.md_node`.
+ `#llvm.md_const`, `#llvm.md_global_value`, `#llvm.md_null`,
+ `#llvm.md_addrspacecast`, or nested `#llvm.md_node`.
Example:
```mlir
@@ -1837,7 +1875,8 @@ def LLVM_MDNodeArrayAttr
def LLVM_AnyMDAttr : AnyAttrOf<[
LLVM_MDStringAttr, LLVM_MDConstantAttr, LLVM_MDGlobalValueAttr,
- LLVM_MDNodeAttr],
- "LLVM metadata attribute (md_string, md_const, md_global_value, or md_node)">;
+ LLVM_MDNullAttr, LLVM_MDAddrSpaceCastAttr, LLVM_MDNodeAttr],
+ "LLVM metadata attribute (md_string, md_const, md_global_value, md_null, "
+ "md_addrspacecast, or md_node)">;
#endif // LLVMIR_ATTRDEFS
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
index 3b26aa2621370..b3a7d2e46030d 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
@@ -598,3 +598,19 @@ ModFlagBehavior ModuleFlagAttr::getModuleFlagBehavior() const {
StringAttr ModuleFlagAttr::getModuleFlagKey() const { return getKey(); }
Attribute ModuleFlagAttr::getModuleFlagValue() const { return getValue(); }
+
+//===----------------------------------------------------------------------===//
+// MDAddrSpaceCastAttr
+//===----------------------------------------------------------------------===//
+
+LogicalResult
+MDAddrSpaceCastAttr::verify(function_ref<InFlightDiagnostic()> emitError,
+ Attribute arg, unsigned addressSpace) {
+ // `addrspacecast` operates on pointers, so the operand must be a metadata
+ // attribute that models a pointer-typed constant.
+ if (!isa<MDGlobalValueAttr, MDNullAttr, MDAddrSpaceCastAttr>(arg))
+ return emitError() << "expected #llvm.md_global_value, #llvm.md_null, or "
+ "#llvm.md_addrspacecast operand, but got "
+ << arg;
+ return success();
+}
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 48e1b7d26ac67..d775457e94646 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -168,18 +168,45 @@ static Attribute convertMetadataToAttrImpl(
if (auto *mdStr = dyn_cast<llvm::MDString>(md))
return MDStringAttr::get(ctx, StringAttr::get(ctx, mdStr->getString()));
if (auto *cam = dyn_cast<llvm::ConstantAsMetadata>(md)) {
- auto *ci = dyn_cast<llvm::ConstantInt>(cam->getValue());
- if (!ci)
- return {};
- auto intType = IntegerType::get(ctx, ci->getBitWidth());
- return MDConstantAttr::get(ctx, IntegerAttr::get(intType, ci->getValue()));
+ llvm::Constant *constant = cam->getValue();
+ if (auto *ci = dyn_cast<llvm::ConstantInt>(constant)) {
+ auto intType = IntegerType::get(ctx, ci->getBitWidth());
+ return MDConstantAttr::get(ctx, IntegerAttr::get(intType, ci->getValue()));
+ }
+ // Global values are constants, so `llvm::ValueAsMetadata::get` wraps them
+ // in a `llvm::ConstantAsMetadata` and they are matched here rather than by
+ // the `llvm::ValueAsMetadata` case below.
+ if (auto *global = dyn_cast<llvm::GlobalValue>(constant)) {
+ StringRef name = global->getName();
+ // Nameless globals have no symbol to reference.
+ if (name.empty())
+ return {};
+ return MDGlobalValueAttr::get(ctx, FlatSymbolRefAttr::get(ctx, name));
+ }
+ if (auto *nullPtr = dyn_cast<llvm::ConstantPointerNull>(constant))
+ return MDNullAttr::get(ctx, nullPtr->getType()->getPointerAddressSpace());
+ if (auto *constExpr = dyn_cast<llvm::ConstantExpr>(constant)) {
+ // Only `addrspacecast` is modelled; other constant expressions have no
+ // metadata-attribute counterpart.
+ if (constExpr->getOpcode() != llvm::Instruction::AddrSpaceCast)
+ return {};
+ Attribute argAttr = convertMetadataToAttrImpl(
+ ctx, llvm::ConstantAsMetadata::get(constExpr->getOperand(0)), path,
+ attrMap);
+ if (!argAttr)
+ return {};
+ return MDAddrSpaceCastAttr::get(
+ ctx, argAttr, constExpr->getType()->getPointerAddressSpace());
+ }
+ return {};
}
if (auto *vam = dyn_cast<llvm::ValueAsMetadata>(md)) {
- auto *fn = dyn_cast<llvm::Function>(vam->getValue());
- if (!fn)
- return {};
- return MDGlobalValueAttr::get(ctx,
- FlatSymbolRefAttr::get(ctx, fn->getName()));
+ // Constants, including global values, are handled by the
+ // `llvm::ConstantAsMetadata` case above. Anything left here is a local
+ // value, which has no metadata-attribute counterpart.
+ assert(!isa<llvm::Constant>(vam->getValue()) &&
+ "constants should be matched as ConstantAsMetadata");
+ return {};
}
if (auto *node = dyn_cast<llvm::MDNode>(md)) {
// Metadata attributes cannot preserve distinctness, so bail out.
diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
index 536327e64bb9f..b87a581a5185e 100644
--- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp
@@ -1615,6 +1615,23 @@ FailureOr<llvm::Metadata *> ModuleTranslation::convertMetadataAttr(
return emitError() << "could not resolve metadata reference '"
<< a.getName() << "'";
})
+ .Case([&](MDNullAttr a) -> FailureOr<llvm::Metadata *> {
+ return llvm::ConstantAsMetadata::get(llvm::ConstantPointerNull::get(
+ llvm::PointerType::get(llvmContext, a.getAddressSpace())));
+ })
+ .Case([&](MDAddrSpaceCastAttr a) -> FailureOr<llvm::Metadata *> {
+ FailureOr<llvm::Metadata *> arg =
+ convertMetadataAttr(a.getArg(), emitError);
+ if (failed(arg))
+ return failure();
+ // The verifier restricts the operand to pointer-valued metadata
+ // attributes, all of which translate to a ConstantAsMetadata.
+ auto *argAsMD = cast<llvm::ConstantAsMetadata>(*arg);
+ return llvm::ConstantAsMetadata::get(
+ llvm::ConstantExpr::getAddrSpaceCast(
+ argAsMD->getValue(),
+ llvm::PointerType::get(llvmContext, a.getAddressSpace())));
+ })
.Case([&](MDNodeAttr a) -> FailureOr<llvm::Metadata *> {
SmallVector<llvm::Metadata *> operands;
for (Attribute operand : a.getOperands()) {
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index 6265f67e594d0..2e0cbda9fa35d 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -2198,3 +2198,19 @@ llvm.mlir.global internal thread_local(invalid) constant @thread_local(42 : i32)
// expected-error at +1{{expected ')'}}
llvm.mlir.global internal thread_local(generaldynamic, localexec) constant @thread_local(42 : i32) : i32
+
+// -----
+
+llvm.func @md_addrspacecast_bad_operand() {
+ // expected-error at +1{{expected #llvm.md_global_value, #llvm.md_null, or #llvm.md_addrspacecast operand, but got #llvm.md_string<"not a pointer">}}
+ %0 = llvm.mlir.metadata_as_value #llvm.md_addrspacecast<#llvm.md_string<"not a pointer">, 0>
+ llvm.return
+}
+
+// -----
+
+llvm.func @md_addrspacecast_int_operand() {
+ // expected-error at +1{{expected #llvm.md_global_value, #llvm.md_null, or #llvm.md_addrspacecast operand, but got #llvm.md_const<42 : i32>}}
+ %0 = llvm.mlir.metadata_as_value #llvm.md_addrspacecast<#llvm.md_const<42 : i32>, 0>
+ llvm.return
+}
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index 0f268cf6a5fe7..f4805c5541f91 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -1165,6 +1165,14 @@ llvm.func @metadata_as_value_shapes() {
%2 = llvm.mlir.metadata_as_value #llvm.md_global_value<@md_kernel>
// CHECK: %{{.*}} = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
%3 = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_string<"sp">>
+ // CHECK: %{{.*}} = llvm.mlir.metadata_as_value #llvm.md_null<0>
+ %4 = llvm.mlir.metadata_as_value #llvm.md_null<0>
+ // CHECK: %{{.*}} = llvm.mlir.metadata_as_value #llvm.md_null<1>
+ %5 = llvm.mlir.metadata_as_value #llvm.md_null<1>
+ // CHECK: %{{.*}} = llvm.mlir.metadata_as_value #llvm.md_addrspacecast<#llvm.md_global_value<@md_kernel>, 0>
+ %6 = llvm.mlir.metadata_as_value #llvm.md_addrspacecast<#llvm.md_global_value<@md_kernel>, 0>
+ // CHECK: %{{.*}} = llvm.mlir.metadata_as_value #llvm.md_addrspacecast<#llvm.md_null<1>, 0>
+ %7 = llvm.mlir.metadata_as_value #llvm.md_addrspacecast<#llvm.md_null<1>, 0>
llvm.return
}
diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll b/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
index e5d6a4391b124..672980af629db 100644
--- a/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
+++ b/mlir/test/Target/LLVMIR/Import/intrinsic-unregistered.ll
@@ -115,3 +115,89 @@ define i32 @read_named_register() {
}
!0 = !{!"sp"}
+
+; // -----
+
+; Pointer constants used as metadata operands must be preserved.
+
+declare i32 @llvm.read_register.i32(metadata)
+
+; CHECK-LABEL: llvm.func @read_null_pointer_metadata
+define i32 @read_null_pointer_metadata() {
+ ; CHECK: %[[MD:.*]] = llvm.mlir.metadata_as_value #llvm.md_null<0>
+ ; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]]) : (!llvm.metadata) -> i32
+ %r = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %r
+}
+
+!0 = !{ptr null}
+
+; // -----
+
+; The address space of a null pointer operand must be preserved.
+
+declare i32 @llvm.read_register.i32(metadata)
+
+; CHECK-LABEL: llvm.func @read_null_pointer_addrspace_metadata
+define i32 @read_null_pointer_addrspace_metadata() {
+ ; CHECK: %[[MD:.*]] = llvm.mlir.metadata_as_value #llvm.md_null<1>
+ ; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]]) : (!llvm.metadata) -> i32
+ %r = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %r
+}
+
+!0 = !{ptr addrspace(1) null}
+
+; // -----
+
+; An addrspacecast constant expression operand must be preserved.
+
+ at g = addrspace(1) global i32 0
+
+declare i32 @llvm.read_register.i32(metadata)
+
+; CHECK-LABEL: llvm.func @read_addrspacecast_metadata
+define i32 @read_addrspacecast_metadata() {
+ ; CHECK: %[[MD:.*]] = llvm.mlir.metadata_as_value #llvm.md_addrspacecast<#llvm.md_global_value<@g>, 0>
+ ; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]]) : (!llvm.metadata) -> i32
+ %r = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %r
+}
+
+!0 = !{ptr addrspacecast (ptr addrspace(1) @g to ptr)}
+
+; // -----
+
+; A global value operand reached through ConstantAsMetadata must be preserved.
+
+ at gv = global i32 0
+
+declare i32 @llvm.read_register.i32(metadata)
+
+; CHECK-LABEL: llvm.func @read_global_value_metadata
+define i32 @read_global_value_metadata() {
+ ; CHECK: %[[MD:.*]] = llvm.mlir.metadata_as_value #llvm.md_global_value<@gv>
+ ; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]]) : (!llvm.metadata) -> i32
+ %r = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %r
+}
+
+!0 = !{ptr @gv}
+
+; // -----
+
+; Pointer constants nested inside a multi-operand metadata node.
+
+ at g = addrspace(1) global i32 0
+
+declare i32 @llvm.read_register.i32(metadata)
+
+; CHECK-LABEL: llvm.func @read_pointer_constants_in_node
+define i32 @read_pointer_constants_in_node() {
+ ; CHECK: %[[MD:.*]] = llvm.mlir.metadata_as_value #llvm.md_node<#llvm.md_null<0>, #llvm.md_addrspacecast<#llvm.md_global_value<@g>, 0>>
+ ; CHECK: %{{.*}} = llvm.call_intrinsic "llvm.read_register.i32"(%[[MD]]) : (!llvm.metadata) -> i32
+ %r = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %r
+}
+
+!0 = !{ptr null, ptr addrspacecast (ptr addrspace(1) @g to ptr)}
diff --git a/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir b/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir
index abb6043b630cb..14769209930b6 100644
--- a/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-named-metadata.mlir
@@ -6,6 +6,7 @@
// CHECK: !foo.language_version = !{![[LANG:[0-9]+]]}
// CHECK: !foo.kernel = !{![[KERNEL:[0-9]+]]}
// CHECK: !foo.global_refs = !{![[GLOBAL_REFS:[0-9]+]]}
+// CHECK: !foo.pointer_constants = !{![[PTR_CONSTS:[0-9]+]]}
llvm.func @my_kernel() {
llvm.return
@@ -74,3 +75,14 @@ llvm.named_metadata "foo.global_refs" [
#llvm.md_global_value<@metadata_ifunc>>
]
// CHECK-DAG: ![[GLOBAL_REFS]] = !{ptr @metadata_global, ptr @metadata_alias, ptr @metadata_ifunc}
+
+llvm.mlir.global external @md_addrspace_global(0 : i32) {addr_space = 1 : i32} : i32
+
+llvm.named_metadata "foo.pointer_constants" [
+ #llvm.md_node<
+ #llvm.md_null<0>,
+ #llvm.md_null<1>,
+ #llvm.md_addrspacecast<#llvm.md_global_value<@md_addrspace_global>, 0>,
+ #llvm.md_addrspacecast<#llvm.md_null<1>, 0>>
+]
+// CHECK-DAG: ![[PTR_CONSTS]] = !{ptr null, ptr addrspace(1) null, ptr addrspacecast (ptr addrspace(1) @md_addrspace_global to ptr), ptr addrspacecast (ptr addrspace(1) null to ptr)}
``````````
</details>
https://github.com/llvm/llvm-project/pull/215743
More information about the Mlir-commits
mailing list