MLIR 24.0.0git
MaskedloadToLoad.cpp
Go to the documentation of this file.
1//===- MaskedloadToLoad.cpp - Lowers maskedload to load -------===//
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
10
23#include "llvm/Support/MathExtras.h"
24
25namespace mlir::amdgpu {
26#define GEN_PASS_DEF_AMDGPUMASKEDLOADTOLOADPASS
27#include "mlir/Dialect/AMDGPU/Transforms/Passes.h.inc"
28} // namespace mlir::amdgpu
29
30using namespace mlir;
31using namespace mlir::amdgpu;
32
33/// This pattern supports lowering of: `vector.maskedload` to `vector.load`
34/// and `arith.select` if the memref is in buffer address space.
35static LogicalResult hasBufferAddressSpace(Type type) {
36 auto memRefType = dyn_cast<MemRefType>(type);
37 if (!memRefType)
38 return failure();
39
40 Attribute addrSpace = memRefType.getMemorySpace();
41 if (!isa_and_nonnull<amdgpu::AddressSpaceAttr>(addrSpace))
42 return failure();
43
44 if (dyn_cast<amdgpu::AddressSpaceAttr>(addrSpace).getValue() !=
45 amdgpu::AddressSpace::FatRawBuffer)
46 return failure();
47
48 return success();
49}
50
52 vector::MaskedLoadOp maskedOp,
53 bool passthru) {
54 VectorType vectorType = maskedOp.getVectorType();
55 Value load = vector::LoadOp::create(
56 builder, loc, vectorType, maskedOp.getBase(), maskedOp.getIndices());
57 if (passthru)
58 load = arith::SelectOp::create(builder, loc, vectorType, maskedOp.getMask(),
59 load, maskedOp.getPassThru());
60 return load;
61}
62
63/// Check if the given value comes from a broadcasted i1 condition.
64static FailureOr<Value> matchFullMask(OpBuilder &b, Value val) {
65 auto broadcastOp = val.getDefiningOp<vector::BroadcastOp>();
66 if (!broadcastOp)
67 return failure();
68 if (isa<VectorType>(broadcastOp.getSourceType()))
69 return failure();
70 return broadcastOp.getSource();
71}
72
73static constexpr char kMaskedloadNeedsMask[] =
74 "amdgpu.buffer_maskedload_needs_mask";
75
76namespace {
77
78struct MaskedLoadLowering final : OpRewritePattern<vector::MaskedLoadOp> {
80
81 LogicalResult matchAndRewrite(vector::MaskedLoadOp maskedOp,
82 PatternRewriter &rewriter) const override {
83 if (maskedOp->hasDiscardableAttr(kMaskedloadNeedsMask))
84 return rewriter.notifyMatchFailure(maskedOp, "already rewritten");
85
86 if (failed(hasBufferAddressSpace(maskedOp.getBase().getType()))) {
87 return rewriter.notifyMatchFailure(
88 maskedOp, "isn't a load from a fat buffer resource");
89 }
90
91 // Check if this is either a full inbounds load or an empty, oob load. If
92 // so, take the fast path and don't generate an if condition, because we
93 // know doing the oob load is always safe.
94 if (succeeded(matchFullMask(rewriter, maskedOp.getMask()))) {
95 Value load = createVectorLoadForMaskedLoad(rewriter, maskedOp.getLoc(),
96 maskedOp, /*passthru=*/true);
97 rewriter.replaceOp(maskedOp, load);
98 return success();
99 }
100
101 Location loc = maskedOp.getLoc();
102 Value src = maskedOp.getBase();
103
104 VectorType vectorType = maskedOp.getVectorType();
105 int64_t vectorSize = vectorType.getNumElements();
106 int64_t elementBitWidth = vectorType.getElementTypeBitWidth();
107 SmallVector<OpFoldResult> indices = maskedOp.getIndices();
108
109 auto stridedMetadata =
110 memref::ExtractStridedMetadataOp::create(rewriter, loc, src);
112 stridedMetadata.getConstifiedMixedStrides();
113 SmallVector<OpFoldResult> sizes = stridedMetadata.getConstifiedMixedSizes();
114 OpFoldResult offset = stridedMetadata.getConstifiedMixedOffset();
115 memref::LinearizedMemRefInfo linearizedInfo;
116 OpFoldResult linearizedIndices;
117 std::tie(linearizedInfo, linearizedIndices) =
118 memref::getLinearizedMemRefOffsetAndSize(rewriter, loc, elementBitWidth,
119 elementBitWidth, offset, sizes,
120 strides, indices);
121
122 // delta = bufferSize - linearizedOffset
123 Value vectorSizeOffset =
124 arith::ConstantIndexOp::create(rewriter, loc, vectorSize);
125 Value linearIndex =
126 getValueOrCreateConstantIndexOp(rewriter, loc, linearizedIndices);
128 rewriter, loc, linearizedInfo.linearizedSize);
129 Value delta = arith::SubIOp::create(rewriter, loc, totalSize, linearIndex);
130
131 // 1) check if delta < vectorSize
132 Value isOutofBounds = arith::CmpIOp::create(
133 rewriter, loc, arith::CmpIPredicate::ult, delta, vectorSizeOffset);
134
135 // 2) check if (detla % elements_per_word != 0)
136 Value elementsPerWord = arith::ConstantIndexOp::create(
137 rewriter, loc, llvm::divideCeil(32, elementBitWidth));
138 Value isNotWordAligned = arith::CmpIOp::create(
139 rewriter, loc, arith::CmpIPredicate::ne,
140 arith::RemUIOp::create(rewriter, loc, delta, elementsPerWord),
141 arith::ConstantIndexOp::create(rewriter, loc, 0));
142
143 // We take the fallback of maskedload default lowering only it is both
144 // out-of-bounds and not word aligned. The fallback ensures correct results
145 // when loading at the boundary of the buffer since buffer load returns
146 // inconsistent zeros for the whole word when boundary is crossed.
147 Value ifCondition =
148 arith::AndIOp::create(rewriter, loc, isOutofBounds, isNotWordAligned);
149
150 auto thenBuilder = [&](OpBuilder &builder, Location loc) {
151 Operation *read = builder.clone(*maskedOp.getOperation());
153 Value readResult = read->getResult(0);
154 scf::YieldOp::create(builder, loc, readResult);
155 };
156
157 auto elseBuilder = [&](OpBuilder &builder, Location loc) {
158 Value res = createVectorLoadForMaskedLoad(builder, loc, maskedOp,
159 /*passthru=*/true);
160 scf::YieldOp::create(rewriter, loc, res);
161 };
162
163 auto ifOp =
164 scf::IfOp::create(rewriter, loc, ifCondition, thenBuilder, elseBuilder);
165
166 rewriter.replaceOp(maskedOp, ifOp);
167
168 return success();
169 }
170};
171
172struct FullMaskedLoadToConditionalLoad
173 : OpRewritePattern<vector::MaskedLoadOp> {
175
176 LogicalResult matchAndRewrite(vector::MaskedLoadOp loadOp,
177 PatternRewriter &rewriter) const override {
178 if (succeeded(hasBufferAddressSpace(loadOp.getBase().getType())))
179 return rewriter.notifyMatchFailure(
180 loadOp, "buffer loads are handled by a more specialized pattern");
181
182 FailureOr<Value> maybeCond = matchFullMask(rewriter, loadOp.getMask());
183 if (failed(maybeCond)) {
184 return rewriter.notifyMatchFailure(loadOp,
185 "isn't loading a broadcasted scalar");
186 }
187
188 Value cond = maybeCond.value();
189 auto trueBuilder = [&](OpBuilder &builder, Location loc) {
190 Value res = createVectorLoadForMaskedLoad(builder, loc, loadOp,
191 /*passthru=*/false);
192 scf::YieldOp::create(rewriter, loc, res);
193 };
194 auto falseBuilder = [&](OpBuilder &builder, Location loc) {
195 scf::YieldOp::create(rewriter, loc, loadOp.getPassThru());
196 };
197 auto ifOp = scf::IfOp::create(rewriter, loadOp.getLoc(), cond, trueBuilder,
198 falseBuilder);
199 rewriter.replaceOp(loadOp, ifOp);
200 return success();
201 }
202};
203
204struct FullMaskedStoreToConditionalStore
205 : OpRewritePattern<vector::MaskedStoreOp> {
207
208 LogicalResult matchAndRewrite(vector::MaskedStoreOp storeOp,
209 PatternRewriter &rewriter) const override {
210 // A condition-free implementation of fully masked stores requires
211 // 1) an accessor for the num_records field on buffer resources/fat pointers
212 // 2) knowledge that said field will always be set accurately - that is,
213 // that writes to x < num_records of offset wouldn't trap, which is
214 // something a pattern user would need to assert or we'd need to prove.
215 //
216 // Therefore, conditional stores to buffers still go down this path at
217 // present.
218
219 FailureOr<Value> maybeCond = matchFullMask(rewriter, storeOp.getMask());
220 if (failed(maybeCond)) {
221 return failure();
222 }
223 Value cond = maybeCond.value();
224
225 auto trueBuilder = [&](OpBuilder &builder, Location loc) {
226 vector::StoreOp::create(rewriter, loc, storeOp.getValueToStore(),
227 storeOp.getBase(), storeOp.getIndices());
228 scf::YieldOp::create(rewriter, loc);
229 };
230 auto ifOp =
231 scf::IfOp::create(rewriter, storeOp.getLoc(), cond, trueBuilder);
232 rewriter.replaceOp(storeOp, ifOp);
233 return success();
234 }
235};
236
237} // namespace
238
240 RewritePatternSet &patterns, PatternBenefit benefit) {
241 patterns.add<MaskedLoadLowering, FullMaskedLoadToConditionalLoad,
242 FullMaskedStoreToConditionalStore>(patterns.getContext(),
243 benefit);
244}
245
247 : amdgpu::impl::AmdgpuMaskedloadToLoadPassBase<AmdgpuMaskedloadToLoadPass> {
248 void runOnOperation() override {
249 RewritePatternSet patterns(&getContext());
251 if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) {
252 return signalPassFailure();
253 }
254 }
255};
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
b getContext())
auto load
static Value createVectorLoadForMaskedLoad(OpBuilder &builder, Location loc, vector::MaskedLoadOp maskedOp, bool passthru)
static constexpr char kMaskedloadNeedsMask[]
static FailureOr< Value > matchFullMask(OpBuilder &b, Value val)
Check if the given value comes from a broadcasted i1 condition.
static LogicalResult hasBufferAddressSpace(Type type)
This pattern supports lowering of: vector.maskedload to vector.load and arith.select if the memref is...
Attributes are known-constant values of operations.
Definition Attributes.h:25
UnitAttr getUnitAttr()
Definition Builders.cpp:106
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
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:581
This class represents a single result from folding an operation.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
void setDiscardableAttr(StringAttr name, Attribute value)
Set a discardable attribute by name.
Definition Operation.h:512
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
void populateAmdgpuMaskedloadToLoadPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
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)
Include the generated interface declarations.
LogicalResult applyPatternsGreedily(Region &region, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:114
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...
For a memref with offset, sizes and strides, returns the offset, size, and potentially the size padde...
Definition MemRefUtils.h:64