[Mlir-commits] [mlir] [MLIR][LLVM] Add range metadata to llvm.load (PR #228571)
Arseniy Obolenskiy
llvmlistbot at llvm.org
Mon Oct 12 23:51:21 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/228571
>From aa78033858d8686b7a290c5cd77e54bce2efb04b Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 2 Oct 2026 22:18:58 +0200
Subject: [PATCH 1/3] [MLIR][LLVM] Add range metadata to llvm.load
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 12 ++++++++++-
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 10 ++++++++-
.../LLVMIR/LLVMIRToLLVMTranslation.cpp | 21 +++++++++++++++++++
mlir/test/Dialect/LLVMIR/invalid.mlir | 21 +++++++++++++++++++
mlir/test/Dialect/LLVMIR/roundtrip.mlir | 9 ++++++++
.../Target/LLVMIR/Import/import-failure.ll | 10 +++++++++
.../test/Target/LLVMIR/Import/instructions.ll | 13 ++++++++++++
mlir/test/Target/LLVMIR/llvmir.mlir | 11 ++++++++++
8 files changed, 105 insertions(+), 2 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 2df02fae26d20..3fabd7b4dc1cf 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -393,7 +393,8 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
DefaultValuedAttr<
AtomicOrdering, "AtomicOrdering::not_atomic">:$ordering,
OptionalAttr<StrAttr>:$syncscope,
- OptionalAttr<LLVM_DereferenceableAttr>:$dereferenceable);
+ OptionalAttr<LLVM_DereferenceableAttr>:$dereferenceable,
+ OptionalAttr<LLVM_ConstantRangeAttr>:$range);
// Append the aliasing related attributes defined in LLVM_MemAccessOpBase.
let arguments = !con(args, aliasAttrs);
let results = (outs LLVM_LoadableType:$res);
@@ -414,6 +415,9 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
// A nontemporal load of a float variable.
%0 = llvm.load %ptr <nontemporal> : !llvm.ptr -> f32
+ // A load of an integer known to lie in [0, 10).
+ %0 = llvm.load %ptr range <i32, 0, 10> : !llvm.ptr -> i32
+
// An atomic load of an integer variable.
%0 = llvm.load %ptr atomic monotonic <alignment = 8>
: !llvm.ptr -> i64
@@ -428,6 +432,7 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
(`invariant` $invariant^)?
(`invariant_group` $invariantGroup^)?
(`dereferenceable` `` $dereferenceable^)?
+ (`range` $range^)?
prop-dict
attr-dict `:` qualified(type($addr)) `->` type($res)
}];
@@ -440,6 +445,11 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
}
if ($dereferenceable)
moduleTranslation.setDereferenceableMetadata(op, inst);
+ if ($range) {
+ llvm::MDNode *metadata = llvm::MDBuilder(inst->getContext())
+ .createRange($range->getLower(), $range->getUpper());
+ inst->setMetadata(llvm::LLVMContext::MD_range, metadata);
+ }
}] # setOrderingCode
# setSyncScopeCode
# setAlignmentCode
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index ff75b5a7dfb2e..9d7bfa1074e8e 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -921,6 +921,14 @@ verifyAtomicMemOp(OpTy memOp, Type valueType,
LogicalResult LoadOp::verify() {
Type valueType = getResult().getType();
+ if (ConstantRangeAttr range = getRangeAttr()) {
+ auto intType = dyn_cast<IntegerType>(getElementTypeOrSelf(valueType));
+ if (!intType || intType.getWidth() != range.getLower().getBitWidth())
+ return emitOpError("expected range bitwidth to match the integer "
+ "(element) result type");
+ if (range.getLower() == range.getUpper())
+ return emitOpError("expected range to be neither empty nor full");
+ }
return verifyAtomicMemOp(*this, valueType,
{AtomicOrdering::release, AtomicOrdering::acq_rel});
}
@@ -933,7 +941,7 @@ void LoadOp::build(OpBuilder &builder, OperationState &state, Type type,
alignment ? builder.getI64IntegerAttr(alignment) : nullptr, isVolatile,
isNonTemporal, isInvariant, isInvariantGroup, ordering,
syncscope.empty() ? nullptr : builder.getStringAttr(syncscope),
- /*dereferenceable=*/nullptr,
+ /*dereferenceable=*/nullptr, /*range=*/nullptr,
/*access_groups=*/nullptr,
/*alias_scopes=*/nullptr, /*noalias_scopes=*/nullptr,
/*tbaa=*/nullptr);
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
index 6733d6ee3ceb0..eb851ef40deb4 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
@@ -92,6 +92,7 @@ getSupportedMetadataImpl(llvm::LLVMContext &llvmContext) {
llvm::LLVMContext::MD_alias_scope,
llvm::LLVMContext::MD_dereferenceable,
llvm::LLVMContext::MD_dereferenceable_or_null,
+ llvm::LLVMContext::MD_range,
llvm::LLVMContext::MD_mmra,
llvmContext.getMDKindID(vecTypeHintMDName),
llvmContext.getMDKindID(workGroupSizeHintMDName),
@@ -256,6 +257,24 @@ static LogicalResult setDereferenceableAttr(const llvm::MDNode *node,
return success();
}
+/// Fails on multi-range metadata since ConstantRangeAttr holds one range.
+static LogicalResult setRangeAttr(const llvm::MDNode *node, Operation *op) {
+ auto loadOp = dyn_cast<LoadOp>(op);
+ if (!loadOp || node->getNumOperands() != 2)
+ return failure();
+
+ auto *lower =
+ llvm::mdconst::dyn_extract<llvm::ConstantInt>(node->getOperand(0));
+ auto *upper =
+ llvm::mdconst::dyn_extract<llvm::ConstantInt>(node->getOperand(1));
+ if (!lower || !upper)
+ return failure();
+
+ loadOp.setRangeAttr(ConstantRangeAttr::get(
+ op->getContext(), lower->getValue(), upper->getValue()));
+ return success();
+}
+
/// Convert the given MMRA metadata (either an MMRA tag or an array of them)
/// into corresponding MLIR attributes and set them on the given operation as a
/// discardable `llvm.mmra` attribute.
@@ -509,6 +528,8 @@ class LLVMDialectLLVMIRImportInterface : public LLVMImportDialectInterface {
return setDereferenceableAttr(
node, llvm::LLVMContext::MD_dereferenceable_or_null, op,
moduleImport);
+ if (kind == llvm::LLVMContext::MD_range)
+ return setRangeAttr(node, op);
if (kind == llvm::LLVMContext::MD_mmra)
return setMmraAttr(node, op, moduleImport);
llvm::LLVMContext &context = node->getContext();
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index 8b652b04d88b2..e405e5c6f95cd 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -183,6 +183,27 @@ func.func @load_unaligned_atomic(%ptr : !llvm.ptr) {
// -----
+func.func @load_range_non_integer(%ptr : !llvm.ptr) {
+ // expected-error at below {{expected range bitwidth to match the integer (element) result type}}
+ %1 = llvm.load %ptr range <i32, 0, 10> : !llvm.ptr -> f32
+}
+
+// -----
+
+func.func @load_range_bitwidth_mismatch(%ptr : !llvm.ptr) {
+ // expected-error at below {{expected range bitwidth to match the integer (element) result type}}
+ %1 = llvm.load %ptr range <i64, 0, 10> : !llvm.ptr -> i32
+}
+
+// -----
+
+func.func @load_range_empty_or_full(%ptr : !llvm.ptr) {
+ // expected-error at below {{expected range to be neither empty nor full}}
+ %1 = llvm.load %ptr range <i32, 5, 5> : !llvm.ptr -> i32
+}
+
+// -----
+
func.func @store_syncscope(%val : f32, %ptr : !llvm.ptr) {
// expected-error at below {{expected syncscope to be null for non-atomic access}}
"llvm.store"(%val, %ptr) <{syncscope = "singlethread"}> : (f32, !llvm.ptr) -> ()
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index 6ef571efe5c51..5bff896e02bf4 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -598,6 +598,15 @@ func.func @invariant_load(%ptr : !llvm.ptr) -> i32 {
func.return %0 : i32
}
+// CHECK-LABEL: @range_load
+func.func @range_load(%ptr : !llvm.ptr) {
+ // CHECK: llvm.load %{{.+}} range <i32, 0, 10> <alignment = 4> : !llvm.ptr -> i32
+ %0 = llvm.load %ptr range <i32, 0, 10> <alignment = 4> : !llvm.ptr -> i32
+ // CHECK: llvm.load %{{.+}} range <i8, 0, 2> : !llvm.ptr -> vector<2xi8>
+ %1 = llvm.load %ptr range <i8, 0, 2> : !llvm.ptr -> vector<2xi8>
+ func.return
+}
+
// CHECK-LABEL: @invariant_group_load
func.func @invariant_group_load(%ptr : !llvm.ptr) -> i32 {
// CHECK: llvm.load %{{.+}} invariant_group <alignment = 4> : !llvm.ptr -> i32
diff --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll
index 7dca9a3ed2be6..56bb6038e2f27 100644
--- a/mlir/test/Target/LLVMIR/Import/import-failure.ll
+++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll
@@ -302,6 +302,16 @@ define void @deref(i64 %0) {
; // -----
+; CHECK: warning: unhandled metadata: ![[RANGE:[0-9]+]] = !{i32 0, i32 2, i32 4, i32 6} on %{{.*}} = load i32, ptr %{{.*}}, align 4, !range ![[RANGE]]
+define i32 @multi_range(ptr %ptr) {
+ %1 = load i32, ptr %ptr, !range !0
+ ret i32 %1
+}
+
+!0 = !{i32 0, i32 2, i32 4, i32 6}
+
+; // -----
+
; CHECK: import-failure.ll
; CHECK-SAME: warning: unhandled data layout token: ni:42
target datalayout = "e-ni:42-i64:64"
diff --git a/mlir/test/Target/LLVMIR/Import/instructions.ll b/mlir/test/Target/LLVMIR/Import/instructions.ll
index 28ab4bc44a40a..ac5eb38c161a0 100644
--- a/mlir/test/Target/LLVMIR/Import/instructions.ll
+++ b/mlir/test/Target/LLVMIR/Import/instructions.ll
@@ -375,6 +375,19 @@ define void @load_store(ptr %ptr) {
; // -----
+; CHECK-LABEL: @range_load
+; CHECK-SAME: %[[PTR:[a-zA-Z0-9]+]]
+define i32 @range_load(ptr %ptr) {
+ ; CHECK: %[[V:[0-9]+]] = llvm.load %[[PTR]] range <i32, 0, 10> <alignment = 4> : !llvm.ptr -> i32
+ %1 = load i32, ptr %ptr, align 4, !range !0
+ ; CHECK: llvm.return %[[V]]
+ ret i32 %1
+}
+
+!0 = !{i32 0, i32 10}
+
+; // -----
+
; CHECK-LABEL: @invariant_load
; CHECK-SAME: %[[PTR:[a-zA-Z0-9]+]]
define float @invariant_load(ptr %ptr) {
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index f29184beb6b4d..49a258b3ad2fc 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2135,6 +2135,17 @@ llvm.func @nontemporal_store_and_load(%ptr : !llvm.ptr) -> i32 {
// -----
+// CHECK-LABEL: @range_load
+llvm.func @range_load(%ptr : !llvm.ptr) -> i32 {
+ // CHECK: load i32, ptr %{{.*}}, align 4, !range ![[RANGE:[0-9]+]]
+ %0 = llvm.load %ptr range <i32, 0, 10> : !llvm.ptr -> i32
+ llvm.return %0 : i32
+}
+
+// CHECK: ![[RANGE]] = !{i32 0, i32 10}
+
+// -----
+
// Check that invariant group attribute is exported as metadata node.
llvm.func @nontemporal_store_and_load(%ptr : !llvm.ptr) -> i32 {
%val = llvm.mlir.constant(42 : i32) : i32
>From 3b14b6190c94837033e3eb75a1afb0e6405655be Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 5 Oct 2026 06:28:24 +0200
Subject: [PATCH 2/3] Address comments
---
.../mlir/Dialect/LLVMIR/LLVMInterfaces.h | 3 ++
.../mlir/Dialect/LLVMIR/LLVMInterfaces.td | 35 +++++++++++++++++++
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 7 ++--
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 8 -----
mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp | 23 ++++++++++++
.../LLVMIR/LLVMIRToLLVMTranslation.cpp | 19 ++++------
mlir/test/Dialect/LLVMIR/invalid.mlir | 6 ++--
mlir/test/Dialect/LLVMIR/roundtrip.mlir | 8 ++---
.../Target/LLVMIR/Import/import-failure.ll | 10 ------
.../test/Target/LLVMIR/Import/instructions.ll | 5 ++-
mlir/test/Target/LLVMIR/llvmir.mlir | 2 +-
11 files changed, 84 insertions(+), 42 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.h b/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.h
index e0e98e167f501..0b8c254eb5e3a 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.h
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.h
@@ -37,6 +37,9 @@ LogicalResult verifyAliasAnalysisOpInterface(Operation *op);
/// exactly one result of LLVM pointer type.
LogicalResult verifyDereferenceableOpInterface(Operation *op);
+/// Verifies that the range of an operation implementing the range interface
+/// matches the bitwidth of its single integer (element) result.
+LogicalResult verifyRangeOpInterface(Operation *op);
} // namespace detail
} // namespace LLVM
} // namespace mlir
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td
index 66a1fe0ce251a..1e671ce42fbd2 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMInterfaces.td
@@ -343,6 +343,41 @@ def DereferenceableOpInterface : OpInterface<"DereferenceableOpInterface"> {
];
}
+def RangeOpInterface : OpInterface<"RangeOpInterface"> {
+ let description = [{
+ An interface for operations that can carry range metadata. The default
+ implementations expect a ConstantRangeAttr named `range`.
+ }];
+
+ let cppNamespace = "::mlir::LLVM";
+ let verify = [{ return detail::verifyRangeOpInterface($_op); }];
+
+ let methods = [
+ InterfaceMethod<
+ /*desc=*/ "Returns the range attribute or nullptr",
+ /*returnType=*/ "::mlir::LLVM::ConstantRangeAttr",
+ /*methodName=*/ "getRangeOrNull",
+ /*args=*/ (ins),
+ /*methodBody=*/ [{}],
+ /*defaultImpl=*/ [{
+ auto op = cast<ConcreteOp>(this->getOperation());
+ return op.getRangeAttr();
+ }]
+ >,
+ InterfaceMethod<
+ /*desc=*/ "Sets the range attribute",
+ /*returnType=*/ "void",
+ /*methodName=*/ "setRange",
+ /*args=*/ (ins "::mlir::LLVM::ConstantRangeAttr":$attr),
+ /*methodBody=*/ [{}],
+ /*defaultImpl=*/ [{
+ auto op = cast<ConcreteOp>(this->getOperation());
+ op.setRangeAttr(attr);
+ }]
+ >
+ ];
+}
+
def FPExceptionBehaviorOpInterface : OpInterface<"FPExceptionBehaviorOpInterface"> {
let description = [{
An interface for operations receiving an exception behavior attribute
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 3fabd7b4dc1cf..98381b0f78951 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -383,7 +383,8 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
DeclareOpInterfaceMethods<PromotableMemOpInterface>,
DeclareOpInterfaceMethods<SafeMemorySlotAccessOpInterface>,
- DeclareOpInterfaceMethods<DereferenceableOpInterface>]> {
+ DeclareOpInterfaceMethods<DereferenceableOpInterface>,
+ DeclareOpInterfaceMethods<RangeOpInterface>]> {
dag args = (ins LLVM_AnyPointer:$addr,
OptionalAttr<I64Attr>:$alignment,
UnitAttr:$volatile_,
@@ -416,7 +417,7 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
%0 = llvm.load %ptr <nontemporal> : !llvm.ptr -> f32
// A load of an integer known to lie in [0, 10).
- %0 = llvm.load %ptr range <i32, 0, 10> : !llvm.ptr -> i32
+ %0 = llvm.load %ptr range<i32, 0, 10> : !llvm.ptr -> i32
// An atomic load of an integer variable.
%0 = llvm.load %ptr atomic monotonic <alignment = 8>
@@ -432,7 +433,7 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
(`invariant` $invariant^)?
(`invariant_group` $invariantGroup^)?
(`dereferenceable` `` $dereferenceable^)?
- (`range` $range^)?
+ (`range` `` $range^)?
prop-dict
attr-dict `:` qualified(type($addr)) `->` type($res)
}];
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 9d7bfa1074e8e..600de9b0727a1 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -921,14 +921,6 @@ verifyAtomicMemOp(OpTy memOp, Type valueType,
LogicalResult LoadOp::verify() {
Type valueType = getResult().getType();
- if (ConstantRangeAttr range = getRangeAttr()) {
- auto intType = dyn_cast<IntegerType>(getElementTypeOrSelf(valueType));
- if (!intType || intType.getWidth() != range.getLower().getBitWidth())
- return emitOpError("expected range bitwidth to match the integer "
- "(element) result type");
- if (range.getLower() == range.getUpper())
- return emitOpError("expected range to be neither empty nor full");
- }
return verifyAtomicMemOp(*this, valueType,
{AtomicOrdering::release, AtomicOrdering::acq_rel});
}
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
index 44a9284f5a5ab..7d5a86d6740af 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMInterfaces.cpp
@@ -13,6 +13,7 @@
#include "mlir/Dialect/LLVMIR/LLVMInterfaces.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/IR/TypeUtilities.h"
using namespace mlir;
using namespace mlir::LLVM;
@@ -80,6 +81,28 @@ mlir::LLVM::detail::verifyDereferenceableOpInterface(Operation *op) {
return success();
}
+//===----------------------------------------------------------------------===//
+// RangeOpInterface
+//===----------------------------------------------------------------------===//
+
+LogicalResult mlir::LLVM::detail::verifyRangeOpInterface(Operation *op) {
+ auto iface = cast<RangeOpInterface>(op);
+
+ ConstantRangeAttr range = iface.getRangeOrNull();
+ if (!range)
+ return success();
+ if (op->getNumResults() != 1)
+ return op->emitOpError("expected op to have a single result");
+ auto intType =
+ dyn_cast<IntegerType>(getElementTypeOrSelf(op->getResult(0).getType()));
+ if (!intType || intType.getWidth() != range.getLower().getBitWidth())
+ return op->emitOpError(
+ "expected range bitwidth to match the integer (element) result type");
+ if (range.getLower() == range.getUpper())
+ return op->emitOpError("expected range to be neither empty nor full");
+ return success();
+}
+
SmallVector<Value> mlir::LLVM::AtomicCmpXchgOp::getAccessedOperands() {
return {getPtr()};
}
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
index eb851ef40deb4..de038c68596cb 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMIRToLLVMTranslation.cpp
@@ -18,6 +18,7 @@
#include "mlir/Target/LLVMIR/ModuleImport.h"
#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instructions.h"
@@ -257,21 +258,15 @@ static LogicalResult setDereferenceableAttr(const llvm::MDNode *node,
return success();
}
-/// Fails on multi-range metadata since ConstantRangeAttr holds one range.
+/// Multi-range metadata is imported as the union of its ranges.
static LogicalResult setRangeAttr(const llvm::MDNode *node, Operation *op) {
- auto loadOp = dyn_cast<LoadOp>(op);
- if (!loadOp || node->getNumOperands() != 2)
- return failure();
-
- auto *lower =
- llvm::mdconst::dyn_extract<llvm::ConstantInt>(node->getOperand(0));
- auto *upper =
- llvm::mdconst::dyn_extract<llvm::ConstantInt>(node->getOperand(1));
- if (!lower || !upper)
+ auto iface = dyn_cast<RangeOpInterface>(op);
+ if (!iface)
return failure();
- loadOp.setRangeAttr(ConstantRangeAttr::get(
- op->getContext(), lower->getValue(), upper->getValue()));
+ llvm::ConstantRange range = llvm::getConstantRangeFromMetadata(*node);
+ iface.setRange(ConstantRangeAttr::get(op->getContext(), range.getLower(),
+ range.getUpper()));
return success();
}
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index e405e5c6f95cd..b67c1b8895113 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -185,21 +185,21 @@ func.func @load_unaligned_atomic(%ptr : !llvm.ptr) {
func.func @load_range_non_integer(%ptr : !llvm.ptr) {
// expected-error at below {{expected range bitwidth to match the integer (element) result type}}
- %1 = llvm.load %ptr range <i32, 0, 10> : !llvm.ptr -> f32
+ %1 = llvm.load %ptr range<i32, 0, 10> : !llvm.ptr -> f32
}
// -----
func.func @load_range_bitwidth_mismatch(%ptr : !llvm.ptr) {
// expected-error at below {{expected range bitwidth to match the integer (element) result type}}
- %1 = llvm.load %ptr range <i64, 0, 10> : !llvm.ptr -> i32
+ %1 = llvm.load %ptr range<i64, 0, 10> : !llvm.ptr -> i32
}
// -----
func.func @load_range_empty_or_full(%ptr : !llvm.ptr) {
// expected-error at below {{expected range to be neither empty nor full}}
- %1 = llvm.load %ptr range <i32, 5, 5> : !llvm.ptr -> i32
+ %1 = llvm.load %ptr range<i32, 5, 5> : !llvm.ptr -> i32
}
// -----
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index 5bff896e02bf4..cd2c4a11fa8a0 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -600,10 +600,10 @@ func.func @invariant_load(%ptr : !llvm.ptr) -> i32 {
// CHECK-LABEL: @range_load
func.func @range_load(%ptr : !llvm.ptr) {
- // CHECK: llvm.load %{{.+}} range <i32, 0, 10> <alignment = 4> : !llvm.ptr -> i32
- %0 = llvm.load %ptr range <i32, 0, 10> <alignment = 4> : !llvm.ptr -> i32
- // CHECK: llvm.load %{{.+}} range <i8, 0, 2> : !llvm.ptr -> vector<2xi8>
- %1 = llvm.load %ptr range <i8, 0, 2> : !llvm.ptr -> vector<2xi8>
+ // CHECK: llvm.load %{{.+}} range<i32, 0, 10> <alignment = 4> : !llvm.ptr -> i32
+ %0 = llvm.load %ptr range<i32, 0, 10> <alignment = 4> : !llvm.ptr -> i32
+ // CHECK: llvm.load %{{.+}} range<i8, 0, 2> : !llvm.ptr -> vector<2xi8>
+ %1 = llvm.load %ptr range<i8, 0, 2> : !llvm.ptr -> vector<2xi8>
func.return
}
diff --git a/mlir/test/Target/LLVMIR/Import/import-failure.ll b/mlir/test/Target/LLVMIR/Import/import-failure.ll
index 56bb6038e2f27..7dca9a3ed2be6 100644
--- a/mlir/test/Target/LLVMIR/Import/import-failure.ll
+++ b/mlir/test/Target/LLVMIR/Import/import-failure.ll
@@ -302,16 +302,6 @@ define void @deref(i64 %0) {
; // -----
-; CHECK: warning: unhandled metadata: ![[RANGE:[0-9]+]] = !{i32 0, i32 2, i32 4, i32 6} on %{{.*}} = load i32, ptr %{{.*}}, align 4, !range ![[RANGE]]
-define i32 @multi_range(ptr %ptr) {
- %1 = load i32, ptr %ptr, !range !0
- ret i32 %1
-}
-
-!0 = !{i32 0, i32 2, i32 4, i32 6}
-
-; // -----
-
; CHECK: import-failure.ll
; CHECK-SAME: warning: unhandled data layout token: ni:42
target datalayout = "e-ni:42-i64:64"
diff --git a/mlir/test/Target/LLVMIR/Import/instructions.ll b/mlir/test/Target/LLVMIR/Import/instructions.ll
index ac5eb38c161a0..33904f55c77d5 100644
--- a/mlir/test/Target/LLVMIR/Import/instructions.ll
+++ b/mlir/test/Target/LLVMIR/Import/instructions.ll
@@ -378,13 +378,16 @@ define void @load_store(ptr %ptr) {
; CHECK-LABEL: @range_load
; CHECK-SAME: %[[PTR:[a-zA-Z0-9]+]]
define i32 @range_load(ptr %ptr) {
- ; CHECK: %[[V:[0-9]+]] = llvm.load %[[PTR]] range <i32, 0, 10> <alignment = 4> : !llvm.ptr -> i32
+ ; CHECK: %[[V:[0-9]+]] = llvm.load %[[PTR]] range<i32, 0, 10> <alignment = 4> : !llvm.ptr -> i32
%1 = load i32, ptr %ptr, align 4, !range !0
+ ; CHECK: llvm.load %[[PTR]] range<i32, 0, 6> <alignment = 4> : !llvm.ptr -> i32
+ %2 = load i32, ptr %ptr, align 4, !range !1
; CHECK: llvm.return %[[V]]
ret i32 %1
}
!0 = !{i32 0, i32 10}
+!1 = !{i32 0, i32 2, i32 4, i32 6}
; // -----
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 49a258b3ad2fc..b0346d1c2893d 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2138,7 +2138,7 @@ llvm.func @nontemporal_store_and_load(%ptr : !llvm.ptr) -> i32 {
// CHECK-LABEL: @range_load
llvm.func @range_load(%ptr : !llvm.ptr) -> i32 {
// CHECK: load i32, ptr %{{.*}}, align 4, !range ![[RANGE:[0-9]+]]
- %0 = llvm.load %ptr range <i32, 0, 10> : !llvm.ptr -> i32
+ %0 = llvm.load %ptr range<i32, 0, 10> : !llvm.ptr -> i32
llvm.return %0 : i32
}
>From 146a506dbfe9ca368d30154a9afbd031cf549831 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 13 Oct 2026 08:51:07 +0200
Subject: [PATCH 3/3] setRangeCode helper
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td | 7 +++++++
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 6 +-----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
index 34a47274a7fdf..d0506f971b2c2 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td
@@ -246,6 +246,13 @@ class LLVM_MemOpPatterns {
inst->setMetadata(llvm::LLVMContext::MD_invariant_group, metadata);
}
}];
+ code setRangeCode = [{
+ if ($range) {
+ llvm::MDNode *metadata = llvm::MDBuilder(inst->getContext())
+ .createRange($range->getLower(), $range->getUpper());
+ inst->setMetadata(llvm::LLVMContext::MD_range, metadata);
+ }
+ }];
code setAccessGroupsMetadataCode = [{
moduleTranslation.setAccessGroupsMetadata(op, inst);
}];
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 773d7df1ca75b..9f658bc7a4b81 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -457,16 +457,12 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
llvm::MDNode *metadata = llvm::MDNode::get(inst->getContext(), {});
inst->setMetadata(llvm::LLVMContext::MD_noundef, metadata);
}
- if ($range) {
- llvm::MDNode *metadata = llvm::MDBuilder(inst->getContext())
- .createRange($range->getLower(), $range->getUpper());
- inst->setMetadata(llvm::LLVMContext::MD_range, metadata);
- }
}] # setOrderingCode
# setSyncScopeCode
# setAlignmentCode
# setNonTemporalMetadataCode
# setInvariantGroupCode
+ # setRangeCode
# setAccessGroupsMetadataCode
# setAliasAnalysisMetadataCode;
string mlirBuilder = [{
More information about the Mlir-commits
mailing list