[Mlir-commits] [flang] [mlir] [MLIR] Implement mayHaveNonZeroOffset class (PR #231285)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sun Oct 25 15:46:21 PDT 2026
https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/231285
>From 2f3c61e3f5ce0793e5e560a79846fc2d28f9fcce Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Mon, 12 Oct 2026 15:39:38 -0500
Subject: [PATCH 1/3] [MLIR] Implement mayHaveNonZeroOffset class
Generic MLIR analysis for alias is not enough as it has no information
for an offset alias. For example, without this patch, CIR ptr_stride
ptr, 0 and ptr_stride ptr, 1 are considered as MustAlias as they uses
the same base. However, there is still a chance for us to do precise
alias analysis and declare not alias by its implementation. As a
result, we provide a mayHaveNonZeroOffset interface method and
downgrade to MayAlias to give the underlying Dialect implementation a
chance to do AliasAnalysis.
---
.../mlir/Interfaces/ViewLikeInterface.td | 13 +++++
.../AliasAnalysis/LocalAliasAnalysis.cpp | 55 ++++++++++++-------
mlir/test/Analysis/test-alias-analysis.mlir | 31 +++++++++++
mlir/test/lib/Dialect/Test/CMakeLists.txt | 1 +
mlir/test/lib/Dialect/Test/TestOps.td | 23 ++++++++
5 files changed, 103 insertions(+), 20 deletions(-)
diff --git a/mlir/include/mlir/Interfaces/ViewLikeInterface.td b/mlir/include/mlir/Interfaces/ViewLikeInterface.td
index 131c1a0d92b24c..0bbcd0401aa830 100644
--- a/mlir/include/mlir/Interfaces/ViewLikeInterface.td
+++ b/mlir/include/mlir/Interfaces/ViewLikeInterface.td
@@ -36,6 +36,19 @@ def ViewLikeOpInterface : OpInterface<"ViewLikeOpInterface"> {
/*defaultImplementation=*/[{
return $_op->getResult(0);
}]
+ >,
+ InterfaceMethod<
+ /*desc=*/[{
+ Returns true if the view may start at a different address than its
+ source.
+ }],
+ /*retTy=*/"bool",
+ /*methodName=*/"mayHaveNonZeroOffset",
+ /*args=*/(ins),
+ /*methodBody=*/"",
+ /*defaultImplementation=*/[{
+ return false;
+ }]
>
];
}
diff --git a/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp b/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
index 4a8c2de8562f8b..ebefb1c490aa64 100644
--- a/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
+++ b/mlir/lib/Analysis/AliasAnalysis/LocalAliasAnalysis.cpp
@@ -39,9 +39,17 @@ using namespace mlir;
/// value.
static constexpr unsigned maxUnderlyingValueSearchDepth = 10;
+namespace {
+struct UnderlyingValueWalk {
+ DenseSet<Value> visited;
+ // True if a value can be aliased by offset.
+ bool throughOffsetView = false;
+};
+} // namespace
+
/// Given a value, collect all of the underlying values being addressed.
static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
- DenseSet<Value> &visited,
+ UnderlyingValueWalk &walk,
SmallVectorImpl<Value> &output);
/// Given a RegionBranchOpInterface operation (`branch`), a Value`inputValue`
@@ -50,7 +58,7 @@ static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
static void collectUnderlyingAddressValues2(
RegionBranchOpInterface branch, RegionSuccessor initialSuccessor,
Value inputValue, unsigned inputIndex, unsigned maxDepth,
- DenseSet<Value> &visited, SmallVectorImpl<Value> &output) {
+ UnderlyingValueWalk &walk, SmallVectorImpl<Value> &output) {
LDBG() << "collectUnderlyingAddressValues2: "
<< OpWithFlags(branch.getOperation(), OpPrintingFlags().skipRegions());
LDBG() << " with initialSuccessor " << initialSuccessor;
@@ -84,13 +92,13 @@ static void collectUnderlyingAddressValues2(
LDBG() << " Found " << predecessorValues.size() << " predecessor values";
for (Value predecessorValue : predecessorValues) {
LDBG() << " Processing predecessor value: " << predecessorValue;
- collectUnderlyingAddressValues(predecessorValue, maxDepth, visited, output);
+ collectUnderlyingAddressValues(predecessorValue, maxDepth, walk, output);
}
}
/// Given a result, collect all of the underlying values being addressed.
static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth,
- DenseSet<Value> &visited,
+ UnderlyingValueWalk &walk,
SmallVectorImpl<Value> &output) {
LDBG() << "collectUnderlyingAddressValues (OpResult): " << result;
LDBG() << " maxDepth: " << maxDepth;
@@ -101,8 +109,10 @@ static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth,
if (ViewLikeOpInterface view = dyn_cast<ViewLikeOpInterface>(op)) {
if (result == view.getViewDest()) {
LDBG() << " Unwrapping view to source: " << view.getViewSource();
+ if (view.mayHaveNonZeroOffset())
+ walk.throughOffsetView = true;
return collectUnderlyingAddressValues(view.getViewSource(), maxDepth,
- visited, output);
+ walk, output);
}
}
// Check to see if we can reason about the control flow of this op.
@@ -110,7 +120,7 @@ static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth,
LDBG() << " Processing region branch operation";
return collectUnderlyingAddressValues2(
branch, RegionSuccessor(branch.getOperation()), result,
- result.getResultNumber(), maxDepth, visited, output);
+ result.getResultNumber(), maxDepth, walk, output);
}
LDBG() << " Adding result to output: " << result;
@@ -120,7 +130,7 @@ static void collectUnderlyingAddressValues(OpResult result, unsigned maxDepth,
/// Given a block argument, collect all of the underlying values being
/// addressed.
static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
- DenseSet<Value> &visited,
+ UnderlyingValueWalk &walk,
SmallVectorImpl<Value> &output) {
LDBG() << "collectUnderlyingAddressValues (BlockArgument): " << arg;
LDBG() << " maxDepth: " << maxDepth;
@@ -154,7 +164,7 @@ static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
return;
}
LDBG() << " Processing operand from predecessor: " << operand;
- collectUnderlyingAddressValues(operand, maxDepth, visited, output);
+ collectUnderlyingAddressValues(operand, maxDepth, walk, output);
}
return;
}
@@ -174,7 +184,7 @@ static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
if (successor.getSuccessor() == region) {
LDBG() << " Found matching region successor: " << successor;
return collectUnderlyingAddressValues2(
- branch, successor, arg, argNumber, maxDepth, visited, output);
+ branch, successor, arg, argNumber, maxDepth, walk, output);
}
}
LDBG() << " No matching region successor found, adding argument to output";
@@ -190,13 +200,13 @@ static void collectUnderlyingAddressValues(BlockArgument arg, unsigned maxDepth,
/// Given a value, collect all of the underlying values being addressed.
static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
- DenseSet<Value> &visited,
+ UnderlyingValueWalk &walk,
SmallVectorImpl<Value> &output) {
LDBG() << "collectUnderlyingAddressValues: " << value;
LDBG() << " maxDepth: " << maxDepth;
// Check that we don't infinitely recurse.
- if (!visited.insert(value).second) {
+ if (!walk.visited.insert(value).second) {
LDBG() << " Value already visited, skipping";
return;
}
@@ -209,21 +219,23 @@ static void collectUnderlyingAddressValues(Value value, unsigned maxDepth,
if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
LDBG() << " Processing as BlockArgument";
- return collectUnderlyingAddressValues(arg, maxDepth, visited, output);
+ return collectUnderlyingAddressValues(arg, maxDepth, walk, output);
}
LDBG() << " Processing as OpResult";
- collectUnderlyingAddressValues(cast<OpResult>(value), maxDepth, visited,
- output);
+ collectUnderlyingAddressValues(cast<OpResult>(value), maxDepth, walk, output);
}
-/// Given a value, collect all of the underlying values being addressed.
-static void collectUnderlyingAddressValues(Value value,
+/// Given a value, collect all of the underlying values being addressed. Returns
+/// true if the walk looked through a view that may start at an offset from its
+/// source.
+static bool collectUnderlyingAddressValues(Value value,
SmallVectorImpl<Value> &output) {
LDBG() << "collectUnderlyingAddressValues: " << value;
- DenseSet<Value> visited;
- collectUnderlyingAddressValues(value, maxUnderlyingValueSearchDepth, visited,
+ UnderlyingValueWalk walk;
+ collectUnderlyingAddressValues(value, maxUnderlyingValueSearchDepth, walk,
output);
LDBG() << " Collected " << output.size() << " underlying values";
+ return walk.throughOffsetView;
}
//===----------------------------------------------------------------------===//
@@ -439,8 +451,8 @@ AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) {
// Get the underlying values being addressed.
SmallVector<Value, 8> lhsValues, rhsValues;
- collectUnderlyingAddressValues(lhs, lhsValues);
- collectUnderlyingAddressValues(rhs, rhsValues);
+ bool throughOffsetView = collectUnderlyingAddressValues(lhs, lhsValues);
+ throughOffsetView |= collectUnderlyingAddressValues(rhs, rhsValues);
LDBG() << " lhs underlying values: " << lhsValues.size();
LDBG() << " rhs underlying values: " << rhsValues.size();
@@ -458,6 +470,9 @@ AliasResult LocalAliasAnalysis::alias(Value lhs, Value rhs) {
for (Value rhsVal : rhsValues) {
LDBG() << " Checking underlying values: " << lhsVal << " vs " << rhsVal;
AliasResult nextResult = aliasImpl(lhsVal, rhsVal);
+ // Downgrade to MayAlias to give impl a chance to do alias anaylasis
+ if (nextResult.isMust() && throughOffsetView)
+ nextResult = AliasResult::MayAlias;
LDBG() << " Result: "
<< (nextResult == AliasResult::MustAlias ? "MustAlias"
: nextResult == AliasResult::NoAlias ? "NoAlias"
diff --git a/mlir/test/Analysis/test-alias-analysis.mlir b/mlir/test/Analysis/test-alias-analysis.mlir
index d71adee05c7a36..a786705bbd80c9 100644
--- a/mlir/test/Analysis/test-alias-analysis.mlir
+++ b/mlir/test/Analysis/test-alias-analysis.mlir
@@ -234,6 +234,37 @@ func.func @view_like(%arg: memref<2xf32>, %size: index) attributes {test.ptr = "
// -----
+// Two addresses traced back to the same value only must alias if no view on
+// the way may have moved them away from it.
+
+// CHECK-LABEL: Testing : "view_like_offset"
+// CHECK-DAG: alloca_1#0 <-> alloca_2#0: NoAlias
+// CHECK-DAG: alloca_1#0 <-> view_1#0: MayAlias
+// CHECK-DAG: alloca_1#0 <-> view_2#0: MayAlias
+// CHECK-DAG: alloca_1#0 <-> view_3#0: NoAlias
+// CHECK-DAG: alloca_1#0 <-> zero_view#0: MustAlias
+// CHECK-DAG: alloca_2#0 <-> view_1#0: NoAlias
+// CHECK-DAG: alloca_2#0 <-> view_2#0: NoAlias
+// CHECK-DAG: alloca_2#0 <-> view_3#0: MayAlias
+// CHECK-DAG: alloca_2#0 <-> zero_view#0: NoAlias
+// CHECK-DAG: view_1#0 <-> view_2#0: MayAlias
+// CHECK-DAG: view_1#0 <-> view_3#0: NoAlias
+// CHECK-DAG: view_1#0 <-> zero_view#0: MayAlias
+// CHECK-DAG: view_2#0 <-> view_3#0: NoAlias
+// CHECK-DAG: view_2#0 <-> zero_view#0: MayAlias
+// CHECK-DAG: view_3#0 <-> zero_view#0: NoAlias
+func.func @view_like_offset() {
+ %0 = memref.alloca() {test.ptr = "alloca_1"} : memref<8xf32>
+ %1 = memref.alloca() {test.ptr = "alloca_2"} : memref<8xf32>
+ %2 = test.offset_view %0 {test.ptr = "view_1"} : memref<8xf32> -> memref<4xf32>
+ %3 = test.offset_view %0 {test.ptr = "view_2"} : memref<8xf32> -> memref<4xf32>
+ %4 = test.offset_view %1 {test.ptr = "view_3"} : memref<8xf32> -> memref<4xf32>
+ %5 = test.offset_view %0 zero_offset {test.ptr = "zero_view"} : memref<8xf32> -> memref<4xf32>
+ return
+}
+
+// -----
+
// CHECK-LABEL: Testing : "constants"
// CHECK-DAG: alloc_1#0 <-> constant_1#0: NoAlias
// CHECK-DAG: alloc_1#0 <-> constant_2#0: NoAlias
diff --git a/mlir/test/lib/Dialect/Test/CMakeLists.txt b/mlir/test/lib/Dialect/Test/CMakeLists.txt
index 4c4721193322b6..00c98546662e3b 100644
--- a/mlir/test/lib/Dialect/Test/CMakeLists.txt
+++ b/mlir/test/lib/Dialect/Test/CMakeLists.txt
@@ -96,6 +96,7 @@ mlir_target_link_libraries(MLIRTestDialect PUBLIC
MLIRTransformUtils
MLIRTransforms
MLIRValueBoundsOpInterface
+ MLIRViewLikeInterface
MLIRBufferizationDialect
)
diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index a6b05b55804b7e..3da8f14a617148 100644
--- a/mlir/test/lib/Dialect/Test/TestOps.td
+++ b/mlir/test/lib/Dialect/Test/TestOps.td
@@ -32,6 +32,7 @@ include "mlir/Interfaces/MemorySlotInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
include "mlir/Interfaces/TilingInterface.td"
include "mlir/Interfaces/ValueBoundsOpInterface.td"
+include "mlir/Interfaces/ViewLikeInterface.td"
include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.td"
include "mlir/Dialect/Bufferization/IR/BufferizationTypeInterfaces.td"
@@ -3466,6 +3467,28 @@ def TestDefaultAttrPrintOp : TEST_Op<"default_value_print"> {
let assemblyFormat = "prop-dict attr-dict $operand";
}
+//===----------------------------------------------------------------------===//
+// Test ViewLikeOpInterface
+//===----------------------------------------------------------------------===//
+
+def TestOffsetViewOp : TEST_Op<"offset_view", [Pure, ViewLikeOpInterface]> {
+ let summary = "view that may start at an offset from its source";
+ let description = [{
+ A view of `source` that may start at an offset from it, like a pointer to a
+ struct member. With `zero_offset`, the view is known to start at `source`.
+ }];
+ let arguments = (ins AnyMemRef:$source, UnitAttr:$zero_offset);
+ let results = (outs AnyMemRef:$result);
+ let assemblyFormat = [{
+ $source (`zero_offset` $zero_offset^)? attr-dict `:` type($source) `->`
+ type($result)
+ }];
+ let extraClassDeclaration = [{
+ ::mlir::Value getViewSource() { return getSource(); }
+ bool mayHaveNonZeroOffset() { return !getZeroOffset(); }
+ }];
+}
+
//===----------------------------------------------------------------------===//
// Test Ops with effects
//===----------------------------------------------------------------------===//
>From 98f7b354db8d2429f2cc5ce1a6f3130c22b120f3 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Sun, 25 Oct 2026 17:26:05 -0500
Subject: [PATCH 2/3] fixup! [MLIR] Implement mayHaveNonZeroOffset class
---
mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 6 +++--
.../mlir/Dialect/MemRef/IR/MemRefOps.td | 14 ++++++++---
mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td | 3 +++
.../mlir/Dialect/Vector/IR/VectorOps.td | 1 +
.../mlir/Interfaces/ViewLikeInterface.td | 2 +-
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 10 ++++++++
mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp | 11 +++++++++
mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp | 4 ++++
mlir/test/Analysis/test-alias-analysis.mlir | 24 +++++++++++++++++++
9 files changed, 69 insertions(+), 6 deletions(-)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index b09cdf9000d1e1..421ed0ccfbea40 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -295,7 +295,8 @@ def LLVM_GEPOp : LLVM_Op<"getelementptr", [Pure,
DeclareOpInterfaceMethods<PromotableOpInterface>,
DeclareOpInterfaceMethods<SafeMemorySlotAccessOpInterface>,
DeclareOpInterfaceMethods<DestructurableAccessorOpInterface>,
- DeclareOpInterfaceMethods<ViewLikeOpInterface>
+ DeclareOpInterfaceMethods<ViewLikeOpInterface,
+ ["mayHaveNonZeroOffset"]>
]> {
let arguments = (ins LLVM_ScalarOrVectorOf<LLVM_AnyPointer>:$base,
Variadic<LLVM_ScalarOrVectorOf<AnySignlessInteger>>:$dynamicIndices,
@@ -650,7 +651,8 @@ def LLVM_AddrSpaceCastOp : LLVM_CastOp<"addrspacecast", "AddrSpaceCast",
LLVM_ScalarOrVectorOf<LLVM_AnyPointer>,
LLVM_ScalarOrVectorOf<LLVM_AnyPointer>,
[DeclareOpInterfaceMethods<PromotableOpInterface>,
- DeclareOpInterfaceMethods<ViewLikeOpInterface>]> {
+ DeclareOpInterfaceMethods<ViewLikeOpInterface,
+ ["mayHaveNonZeroOffset"]>]> {
let hasFolder = 1;
}
def LLVM_IntToPtrOp : LLVM_DereferenceableCastOp<"inttoptr", "IntToPtr",
diff --git a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
index 86e466c46c2285..942a849b0f3bd6 100644
--- a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
+++ b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
@@ -178,6 +178,7 @@ def AssumeAlignmentOp : MemRef_Op<"assume_alignment", [
MemRefType getType() { return ::llvm::cast<MemRefType>(getResult().getType()); }
Value getViewSource() { return getMemref(); }
+ bool mayHaveNonZeroOffset() { return false; }
}];
let hasVerifier = 1;
@@ -568,6 +569,7 @@ def MemRef_CastOp : MemRef_Op<"cast", [
static bool canFoldIntoConsumerOp(CastOp castOp);
Value getViewSource() { return getSource(); }
+ bool mayHaveNonZeroOffset() { return false; }
}];
let hasFolder = 1;
@@ -1419,6 +1421,7 @@ def MemRef_MemorySpaceCastOp : MemRef_Op<"memory_space_cast", [
let extraClassDeclaration = [{
Value getViewSource() { return getSource(); }
+ bool mayHaveNonZeroOffset() { return false; }
//===------------------------------------------------------------------===//
// MemorySpaceCastConsumerOpInterface
@@ -1785,6 +1788,7 @@ def MemRef_ReshapeOp: MemRef_Op<"reshape", [
let extraClassDeclaration = [{
Value getViewSource() { return getSource(); }
+ bool mayHaveNonZeroOffset() { return false; }
}];
let assemblyFormat = [{
@@ -1822,6 +1826,7 @@ class MemRef_ReassociativeReshapeOp<string mnemonic, list<Trait> traits = []> :
MemRefType getResultType() { return ::llvm::cast<MemRefType>(getResult().getType()); }
Value getViewSource() { return getSrc(); }
+ bool mayHaveNonZeroOffset() { return false; }
}];
let hasFolder = 1;
@@ -2156,7 +2161,8 @@ def SubViewOp : MemRef_OpWithOffsetSizesAndStrides<"subview", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
DeclareOpInterfaceMethods<InferStridedMetadataOpInterface>,
DeclareOpInterfaceMethods<MemorySpaceCastConsumerOpInterface>,
- DeclareOpInterfaceMethods<ViewLikeOpInterface>,
+ DeclareOpInterfaceMethods<ViewLikeOpInterface,
+ ["mayHaveNonZeroOffset"]>,
AttrSizedOperandSegments,
OffsetSizeAndStrideOpInterface,
Pure
@@ -2434,7 +2440,8 @@ def SubViewOp : MemRef_OpWithOffsetSizesAndStrides<"subview", [
def MemRef_TransposeOp : MemRef_Op<"transpose", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
DeclareOpInterfaceMethods<MemorySpaceCastConsumerOpInterface>,
- DeclareOpInterfaceMethods<ViewLikeOpInterface>,
+ DeclareOpInterfaceMethods<ViewLikeOpInterface,
+ ["mayHaveNonZeroOffset"]>,
Pure]>,
Arguments<(ins AnyStridedMemRef:$in, AffineMapAttr:$permutation)>,
Results<(outs AnyStridedMemRef)> {
@@ -2471,7 +2478,8 @@ def MemRef_TransposeOp : MemRef_Op<"transpose", [
def MemRef_ViewOp : MemRef_Op<"view", [
DeclareOpInterfaceMethods<OpAsmOpInterface, ["getAsmResultNames"]>,
DeclareOpInterfaceMethods<MemorySpaceCastConsumerOpInterface>,
- DeclareOpInterfaceMethods<ViewLikeOpInterface>,
+ DeclareOpInterfaceMethods<ViewLikeOpInterface,
+ ["mayHaveNonZeroOffset"]>,
Pure]> {
let summary = "memref view operation";
let description = [{
diff --git a/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td b/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
index e14f64330c2949..8e242c645e56f0 100644
--- a/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
+++ b/mlir/include/mlir/Dialect/Ptr/IR/PtrOps.td
@@ -410,6 +410,9 @@ def Ptr_PtrAddOp : Pointer_Op<"ptr_add", [
/// `ViewLikeOp::getViewSource` method.
Value getViewSource() { return getBase(); }
+ /// `ViewLikeOp::mayHaveNonZeroOffset` method.
+ bool mayHaveNonZeroOffset();
+
/// Returns the ptr type of the operation.
ptr::PtrType getPtrType();
}];
diff --git a/mlir/include/mlir/Dialect/Vector/IR/VectorOps.td b/mlir/include/mlir/Dialect/Vector/IR/VectorOps.td
index 83fe3165b88540..c373235521cf1d 100644
--- a/mlir/include/mlir/Dialect/Vector/IR/VectorOps.td
+++ b/mlir/include/mlir/Dialect/Vector/IR/VectorOps.td
@@ -2621,6 +2621,7 @@ def Vector_TypeCastOp :
}
// Implement ViewLikeOpInterface.
Value getViewSource() { return getMemref(); }
+ bool mayHaveNonZeroOffset() { return false; }
}];
let assemblyFormat = [{
diff --git a/mlir/include/mlir/Interfaces/ViewLikeInterface.td b/mlir/include/mlir/Interfaces/ViewLikeInterface.td
index 0bbcd0401aa830..420c1b30e6506f 100644
--- a/mlir/include/mlir/Interfaces/ViewLikeInterface.td
+++ b/mlir/include/mlir/Interfaces/ViewLikeInterface.td
@@ -47,7 +47,7 @@ def ViewLikeOpInterface : OpInterface<"ViewLikeOpInterface"> {
/*args=*/(ins),
/*methodBody=*/"",
/*defaultImplementation=*/[{
- return false;
+ return true;
}]
>
];
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index be5ea6df0caba7..b170db52a7b188 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -4105,6 +4105,8 @@ OpFoldResult LLVM::AddrSpaceCastOp::fold(FoldAdaptor adaptor) {
Value LLVM::AddrSpaceCastOp::getViewSource() { return getArg(); }
+bool LLVM::AddrSpaceCastOp::mayHaveNonZeroOffset() { return false; }
+
//===----------------------------------------------------------------------===//
// Folder for LLVM::GEPOp
//===----------------------------------------------------------------------===//
@@ -4161,6 +4163,14 @@ OpFoldResult LLVM::GEPOp::fold(FoldAdaptor adaptor) {
Value LLVM::GEPOp::getViewSource() { return getBase(); }
+bool LLVM::GEPOp::mayHaveNonZeroOffset() {
+ return llvm::any_of(getIndices(), [](auto index) {
+ if (auto value = llvm::dyn_cast<Value>(index))
+ return !matchPattern(value, m_Zero());
+ return !llvm::cast<IntegerAttr>(index).getValue().isZero();
+ });
+}
+
//===----------------------------------------------------------------------===//
// ShlOp
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
index 245b2ba6babf04..2e50308799ed82 100644
--- a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
+++ b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
@@ -3311,6 +3311,11 @@ void SubViewOp::build(OpBuilder &b, OperationState &result, Value source,
/// For ViewLikeOpInterface.
Value SubViewOp::getViewSource() { return getSource(); }
+bool SubViewOp::mayHaveNonZeroOffset() {
+ return llvm::any_of(getStaticOffsets(),
+ [](int64_t offset) { return offset != 0; });
+}
+
/// Return true if `t1` and `t2` have equal offsets (both dynamic or of same
/// static value).
static bool haveCompatibleOffsets(MemRefType t1, MemRefType t2) {
@@ -3837,6 +3842,8 @@ static MemRefType inferTransposeResultType(MemRefType memRefType,
Value TransposeOp::getViewSource() { return getIn(); }
+bool TransposeOp::mayHaveNonZeroOffset() { return false; }
+
void TransposeOp::build(OpBuilder &b, OperationState &result, Value in,
AffineMapAttr permutation,
ArrayRef<NamedAttribute> attrs) {
@@ -3954,6 +3961,10 @@ LogicalResult ViewOp::verify() {
Value ViewOp::getViewSource() { return getSource(); }
+bool ViewOp::mayHaveNonZeroOffset() {
+ return !matchPattern(getByteShift(), m_Zero());
+}
+
OpFoldResult ViewOp::fold(FoldAdaptor adaptor) {
MemRefType sourceMemrefType = getSource().getType();
MemRefType resultMemrefType = getResult().getType();
diff --git a/mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp b/mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp
index 444197d12097f9..d0907b5fdfaace 100644
--- a/mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp
+++ b/mlir/lib/Dialect/Ptr/IR/PtrDialect.cpp
@@ -353,6 +353,10 @@ OpFoldResult PtrAddOp::fold(FoldAdaptor adaptor) {
return nullptr;
}
+bool PtrAddOp::mayHaveNonZeroOffset() {
+ return !matchPattern(getOffset(), m_Zero());
+}
+
LogicalResult PtrAddOp::inferReturnTypes(
MLIRContext *context, std::optional<Location> location, ValueRange operands,
DictionaryAttr attributes, PropertyRef properties, RegionRange regions,
diff --git a/mlir/test/Analysis/test-alias-analysis.mlir b/mlir/test/Analysis/test-alias-analysis.mlir
index a786705bbd80c9..ab430e69b63b7e 100644
--- a/mlir/test/Analysis/test-alias-analysis.mlir
+++ b/mlir/test/Analysis/test-alias-analysis.mlir
@@ -265,6 +265,30 @@ func.func @view_like_offset() {
// -----
+// GEPs keep MustAlias with their base only when all indices are zero.
+
+// CHECK-LABEL: Testing : "gep_offset"
+// CHECK-DAG: alloca_1#0 <-> alloca_2#0: NoAlias
+// CHECK-DAG: alloca_1#0 <-> gep_zero#0: MustAlias
+// CHECK-DAG: alloca_1#0 <-> gep_one#0: MayAlias
+// CHECK-DAG: alloca_1#0 <-> gep_dyn#0: MayAlias
+// CHECK-DAG: alloca_1#0 <-> gep_other#0: NoAlias
+// CHECK-DAG: gep_zero#0 <-> gep_one#0: MayAlias
+// CHECK-DAG: gep_one#0 <-> gep_dyn#0: MayAlias
+// CHECK-DAG: gep_one#0 <-> gep_other#0: NoAlias
+func.func @gep_offset(%idx: i64) {
+ %c1 = llvm.mlir.constant(1 : i32) : i32
+ %0 = llvm.alloca %c1 x !llvm.array<4 x i32> {test.ptr = "alloca_1"} : (i32) -> !llvm.ptr
+ %1 = llvm.alloca %c1 x !llvm.array<4 x i32> {test.ptr = "alloca_2"} : (i32) -> !llvm.ptr
+ %2 = llvm.getelementptr %0[0, 0] {test.ptr = "gep_zero"} : (!llvm.ptr) -> !llvm.ptr, !llvm.array<4 x i32>
+ %3 = llvm.getelementptr %0[0, 1] {test.ptr = "gep_one"} : (!llvm.ptr) -> !llvm.ptr, !llvm.array<4 x i32>
+ %4 = llvm.getelementptr %0[%idx] {test.ptr = "gep_dyn"} : (!llvm.ptr, i64) -> !llvm.ptr, i32
+ %5 = llvm.getelementptr %1[0, 1] {test.ptr = "gep_other"} : (!llvm.ptr) -> !llvm.ptr, !llvm.array<4 x i32>
+ return
+}
+
+// -----
+
// CHECK-LABEL: Testing : "constants"
// CHECK-DAG: alloc_1#0 <-> constant_1#0: NoAlias
// CHECK-DAG: alloc_1#0 <-> constant_2#0: NoAlias
>From 96b428ec4aa21ea95b843fe60de234cf24c737f0 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Sun, 25 Oct 2026 17:45:07 -0500
Subject: [PATCH 3/3] fixup! [MLIR] Implement mayHaveNonZeroOffset class
---
flang/include/flang/Optimizer/Dialect/FIROps.td | 2 ++
1 file changed, 2 insertions(+)
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 7fc12430d0663f..176740d9dbf35f 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -2751,6 +2751,7 @@ def fir_VolatileCastOp
let extraClassDeclaration = [{
// ViewLikeOpInterface methods:
mlir::Value getViewSource() { return getValue(); }
+ bool mayHaveNonZeroOffset() { return false; }
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getValue(); }
@@ -2806,6 +2807,7 @@ def fir_ConvertOp
// ViewLikeOpInterface methods:
mlir::Value getViewSource() { return getValue(); }
+ bool mayHaveNonZeroOffset() { return false; }
// FortranObjectViewOpInterface methods:
mlir::Value getViewSource(mlir::OpResult) { return getValue(); }
More information about the Mlir-commits
mailing list