24#include "llvm/ADT/STLExtras.h"
25#include "llvm/Support/DebugLog.h"
30#define GEN_PASS_DEF_CONVERTVECTORTOAMX
31#include "mlir/Conversion/Passes.h.inc"
36#define DEBUG_TYPE "vector-to-amx"
42static bool verifyAmxShape(VectorType vec) {
46 if (vec.getRank() != 2 && vec.getRank() != 3)
52 unsigned elemBitWidth = vec.getElementType().getIntOrFloatBitWidth();
55 if (vec.getRank() == 3) {
56 int64_t vnniFactor = 32 / elemBitWidth;
57 if (
shape.back() != vnniFactor) {
58 LDBG() <<
"invalid VNNI packing factor";
65 constexpr unsigned maxRows = 16;
66 constexpr unsigned maxBitsPerRow = 64 * 8;
67 return rows <= maxRows && (cols * elemBitWidth) <= maxBitsPerRow;
72 vector::ContractionOp contractOp) {
73 VectorType accType = dyn_cast<VectorType>(contractOp.getAcc().getType());
74 if (!accType || accType.getRank() != 2)
78 VectorType lhsType = contractOp.getLhs().getType();
79 VectorType rhsType = contractOp.getRhs().getType();
80 if (lhsType.getRank() != 3 || rhsType.getRank() != 3)
82 "Expects lhs and rhs 3D vectors");
85 if (!verifyAmxShape(lhsType) || !verifyAmxShape(rhsType) ||
86 !verifyAmxShape(accType))
103 "Invalid input indexing maps");
104 FailureOr<linalg::ContractionDimensions> dims =
108 "Failed to infer contraction dims");
112 if (dims->k.size() != 2)
114 "Expected two reduction dims");
115 assert(dims->m.size() == 1 && dims->n.size() == 1 &&
116 "Invalid parallel contraction dims");
119 contractOp.getIteratorTypesArray();
121 auto vnniDimA = dyn_cast<AffineDimExpr>(mapA.
getResult(2));
122 auto vnniDimB = dyn_cast<AffineDimExpr>(mapB.
getResult(2));
123 if (!vnniDimA || !vnniDimB || vnniDimA != vnniDimB ||
124 iteratorTypes[vnniDimA.getPosition()] != vector::IteratorType::reduction)
127 auto redDimA = dyn_cast<AffineDimExpr>(mapA.
getResult(1));
128 auto redDimB = dyn_cast<AffineDimExpr>(mapB.
getResult(0));
129 if (!redDimA || !redDimB || redDimA != redDimB ||
130 iteratorTypes[redDimA.getPosition()] != vector::IteratorType::reduction)
134 auto mDimC = dyn_cast<AffineDimExpr>(mapC.
getResult(0));
135 auto nDimC = dyn_cast<AffineDimExpr>(mapC.
getResult(1));
136 if (!mDimC || !nDimC)
138 auto parallelDimA = dyn_cast<AffineDimExpr>(mapA.
getResult(0));
140 iteratorTypes[parallelDimA.getPosition()] !=
141 vector::IteratorType::parallel ||
142 parallelDimA != mDimC)
144 auto parallelDimB = dyn_cast<AffineDimExpr>(mapB.
getResult(1));
146 iteratorTypes[parallelDimB.getPosition()] !=
147 vector::IteratorType::parallel ||
148 parallelDimB != nDimC)
156 vector::ContractionOp contractOp) {
157 VectorType accType = dyn_cast<VectorType>(contractOp.getAcc().getType());
162 bool validElemTypes =
false;
163 Type lhsElemType = contractOp.getLhs().getType().getElementType();
164 Type rhsElemType = contractOp.getRhs().getType().getElementType();
165 Type accElemType = accType.getElementType();
168 }
else if (accElemType.
isF32()) {
169 validElemTypes = (lhsElemType.
isF16() && rhsElemType.
isF16()) ||
174 "Invalid combination of operand types");
176 if (failed(isAmxVnniLayout(rewriter, contractOp)))
187 for (
auto i : llvm::seq<int64_t>(0, rank - 2))
188 reassocIndices.push_back({i});
189 reassocIndices.push_back({rank - 2, rank - 1});
190 return memref::CollapseShapeOp::create(rewriter,
memref.getLoc(),
memref,
200 VectorTransferOpInterface xferOp,
bool isPacked,
202 if (!xferOp || !isa<vector::TransferReadOp, vector::TransferWriteOp>(xferOp))
204 if (xferOp.hasOutOfBoundsDim() ||
205 !xferOp.getPermutationMap().isMinorIdentity())
210 if (isa<vector::TransferWriteOp>(xferOp) &&
211 (!tileToStore || isPacked ||
212 tileToStore.getType().getShape() != xferOp.getVectorType().getShape()))
218 Value base = xferOp.getBase();
219 auto memTy = dyn_cast<MemRefType>(base.
getType());
220 int64_t memRank = memTy.getRank();
221 if (!memTy || memRank < 2)
233 if (!memTy.areTrailingDimsContiguous(isPacked ? 2 : 1))
235 VectorType vecTy = xferOp.getVectorType();
238 if (memShape.back() == ShapedType::kDynamic ||
239 memShape.back() < vecShape.back())
242 (memShape.back() != vecShape.back() ||
243 memShape[memShape.size() - 2] == ShapedType::kDynamic ||
244 memShape[memShape.size() - 2] < vecShape[vecShape.size() - 2]))
255 int64_t vecRank = vecTy.getRank();
256 assert(memRank >= vecRank &&
257 "Expects buffer to be the same or greater rank than vector");
259 shape.append(vecShape.begin(), vecShape.end());
261 memref::SubViewOp::create(
268 src = collapseLastDim(rewriter, src);
270 int64_t cols = llvm::product_of(vecShape.drop_front());
271 auto tileType = x86::amx::TileType::get({rows, cols}, vecTy.getElementType());
277 if (isa<vector::TransferReadOp>(xferOp)) {
278 amxTileOp = x86::amx::TileLoadOp::create(rewriter, loc, tileType, src,
280 }
else if (isa<vector::TransferWriteOp>(xferOp)) {
281 amxTileOp = x86::amx::TileStoreOp::create(rewriter, loc, src, tileIndicides,
284 llvm_unreachable(
"unsupported vector transfer op");
293static FailureOr<TypedValue<x86::amx::TileType>>
294loadFromTransfer(
PatternRewriter &rewriter, vector::TransferReadOp readOp,
296 x86::amx::TileLoadOp loadOp = dyn_cast_if_present<x86::amx::TileLoadOp>(
297 loadStoreFromTransfer(rewriter, readOp, isPacked));
300 return loadOp.getRes();
306storeFromTransfer(
PatternRewriter &rewriter, vector::TransferWriteOp writeOp,
308 return success(loadStoreFromTransfer(rewriter, writeOp,
false,
317 VectorType vecTy = vec.getType();
318 bool isPacked = vecTy.getRank() == 3;
321 auto readOp = vec.getDefiningOp<vector::TransferReadOp>();
322 FailureOr<TypedValue<x86::amx::TileType>>
tile =
323 loadFromTransfer(rewriter, readOp, isPacked);
328 Value buf = memref::AllocaOp::create(
329 rewriter, loc, MemRefType::get(vecTy.getShape(), vecTy.getElementType()));
332 vector::TransferWriteOp::create(rewriter, loc, vec, buf,
indices);
341 auto tileType = x86::amx::TileType::get({rows, cols}, vecTy.getElementType());
343 return x86::amx::TileLoadOp::create(rewriter, loc, tileType, buf,
344 {zeroIndex, zeroIndex});
354 Value buf = memref::AllocaOp::create(
356 MemRefType::get(tileTy.getShape(), tileTy.getElementType()));
359 x86::amx::TileStoreOp::create(rewriter, loc, buf,
indices,
tile);
361 auto vecTy = VectorType::get(tileTy.getShape(), tileTy.getElementType());
362 return vector::TransferReadOp::create(rewriter, loc, vecTy, buf,
indices, {});
369 matchAndRewriteAddContractOp(vector::ContractionOp contractOp,
373 if (failed(validateOperands(rewriter, contractOp)))
377 loadTile(rewriter, contractOp.getLhs());
379 loadTile(rewriter, contractOp.getRhs());
380 auto acc = dyn_cast<TypedValue<VectorType>>(contractOp.getAcc());
381 assert(
acc &&
"Invalid accumulator type");
385 if (
acc.getType().getElementType().isFloat()) {
386 tileMul = x86::amx::TileMulFOp::create(rewriter, loc, accTile.getType(),
387 lhsTile, rhsTile, accTile);
389 tileMul = x86::amx::TileMulIOp::create(rewriter, loc, accTile.getType(),
390 lhsTile, rhsTile, accTile);
395 Value res = contractOp.getResult();
396 if (res.hasOneUse()) {
397 auto writeOp = dyn_cast<vector::TransferWriteOp>(*res.getUsers().begin());
398 LogicalResult storeRes = storeFromTransfer(rewriter, writeOp, tileMul);
399 if (succeeded(storeRes)) {
407 Value newResult = storeTile(rewriter, tileMul);
408 rewriter.
replaceOp(contractOp, newResult);
414struct ConvertVectorToAMXPass
416 void runOnOperation()
override {
421 return signalPassFailure();
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
unsigned getNumResults() const
unsigned getNumInputs() const
AffineExpr getResult(unsigned idx) const
IntegerAttr getIndexAttr(int64_t value)
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
RAII guard to reset the insertion point of the builder when destroyed.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
Operation is the basic unit of execution within MLIR.
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...
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,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isInteger() const
Return true if this is an integer type (with the specified width).
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Specialization of arith.constant op that returns an integer of index type.
FailureOr< ContractionDimensions > inferContractionDims(LinalgOp linalgOp)
Find at least 2 parallel (m and n) and 1 reduction (k) dimension candidates that form a matmul subcom...
mlir::x86::AMXTileType TileType
Include the generated interface declarations.
void populateVectorToAMXConversionPatterns(RewritePatternSet &patterns)
Collect a set of patterns to convert from the vector to X86 AMX ops.
LogicalResult applyPatternsGreedily(Region ®ion, 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...
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
SmallVector< Loops, 8 > tile(ArrayRef< scf::ForOp > forOps, ArrayRef< Value > sizes, ArrayRef< scf::ForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
Convenience wrapper for patterns that are only intended for default contractions, i....