MLIR 24.0.0git
VectorToGPUUtils.cpp
Go to the documentation of this file.
1//===- VectorToGPUUtils.cpp - Shared vector-to-MMA helpers ----------------===//
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
14#include "mlir/IR/AffineMap.h"
15#include "mlir/IR/Builders.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/ADT/SmallVector.h"
19#include "llvm/ADT/TypeSwitch.h"
20#include "llvm/Support/DebugLog.h"
21#include <cstddef>
22#include <tuple>
23#include <utility>
24
25#define DEBUG_TYPE "vector-to-gpu-common"
26
27using namespace mlir;
28
29bool mlir::constantSupportsMMAMatrixType(arith::ConstantOp constantOp) {
30 auto vecType = dyn_cast<VectorType>(constantOp.getType());
31 if (!vecType || vecType.getRank() != 2)
32 return false;
33 return isa<SplatElementsAttr>(constantOp.getValue());
34}
35
36bool mlir::broadcastSupportsMMAMatrixType(vector::BroadcastOp broadcastOp) {
37 return broadcastOp.getResultVectorType().getRank() == 2;
38}
39
40template <typename ExtOpTy>
42 auto transferReadOp =
43 extOp.getOperand().template getDefiningOp<vector::TransferReadOp>();
44 if (!transferReadOp)
45 return false;
46 return llvm::all_of(extOp->getUsers(), llvm::IsaPred<vector::ContractionOp>);
47}
48
49template bool
51
52template bool
54
55std::optional<gpu::MMAElementwiseOp>
57 using MMAEwO = gpu::MMAElementwiseOp;
59 .Case([](arith::AddFOp) { return MMAEwO::ADDF; })
60 .Case([](arith::AddIOp) { return MMAEwO::ADDI; })
61 .Case([](arith::DivFOp) { return MMAEwO::DIVF; })
62 .Case([](arith::DivSIOp) { return MMAEwO::DIVS; })
63 .Case([](arith::DivUIOp) { return MMAEwO::DIVU; })
64 .Case([](arith::ExtFOp) { return MMAEwO::EXTF; })
65 .Case([](arith::MaximumFOp) { return MMAEwO::MAXF; })
66 .Case([](arith::MinimumFOp) { return MMAEwO::MINF; })
67 .Case([](arith::MulFOp) { return MMAEwO::MULF; })
68 .Case([](arith::MulIOp) { return MMAEwO::MULI; })
69 .Case([](arith::NegFOp) { return MMAEwO::NEGATEF; })
70 .Case([](arith::SubFOp) { return MMAEwO::SUBF; })
71 .Case([](arith::SubIOp) { return MMAEwO::SUBI; })
72 .Case([](arith::TruncFOp) { return MMAEwO::TRUNCF; })
73 .Default(std::nullopt);
74}
75
79
80// Replace ForOp with a new ForOp with extra operands. The YieldOp is not
81// updated and needs to be updated separately for the loop to be correct.
82static scf::ForOp replaceForOpWithNewSignature(RewriterBase &rewriter,
83 scf::ForOp loop,
84 ValueRange newInitArgs) {
85 OpBuilder::InsertionGuard g(rewriter);
86
87 // Create a new loop before the existing one, with the extra operands.
88 rewriter.setInsertionPoint(loop);
89 auto operands = llvm::to_vector<4>(loop.getInitArgs());
90 llvm::append_range(operands, newInitArgs);
91 scf::ForOp newLoop =
92 scf::ForOp::create(rewriter, loop.getLoc(), loop.getLowerBound(),
93 loop.getUpperBound(), loop.getStep(), operands);
94 rewriter.eraseBlock(newLoop.getBody());
95
96 newLoop.getRegion().getBlocks().splice(
97 newLoop.getRegion().getBlocks().begin(), loop.getRegion().getBlocks());
98 for (Value operand : newInitArgs)
99 newLoop.getBody()->addArgument(operand.getType(), operand.getLoc());
100
101 for (auto it : llvm::zip(loop.getResults(), newLoop.getResults().take_front(
102 loop.getNumResults())))
103 rewriter.replaceAllUsesWith(std::get<0>(it), std::get<1>(it));
104
105 LDBG() << "newLoop now: " << newLoop;
106 LDBG() << "stripped scf.for: " << loop;
107 LDBG() << "erase: " << loop;
108
109 rewriter.eraseOp(loop);
110 return newLoop;
111}
112
113LogicalResult
114mlir::convertForOpForMMA(RewriterBase &rewriter, scf::ForOp op,
115 llvm::DenseMap<Value, Value> &valueMapping) {
116 OpBuilder::InsertionGuard g(rewriter);
117 rewriter.setInsertionPoint(op);
118
119 SmallVector<Value> newOperands;
121 for (const auto &operand : llvm::enumerate(op.getInitArgs())) {
122 auto it = valueMapping.find(operand.value());
123 if (it == valueMapping.end()) {
124 LDBG() << "no value mapping for: " << operand.value();
125 continue;
126 }
127 argMapping.push_back(std::make_pair(
128 operand.index(), op.getInitArgs().size() + newOperands.size()));
129 newOperands.push_back(it->second);
130 }
131
132 scf::ForOp newForOp = replaceForOpWithNewSignature(rewriter, op, newOperands);
133 Block &loopBody = *newForOp.getBody();
134 for (auto mapping : argMapping) {
135 valueMapping[newForOp.getResult(mapping.first)] =
136 newForOp.getResult(mapping.second);
137 valueMapping[loopBody.getArgument(mapping.first +
138 newForOp.getNumInductionVars())] =
139 loopBody.getArgument(mapping.second + newForOp.getNumInductionVars());
140 }
141
142 LDBG() << "scf.for to: " << newForOp;
143 return success();
144}
145
146LogicalResult
147mlir::convertYieldOpForMMA(RewriterBase &rewriter, scf::YieldOp op,
148 llvm::DenseMap<Value, Value> &valueMapping) {
149 OpBuilder::InsertionGuard g(rewriter);
150 rewriter.setInsertionPoint(op);
151
152 auto loop = cast<scf::ForOp>(op->getParentOp());
153 auto yieldOperands = llvm::to_vector<4>(op.getOperands());
154 for (const auto &operand : llvm::enumerate(op.getOperands())) {
155 auto it = valueMapping.find(operand.value());
156 if (it == valueMapping.end())
157 continue;
158 // Replace the yield of old value with the for op argument to make it easier
159 // to remove the dead code.
160 yieldOperands[operand.index()] = loop.getInitArgs()[operand.index()];
161 yieldOperands.push_back(it->second);
162 }
163 scf::YieldOp::create(rewriter, op.getLoc(), yieldOperands);
164
165 LDBG() << "erase: " << op;
166 rewriter.eraseOp(op);
167 return success();
168}
169
170namespace {
171// Fold vector.transpose into vector.transfer_read, including through
172// arithmetic extension operations.
173struct CombineTransferReadOpTranspose final
174 : public OpRewritePattern<vector::TransposeOp> {
175 using Base::Base;
176
177 LogicalResult matchAndRewrite(vector::TransposeOp op,
178 PatternRewriter &rewriter) const override {
179 // Look through integer extend ops.
180 Value source = op.getVector();
181 Type resultType = op.getType();
182 Operation *extOp;
183 if ((extOp = source.getDefiningOp<arith::ExtSIOp>()) ||
184 (extOp = source.getDefiningOp<arith::ExtUIOp>()) ||
185 (extOp = source.getDefiningOp<arith::ExtFOp>())) {
186 source = extOp->getOperand(0);
187 resultType =
188 VectorType::get(cast<VectorType>(resultType).getShape(),
189 cast<VectorType>(source.getType()).getElementType());
190 }
191
192 auto transferReadOp = source.getDefiningOp<vector::TransferReadOp>();
193 if (!transferReadOp)
194 return rewriter.notifyMatchFailure(op, "no transfer read");
195
196 // TODO: support 0-d corner case.
197 if (transferReadOp.getTransferRank() == 0)
198 return rewriter.notifyMatchFailure(op, "0-D transfer read");
199
200 if (transferReadOp.getMask() || transferReadOp.hasOutOfBoundsDim())
201 return rewriter.notifyMatchFailure(op, "not inbounds transfer read");
202
203 AffineMap permutationMap =
204 AffineMap::getPermutationMap(op.getPermutation(), op.getContext());
205 AffineMap newMap =
206 permutationMap.compose(transferReadOp.getPermutationMap());
207
208 auto loc = op.getLoc();
209 Value result = vector::TransferReadOp::create(
210 rewriter, loc, resultType, transferReadOp.getBase(),
211 transferReadOp.getIndices(), AffineMapAttr::get(newMap),
212 transferReadOp.getPadding(), transferReadOp.getMask(),
213 transferReadOp.getInBoundsAttr())
214 .getResult();
215
216 // Fuse through the integer extend op.
217 if (extOp) {
218 if (isa<arith::ExtSIOp>(extOp))
219 result = arith::ExtSIOp::create(rewriter, loc, op.getType(), result)
220 .getResult();
221 else if (isa<arith::ExtUIOp>(extOp))
222 result = arith::ExtUIOp::create(rewriter, loc, op.getType(), result)
223 .getResult();
224 else
225 result = arith::ExtFOp::create(rewriter, loc, TypeRange{op.getType()},
227 arith::ExtFOp::Properties{})
228 .getResult();
229 }
230
231 rewriter.replaceOp(op, result);
232 return success();
233 }
234};
235} // namespace
236
238 RewritePatternSet &patterns) {
239 patterns.add<CombineTransferReadOpTranspose>(patterns.getContext());
240}
return success()
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Definition Traits.cpp:117
static scf::ForOp replaceForOpWithNewSignature(RewriterBase &rewriter, scf::ForOp loop, ValueRange newInitArgs)
AffineExpr getResult(unsigned idx) const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
Block represents an ordered list of Operations.
Definition Block.h:34
BlockArgument getArgument(unsigned i)
Definition Block.h:154
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Value getOperand(unsigned idx)
Definition Operation.h:377
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.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void eraseBlock(Block *block)
This method erases all operations in a block.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
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,...
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:392
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
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
Include the generated interface declarations.
LogicalResult convertYieldOpForMMA(RewriterBase &rewriter, scf::YieldOp op, llvm::DenseMap< Value, Value > &valueMapping)
Update a yield operation to yield the converted values.
bool broadcastSupportsMMAMatrixType(vector::BroadcastOp broadcastOp)
Return true if this is a broadcast from scalar to a 2D vector.
bool elementwiseSupportsMMAMatrixType(Operation *op)
Return true if the operation is supported as elementwise on an MMA matrix.
LogicalResult convertForOpForMMA(RewriterBase &rewriter, scf::ForOp op, llvm::DenseMap< Value, Value > &valueMapping)
Extend loop-carried values with their converted counterparts.
std::optional< gpu::MMAElementwiseOp > convertElementwiseOpToMMA(Operation *op)
Return the MMA elementwise enum associated with the operation if supported.
bool constantSupportsMMAMatrixType(arith::ConstantOp constantOp)
Return true if the constant is a splat to a 2D vector so that it can be converted to an MMA constant ...
void populateVectorTransferReadTransposePatterns(RewritePatternSet &patterns)
Populate patterns that fold vector.transpose into vector.transfer_read, including through arithmetic ...
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:147
template bool integerExtendSupportsMMAMatrixType< arith::ExtSIOp >(arith::ExtSIOp)
bool integerExtendSupportsMMAMatrixType(ExtOpTy extOp)
Return true if this integer extend op can be folded into a contract op.
template bool integerExtendSupportsMMAMatrixType< arith::ExtUIOp >(arith::ExtUIOp)
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...