MLIR 24.0.0git
MemRefUtils.cpp
Go to the documentation of this file.
1//===- MemRefUtils.cpp - Utilities to support the MemRef dialect ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements utilities for the MemRef dialect.
10//
11//===----------------------------------------------------------------------===//
12
19#include "llvm/ADT/STLExtras.h"
20
21namespace mlir {
22namespace memref {
23
25 if (!type.hasStaticShape())
26 return false;
27
28 int64_t rank = type.getRank();
29 if (rank == 0)
30 return true;
31
33 int64_t offset;
34 if (failed(type.getStridesAndOffset(strides, offset)))
35 return false;
36
37 // MemRef is contiguous if outer dimensions are size-1 and inner
38 // dimensions have unit strides.
39 int64_t runningStride = 1;
40 int64_t curDim = rank - 1;
41 // Finds all inner dimensions with unit strides.
42 while (curDim >= 0 && strides[curDim] == runningStride) {
43 runningStride *= type.getDimSize(curDim);
44 --curDim;
45 }
46
47 // Check if other dimensions are size-1.
48 while (curDim >= 0 && type.getDimSize(curDim) == 1) {
49 --curDim;
50 }
51
52 // All dims are unit-strided or size-1.
53 return curDim < 0;
54}
55
56std::pair<LinearizedMemRefInfo, OpFoldResult> getLinearizedMemRefOffsetAndSize(
57 OpBuilder &builder, Location loc, int srcBits, int dstBits,
60 LinearizedDivKind sizeDivKind) {
61 unsigned sourceRank = sizes.size();
62 assert(sizes.size() == strides.size() &&
63 "expected as many sizes as strides for a memref");
64 SmallVector<OpFoldResult> indicesVec = llvm::to_vector(indices);
65 if (indices.empty())
66 indicesVec.resize(sourceRank, builder.getIndexAttr(0));
67 assert(indicesVec.size() == strides.size() &&
68 "expected as many indices as rank of memref");
69
70 // Create the affine symbols and values for linearization.
71 SmallVector<AffineExpr> symbols(2 * sourceRank);
72 bindSymbolsList(builder.getContext(), MutableArrayRef{symbols});
73 AffineExpr addMulMap = builder.getAffineConstantExpr(0);
74
75 SmallVector<OpFoldResult> offsetValues(2 * sourceRank);
76
77 for (unsigned i = 0; i < sourceRank; ++i) {
78 unsigned offsetIdx = 2 * i;
79 addMulMap = addMulMap + symbols[offsetIdx] * symbols[offsetIdx + 1];
80 offsetValues[offsetIdx] = indicesVec[i];
81 offsetValues[offsetIdx + 1] = strides[i];
82 }
83 // Adjust linearizedIndices and size by the scale factor (dstBits / srcBits).
84 int64_t scaler = dstBits / srcBits;
86 builder, loc, addMulMap.floorDiv(scaler), offsetValues);
87
88 size_t symbolIndex = 0;
90 SmallVector<AffineExpr> productExpressions;
91 for (unsigned i = 0; i < sourceRank; ++i) {
92 AffineExpr strideExpr = symbols[symbolIndex++];
93 values.push_back(strides[i]);
94 AffineExpr sizeExpr = symbols[symbolIndex++];
95 values.push_back(sizes[i]);
96
97 AffineExpr product = strideExpr * sizeExpr;
98 productExpressions.push_back(sizeDivKind == LinearizedDivKind::Ceil
99 ? product.ceilDiv(scaler)
100 : product.floorDiv(scaler));
101 }
102 AffineMap maxMap = AffineMap::get(
103 /*dimCount=*/0, /*symbolCount=*/symbolIndex, productExpressions,
104 builder.getContext());
105 OpFoldResult linearizedSize =
106 affine::makeComposedFoldedAffineMax(builder, loc, maxMap, values);
107
108 // Adjust baseOffset by the scale factor (dstBits / srcBits).
109 AffineExpr s0;
110 bindSymbols(builder.getContext(), s0);
112 builder, loc, s0.floorDiv(scaler), {offset});
113
115 builder, loc, addMulMap % scaler, offsetValues);
116
117 return {{adjustBaseOffset, linearizedSize, intraVectorOffset},
118 linearizedIndices};
119}
120
121LinearizedMemRefInfo
123 int dstBits, OpFoldResult offset,
125 LinearizedDivKind sizeDivKind) {
126 SmallVector<OpFoldResult> strides(sizes.size());
127 if (!sizes.empty()) {
128 strides.back() = builder.getIndexAttr(1);
129 AffineExpr s0, s1;
130 bindSymbols(builder.getContext(), s0, s1);
131 for (int64_t index = static_cast<int64_t>(sizes.size()) - 1; index > 0;
132 --index) {
134 builder, loc, s0 * s1,
135 ArrayRef<OpFoldResult>{strides[index], sizes[index]});
136 }
137 }
138
139 LinearizedMemRefInfo linearizedMemRefInfo;
140 std::tie(linearizedMemRefInfo, std::ignore) =
141 getLinearizedMemRefOffsetAndSize(builder, loc, srcBits, dstBits, offset,
142 sizes, strides, /*indices=*/{},
143 sizeDivKind);
144 return linearizedMemRefInfo;
145}
146
147/// Returns true if all the uses of op are not read/load.
148/// There can be view-like-op users as long as all its users are also
149/// StoreOp/transfer_write. If return true it also fills out the uses, if it
150/// returns false uses is unchanged.
151static bool resultIsNotRead(Operation *op, std::vector<Operation *> &uses) {
152 std::vector<Operation *> opUses;
153 for (OpOperand &use : op->getUses()) {
154 Operation *useOp = use.getOwner();
155 // Use escaped the scope
157 return false;
158 if (isa<memref::DeallocOp>(useOp) ||
159 (useOp->getNumResults() == 0 && useOp->getNumRegions() == 0 &&
161 (isa<ViewLikeOpInterface>(useOp) && resultIsNotRead(useOp, opUses))) {
162 opUses.push_back(useOp);
163 continue;
164 }
165 return false;
166 }
167 llvm::append_range(uses, opUses);
168 return true;
169}
170
172 std::vector<Operation *> opToErase;
173 parentOp->walk([&](Operation *op) {
174 std::vector<Operation *> candidates;
175 if (isa<memref::AllocOp, memref::AllocaOp>(op) &&
176 resultIsNotRead(op, candidates)) {
177 llvm::append_range(opToErase, candidates);
178 opToErase.push_back(op);
179 }
180 });
181
182 for (Operation *op : opToErase)
183 rewriter.eraseOp(op);
184}
185
189 OpFoldResult unit) {
190 SmallVector<OpFoldResult> strides(sizes.size(), unit);
191 AffineExpr s0, s1;
192 bindSymbols(builder.getContext(), s0, s1);
193
194 for (int64_t r = static_cast<int64_t>(strides.size()) - 1; r > 0; --r) {
196 builder, loc, s0 * s1, {strides[r], sizes[r]});
197 }
198 return strides;
199}
200
204 OpFoldResult unit = builder.getIndexAttr(1);
205 return computeSuffixProductIRBlockImpl(loc, builder, sizes, unit);
206}
207
209 while (auto *op = source.getDefiningOp()) {
210 if (auto subViewOp = dyn_cast<memref::SubViewOp>(op);
211 subViewOp && subViewOp.hasZeroOffset() && subViewOp.hasUnitStride()) {
212 // A `memref.subview` with an all zero offset, and all unit strides, still
213 // points to the same memory.
214 source = cast<MemrefValue>(subViewOp.getSource());
215 } else if (auto castOp = dyn_cast<memref::CastOp>(op)) {
216 // A `memref.cast` still points to the same memory.
217 source = castOp.getSource();
218 } else {
219 return source;
220 }
221 }
222 return source;
223}
224
226 while (auto *op = source.getDefiningOp()) {
227 if (auto viewLike = dyn_cast<ViewLikeOpInterface>(op)) {
228 if (source == viewLike.getViewDest()) {
229 source = cast<MemrefValue>(viewLike.getViewSource());
230 continue;
231 }
232 }
233 return source;
234 }
235 return source;
236}
237
239 memref::ExpandShapeOp expandShapeOp,
241 SmallVectorImpl<Value> &sourceIndices,
242 bool startsInbounds) {
243 SmallVector<OpFoldResult> destShape = expandShapeOp.getMixedOutputShape();
244
245 // Traverse all reassociation groups to determine the appropriate indices
246 // corresponding to each one of them post op folding.
247 for (ArrayRef<int64_t> group : expandShapeOp.getReassociationIndices()) {
248 assert(!group.empty() && "association indices groups cannot be empty");
249 int64_t groupSize = group.size();
250 if (groupSize == 1) {
251 sourceIndices.push_back(indices[group[0]]);
252 continue;
253 }
254 SmallVector<OpFoldResult> groupBasis =
255 llvm::map_to_vector(group, [&](int64_t d) { return destShape[d]; });
256 SmallVector<Value> groupIndices =
257 llvm::map_to_vector(group, [&](int64_t d) { return indices[d]; });
258 Value collapsedIndex = affine::AffineLinearizeIndexOp::create(
259 rewriter, loc, groupIndices, groupBasis, /*disjoint=*/startsInbounds);
260 sourceIndices.push_back(collapsedIndex);
261 }
262}
263
265 memref::CollapseShapeOp collapseShapeOp,
267 SmallVectorImpl<Value> &sourceIndices,
268 bool startsInbounds) {
269 // Note: collapse_shape requires a strided memref, we can do this.
270 auto metadata = memref::ExtractStridedMetadataOp::create(
271 rewriter, loc, collapseShapeOp.getSrc());
272 SmallVector<OpFoldResult> sourceSizes = metadata.getConstifiedMixedSizes();
273 for (auto [index, group] :
274 llvm::zip(indices, collapseShapeOp.getReassociationIndices())) {
275 assert(!group.empty() && "association indices groups cannot be empty");
276 int64_t groupSize = group.size();
277
278 if (groupSize == 1) {
279 sourceIndices.push_back(index);
280 continue;
281 }
282
283 // If we don't know that this value is in-bounds, the largest return value
284 // of the delinearization may exceed `sourceSizes[d]`, so we drop that first
285 // group entry in order to maintain soundness.
286 auto trimmedGroup =
287 ArrayRef<int64_t>(group).drop_front(startsInbounds ? 0 : 1);
288 SmallVector<OpFoldResult> basis = llvm::map_to_vector(
289 trimmedGroup, [&](int64_t d) { return sourceSizes[d]; });
290 auto delinearize = affine::AffineDelinearizeIndexOp::create(
291 rewriter, loc, index, basis, /*hasOuterBound=*/startsInbounds);
292 llvm::append_range(sourceIndices, delinearize.getResults());
293 }
294 if (collapseShapeOp.getReassociationIndices().empty()) {
295 auto zeroAffineMap = rewriter.getConstantAffineMap(0);
296 int64_t srcRank =
297 cast<MemRefType>(collapseShapeOp.getViewSource().getType()).getRank();
299 rewriter, loc, zeroAffineMap, ArrayRef<OpFoldResult>{});
300 for (int64_t i = 0; i < srcRank; i++) {
301 sourceIndices.push_back(
302 getValueOrCreateConstantIndexOp(rewriter, loc, ofr));
303 }
304 }
305}
306
308 Location loc, OpBuilder &b, memref::SubViewOp subViewOp, ValueRange indices,
309 SmallVectorImpl<Value> &sourceIndices) {
310 if (!subViewOp.hasZeroOffset() || !subViewOp.hasUnitStride())
311 return failure();
312
313 MemRefType srcType = subViewOp.getSourceType();
314 MemRefType resType = subViewOp.getType();
315 unsigned srcRank = srcType.getRank();
316 unsigned resRank = resType.getRank();
317 if (srcRank <= resRank || indices.size() != resRank)
318 return failure();
319
320 auto droppedDims = subViewOp.getDroppedDims();
321 if (droppedDims.none() || droppedDims.count() != srcRank - resRank)
322 return failure();
323
324 auto mixedSizes = subViewOp.getMixedSizes();
325 if (mixedSizes.size() != srcRank)
326 return failure();
327
328 unsigned resultDim = 0;
329 for (unsigned sourceDim = 0; sourceDim < srcRank; ++sourceDim) {
330 if (droppedDims.test(sourceDim)) {
331 auto sizeCst = getConstantIntValue(mixedSizes[sourceDim]);
332 if (!sizeCst || *sizeCst != 1)
333 return failure();
334 sourceIndices.push_back(
335 getValueOrCreateConstantIndexOp(b, loc, b.getIndexAttr(0)));
336 continue;
337 }
338 if (resultDim >= indices.size())
339 return failure();
340 sourceIndices.push_back(indices[resultDim++]);
341 }
342 if (resultDim != indices.size())
343 return failure();
344
345 return success();
346}
347
348bool hasNegativeStaticStride(MemRefType memRefTy) {
349 auto [strides, offset] = memRefTy.getStridesAndOffset();
350 return llvm::any_of(strides, [](int64_t stride) {
351 return ShapedType::isStatic(stride) && stride < 0;
352 });
353}
354
355namespace {
356// TODO: This footprint modelling and its logic overlap with
357// `SubsetOpInterface`, which is unfortunately tensor-only today; if it gains
358// memref support, look into unifying with it to improve code reuse.
359//
360/// Footprint of a memref value relative to a `root` memref reached through a
361/// chain of rank-preserving `memref.subview` ops whose `strides` operand is all
362/// ones (so each result index steps one source element in every dimension): per
363/// `root` dimension, the `[offset, offset + size)` interval the value covers (a
364/// `size` may be dynamic). The root is the underlying buffer if the whole chain
365/// composes, otherwise the result of the first subview (walking toward the
366/// root) that cannot be composed. Two footprints are therefore comparable only
367/// when they resolve to the *same* root value.
368struct SubviewFootprint {
369 Value root;
370 SmallVector<int64_t> offsets;
372};
373} // namespace
374
375/// Resolve `base` through a `memref.subview` chain to a footprint, or
376/// `std::nullopt` if it cannot be modelled. Composition stops at the first
377/// subview with a dynamic offset, a `strides` operand other than all ones, or a
378/// rank reduction; that subview's own result becomes the footprint root. This
379/// keeps two slices that share a common (possibly dynamically-offset) base
380/// comparable through their static offsets relative to that base. Sizes may be
381/// dynamic: such dimensions simply cannot be used to prove disjointness.
382static std::optional<SubviewFootprint> resolveSubviewFootprint(Value base) {
383 auto baseTy = dyn_cast<MemRefType>(base.getType());
384 if (!baseTy)
385 return std::nullopt;
386 SubviewFootprint footprint;
387 // Size is fixed by `base` itself; offset accumulates the static offsets of
388 // the composable subviews walked toward the root.
389 footprint.offsets.assign(baseTy.getRank(), 0);
390 footprint.sizes.assign(baseTy.getShape().begin(), baseTy.getShape().end());
391
392 Value cur = base;
393 while (auto sv = cur.getDefiningOp<SubViewOp>()) {
394 ArrayRef<int64_t> staticOffsets = sv.getStaticOffsets();
395 ArrayRef<int64_t> staticStrides = sv.getStaticStrides();
396 // Rank-reducing subviews, or those with a dynamic offset or a `strides`
397 // operand other than all ones, cannot be composed: stop here and use `sv`'s
398 // result as the root.
399 if (sv.getSourceType().getRank() != sv.getType().getRank() ||
400 llvm::any_of(staticOffsets, ShapedType::isDynamic) ||
401 llvm::any_of(staticStrides, [](int64_t s) { return s != 1; }))
402 break;
403 for (unsigned d = 0, e = staticOffsets.size(); d < e; ++d)
404 footprint.offsets[d] += staticOffsets[d];
405 cur = sv.getSource();
406 }
407 // The root must be a genuine buffer or a `memref.subview` result, not another
408 // kind of view (collapse/expand/reshape) whose coordinate remapping would
409 // invalidate the per-dimension offset bookkeeping.
410 if (auto *def = cur.getDefiningOp())
411 if (isa<ViewLikeOpInterface>(def) && !isa<SubViewOp>(def))
412 return std::nullopt;
413 footprint.root = cur;
414 return footprint;
415}
416
417/// True if two footprints provably cover disjoint memory: they must share the
418/// same root and be separated along at least one statically-sized dimension.
419static bool areFootprintDisjoint(const SubviewFootprint &a,
420 const SubviewFootprint &b) {
421 if (a.root != b.root)
422 return false;
423 for (unsigned d = 0, e = a.offsets.size(); d < e; ++d) {
424 // A dimension can prove disjointness only when both extents are static.
425 if (ShapedType::isDynamic(a.sizes[d]) || ShapedType::isDynamic(b.sizes[d]))
426 continue;
427 int64_t aHi = a.offsets[d] + a.sizes[d];
428 int64_t bHi = b.offsets[d] + b.sizes[d];
429 if (aHi <= b.offsets[d] || bHi <= a.offsets[d])
430 return true;
431 }
432 return false;
433}
434
436 ArrayRef<Operation *> excludedOps,
437 bool readsAreSafe) {
438 auto baseMemref = dyn_cast<MemrefValue>(base);
439 if (!baseMemref)
440 return false;
441 Value rootBuffer = skipViewLikeOps(baseMemref);
442 // Footprint of `base`, if modellable; enables the disjointness escape.
443 std::optional<SubviewFootprint> baseFp = resolveSubviewFootprint(base);
444
445 // Visit the transitive users of the root buffer, following views.
446 SmallVector<Operation *> worklist(rootBuffer.getUsers().begin(),
447 rootBuffer.getUsers().end());
449 while (!worklist.empty()) {
450 Operation *user = worklist.pop_back_val();
451 if (!processed.insert(user).second)
452 continue;
453 // Views do not access memory; follow them to their own users.
454 if (auto viewLike = dyn_cast<ViewLikeOpInterface>(user)) {
455 Value viewDest = viewLike.getViewDest();
456 worklist.append(viewDest.getUsers().begin(), viewDest.getUsers().end());
457 continue;
458 }
459 if (isMemoryEffectFree(user) || llvm::is_contained(excludedOps, user))
460 continue;
461 if (!scope->isAncestor(user))
462 continue;
463 // In-`scope`, memory-effecting op: each of its operands that resolves to
464 // the root buffer conflicts unless it is a provably-disjoint slice or (when
465 // allowed) is only read.
466 for (Value operand : user->getOperands()) {
467 auto slice = dyn_cast<MemrefValue>(operand);
468 if (!slice || skipViewLikeOps(slice) != rootBuffer)
469 continue;
470 if (baseFp) {
471 std::optional<SubviewFootprint> sliceFp =
473 if (sliceFp && areFootprintDisjoint(*baseFp, *sliceFp))
474 continue;
475 }
476 if (readsAreSafe && isa<MemoryEffectOpInterface>(user) &&
478 continue;
479 return false;
480 }
481 }
482 return true;
483}
484
485} // namespace memref
486} // namespace mlir
return success()
static int64_t product(ArrayRef< int64_t > vals)
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
Base type for affine expression.
Definition AffineExpr.h:68
AffineExpr floorDiv(uint64_t v) const
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:116
AffineExpr getAffineConstantExpr(int64_t constant)
Definition Builders.cpp:381
AffineMap getConstantAffineMap(int64_t val)
Returns a single constant result affine map with 0 dimensions and 0 symbols.
Definition Builders.cpp:387
MLIRContext * getContext() const
Definition Builders.h:56
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
This class helps build Operations.
Definition Builders.h:210
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Definition Value.h:254
This class provides the API for ops that are known to be terminators.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
bool mightHaveTrait()
Returns true if the operation might have the provided trait.
Definition Operation.h:809
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition Operation.h:726
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
Definition Operation.h:288
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Definition Operation.h:849
use_range getUses()
Returns a range of all uses, which is useful for iterating over all uses.
Definition Operation.h:898
unsigned getNumResults()
Return the number of results held by this operation.
Definition Operation.h:429
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
user_range getUsers() const
Definition Value.h:218
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
OpFoldResult makeComposedFoldedAffineMax(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
Constructs an AffineMinOp that computes a maximum across the results of applying map to operands,...
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
std::pair< LinearizedMemRefInfo, OpFoldResult > getLinearizedMemRefOffsetAndSize(OpBuilder &builder, Location loc, int srcBits, int dstBits, OpFoldResult offset, ArrayRef< OpFoldResult > sizes, ArrayRef< OpFoldResult > strides, ArrayRef< OpFoldResult > indices={}, LinearizedDivKind sizeDivKind=LinearizedDivKind::Floor)
static bool resultIsNotRead(Operation *op, std::vector< Operation * > &uses)
Returns true if all the uses of op are not read/load.
bool hasNegativeStaticStride(MemRefType memRefTy)
Returns true if any stride of memRefTy is statically known to be negative.
MemrefValue skipFullyAliasingOperations(MemrefValue source)
Walk up the source chain until an operation that changes/defines the view of memory is found (i....
static std::optional< SubviewFootprint > resolveSubviewFootprint(Value base)
Resolve base through a memref.subview chain to a footprint, or std::nullopt if it cannot be modelled.
void eraseDeadAllocAndStores(RewriterBase &rewriter, Operation *parentOp)
Track temporary allocations that are never read from.
MemrefValue skipViewLikeOps(MemrefValue source)
Walk up the source chain until we find an operation that is not a view of the source memref (i....
bool isStaticShapeAndContiguousRowMajor(MemRefType type)
Returns true, if the memref type has static shapes and represents a contiguous chunk of memory.
bool hasNoAliasingAccessInScope(Value base, Operation *scope, ArrayRef< Operation * > excludedOps={}, bool readsAreSafe=false)
Return "true" when no other access nested in scope can alias base.
void resolveSourceIndicesCollapseShape(Location loc, PatternRewriter &rewriter, memref::CollapseShapeOp collapseShapeOp, ValueRange indices, SmallVectorImpl< Value > &sourceIndices, bool startsInbounds)
Given the 'indices' of a load/store operation where the memref is a result of a collapse_shape op,...
LinearizedDivKind
Controls how the per-dimension contribution to linearizedSize is divided by dstBits / srcBits when sc...
Definition MemRefUtils.h:44
LogicalResult resolveSourceIndicesRankReducingSubview(Location loc, OpBuilder &b, memref::SubViewOp subViewOp, ValueRange indices, SmallVectorImpl< Value > &sourceIndices)
Given the 'indices' of a load/store operation where the memref is a result of a rank-reducing full su...
static SmallVector< OpFoldResult > computeSuffixProductIRBlockImpl(Location loc, OpBuilder &builder, ArrayRef< OpFoldResult > sizes, OpFoldResult unit)
SmallVector< OpFoldResult > computeSuffixProductIRBlock(Location loc, OpBuilder &builder, ArrayRef< OpFoldResult > sizes)
Given a set of sizes, return the suffix product.
void resolveSourceIndicesExpandShape(Location loc, PatternRewriter &rewriter, memref::ExpandShapeOp expandShapeOp, ValueRange indices, SmallVectorImpl< Value > &sourceIndices, bool startsInbounds)
Given the 'indices' of a load/store operation where the memref is a result of a expand_shape op,...
static bool areFootprintDisjoint(const SubviewFootprint &a, const SubviewFootprint &b)
True if two footprints provably cover disjoint memory: they must share the same root and be separated...
Include the generated interface declarations.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
SmallVector< int64_t > delinearize(int64_t linearIndex, ArrayRef< int64_t > strides)
Given the strides together with a linear index in the dimension space, return the vector-space offset...
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
Definition AffineExpr.h:325
TypedValue< BaseMemRefType > MemrefValue
A value with a memref type.
Definition MemRefUtils.h:26
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:114
bool hasEffect(Operation *op)
Returns "true" if op has an effect of type EffectTy.
void bindSymbolsList(MLIRContext *ctx, MutableArrayRef< AffineExprTy > exprs)
Definition AffineExpr.h:330
For a memref with offset, sizes and strides, returns the offset, size, and potentially the size padde...
Definition MemRefUtils.h:64