[Mlir-commits] [mlir] [MLIR][LLVM] Support inrange in GEP (PR #220424)
Letu Ren
llvmlistbot at llvm.org
Sat Sep 12 19:52:44 PDT 2026
https://github.com/FantasqueX updated https://github.com/llvm/llvm-project/pull/220424
>From f81c9e928fc96e923f36b1767c1157e7f0647b4e Mon Sep 17 00:00:00 2001
From: Letu Ren <fantasquex at gmail.com>
Date: Mon, 31 Aug 2026 16:28:43 +0800
Subject: [PATCH] [MLIR][LLVM] Support inrange in GEP
LLVM IR allows inrange(Start, End) on constant GEP expressions.
Import dropped it because getAsInstruction() does not preserve it.
Model it as LLVM_ConstantRangeAttr on GEPOp, reject empty ranges,
keep it through fold/SROA, and emit ConstantExpr::getGetElementPtr.
Fixes #128031
Assisted-by: gpt-5.6-sol
Assisted-by: grok-4.6
Signed-off-by: Letu Ren <fantasquex at gmail.com>
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 35 ++++++------
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 16 ++++--
mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp | 7 ++-
.../LLVMIR/LLVMToLLVMIRTranslation.cpp | 54 +++++++++++++++++++
mlir/lib/Target/LLVMIR/ModuleImport.cpp | 14 ++++-
mlir/test/Dialect/LLVMIR/canonicalize.mlir | 11 ++++
mlir/test/Dialect/LLVMIR/invalid.mlir | 16 ++++++
mlir/test/Dialect/LLVMIR/mem2reg.mlir | 16 ++++++
mlir/test/Dialect/LLVMIR/roundtrip.mlir | 4 ++
mlir/test/Dialect/LLVMIR/sroa.mlir | 17 ++++++
mlir/test/Target/LLVMIR/Import/constant.ll | 12 +++++
.../Target/LLVMIR/Import/global-variables.ll | 5 ++
mlir/test/Target/LLVMIR/llvmir-invalid.mlir | 35 ++++++++++++
mlir/test/Target/LLVMIR/llvmir.mlir | 9 ++++
14 files changed, 226 insertions(+), 25 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index b769e3fd0e6f1..f2252ac109f0d 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -301,7 +301,8 @@ def LLVM_GEPOp : LLVM_Op<"getelementptr", [Pure,
Variadic<LLVM_ScalarOrVectorOf<AnySignlessInteger>>:$dynamicIndices,
DenseI32ArrayAttr:$rawConstantIndices,
TypeAttr:$elem_type,
- GEPNoWrapFlagsProp:$noWrapFlags);
+ GEPNoWrapFlagsProp:$noWrapFlags,
+ OptionalAttr<LLVM_ConstantRangeAttr>:$inrange);
let results = (outs LLVM_ScalarOrVectorOf<LLVM_AnyPointer>:$res);
let skipDefaultBuilders = 1;
@@ -320,6 +321,12 @@ def LLVM_GEPOp : LLVM_Op<"getelementptr", [Pure,
Note that 'inbounds' implies 'nusw' which is ensured by the enum
definition. The flags can be set individually or in combination.
+ An optional `inrange` attribute corresponds to the LLVM IR `inrange(Start,
+ End)` qualifier on constant GEP expressions. Loading from or storing to any
+ pointer derived from the GEP has undefined behavior if the access would fall
+ outside the half-open range `[Start, End)` from the GEP result. LLVM IR
+ permits `inrange` only on constant GEP expressions.
+
Examples:
```mlir
@@ -332,6 +339,10 @@ def LLVM_GEPOp : LLVM_Op<"getelementptr", [Pure,
// GEP with constant offsets into a structure
%0 = llvm.getelementptr %1[0, 1]
: (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(i32, f32)>
+
+ // Constant GEP with an inrange qualifier
+ %0 = llvm.getelementptr inbounds inrange <i32, -16, 8> %1[0, 0, 2]
+ : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(array<3 x ptr>)>
```
}];
@@ -339,33 +350,19 @@ def LLVM_GEPOp : LLVM_Op<"getelementptr", [Pure,
OpBuilder<(ins "Type":$resultType, "Type":$elementType, "Value":$basePtr,
"ValueRange":$indices,
CArg<"GEPNoWrapFlags", "GEPNoWrapFlags::none">:$noWrapFlags,
+ CArg<"ConstantRangeAttr", "{}">:$inrange,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
OpBuilder<(ins "Type":$resultType, "Type":$elementType, "Value":$basePtr,
"ArrayRef<GEPArg>":$indices,
CArg<"GEPNoWrapFlags", "GEPNoWrapFlags::none">:$noWrapFlags,
+ CArg<"ConstantRangeAttr", "{}">:$inrange,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attributes)>,
];
let llvmBuilder = [{
- SmallVector<llvm::Value *> indices;
- indices.reserve($rawConstantIndices.size());
- GEPIndicesAdaptor<decltype($dynamicIndices)>
- gepIndices(op.getRawConstantIndicesAttr(), $dynamicIndices);
- for (PointerUnion<IntegerAttr, llvm::Value*> valueOrAttr : gepIndices) {
- if (llvm::Value* value = ::llvm::dyn_cast<llvm::Value*>(valueOrAttr))
- indices.push_back(value);
- else
- indices.push_back(
- builder.getInt32(cast<IntegerAttr>(valueOrAttr).getInt()));
- }
- Type baseElementType = op.getElemType();
- llvm::Type *elementType = moduleTranslation.convertType(baseElementType);
- $res = builder.CreateGEP(elementType, $base, indices, "",
- llvm::GEPNoWrapFlags::fromRaw(
- static_cast<unsigned>(
- op.getNoWrapFlags())));
+ return convertGEPOp(op, builder, moduleTranslation);
}];
let assemblyFormat = [{
- ($noWrapFlags^)?
+ ($noWrapFlags^)? (`inrange` $inrange^)?
$base `[` custom<GEPIndices>($dynamicIndices, $rawConstantIndices) `]` attr-dict
`:` functional-type(operands, results) `,` $elem_type
}];
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 52cd73f521326..4ceea572f3b11 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -698,7 +698,7 @@ static void destructureIndices(Type currType, ArrayRef<GEPArg> indices,
void GEPOp::build(OpBuilder &builder, OperationState &result, Type resultType,
Type elementType, Value basePtr, ArrayRef<GEPArg> indices,
- GEPNoWrapFlags noWrapFlags,
+ GEPNoWrapFlags noWrapFlags, ConstantRangeAttr inrange,
ArrayRef<NamedAttribute> attributes) {
SmallVector<int32_t> rawConstantIndices;
SmallVector<Value> dynamicIndices;
@@ -711,16 +711,17 @@ void GEPOp::build(OpBuilder &builder, OperationState &result, Type resultType,
result.getOrAddProperties<Properties>().noWrapFlags = noWrapFlags;
result.getOrAddProperties<Properties>().elem_type =
TypeAttr::get(elementType);
+ result.getOrAddProperties<Properties>().inrange = inrange;
result.addOperands(basePtr);
result.addOperands(dynamicIndices);
}
void GEPOp::build(OpBuilder &builder, OperationState &result, Type resultType,
Type elementType, Value basePtr, ValueRange indices,
- GEPNoWrapFlags noWrapFlags,
+ GEPNoWrapFlags noWrapFlags, ConstantRangeAttr inrange,
ArrayRef<NamedAttribute> attributes) {
build(builder, result, resultType, elementType, basePtr,
- SmallVector<GEPArg>(indices), noWrapFlags, attributes);
+ SmallVector<GEPArg>(indices), noWrapFlags, inrange, attributes);
}
ParseResult mlir::LLVM::parseGEPIndices(
@@ -822,6 +823,11 @@ LogicalResult LLVM::GEPOp::verify() {
if (getNoWrapFlags() == GEPNoWrapFlags::inboundsFlag)
return emitOpError("'inbounds_flag' cannot be used directly.");
+ if (auto inrange = getInrangeAttr()) {
+ if (inrange.getLower().sge(inrange.getUpper()))
+ return emitOpError("expected 'inrange' end to be larger than start");
+ }
+
return verifyStructIndices(getElemType(), getIndices(),
[&] { return emitOpError(); });
}
@@ -4050,6 +4056,10 @@ OpFoldResult LLVM::GEPOp::fold(FoldAdaptor adaptor) {
GEPIndicesAdaptor<ArrayRef<Attribute>> indices(getRawConstantIndicesAttr(),
adaptor.getDynamicIndices());
+ // Avoid losing inrange information.
+ if (getInrangeAttr())
+ return {};
+
// gep %x:T, 0 -> %x
if (getBase().getType() == getType() && indices.size() == 1)
if (auto integer = llvm::dyn_cast_or_null<IntegerAttr>(indices[0]))
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
index 2416e01a67955..4398c678b2cd6 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
@@ -695,7 +695,9 @@ bool LLVM::GEPOp::canUsesBeRemoved(
SmallVectorImpl<OpOperand *> &newBlockingUses,
const DataLayout &dataLayout) {
// GEP can be removed as long as it is a no-op and its users can be removed.
- if (!hasAllZeroIndices(*this))
+ // `inrange` is defined relative to the GEP result, so a zero-index GEP is
+ // not a no-op when that would drop the qualifier.
+ if (getInrangeAttr() || !hasAllZeroIndices(*this))
return false;
return forwardToUsers(*this, newBlockingUses);
}
@@ -909,7 +911,8 @@ DeletionKind LLVM::GEPOp::rewire(const DestructurableMemorySlot &slot,
auto byteType = IntegerType::get(builder.getContext(), 8);
auto newPtr = builder.createOrFold<LLVM::GEPOp>(
getLoc(), getResult().getType(), byteType, newSlot.ptr,
- ArrayRef<GEPArg>(accessInfo->subslotOffset), getNoWrapFlags());
+ ArrayRef<GEPArg>(accessInfo->subslotOffset), getNoWrapFlags(),
+ getInrangeAttr());
getResult().replaceAllUsesWith(newPtr);
return DeletionKind::Delete;
}
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 0c715da6ba954..0447615850250 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -19,6 +19,8 @@
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/Constants.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InlineAsm.h"
@@ -234,6 +236,58 @@ convertNamedMetadataOp(NamedMetadataOp op,
return success();
}
+/// Translate `llvm.getelementptr`. `inrange` is only representable on LLVM
+/// constant GEP expressions.
+static LogicalResult convertGEPOp(GEPOp op, llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ SmallVector<llvm::Value *> indices;
+ indices.reserve(op.getRawConstantIndices().size());
+ for (PointerUnion<IntegerAttr, Value> valueOrAttr : op.getIndices()) {
+ if (Value value = dyn_cast_if_present<Value>(valueOrAttr))
+ indices.push_back(moduleTranslation.lookupValue(value));
+ else
+ indices.push_back(
+ builder.getInt32(cast<IntegerAttr>(valueOrAttr).getInt()));
+ }
+
+ llvm::Type *elementType = moduleTranslation.convertType(op.getElemType());
+ llvm::GEPNoWrapFlags nwFlags =
+ llvm::GEPNoWrapFlags::fromRaw(static_cast<unsigned>(op.getNoWrapFlags()));
+ llvm::Value *base = moduleTranslation.lookupValue(op.getBase());
+ llvm::Value *res;
+ if (ConstantRangeAttr inrangeAttr = op.getInrangeAttr()) {
+ auto *baseConst = dyn_cast<llvm::Constant>(base);
+ if (!baseConst || !llvm::all_of(indices, [](llvm::Value *value) {
+ return isa<llvm::Constant>(value);
+ }))
+ return op.emitError("'inrange' requires the base and indices to "
+ "translate to LLVM constants");
+
+ unsigned indexBitWidth = moduleTranslation.getLLVMModule()
+ ->getDataLayout()
+ .getIndexTypeSizeInBits(base->getType());
+ llvm::APInt lower = inrangeAttr.getLower().sextOrTrunc(indexBitWidth);
+ llvm::APInt upper = inrangeAttr.getUpper().sextOrTrunc(indexBitWidth);
+ // The dialect verifier checks the range at the attribute bitwidth.
+ // Recheck after converting to the pointer index type.
+ if (lower.sge(upper))
+ return op.emitError("expected 'inrange' end to be larger than start "
+ "after conversion to the pointer index type");
+
+ SmallVector<llvm::Constant *> constIndices;
+ constIndices.reserve(indices.size());
+ for (llvm::Value *value : indices)
+ constIndices.push_back(cast<llvm::Constant>(value));
+ res = llvm::ConstantExpr::getGetElementPtr(
+ elementType, baseConst, constIndices, nwFlags,
+ llvm::ConstantRange::getNonEmpty(std::move(lower), std::move(upper)));
+ } else {
+ res = builder.CreateGEP(elementType, base, indices, "", nwFlags);
+ }
+ moduleTranslation.mapValue(op.getRes()) = res;
+ return success();
+}
+
static void convertLinkerOptionsOp(ArrayAttr options,
llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation) {
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index ce6da7b1b6c3d..5d8682d91879c 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -1973,7 +1973,19 @@ FailureOr<Value> ModuleImport::convertConstant(llvm::Constant *constant) {
}));
if (failed(processInstruction(inst)))
return failure();
- return lookupValue(inst);
+ Value result = lookupValue(inst);
+ // getAsInstruction() does not preserve GEP `inrange`, which exists only on
+ // constant expressions. Reattach it to the imported GEPOp.
+ if (constExpr->getOpcode() == llvm::Instruction::GetElementPtr) {
+ if (std::optional<llvm::ConstantRange> inRange =
+ llvm::cast<llvm::GEPOperator>(constExpr)->getInRange()) {
+ auto gepOp = result.getDefiningOp<GEPOp>();
+ assert(gepOp && "expected GEPOp for getelementptr constexpr");
+ gepOp.setInrangeAttr(LLVM::ConstantRangeAttr::get(
+ context, inRange->getLower(), inRange->getUpper()));
+ }
+ }
+ return result;
}
// Convert zero-initialized aggregates to ZeroOp.
diff --git a/mlir/test/Dialect/LLVMIR/canonicalize.mlir b/mlir/test/Dialect/LLVMIR/canonicalize.mlir
index 3a7e514ca8f8a..2dd0bbfe4922b 100644
--- a/mlir/test/Dialect/LLVMIR/canonicalize.mlir
+++ b/mlir/test/Dialect/LLVMIR/canonicalize.mlir
@@ -415,6 +415,17 @@ llvm.func @fold_gep(%x : !llvm.ptr) -> !llvm.ptr {
// -----
+// CHECK-LABEL: fold_gep_inrange
+// CHECK-SAME: %[[ARG:[[:alnum:]]+]]
+// CHECK-NEXT: %[[GEP:.*]] = llvm.getelementptr inrange <i64, -4, 4> %[[ARG]][0]
+// CHECK-NEXT: llvm.return %[[GEP]]
+llvm.func @fold_gep_inrange(%x : !llvm.ptr) -> !llvm.ptr {
+ %0 = llvm.getelementptr inrange <i64, -4, 4> %x[0] : (!llvm.ptr) -> !llvm.ptr, i8
+ llvm.return %0 : !llvm.ptr
+}
+
+// -----
+
// CHECK-LABEL: fold_gep_neg
// CHECK-SAME: %[[ARG:[[:alnum:]]+]]
// CHECK-NEXT: %[[RES:.*]] = llvm.getelementptr inbounds %[[ARG]][0, 1]
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index 4ac160aa9f0fb..4d16e4c7df64d 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -2064,6 +2064,22 @@ llvm.func @gep_inbounds_flag_usage(%ptr: !llvm.ptr, %idx: i64) {
// -----
+llvm.func @gep_inrange_reversed(%ptr: !llvm.ptr) {
+ // expected-error at +1 {{expected 'inrange' end to be larger than start}}
+ llvm.getelementptr inrange <i32, 4, -4> %ptr[0] : (!llvm.ptr) -> !llvm.ptr, i8
+ llvm.return
+}
+
+// -----
+
+llvm.func @gep_inrange_empty(%ptr: !llvm.ptr) {
+ // expected-error at +1 {{expected 'inrange' end to be larger than start}}
+ llvm.getelementptr inrange <i32, 1, 1> %ptr[0] : (!llvm.ptr) -> !llvm.ptr, i8
+ llvm.return
+}
+
+// -----
+
llvm.mlir.global @bad_struct_array_init_size() : !llvm.array<2x!llvm.struct<(i32, f32)>> {
// expected-error at below {{'llvm.mlir.constant' op array attribute size does not match array type size in dimension 0: 1 vs. 2}}
%0 = llvm.mlir.constant([[42 : i32, 1.000000e+00 : f32]]) : !llvm.array<2x!llvm.struct<(i32, f32)>>
diff --git a/mlir/test/Dialect/LLVMIR/mem2reg.mlir b/mlir/test/Dialect/LLVMIR/mem2reg.mlir
index 67a543768f4ef..4c0b1e70664bb 100644
--- a/mlir/test/Dialect/LLVMIR/mem2reg.mlir
+++ b/mlir/test/Dialect/LLVMIR/mem2reg.mlir
@@ -557,6 +557,22 @@ llvm.func @trivial_get_element_ptr() {
// -----
+// inrange is defined relative to the GEP result, so a zero-index GEP is not a
+// no-op. mem2reg must not forward the GEP to the alloca.
+// CHECK-LABEL: llvm.func @inrange_get_element_ptr
+llvm.func @inrange_get_element_ptr() {
+ %0 = llvm.mlir.constant(1 : i32) : i32
+ // CHECK: = llvm.alloca
+ %2 = llvm.alloca %0 x i8 {alignment = 8 : i64} : (i32) -> !llvm.ptr
+ // CHECK: llvm.getelementptr inrange <i32, -4, 4>
+ %4 = llvm.getelementptr inrange <i32, -4, 4> %2[0] : (!llvm.ptr) -> !llvm.ptr, i8
+ llvm.intr.lifetime.start %2 : !llvm.ptr
+ llvm.intr.lifetime.start %4 : !llvm.ptr
+ llvm.return
+}
+
+// -----
+
// CHECK-LABEL: llvm.func @nontrivial_get_element_ptr
llvm.func @nontrivial_get_element_ptr() {
%0 = llvm.mlir.constant(1 : i32) : i32
diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
index 59a810116a040..b18e590c68780 100644
--- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir
@@ -316,6 +316,10 @@ llvm.func @gep(%ptr: !llvm.ptr, %idx: i64, %ptr2: !llvm.ptr) {
llvm.getelementptr nusw | nuw %ptr2[%idx, 0, %idx] : (!llvm.ptr, i64, i64) -> !llvm.ptr, !llvm.struct<(array<10 x f32>)>
// CHECK: llvm.getelementptr nuw %{{.*}}[%{{.*}}, 0, %{{.*}}] : (!llvm.ptr, i64, i64) -> !llvm.ptr, !llvm.struct<(array<10 x f32>)>
llvm.getelementptr nuw %ptr2[%idx, 0, %idx] : (!llvm.ptr, i64, i64) -> !llvm.ptr, !llvm.struct<(array<10 x f32>)>
+ // CHECK: llvm.getelementptr inrange <i32, -16, 8> %{{.*}}[0, 0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(array<3 x ptr>)>
+ llvm.getelementptr inrange <i32, -16, 8> %ptr[0, 0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(array<3 x ptr>)>
+ // CHECK: llvm.getelementptr inbounds inrange <i32, -16, 8> %{{.*}}[0, 0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(array<3 x ptr>)>
+ llvm.getelementptr inbounds inrange <i32, -16, 8> %ptr[0, 0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(array<3 x ptr>)>
llvm.return
}
diff --git a/mlir/test/Dialect/LLVMIR/sroa.mlir b/mlir/test/Dialect/LLVMIR/sroa.mlir
index b019e66d5a430..9b35a8a2515c5 100644
--- a/mlir/test/Dialect/LLVMIR/sroa.mlir
+++ b/mlir/test/Dialect/LLVMIR/sroa.mlir
@@ -463,3 +463,20 @@ llvm.func @empty_struct_not_destructured() -> !llvm.struct<()> {
%2 = llvm.load %1 : !llvm.ptr -> !llvm.struct<()>
llvm.return %2 : !llvm.struct<()>
}
+
+// -----
+
+// inrange is defined relative to the GEP result, so SROA must keep it on the
+// rewritten GEP even when the new GEP would otherwise fold (offset 0).
+// CHECK-LABEL: llvm.func @sroa_preserves_inrange
+llvm.func @sroa_preserves_inrange() -> i32 {
+ %0 = llvm.mlir.constant(1 : i32) : i32
+ // CHECK: %[[ALLOCA:.*]] = llvm.alloca %{{.*}} x i32
+ %1 = llvm.alloca %0 x !llvm.struct<"foo", (i32, f64, i32)> {alignment = 8 : i64} : (i32) -> !llvm.ptr
+ %2 = llvm.getelementptr inbounds inrange <i32, -4, 4> %1[0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<"foo", (i32, f64, i32)>
+ // CHECK: %[[GEP:.*]] = llvm.getelementptr inbounds inrange <i32, -4, 4> %[[ALLOCA]][0] : (!llvm.ptr) -> !llvm.ptr, i8
+ // CHECK: %[[RES:.*]] = llvm.load %[[GEP]]
+ %3 = llvm.load %2 : !llvm.ptr -> i32
+ // CHECK: llvm.return %[[RES]] : i32
+ llvm.return %3 : i32
+}
diff --git a/mlir/test/Target/LLVMIR/Import/constant.ll b/mlir/test/Target/LLVMIR/Import/constant.ll
index cd4f79ce741bc..d43306d1f4b20 100644
--- a/mlir/test/Target/LLVMIR/Import/constant.ll
+++ b/mlir/test/Target/LLVMIR/Import/constant.ll
@@ -80,6 +80,18 @@ define ptr @gep_const_expr() {
; // -----
+ at vt = external constant [3 x ptr]
+
+; CHECK-LABEL: @gep_const_expr_inrange
+define ptr @gep_const_expr_inrange() {
+ ; CHECK-DAG: %[[ADDR:[0-9]+]] = llvm.mlir.addressof @vt : !llvm.ptr
+ ; CHECK-DAG: %[[GEP:[0-9]+]] = llvm.getelementptr inbounds inrange <i{{[0-9]+}}, -16, 8> %[[ADDR]][{{.*}}] : (!llvm.ptr{{.*}}) -> !llvm.ptr
+ ; CHECK-DAG: llvm.return %[[GEP]] : !llvm.ptr
+ ret ptr getelementptr inbounds inrange(-16, 8) ([3 x ptr], ptr @vt, i64 0, i64 2)
+}
+
+; // -----
+
@global = external global i32, align 8
; CHECK-LABEL: @const_expr_with_duplicate
diff --git a/mlir/test/Target/LLVMIR/Import/global-variables.ll b/mlir/test/Target/LLVMIR/Import/global-variables.ll
index 905e9cf3982e5..d57c92db7253b 100644
--- a/mlir/test/Target/LLVMIR/Import/global-variables.ll
+++ b/mlir/test/Target/LLVMIR/Import/global-variables.ll
@@ -40,6 +40,11 @@
; CHECK-DAG: llvm.return %[[GEP]] : !llvm.ptr
@global_gep_const_expr = internal constant ptr getelementptr (i32, ptr @global_int, i32 2)
+; CHECK: llvm.mlir.global internal constant @global_gep_inrange
+; CHECK: llvm.getelementptr inbounds inrange <i{{[0-9]+}}, -16, 8>
+ at vt = external constant [3 x ptr]
+ at global_gep_inrange = internal constant ptr getelementptr inbounds inrange(-16, 8) ([3 x ptr], ptr @vt, i64 0, i64 2)
+
; // -----
; Verifies that converting a reference to a global does not convert the global
diff --git a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
index 6e8301bb626e3..bba8d6248beab 100644
--- a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
@@ -1,5 +1,40 @@
// RUN: mlir-translate -verify-diagnostics -split-input-file -mlir-to-llvmir %s
+llvm.func @gep_inrange_nonconstant_base(%ptr: !llvm.ptr) -> !llvm.ptr {
+ // expected-error @below{{'inrange' requires the base and indices to translate to LLVM constants}}
+ // expected-error @below{{LLVM Translation failed for operation: llvm.getelementptr}}
+ %0 = llvm.getelementptr inrange <i64, -4, 4> %ptr[0] : (!llvm.ptr) -> !llvm.ptr, i8
+ llvm.return %0 : !llvm.ptr
+}
+
+// -----
+
+llvm.mlir.global external @gep_base() : i8
+
+llvm.func @gep_inrange_nonconstant_index(%idx: i64) -> !llvm.ptr {
+ %addr = llvm.mlir.addressof @gep_base : !llvm.ptr
+ // expected-error @below{{'inrange' requires the base and indices to translate to LLVM constants}}
+ // expected-error @below{{LLVM Translation failed for operation: llvm.getelementptr}}
+ %0 = llvm.getelementptr inrange <i64, -4, 4> %addr[%idx] : (!llvm.ptr, i64) -> !llvm.ptr, i8
+ llvm.return %0 : !llvm.ptr
+}
+
+// -----
+
+module attributes {llvm.data_layout = "e-p:32:32"} {
+ llvm.mlir.global external @gep_base() : i8
+ llvm.mlir.global internal @gep_inrange_truncated() : !llvm.ptr {
+ %addr = llvm.mlir.addressof @gep_base : !llvm.ptr
+ // expected-error @below{{expected 'inrange' end to be larger than start after conversion to the pointer index type}}
+ // expected-error @below{{LLVM Translation failed for operation: llvm.getelementptr}}
+ // expected-error @below{{fail to convert global initializer}}
+ %0 = llvm.getelementptr inrange <i64, 2147483647, 2147483648> %addr[0] : (!llvm.ptr) -> !llvm.ptr, i8
+ llvm.return %0 : !llvm.ptr
+ }
+}
+
+// -----
+
// expected-error @below{{cannot be converted to LLVM IR}}
func.func @foo() {
llvm.return
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index d9bc379ade0a4..fed1a6bee6bf4 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -122,6 +122,15 @@ llvm.mlir.global internal constant @int_gep() : !llvm.ptr {
llvm.return %gepinit : !llvm.ptr
}
+// CHECK: @vt = external constant { [3 x ptr] }
+llvm.mlir.global external constant @vt() : !llvm.struct<(array<3 x ptr>)>
+// CHECK: @int_gep_inrange = internal constant ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr] }, ptr @vt, i32 0, i32 0, i32 2)
+llvm.mlir.global internal constant @int_gep_inrange() : !llvm.ptr {
+ %addr = llvm.mlir.addressof @vt : !llvm.ptr
+ %gepinit = llvm.getelementptr inbounds inrange <i32, -16, 8> %addr[0, 0, 2] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<(array<3 x ptr>)>
+ llvm.return %gepinit : !llvm.ptr
+}
+
// CHECK{LITERAL}: @dense_float_vector = internal global <3 x float> <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00>
llvm.mlir.global internal @dense_float_vector(dense<[1.0, 2.0, 3.0]> : vector<3xf32>) : vector<3xf32>
More information about the Mlir-commits
mailing list