[Mlir-commits] [mlir] [MLIR][LLVM] Add noundef metadata to llvm.load (PR #228517)
Arseniy Obolenskiy
llvmlistbot at llvm.org
Sun Oct 4 21:44:22 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/228517
>From 2a9d5e442ddab4cece2f35904e15a6fbfeade0b4 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 2 Oct 2026 18:45:45 +0200
Subject: [PATCH 1/2] [MLIR][LLVM] Add noundef metadata to llvm.load
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 17 ++++++++++++++---
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 2 +-
mlir/test/Dialect/LLVMIR/roundtrip.mlir | 7 +++++++
mlir/test/Target/LLVMIR/Import/instructions.ll | 13 +++++++++++++
mlir/test/Target/LLVMIR/llvmir.mlir | 11 +++++++++++
5 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 2df02fae26d20..129d62ab97fea 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,
+ UnitAttr:$noundef);
// 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 a value that is neither undef nor poison.
+ %0 = llvm.load %ptr noundef : !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^)?
+ (`noundef` $noundef^)?
prop-dict
attr-dict `:` qualified(type($addr)) `->` type($res)
}];
@@ -440,6 +445,10 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
}
if ($dereferenceable)
moduleTranslation.setDereferenceableMetadata(op, inst);
+ if ($noundef) {
+ llvm::MDNode *metadata = llvm::MDNode::get(inst->getContext(), {});
+ inst->setMetadata(llvm::LLVMContext::MD_noundef, metadata);
+ }
}] # setOrderingCode
# setSyncScopeCode
# setAlignmentCode
@@ -450,13 +459,15 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
string mlirBuilder = [{
auto *loadInst = cast<llvm::LoadInst>(inst);
unsigned alignment = loadInst->getAlign().value();
- $res = LLVM::LoadOp::create($_builder, $_location, $_resultType, $addr,
- alignment, loadInst->isVolatile(),
+ auto loadOp = LLVM::LoadOp::create($_builder, $_location, $_resultType,
+ $addr, alignment, loadInst->isVolatile(),
loadInst->hasMetadata(llvm::LLVMContext::MD_nontemporal),
loadInst->hasMetadata(llvm::LLVMContext::MD_invariant_load),
loadInst->hasMetadata(llvm::LLVMContext::MD_invariant_group),
convertAtomicOrderingFromLLVM(loadInst->getOrdering()),
getLLVMSyncScope(loadInst));
+ loadOp.setNoundef(loadInst->hasMetadata(llvm::LLVMContext::MD_noundef));
+ $res = loadOp;
}];
let builders = [
OpBuilder<(ins "Type":$type, "Value":$addr,
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index ff75b5a7dfb2e..cf08fa3bac9e7 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -933,7 +933,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, /*noundef=*/false,
/*access_groups=*/nullptr,
/*alias_scopes=*/nullptr, /*noalias_scopes=*/nullptr,
/*tbaa=*/nullptr);
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index 6ef571efe5c51..48d1a12eb43e1 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -598,6 +598,13 @@ func.func @invariant_load(%ptr : !llvm.ptr) -> i32 {
func.return %0 : i32
}
+// CHECK-LABEL: @noundef_load
+func.func @noundef_load(%ptr : !llvm.ptr) -> i32 {
+ // CHECK: llvm.load %{{.+}} noundef <alignment = 4> : !llvm.ptr -> i32
+ %0 = llvm.load %ptr noundef <alignment = 4> : !llvm.ptr -> i32
+ func.return %0 : i32
+}
+
// 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/instructions.ll b/mlir/test/Target/LLVMIR/Import/instructions.ll
index 28ab4bc44a40a..0cdaa667b3dc5 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: @noundef_load
+; CHECK-SAME: %[[PTR:[a-zA-Z0-9]+]]
+define i32 @noundef_load(ptr %ptr) {
+ ; CHECK: %[[V:[0-9]+]] = llvm.load %[[PTR]] noundef <alignment = 4> : !llvm.ptr -> i32
+ %1 = load i32, ptr %ptr, align 4, !noundef !0
+ ; CHECK: llvm.return %[[V]]
+ ret i32 %1
+}
+
+!0 = !{}
+
+; // -----
+
; 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..cccf402d8e2eb 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: @noundef_load
+llvm.func @noundef_load(%ptr : !llvm.ptr) -> i32 {
+ // CHECK: load i32, ptr %{{.*}}, align 4, !noundef ![[NODE:[0-9]+]]
+ %0 = llvm.load %ptr noundef : !llvm.ptr -> i32
+ llvm.return %0 : i32
+}
+
+// CHECK: ![[NODE]] = !{}
+
+// -----
+
// 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 63f542da4823b9a5d244ad4d3bfed39359206d2d Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 5 Oct 2026 06:44:08 +0200
Subject: [PATCH 2/2] comments
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 9 ++++-----
mlir/lib/Conversion/AMDGPUToROCDL/AMDGPUToROCDL.cpp | 3 ++-
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 5 +++--
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 129d62ab97fea..a2eb4d7a51671 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -459,21 +459,20 @@ def LLVM_LoadOp : LLVM_MemAccessOpBase<"load",
string mlirBuilder = [{
auto *loadInst = cast<llvm::LoadInst>(inst);
unsigned alignment = loadInst->getAlign().value();
- auto loadOp = LLVM::LoadOp::create($_builder, $_location, $_resultType,
- $addr, alignment, loadInst->isVolatile(),
+ $res = LLVM::LoadOp::create($_builder, $_location, $_resultType, $addr,
+ alignment, loadInst->isVolatile(),
loadInst->hasMetadata(llvm::LLVMContext::MD_nontemporal),
loadInst->hasMetadata(llvm::LLVMContext::MD_invariant_load),
loadInst->hasMetadata(llvm::LLVMContext::MD_invariant_group),
+ loadInst->hasMetadata(llvm::LLVMContext::MD_noundef),
convertAtomicOrderingFromLLVM(loadInst->getOrdering()),
getLLVMSyncScope(loadInst));
- loadOp.setNoundef(loadInst->hasMetadata(llvm::LLVMContext::MD_noundef));
- $res = loadOp;
}];
let builders = [
OpBuilder<(ins "Type":$type, "Value":$addr,
CArg<"unsigned", "0">:$alignment, CArg<"bool", "false">:$isVolatile,
CArg<"bool", "false">:$isNonTemporal, CArg<"bool", "false">:$isInvariant,
- CArg<"bool", "false">:$isInvariantGroup,
+ CArg<"bool", "false">:$isInvariantGroup, CArg<"bool", "false">:$isNoundef,
CArg<"AtomicOrdering", "AtomicOrdering::not_atomic">:$ordering,
CArg<"StringRef", "StringRef()">:$syncscope)>
];
diff --git a/mlir/lib/Conversion/AMDGPUToROCDL/AMDGPUToROCDL.cpp b/mlir/lib/Conversion/AMDGPUToROCDL/AMDGPUToROCDL.cpp
index 6bbe65731964f..063aab92c475c 100644
--- a/mlir/lib/Conversion/AMDGPUToROCDL/AMDGPUToROCDL.cpp
+++ b/mlir/lib/Conversion/AMDGPUToROCDL/AMDGPUToROCDL.cpp
@@ -3490,7 +3490,8 @@ struct DsBarrierPollStateOpLowering
rewriter.replaceOpWithNewOp<LLVM::LoadOp>(
op, i64, ptr, /*alignment=*/8, /*volatile_=*/false,
/*nontemporal=*/false, /*invariant=*/false,
- /*invariantGroup=*/false, LLVM::AtomicOrdering::acquire,
+ /*invariantGroup=*/false, /*noundef=*/false,
+ LLVM::AtomicOrdering::acquire,
/*syncscope=*/"workgroup");
return success();
}
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index cf08fa3bac9e7..6316727bdb565 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -928,12 +928,13 @@ LogicalResult LoadOp::verify() {
void LoadOp::build(OpBuilder &builder, OperationState &state, Type type,
Value addr, unsigned alignment, bool isVolatile,
bool isNonTemporal, bool isInvariant, bool isInvariantGroup,
- AtomicOrdering ordering, StringRef syncscope) {
+ bool isNoundef, AtomicOrdering ordering,
+ StringRef syncscope) {
build(builder, state, type, addr,
alignment ? builder.getI64IntegerAttr(alignment) : nullptr, isVolatile,
isNonTemporal, isInvariant, isInvariantGroup, ordering,
syncscope.empty() ? nullptr : builder.getStringAttr(syncscope),
- /*dereferenceable=*/nullptr, /*noundef=*/false,
+ /*dereferenceable=*/nullptr, isNoundef,
/*access_groups=*/nullptr,
/*alias_scopes=*/nullptr, /*noalias_scopes=*/nullptr,
/*tbaa=*/nullptr);
More information about the Mlir-commits
mailing list