MLIR 24.0.0git
MemorySlotInterfacesImpl.cpp
Go to the documentation of this file.
1//===- MemorySlotInterfacesImpl.cpp - Mem2Reg for MemRef ops -------------===//
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// External-model-based implementation of Memory Slot Interfaces for MemRef Ops.
10//
11// This file implements memory slot interface models used by Mem2Reg to promote
12// a memref to a single vector SSA value. `PromotableAliaserInterface` exposes
13// `memref.subview` aliases, and `PromotableOpInterface` removes the subviews
14// after promotion. Vector transfer ops are handled by the models in the
15// Vector dialect.
16//
17// A memory slot pairs a memref with the vector type used to represent its
18// contents. Promotion replaces accesses to the slot with uses and definitions
19// of that vector value. All uses, including those through subviews, must be
20// supported by the models.
21//
22// The accesses are rewritten as follows:
23//
24// * A same-rank, unit-stride `memref.subview` gets an alias slot that is
25// promoted together with the parent:
26//
27// - A static subview holds the corresponding sub-vector. Reads extract it
28// with `vector.extract_strided_slice`; writes update the parent with
29// `vector.insert_strided_slice`.
30//
31// - A dynamic subview holds the whole parent vector because vector shapes
32// must be static. `vector.create_mask` and `arith.select` restrict
33// accesses to the subview's extent. The subview must start at the
34// parent's origin.
35//
36// Subviews require a statically shaped parent buffer. A dynamically
37// shaped buffer can instead promote to a 1-D scalable vector through
38// whole-buffer transfers, but subviews are not supported.
39//
40// Unsupported accesses, such as dynamic subview offsets, rank reduction,
41// non-unit subview strides, or non-zero transfer indices, prevent promotion.
42//
43//===----------------------------------------------------------------------===//
44
46
52
53using namespace mlir;
54using namespace mlir::memref;
55using namespace mlir::vector;
56
57//===----------------------------------------------------------------------===//
58// Utilities
59//===----------------------------------------------------------------------===//
60
61/// Checks the requirements shared by static and dynamic subview aliases: a
62/// parent with a static shape and a valid vector element type, no rank
63/// reduction, and unit slice strides.
64static bool isUnitStrideSliceOfStaticParent(memref::SubViewOp subView) {
65 MemRefType srcType = subView.getSourceType();
66 // vector requires a static parent shape.
67 if (!srcType.hasStaticShape())
68 return false;
69
70 // implementation limitation: vector.extract_strided_slice must have the same
71 // rank (static). Alternative vector.extract could be used but only drop
72 // leading dimensions.
73 if (srcType.getRank() != subView.getType().getRank())
74 return false;
75
76 // No straightforward mapping to vector code for non-unit stride slices.
77 if (!subView.hasUnitStride())
78 return false;
79
80 return VectorType::isValidElementType(srcType.getElementType());
81}
82
83/// Returns whether static `subView` can alias a slice of the parent vector
84/// using `vector.extract_strided_slice` and `vector.insert_strided_slice`.
85/// Requires static source and result shapes, constant offsets, unit strides,
86/// and no rank reduction.
87static bool isAliasableStaticShapeSubView(memref::SubViewOp subView) {
88 if (!subView.getType().hasStaticShape())
89 return false;
90
92 return false;
93
94 // extract/insert_strided_slice requires all offsets to be constant.
95 return llvm::all_of(subView.getMixedOffsets(), [](OpFoldResult offset) {
96 return getConstantIntValue(offset).has_value();
97 });
98}
99
100/// Returns whether dynamic `subView` can alias the whole parent vector. The
101/// alias uses the parent vector's shape because vector dimensions cannot be
102/// dynamic. Requires a static source shape, a dynamic result shape, zero
103/// offsets, unit strides, and no rank reduction.
104static bool isAliasableDynamicShapeSubView(memref::SubViewOp subView) {
105 if (subView.getType().hasStaticShape())
106 return false;
107
109 return false;
110
111 /// TODO: Allow non-zero offsets by improving buildSubViewMask().
112 return subView.hasZeroOffset();
113}
114
115/// Returns the static offsets of `subView` within the parent vector for
116/// `vector.extract_strided_slice` and `vector.insert_strided_slice`.
117static SmallVector<int64_t> getStaticSubViewOffsets(memref::SubViewOp subView) {
118 SmallVector<int64_t> offsets;
119 for (OpFoldResult offset : subView.getMixedOffsets()) {
120 std::optional<int64_t> value = getConstantIntValue(offset);
121 assert(value && "expected a static offset");
122 offsets.push_back(*value);
123 }
124 return offsets;
125}
126
127/// The vector type covering `subView`'s whole parent buffer, which is the type
128/// the alias slot of a dynamically-shaped subview takes.
129static VectorType getWholeParentVectorType(memref::SubViewOp subView) {
130 auto parentType = cast<MemRefType>(subView.getSource().getType());
131 assert(parentType.hasStaticShape() && "expected a statically-shaped parent");
132 return VectorType::get(parentType.getShape(), parentType.getElementType());
133}
134
135/// If `slotPtr` is a dynamic subview, returns it.
136static memref::SubViewOp getDynamicSubView(Value slotPtr) {
137 auto subView = slotPtr.getDefiningOp<memref::SubViewOp>();
138 if (subView && isAliasableDynamicShapeSubView(subView))
139 return subView;
140 return {};
141}
142
143/// Builds the mask of `subView`'s valid region: a `vector.create_mask` of the
144/// subview's sizes, in the shape of the whole parent that its alias holds.
146 memref::SubViewOp subView) {
147 VectorType parentVecType = getWholeParentVectorType(subView);
148 SmallVector<Value> bounds =
149 getValueOrCreateConstantIndexOp(builder, loc, subView.getMixedSizes());
150 return vector::CreateMaskOp::create(
151 builder, loc,
152 VectorType::get(parentVecType.getShape(), builder.getI1Type()), bounds);
153}
154
156 return static_cast<bool>(getDynamicSubView(slotPtr));
157}
158
160 Value slotPtr) {
161 memref::SubViewOp subView = getDynamicSubView(slotPtr);
162 if (!subView)
163 return {};
164 return buildSubViewMask(builder, loc, subView);
165}
166
167namespace {
168//===----------------------------------------------------------------------===//
169// PromotableOpInterface model for memref.subview
170//===----------------------------------------------------------------------===//
171
172/// `PromotableOpInterface` model for `memref.subview`. Works with the
173/// `PromotableAliaserInterface` model below to remove the subview once its
174/// result's uses have been promoted.
175struct SubViewOpPromotableModel
176 : public PromotableOpInterface::ExternalModel<SubViewOpPromotableModel,
177 memref::SubViewOp> {
178 bool canUsesBeRemoved(Operation *op,
179 const SmallPtrSetImpl<OpOperand *> &blockingUses,
180 SmallVectorImpl<OpOperand *> &newBlockingUses,
181 const DataLayout &dataLayout) const {
182 // Removing the subview requires removing all uses of its memref result.
183 for (OpOperand &use : op->getResult(0).getUses())
184 newBlockingUses.push_back(&use);
185 return true;
186 }
187
189 removeBlockingUses(Operation *op,
190 const SmallPtrSetImpl<OpOperand *> &blockingUses,
191 OpBuilder &builder) const {
193 }
194};
195
196//===----------------------------------------------------------------------===//
197// PromotableAliaserInterface model for memref.subview
198//===----------------------------------------------------------------------===//
199
200/// `PromotableAliaserInterface` model exposing a same-rank `memref.subview` as
201/// an alias of a vector slot.
202///
203/// `projectSlotValueToAliasValue` provides the alias value for loads and for a
204/// store's `getStored` hook. `projectAliasValueToSlotValue` merges the stored
205/// alias value back into the parent.
206///
207/// Static subviews use `vector.extract_strided_slice` and
208/// `vector.insert_strided_slice`. Dynamic subviews keep the whole parent
209/// vector: loads use it directly, and stores use `arith.select` with a mask of
210/// the subview's sizes to preserve the parent value outside the subview.
211struct SubViewOpAliasModel
212 : public PromotableAliaserInterface::ExternalModel<SubViewOpAliasModel,
213 memref::SubViewOp> {
214 void getPromotableSlotAliases(Operation *op,
215 OpOperand &aliasedSlotPointerOperand,
216 const MemorySlot &parentSlot,
217 SmallVectorImpl<MemorySlot> &newSlots) const {
218 auto subView = cast<memref::SubViewOp>(op);
219 // Called once per operand holding the slot pointer; only the viewed source
220 // exposes an alias.
221 if (aliasedSlotPointerOperand.get() != subView.getSource())
222 return;
223
224 // The parent slot must promote to a vector. A scalar (single-element)
225 // parent slot cannot be sliced.
226 auto parentVecType = dyn_cast<VectorType>(parentSlot.valueType);
227 if (!parentVecType)
228 return;
229
230 // A static slice aliases the sub-vector matching the subview's shape.
231 if (isAliasableStaticShapeSubView(subView)) {
232 auto resType = cast<MemRefType>(subView.getResult().getType());
233 VectorType aliasVecType =
234 VectorType::get(resType.getShape(), resType.getElementType());
235 newSlots.push_back(MemorySlot{subView.getResult(), aliasVecType});
236 }
237
238 // A dynamic slice aliases the whole parent value.
239 if (isAliasableDynamicShapeSubView(subView)) {
240 newSlots.push_back(MemorySlot{subView.getResult(), parentVecType});
241 }
242 }
243
244 Value projectSlotValueToAliasValue(Operation *op,
245 OpOperand & /*aliasedSlotPointerOperand*/,
246 const MemorySlot & /*parentSlot*/,
247 const MemorySlot &aliasSlot,
248 Value slotValue,
249 OpBuilder &builder) const {
250 auto subView = cast<memref::SubViewOp>(op);
251 // Identity: the alias holds the whole parent value. Masking is left to
252 // `TransferReadOpMemOpModel::removeBlockingUses`, which unlike this hook
253 // can see the consuming read's padding value.
255 return slotValue;
256
257 SmallVector<int64_t> offsets = getStaticSubViewOffsets(subView);
258 auto aliasVecType = cast<VectorType>(aliasSlot.valueType);
259 SmallVector<int64_t> strides(offsets.size(), 1);
260 return vector::ExtractStridedSliceOp::create(
261 builder, op->getLoc(), slotValue, offsets,
262 aliasVecType.getShape(), strides)
263 .getResult();
264 }
265
266 Value projectAliasValueToSlotValue(Operation *op,
267 OpOperand & /*aliasedSlotPointerOperand*/,
268 const MemorySlot & /*parentSlot*/,
269 const MemorySlot & /*aliasSlot*/,
270 Value aliasValue, Value reachingDef,
271 OpBuilder &builder) const {
272 auto subView = cast<memref::SubViewOp>(op);
273 // Compose the stored value over the subview's extent only, leaving the rest
274 // of the parent value untouched.
275 if (isAliasableDynamicShapeSubView(subView)) {
276 Location loc = op->getLoc();
277 Value mask = buildSubViewMask(builder, loc, subView);
278 return arith::SelectOp::create(builder, loc, mask, aliasValue,
279 reachingDef);
280 }
281
282 SmallVector<int64_t> offsets = getStaticSubViewOffsets(subView);
283 SmallVector<int64_t> strides(offsets.size(), 1);
284 return vector::InsertStridedSliceOp::create(
285 builder, op->getLoc(), aliasValue, reachingDef, offsets, strides)
286 .getResult();
287 }
288};
289} // namespace
290
291//===----------------------------------------------------------------------===//
292// Register external models
293//===----------------------------------------------------------------------===//
294
296 DialectRegistry &registry) {
297 registry.addExtension(+[](MLIRContext *ctx, memref::MemRefDialect *dialect) {
298 memref::SubViewOp::attachInterface<SubViewOpAliasModel>(*ctx);
299 memref::SubViewOp::attachInterface<SubViewOpPromotableModel>(*ctx);
300 });
301}
static SmallVector< int64_t > getStaticSubViewOffsets(memref::SubViewOp subView)
Returns the static offsets of subView within the parent vector for vector.extract_strided_slice and v...
static memref::SubViewOp getDynamicSubView(Value slotPtr)
If slotPtr is a dynamic subview, returns it.
static bool isAliasableStaticShapeSubView(memref::SubViewOp subView)
Returns whether static subView can alias a slice of the parent vector using vector....
static Value buildSubViewMask(OpBuilder &builder, Location loc, memref::SubViewOp subView)
Builds the mask of subView's valid region: a vector.create_mask of the subview's sizes,...
static VectorType getWholeParentVectorType(memref::SubViewOp subView)
The vector type covering subView's whole parent buffer, which is the type the alias slot of a dynamic...
static bool isAliasableDynamicShapeSubView(memref::SubViewOp subView)
Returns whether dynamic subView can alias the whole parent vector.
static bool isUnitStrideSliceOfStaticParent(memref::SubViewOp subView)
Checks the requirements shared by static and dynamic subview aliases: a parent with a static shape an...
IntegerType getI1Type()
Definition Builders.cpp:61
The main mechanism for performing data layout queries.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
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
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition Value.h:188
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
void registerMemorySlotOpInterfaceExternalModels(DialectRegistry &registry)
Value buildDynamicSubViewMask(OpBuilder &builder, Location loc, Value slotPtr)
Builds a mask for the subview's valid region in the parent vector, or returns null if isDynamicSubVie...
bool isDynamicSubViewSlot(Value slotPtr)
Returns whether slotPtr is a dynamic memref.subview that can be promoted as an alias of the whole par...
Include the generated interface declarations.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
DeletionKind
Returned by operation promotion logic requesting the deletion of an operation.
@ Delete
Delete the operation after promotion.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:114
Type valueType
Type of the value contained in the slot.