MLIR 24.0.0git
VectorContractToAMXDotProduct.cpp
Go to the documentation of this file.
1//===- VectorContractToAMXDotProduct.cpp ----------------------------------===//
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
15
17#include "mlir/IR/Dominance.h"
19#include "llvm/Support/Casting.h"
20
21#include "mlir/Pass/Pass.h"
23
24using namespace mlir;
25using namespace mlir::vector;
26using namespace mlir::x86;
27
28namespace {
29
30// Recursively follows single-use values through scf.yield operations
31// and returns the first non-yield user result in the contraction chain.
33 if (v.getNumUses() != 1)
34 return nullptr;
35
36 OpOperand &use = *v.use_begin();
37 Operation *user = use.getOwner();
38
39 if (!isa<scf::YieldOp>(user))
40 return v;
41
42 auto yield = cast<scf::YieldOp>(user);
43 Operation *parent = yield->getParentOp();
44 unsigned idx = use.getOperandNumber();
45
46 return contractionUsersAfterYield(parent->getResult(idx));
47}
48
49// Function to collapse the last two dimension (vnni and k) to help the
50// amx.tile_load to correctly load the packed element type.
52 Value input) {
53 ShapedType inputType = cast<ShapedType>(input.getType());
54 int64_t firstDimToCollapse = inputType.getRank() - 2;
55
56 if (inputType.getRank() == 1)
57 return input;
58
60 for (int64_t i = 0; i < firstDimToCollapse; ++i)
61 reassociation.push_back(ReassociationIndices{i});
62
63 ReassociationIndices collapsedIndices;
64 for (int64_t i = firstDimToCollapse; i < inputType.getRank(); ++i)
65 collapsedIndices.push_back(i);
66
67 reassociation.push_back(collapsedIndices);
68 return memref::CollapseShapeOp::create(builder, loc, input, reassociation);
69}
70
71// Check if a vector.contract operand has a memref read source.
72static bool isReadSrcMemref(Value operand) {
73 Operation *defOp = operand.getDefiningOp();
74 if (!defOp)
75 return false;
76
77 Value srcBuff;
79 .Case<TransferReadOp, LoadOp>(
80 [&](auto readOp) { srcBuff = readOp.getOperand(0); });
81
82 return srcBuff && isa<MemRefType>(srcBuff.getType());
83}
84
85// Get the MemRef source and offset index for the operands of
86// vector.contract.
87static FailureOr<std::pair<Value, SmallVector<Value>>>
88getSrcIndxValue(OpBuilder &rewriter, Location loc, Value operand,
89 bool isNotAcc) {
90 Operation *defOp = operand.getDefiningOp();
91 if (!defOp)
92 return failure();
93
94 Value srcBuff;
97 .Case<TransferReadOp, LoadOp>([&](auto readOp) {
98 indexVals = SmallVector<OpFoldResult>(readOp.getIndices().begin(),
99 readOp.getIndices().end());
100 srcBuff = readOp.getOperand(0);
101 });
102
103 if (!srcBuff || !isa<MemRefType>(srcBuff.getType()))
104 return failure();
105
106 if (isNotAcc)
107 indexVals.pop_back();
108
110 indices.reserve(indexVals.size());
111
112 for (OpFoldResult ofr : indexVals) {
113 indices.push_back(
114 mlir::getValueOrCreateConstantIndexOp(rewriter, loc, ofr));
115 }
116
117 if (isNotAcc) {
118 srcBuff = collapseInnerDims(rewriter, loc, srcBuff);
119 }
120
121 return std::make_pair(srcBuff, indices);
122}
123
124// Function to validate the loop step value.
125static LogicalResult validateLoopStep(OpBuilder &rewriter, Value step,
126 int64_t value) {
127
128 auto cst = step.getDefiningOp<arith::ConstantIndexOp>();
129 if (!cst)
130 return failure();
131
132 if (cst.value() != value && cst.value() != 1)
133 return failure();
134
135 return success();
136}
137
138// Function to validate the vector.contract operation.
139static LogicalResult validateContractOps(OpBuilder &rewriter,
140 vector::ContractionOp contractOp,
141 unsigned int blockingFactor,
142 Value srcBuffLhs, Value srcBuffRhs,
143 bool srcValidate) {
144
145 if (srcValidate) {
146 // Get the MemRef buffer of LHS operand.
147 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
148 contractOp.getLhs(), false);
149 if (failed(srcIndxLhs))
150 return failure();
151 auto [buffLhs, indicesLhs] = *srcIndxLhs;
152
153 // Get the MemRef buffer of RHS operand.
154 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
155 contractOp.getRhs(), false);
156 if (failed(srcIndxRhs))
157 return failure();
158 auto [buffRhs, indicesRhs] = *srcIndxRhs;
159
160 // Return failure if the Memref buff didn't match.
161 if (buffLhs != srcBuffLhs)
162 return failure();
163
164 if (buffRhs != srcBuffRhs)
165 return failure();
166 }
167
168 if (!contractionUsersAfterYield(contractOp.getResult()))
169 return failure();
170
171 VectorType accTy = dyn_cast<VectorType>(contractOp.getAccType());
172 if (!accTy)
173 return failure();
174
175 // The Accumulator dims should be 16 or 1. Like <1x16x16> or <16x16>.
176 ArrayRef<int64_t> accShape = accTy.getShape();
177 llvm::SmallVector<int64_t> nonUnitDimAcc;
178 llvm::copy_if(accShape, std::back_inserter(nonUnitDimAcc),
179 [](int64_t dim) { return (dim != 16 && dim != 1); });
180
181 if (nonUnitDimAcc.size() != 0)
182 return failure();
183
184 // The LHS dims should be 16 or vnni or 1. Like <1x16x16x2> or
185 // <16x16x4>. The vnni dims should be 2 or 4.
186 VectorType lhsTy = contractOp.getLhsType();
187 ArrayRef<int64_t> lhsShape = lhsTy.getShape();
188 llvm::SmallVector<int64_t> nonUnitDimLhs;
189 llvm::copy_if(lhsShape, std::back_inserter(nonUnitDimLhs),
190 [](int64_t dim) { return (dim != 16 && dim != 1); });
191
192 if (nonUnitDimLhs.size() != 1)
193 return failure();
194
195 if (nonUnitDimLhs[0] != blockingFactor)
196 return failure();
197
198 // The RHS dims should be 16 or vnni or 1. Like <1x16x16x2> or
199 // <16x16x4>. The vnni dims should be 2 or 4.
200 VectorType rhsTy = contractOp.getRhsType();
201 ArrayRef<int64_t> rhsShape = rhsTy.getShape();
202 llvm::SmallVector<int64_t> nonUnitDimRhs;
203 llvm::copy_if(rhsShape, std::back_inserter(nonUnitDimRhs),
204 [](int64_t dim) { return (dim != 16 && dim != 1); });
205
206 if (nonUnitDimRhs.size() != 1)
207 return failure();
208
209 if (nonUnitDimRhs[0] != blockingFactor)
210 return failure();
211
212 return success();
213}
214
215// Returns the loop index position to get mapped during the
216// MemRef type clone.
217static unsigned getIndexPosition(Value operand, scf::ForOp loop) {
218 Value iv = loop.getInductionVar();
219
220 Value srcBuff;
222 .Case<TransferReadOp, LoadOp>(
223 [&](auto readOp) { srcBuff = readOp.getOperand(0); });
224
225 auto subview = srcBuff.getDefiningOp<memref::SubViewOp>();
226 if (!subview)
227 return 0;
228
229 auto offsets = subview.getOffsets();
230
231 for (auto it : llvm::enumerate(offsets)) {
232 if (it.value() == iv)
233 return it.index();
234 }
235
236 return 0;
237}
238
239// Creates amx.tile_loads.
240static amx::TileLoadOp createTileLoads(OpBuilder &rewriter, Location loc,
241 Value operand, Value mat, Type ipType,
242 bool rhs, unsigned int offset,
243 bool isVnni) {
244
245 auto srcIndx = getSrcIndxValue(rewriter, loc, operand, false);
246 auto [srcBuff, indices] = *srcIndx;
247 if (isVnni) {
248 indices.pop_back();
249 }
250
251 if (rhs && isVnni) {
252 auto cOffset = arith::ConstantIndexOp::create(rewriter, loc, offset);
253 indices[indices.size() - 1] = arith::MulIOp::create(
254 rewriter, loc, indices[indices.size() - 1], cOffset);
255 }
256
257 amx::TileType tileType = amx::TileType::get({16, (16 * offset)}, ipType);
258 return amx::TileLoadOp::create(rewriter, loc, tileType, mat, indices);
259}
260
261static void performShuffle(OpBuilder &rewriter, Location loc, Value matB,
262 Type ipType, unsigned int offset, Value packedBuffer,
263 Value indxToStoreInBuffer) {
264
265 Value c0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
266 Value c16 = arith::ConstantIndexOp::create(rewriter, loc, 16);
267 SmallVector<Value> subviewOffset(
268 llvm::cast<MemRefType>(matB.getType()).getRank(), c0);
269
270 Value cStep = arith::ConstantIndexOp::create(rewriter, loc, offset);
271 Value cBound = arith::ConstantIndexOp::create(rewriter, loc, (16 * offset));
272 Value offsetIndx =
273 arith::ConstantIndexOp::create(rewriter, loc, (offset / 2));
274
275 scf::ForOp::create(
276 rewriter, loc, c0, cBound, cStep, ValueRange{},
277 [&](OpBuilder &nestedBuilder, Location loc, Value iv,
278 ValueRange iterArgs) {
279 subviewOffset[subviewOffset.size() - 2] = iv;
280
281 // Retrieve two rows of vector (32) for int8 and f8 type. For bf16,
282 // retrieve one row of vector (32).
283 auto vectorType = VectorType::get({2, (16 * (offset / 2))}, ipType);
284 if (ipType.isBF16())
285 vectorType = VectorType::get((16 * offset), ipType);
286
287 int64_t srcRank = (dyn_cast<ShapedType>(matB.getType())).getRank();
288 Value padding = ub::PoisonOp::create(rewriter, loc, ipType);
289 auto map = AffineMap::getMinorIdentityMap(srcRank, vectorType.getRank(),
290 rewriter.getContext());
291 SmallVector<bool> inBounds(vectorType.getRank(), true);
292 Value vec1 = vector::TransferReadOp::create(
293 rewriter, loc, vectorType, matB, ValueRange(subviewOffset), padding,
294 map, inBounds);
295
296 if (!ipType.isBF16())
297 vec1 = vector::ShapeCastOp::create(
298 rewriter, loc, VectorType::get((16 * offset), ipType), vec1);
299
300 // Increment the iv by 1 or 2 based on the type to load the next 32/64
301 // elements
302 Value incIV = arith::AddIOp::create(rewriter, loc, offsetIndx, iv);
303 subviewOffset[subviewOffset.size() - 2] = incIV;
304
305 Value vec2 = vector::TransferReadOp::create(
306 rewriter, loc, vectorType, matB, ValueRange(subviewOffset), padding,
307 map, inBounds);
308 if (!ipType.isBF16())
309 vec2 = vector::ShapeCastOp::create(
310 rewriter, loc, VectorType::get((16 * offset), ipType), vec2);
311
312 vector::ShuffleOp shuffle1;
313 vector::ShuffleOp shuffle2;
314
315 if (ipType.isBF16()) {
316
317 shuffle1 = vector::ShuffleOp::create(
318 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
319 vec2,
320 ArrayRef<int64_t>{0, 32, 1, 33, 2, 34, 3, 35, 8, 40, 9,
321 41, 10, 42, 11, 43, 16, 48, 17, 49, 18, 50,
322 19, 51, 24, 56, 25, 57, 26, 58, 27, 59});
323
324 shuffle2 = vector::ShuffleOp::create(
325 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
326 vec2,
327 ArrayRef<int64_t>{4, 36, 5, 37, 6, 38, 7, 39, 12, 44, 13,
328 45, 14, 46, 15, 47, 20, 52, 21, 53, 22, 54,
329 23, 55, 28, 60, 29, 61, 30, 62, 31, 63});
330 }
331
332 if (ipType.isSignlessInteger(8) || ipType.isF8E5M2() ||
333 ipType.isF8E4M3FN()) {
334
335 shuffle1 = vector::ShuffleOp::create(
336 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
337 vec2,
339 0, 32, 64, 96, 1, 33, 65, 97, 2, 34, 66, 98, 3,
340 35, 67, 99, 8, 40, 72, 104, 9, 41, 73, 105, 10, 42,
341 74, 106, 11, 43, 75, 107, 16, 48, 80, 112, 17, 49, 81,
342 113, 18, 50, 82, 114, 19, 51, 83, 115, 24, 56, 88, 120,
343 25, 57, 89, 121, 26, 58, 90, 122, 27, 59, 91, 123});
344
345 shuffle2 = vector::ShuffleOp::create(
346 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
347 vec2,
349 4, 36, 68, 100, 5, 37, 69, 101, 6, 38, 70, 102, 7, 39,
350 71, 103, 12, 44, 76, 108, 13, 45, 77, 109, 14, 46, 78, 110,
351 15, 47, 79, 111, 20, 52, 84, 116, 21, 53, 85, 117, 22, 54,
352 86, 118, 23, 55, 87, 119, 28, 60, 92, 124, 29, 61, 93, 125,
353 30, 62, 94, 126, 31, 63, 95, 127});
354 }
355
356 // iv to store the shuffled elements
357 Value ivShuff1 = arith::DivUIOp::create(rewriter, loc, iv, cStep);
358 Value ivShuff2 = arith::AddIOp::create(rewriter, loc, ivShuff1, c16);
359
360 vector::StoreOp::create(rewriter, loc, shuffle1, packedBuffer,
361 ValueRange{indxToStoreInBuffer, ivShuff1, c0});
362 vector::StoreOp::create(rewriter, loc, shuffle2, packedBuffer,
363 ValueRange{indxToStoreInBuffer, ivShuff2, c0});
364
365 scf::YieldOp::create(nestedBuilder, loc);
366 });
367}
368
370packInputs(OpBuilder &rewriter, Location loc,
372 unsigned int offset, Value packedBuffer, bool pack,
373 Value indxToStoreInBuffer, Value indxToLoadFromMatB) {
374
376 Value c0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
377 Value c16 = arith::ConstantIndexOp::create(rewriter, loc, 16);
378
379 for (size_t j = 0; j < ops.size(); j++) {
380 for (size_t i = 0; i < ops.size(); i++) {
381
382 if (i != j && validatePairVectorContract(ops[j], ops[i], true, 16)) {
383
384 Operation *readOpRhs = ops[j].getRhs().getDefiningOp();
385 auto itRhs = readsToTileLoads.find(readOpRhs);
386 if (itRhs != readsToTileLoads.end()) {
387 continue;
388 }
389
390 if (pack) {
391 performShuffle(rewriter, loc, matB, ipType, offset, packedBuffer,
392 indxToStoreInBuffer);
393 }
394
395 amx::TileType tileType =
396 amx::TileType::get({16, (16 * offset)}, ipType);
397 auto loadRow1 =
398 amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
399 ValueRange{indxToLoadFromMatB, c0, c0});
400
401 auto loadRow2 =
402 amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
403 ValueRange{indxToLoadFromMatB, c16, c0});
404
405 readsToTileLoads.try_emplace(readOpRhs, loadRow1);
406 readsToTileLoads.try_emplace(ops[i].getRhs().getDefiningOp(), loadRow2);
407 }
408 }
409 }
410
411 return readsToTileLoads;
412}
413
414// Creates tiled amx dot-products.
416createTiledDp(OpBuilder &rewriter, Location loc,
418 Type ipType, Type opType, ValueRange accIterArgs,
419 unsigned int offset, bool isVnni, Value packedBuffer, bool pack,
420 Value indxToStoreInBuffer, Value indxToLoadFromMatB) {
421
422 if (isVnni) {
423 matA = collapseInnerDims(rewriter, loc, matA);
424 matB = collapseInnerDims(rewriter, loc, matB);
425 }
426
427 SmallVector<Value> accumulators;
428 // Stores the amx.tile_load operation vs it's equivalent vector tranfer_read
429 // or load operations.
431
432 // function call to online pack the input B matrix
433 if (!isVnni) {
434 readsToTileLoads =
435 packInputs(rewriter, loc, ops, matB, ipType, offset, packedBuffer, pack,
436 indxToStoreInBuffer, indxToLoadFromMatB);
437 }
438
439 // Iterate over the contraction operations and compute the tiled dot-product.
440 for (size_t i = 0; i < ops.size(); i++) {
441
442 Operation *readOpLhs = ops[i].getLhs().getDefiningOp();
443 amx::TileLoadOp tilesLhs;
444 auto itLhs = readsToTileLoads.find(readOpLhs);
445 if (itLhs != readsToTileLoads.end()) {
446 tilesLhs = itLhs->second;
447 } else {
448 tilesLhs = createTileLoads(rewriter, loc, ops[i].getLhs(), matA, ipType,
449 false, offset, isVnni);
450 readsToTileLoads.try_emplace(readOpLhs, tilesLhs);
451 }
452
453 Operation *readOpRhs = ops[i].getRhs().getDefiningOp();
454 amx::TileLoadOp tilesRhs;
455 auto itRhs = readsToTileLoads.find(readOpRhs);
456 if (itRhs != readsToTileLoads.end()) {
457 tilesRhs = itRhs->second;
458 } else {
459 tilesRhs = createTileLoads(rewriter, loc, ops[i].getRhs(), matB, ipType,
460 true, offset, isVnni);
461 readsToTileLoads.try_emplace(readOpRhs, tilesRhs);
462 }
463
464 auto accTileType = amx::TileType::get({16, 16}, opType);
465
466 Value dp;
467 if (ipType.isBF16() || ipType.isF8E5M2() || ipType.isF8E4M3FN())
468 dp = amx::TileMulFOp::create(rewriter, loc, accTileType, tilesLhs,
469 tilesRhs, accIterArgs[i]);
470
471 if (ipType.isSignlessInteger(8))
472 dp = amx::TileMulIOp::create(rewriter, loc, accTileType, tilesLhs,
473 tilesRhs, accIterArgs[i]);
474
475 accumulators.push_back(dp);
476 }
477 return accumulators;
478}
479
480static SmallVector<Value> createTileZeros(OpBuilder &rewriter, Location loc,
481 Type opType, scf::ForOp outerLoop,
482 int64_t size) {
483 rewriter.setInsertionPoint(outerLoop);
484
485 SmallVector<Value> loopItrArgs;
486 auto zeroTileType = amx::TileType::get({16, 16}, opType);
487
488 for (int i = 0; i < size; i++) {
489 auto zeroTile = amx::TileZeroOp::create(rewriter, loc, zeroTileType);
490 loopItrArgs.push_back(zeroTile);
491 }
492 return loopItrArgs;
493}
494
495static Value getIndxToLoadStoreFromPckBuffer(OpBuilder &rewriter, Location loc,
496 Value ivInnerLoop,
497 Value ivOuterLoop,
498 bool isInnerLoopUBHasOddQuot,
499 bool isInnerLoopUBLarger,
500 bool pack, Value blockStride) {
501
502 Value c2 = arith::ConstantIndexOp::create(rewriter, loc, 2);
503
504 // `blockStride` is the reduction (K) loop step, i.e. the amount by which the
505 // induction variable advances for one K-block. Dividing the induction value
506 // by it yields the K-block index regardless of whether the loop counts
507 // K-elements (step == 16*blockingFactor) or pre-blocked K-tiles (step == 1).
508 Value quotientInnerLoop =
509 arith::DivUIOp::create(rewriter, loc, ivInnerLoop, blockStride);
510 Value remInnerLoop = arith::RemUIOp::create(
511 rewriter, loc, rewriter.getIndexType(), quotientInnerLoop, c2);
512
513 if (!isInnerLoopUBLarger && !pack) {
514 remInnerLoop = arith::RemUIOp::create(
515 rewriter, loc, rewriter.getIndexType(), ivOuterLoop, c2);
516 }
517
518 if (isInnerLoopUBHasOddQuot) {
519 auto remOuterLoop = arith::RemUIOp::create(
520 rewriter, loc, rewriter.getIndexType(), ivOuterLoop, c2);
521 auto remAdd = arith::AddIOp::create(rewriter, loc, rewriter.getIndexType(),
522 remInnerLoop, remOuterLoop);
523 remInnerLoop = arith::RemUIOp::create(rewriter, loc,
524 rewriter.getIndexType(), remAdd, c2);
525 }
526
527 return remInnerLoop;
528}
529
530static scf::ForOp
531createLoops(OpBuilder &rewriter, Location loc, Value lowerBound,
532 Value upperBound, Value step, SmallVector<Value> loopItrArgs,
533 Type ipType, Type opType, unsigned int blockingFactor, bool isVnni,
534 Operation *vectorOpLhs, Operation *vectorOpRhs,
535 vector::ContractionOp contractOp, scf::ForOp outerLoop,
536 scf::ForOp innerLoop, SmallVector<vector::ContractionOp> ops,
537 Value ivOuterLoop, Value packedBuffer, bool pack,
538 arith::ConstantIndexOp innerLoopIndex, bool isInnerLoopUBLarger,
539 bool isInnerLoopUBHasOddQuot) {
540
541 Value c0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
542 Value c1 = arith::ConstantIndexOp::create(rewriter, loc, 1);
543 Value c2 = arith::ConstantIndexOp::create(rewriter, loc, 2);
544
545 int64_t offset = 16 * blockingFactor;
546 if (auto cst = step.getDefiningOp<arith::ConstantIndexOp>())
547 offset = cst.value();
548
549 auto newLoop = scf::ForOp::create(
550 rewriter, loc, lowerBound, upperBound, step, loopItrArgs,
551 [&](OpBuilder &rewriterNewInnerLoop, Location locNewInnerLoop,
552 Value ivNewInnerLoop, ValueRange iterArgsNewInnerLoop) {
553 IRMapping mapping;
554 if (outerLoop)
555 mapping.map(vectorOpLhs->getOperand(
556 getIndexPosition(contractOp.getLhs(), outerLoop) + 1),
557 ivOuterLoop);
558
559 mapping.map(vectorOpLhs->getOperand(
560 getIndexPosition(contractOp.getLhs(), innerLoop) + 1),
561 ivNewInnerLoop);
562 auto lhsClone = rewriterNewInnerLoop.clone(*vectorOpLhs, mapping);
563
564 Value indxToStoreInBuffer = c0;
565 Value indxToLoadFromBuffer = c0;
566 if (!isVnni) {
567 if (outerLoop) {
568 if (innerLoopIndex.value() == 0) {
569 if (pack) {
570 ivNewInnerLoop = c0;
571 ivOuterLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
572 c1, ivOuterLoop);
573
574 if (!isInnerLoopUBLarger || isInnerLoopUBHasOddQuot) {
575 indxToStoreInBuffer = arith::RemUIOp::create(
576 rewriter, locNewInnerLoop, rewriter.getIndexType(),
577 ivOuterLoop, c2);
578 }
579
580 Value indxToLoadFromMatB = arith::AddIOp::create(
581 rewriter, loc, indxToStoreInBuffer, c1);
582 indxToLoadFromBuffer = arith::RemUIOp::create(
583 rewriter, loc, rewriter.getIndexType(), indxToLoadFromMatB,
584 c2);
585 }
586
587 } else {
589 rewriter, locNewInnerLoop, offset);
590 ivNewInnerLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
591 nLoadIndx, ivNewInnerLoop);
592 indxToStoreInBuffer = getIndxToLoadStoreFromPckBuffer(
593 rewriter, loc, ivNewInnerLoop, ivOuterLoop,
594 isInnerLoopUBHasOddQuot, isInnerLoopUBLarger, pack, step);
595 Value indxToLoadFromMatB =
596 arith::AddIOp::create(rewriter, loc, indxToStoreInBuffer, c1);
597 indxToLoadFromBuffer =
598 arith::RemUIOp::create(rewriter, loc, rewriter.getIndexType(),
599 indxToLoadFromMatB, c2);
600 }
601 } else {
602 if (pack) {
604 rewriter, locNewInnerLoop, offset);
605 ivNewInnerLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
606 nLoadIndx, ivNewInnerLoop);
607 Value quotient_K = arith::DivUIOp::create(
608 rewriter, loc, ivNewInnerLoop, nLoadIndx);
609 indxToStoreInBuffer = arith::RemUIOp::create(
610 rewriter, loc, rewriter.getIndexType(), quotient_K, c2);
611
612 Value indxToLoadFromMatB =
613 arith::AddIOp::create(rewriter, loc, indxToStoreInBuffer, c1);
614 indxToLoadFromBuffer =
615 arith::RemUIOp::create(rewriter, loc, rewriter.getIndexType(),
616 indxToLoadFromMatB, c2);
617 }
618 }
619 }
620 IRMapping rhsMapping;
621
622 Value matB;
623 Operation *rhsOp = vectorOpRhs;
624
625 // Clone for the subview type operations
626 if (rhsOp->getNumOperands() > 0) {
627
628 if (outerLoop) {
629 int64_t outerPos = getIndexPosition(contractOp.getRhs(), outerLoop);
630
631 if (outerPos >= 0) {
632 unsigned operandIdx = static_cast<unsigned>(outerPos + 1);
633
634 if (operandIdx < rhsOp->getNumOperands())
635 rhsMapping.map(rhsOp->getOperand(operandIdx), ivOuterLoop);
636 }
637 }
638
639 int64_t innerPos = getIndexPosition(contractOp.getRhs(), innerLoop);
640
641 if (innerPos >= 0) {
642 unsigned operandIdx = static_cast<unsigned>(innerPos + 1);
643
644 if (operandIdx < rhsOp->getNumOperands())
645 rhsMapping.map(rhsOp->getOperand(operandIdx), ivNewInnerLoop);
646 }
647
648 auto rhsClone = rewriterNewInnerLoop.clone(*rhsOp, rhsMapping);
649 matB = rhsClone->getResult(0);
650
651 } else {
652 // The mat B is of kind 'memref.get_global @__constant'
653 matB = rhsOp->getResult(0);
654 }
655
656 if (!isVnni) {
657 if (outerLoop) {
658 if (!pack) {
659 matB = Value();
660 indxToLoadFromBuffer = c0;
661 // Use the real spill-block induction value (== spillInnerLoop)
662 // together with the loop step so the computed ping-pong slot
663 // matches the prefetch store side for any number of register
664 // blocks, including odd counts (e.g. 96 = 3 blocks). Passing a
665 // constant here mis-parities the slot for odd block counts.
666 indxToLoadFromBuffer = getIndxToLoadStoreFromPckBuffer(
667 rewriter, loc, ivNewInnerLoop, ivOuterLoop,
668 isInnerLoopUBHasOddQuot, isInnerLoopUBLarger, pack, step);
669 }
670 } else {
671 if (!pack) {
673 rewriter, locNewInnerLoop, offset);
674 matB = Value();
675 Value quotient_K = arith::DivUIOp::create(
676 rewriter, loc, ivNewInnerLoop, nLoadIndx);
677 indxToLoadFromBuffer = arith::RemUIOp::create(
678 rewriter, loc, rewriter.getIndexType(), quotient_K, c2);
679 }
680 }
681 }
682 // compute tiled dot-product
683 SmallVector<Value> accumulators = createTiledDp(
684 rewriter, locNewInnerLoop, ops, lhsClone->getResult(0), matB,
685 ipType, opType, iterArgsNewInnerLoop, blockingFactor, isVnni,
686 packedBuffer, pack, indxToStoreInBuffer, indxToLoadFromBuffer);
687
688 scf::YieldOp::create(rewriterNewInnerLoop, locNewInnerLoop,
689 accumulators);
690 });
691
692 return newLoop;
693}
694
695// Implements tiled dot-product operation for a vector.contract operation or a
696// sequence of vector.contracts inside the reduction loops.
697//
698// For example:
699// Case 1: register blocked vector.contract with prepacked input
700// ```
701// vector.transfer_read %arg0 {{.}*} : memref<16x32x4xi8>, vector<16x16x4xi8>
702// vector.transfer_read %arg1 {{.}*} : memref<16x32x4xi8>, vector<16x16x4xi8>
703// vector.contract <16x16x4xi8>, <16x16x4xi8> into <16x16xi32>
704// vector.transfer_write arg2 {{.}*} : vector<16x16xi32>, memref<32x32xi32>
705// ```
706// to
707// ```
708// amx.tile_load %arg0 {{.}*} : memref<16x32x4xi8> into !amx.tile<16x64xi8>
709// amx.tile_load %arg1 {{.}*} : memref<16x32x4xi8> into !amx.tile<16x64xi8>
710// amx.tile_muli !amx.tile<16x64xi8> -> !amx.tile<16x16xi32>
711// amx.tile_store %arg2{{.}*} : memref<32x32xi32>, !amx.tile<16x16xi32>
712// ```
713//
714//
715// Case2: vector.contract with register blocked
716//
717// Output IR with online packing (with s/w pipeline advantage):
718// s/w pipeline: load, pack to VNNI, and store the B sub matrix
719// of the 0th batch-reduce and K iteration.
720// scf.for (0 to 31) {
721// - load 0th and 1st vector<32xbf16>, pack into VNNI, store the
722// first shuffle in 0th and 2nd shuffle in 16th index of the
723// buffer.
724// }
725// scf.for (0 to br-2) { batch-reduce loop
726// scf.for (0 to k-2) { K loop
727// - load A matrix
728// - scf.loop for s/w pipeline: load, pack to VNNI, and store the B sub
729// matrix for the next K loop iteration (c) load VNNI pack B matrix of K
730// iteration from the buffer (d) compute the tiled dot-product
731// }
732// Last iteration of the the K Loop (k-1) {
733// - load A matrix
734// - scf.loop for s/w pipeline: load, pack to VNNI, and store the B sub
735// matrix for the next batch-reduce + K loop iteration (c) load VNNI pack B
736// matrix of K iteration from the buffer (d) compute the tiled dot-product
737// }
738// }
739// Last iteration of the batch-reduce loop (br-1) {
740// scf.for (0 to k-2) { K loop
741// - load A matrix
742// - scf.loop for s/w pipeline: load, pack to VNNI, and store the B sub
743// matrix for the next K loop iteration (c) load VNNI pack B matrix of K
744// iteration from the buffer (d) compute the tiled dot-product
745// }
746// Last iteration of the the K Loop (k-1) {
747// - load A matrix
748// - load VNNI pack B matrix of K iteration from the buffer
749// - compute the tiled dot-product
750// }
751// }
752//
753// scf.for (0 to M)
754// scf.for (0 to N)
755// - Load the ith and i+1th acc
756// - Shuffle them as we packed using vpunpack
757// - Load C matrix and do arith.add with the shuffle
758// - Store back into C matrix
759struct VectorContractToAMXDotProduct
760 : public OpRewritePattern<vector::ContractionOp> {
761 using OpRewritePattern<vector::ContractionOp>::OpRewritePattern;
762
763 LogicalResult matchAndRewrite(vector::ContractionOp contractOp,
764 PatternRewriter &rewriter) const override {
765
766 if (contractOp.getKind() != vector::CombiningKind::ADD)
767 return rewriter.notifyMatchFailure(contractOp,
768 "Expects add combining kind.");
769
770 unsigned int blockingFactor =
771 contractOp.getLhsType().getElementType().isBF16() ? 2 : 4;
772 bool isVnni =
773 isInVnniLayout(contractOp.getOperation(),
774 contractOp.getIndexingMapsArray(), blockingFactor);
775
776 VectorType lhsTy = contractOp.getLhsType();
777 if (!lhsTy.getElementType().isBF16() &&
778 !lhsTy.getElementType().isSignlessInteger(8) &&
779 !lhsTy.getElementType().isF8E4M3FN() &&
780 !lhsTy.getElementType().isF8E5M2())
781 return rewriter.notifyMatchFailure(
782 contractOp, "Only BF16/Int8/F8 lowering is supported.");
783
784 if (lhsTy.getElementType() != contractOp.getRhsType().getElementType())
785 return rewriter.notifyMatchFailure(
786 contractOp, "Contraction should have same lhs and rhs type.");
787
788 VectorType accTy = dyn_cast<VectorType>(contractOp.getAccType());
789 if (!accTy)
790 return rewriter.notifyMatchFailure(contractOp, "Wrong accmulator type.");
791
792 if (((lhsTy.getElementType().isBF16() ||
793 lhsTy.getElementType().isF8E4M3FN() ||
794 lhsTy.getElementType().isF8E5M2()) &&
795 !accTy.getElementType().isF32()) ||
796 (lhsTy.getElementType().isSignlessInteger(8) &&
797 !accTy.getElementType().isSignlessInteger(32)))
798 return rewriter.notifyMatchFailure(contractOp,
799 "Only F32 for BF16 or Int32 for Int8 "
800 "accumulation type is supported.");
801
802 Operation *accReadOp =
803 traceToVectorReadLikeParentOperation(contractOp.getAcc());
804
805 // Only the contract result's first consumer is needed, not the final
806 // store. This keeps the lowering independent of the epilogue ops (truncf,
807 // bias add, ReLU, ...) that sit between the contraction and the write.
808 Value resultChainEnd = contractionUsersAfterYield(contractOp.getResult());
809
810 if (!accReadOp || !resultChainEnd)
811 return rewriter.notifyMatchFailure(
812 contractOp, "The ACC operand of the vector.contract should be a "
813 "transfer_read or a load. And, the result should have a "
814 "single-use chain to its consumer.");
815
816 Block *resultBlock = resultChainEnd.user_begin()->getBlock();
817
818 Type ipType = rewriter.getBF16Type();
819 Type opType = rewriter.getF32Type();
820
821 if (lhsTy.getElementType().isSignlessInteger(8)) {
822 ipType = rewriter.getIntegerType(8);
823 opType = rewriter.getIntegerType(32);
824 }
825
826 if (lhsTy.getElementType().isF8E4M3FN())
827 ipType = rewriter.getF8E4M3FNType();
828
829 if (lhsTy.getElementType().isF8E5M2())
830 ipType = rewriter.getF8E5M2Type();
831
832 if (accReadOp->getBlock() == contractOp->getBlock() &&
833 resultBlock != contractOp->getBlock())
834 return rewriter.notifyMatchFailure(
835 contractOp, "The accumulator store is in different block.");
836
837 if (accReadOp->getBlock() != contractOp->getBlock() &&
838 resultBlock == contractOp->getBlock())
839 return rewriter.notifyMatchFailure(
840 contractOp, "The accumulator read is in different block.");
841
842 if (!(isReadSrcMemref(contractOp.getLhs()) &&
843 isReadSrcMemref(contractOp.getRhs())))
844 return rewriter.notifyMatchFailure(
845 contractOp, "The LHS or RHS src is not a MemRef type.");
846
847 unsigned int dimValue = blockingFactor;
848 if (!isVnni)
849 dimValue = 16 * blockingFactor;
850
851 // Case 1: For just one VC rewrite. Where all accumulator read/write
852 // within the same block.
853 if (accReadOp->getBlock() == contractOp->getBlock() &&
854 resultBlock == contractOp->getBlock()) {
855
856 if (!isReadSrcMemref(contractOp.getAcc()))
857 return rewriter.notifyMatchFailure(contractOp,
858 "The ACC src is not a MemRef type.");
859
860 bool collapse = false;
861 if (isVnni)
862 collapse = true;
863
864 LogicalResult validate = validateContractOps(
865 rewriter, contractOp, dimValue, Value(), Value(), false);
866
867 if (failed(validate))
868 return rewriter.notifyMatchFailure(
869 contractOp, "The contract operation doesn't satisfy the operands "
870 "dimensions. M, N, and vnni dims are 16, 16, and 2/4. "
871 "The rest dims should be 1. Op should have one user.");
872
873 Location loc = contractOp.getLoc();
874
875 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
876 contractOp.getLhs(), collapse);
877 if (failed(srcIndxLhs))
878 return rewriter.notifyMatchFailure(contractOp,
879 "Failed to get the LHS src.");
880 auto [srcBuffLhs, indicesLhs] = *srcIndxLhs;
881
882 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
883 contractOp.getRhs(), collapse);
884 if (failed(srcIndxRhs))
885 return rewriter.notifyMatchFailure(contractOp,
886 "Failed to get the RHS src.");
887 auto rhsSrc = *srcIndxRhs;
888 auto srcBuffRhs = rhsSrc.first;
889 auto indicesRhs = rhsSrc.second;
890
891 auto srcIndxAcc = getSrcIndxValue(rewriter, contractOp.getLoc(),
892 contractOp.getAcc(), false);
893 if (failed(srcIndxAcc))
894 return rewriter.notifyMatchFailure(contractOp,
895 "Failed to get the ACC src.");
896 auto [srcBuffAcc, indicesAcc] = *srcIndxAcc;
897
898 Value c0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
899
900 // amx.tile_loads
901 auto tileType = amx::TileType::get({16, (16 * blockingFactor)}, ipType);
902 auto loadLhs = amx::TileLoadOp::create(rewriter, loc, tileType,
903 srcBuffLhs, indicesLhs);
904
905 // Create the subview and then load.
906 amx::TileLoadOp loadRhs;
907 if (!isVnni) {
908 VectorType vecTy;
909 SmallVector<OpFoldResult> indexVals;
910 llvm::TypeSwitch<Operation *>(contractOp.getRhs().getDefiningOp())
911 .Case<TransferReadOp, LoadOp>([&](auto readOp) {
912 indexVals = SmallVector<OpFoldResult>(readOp.getIndices().begin(),
913 readOp.getIndices().end());
914 vecTy = readOp.getType();
915 });
916 auto one = rewriter.getIndexAttr(1);
917 SmallVector<OpFoldResult> strides(indexVals.size(), one);
918 SmallVector<OpFoldResult> sizes = getAsIndexOpFoldResult(
919 contractOp.getRhs().getDefiningOp()->getContext(),
920 vecTy.getShape());
921 auto subview = memref::SubViewOp::create(rewriter, loc, srcBuffRhs,
922 indexVals, sizes, strides);
923 auto bufferType = MemRefType::get({16, (16 * blockingFactor)}, ipType);
924 auto packedBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
925
926 // create a loop that does online packing.
927 Value step =
928 arith::ConstantIndexOp::create(rewriter, loc, blockingFactor);
929 Value uBound = arith::ConstantIndexOp::create(rewriter, loc,
930 (blockingFactor * 16));
931 Value nextLoadIndx =
932 arith::ConstantIndexOp::create(rewriter, loc, (blockingFactor / 2));
933 Value nextStoreIndx = arith::ConstantIndexOp::create(
934 rewriter, loc, 16 * (blockingFactor / 2));
935
936 scf::ForOp::create(
937 rewriter, loc, c0, uBound, step, ValueRange{},
938 [&](OpBuilder &nestedBuilder, Location loc, Value iv,
939 ValueRange iterArgs) {
940 Value i1_load =
941 arith::AddIOp::create(rewriter, loc, nextLoadIndx, iv);
942
943 indicesRhs[indicesRhs.size() - 2] = iv;
944 indicesRhs[indicesRhs.size() - 1] = c0;
945 ValueRange range1(indicesRhs);
946 auto vec1 = vector::LoadOp::create(
947 rewriter, loc,
948 VectorType::get(16 * (blockingFactor / 2), ipType), subview,
949 range1);
950
951 indicesRhs[indicesRhs.size() - 2] = i1_load;
952 ValueRange range2(indicesRhs);
953 auto vec2 = vector::LoadOp::create(
954 rewriter, loc,
955 VectorType::get(16 * (blockingFactor / 2), ipType), subview,
956 range2);
957
958 vector::ShuffleOp shuffle1;
959 vector::ShuffleOp shuffle2;
960
961 if (blockingFactor == 2) {
962
963 shuffle1 = vector::ShuffleOp::create(
964 rewriter, loc, VectorType::get({16}, ipType), vec1, vec2,
965 ArrayRef<int64_t>{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21,
966 6, 22, 7, 23});
967
968 shuffle2 = vector::ShuffleOp::create(
969 rewriter, loc, VectorType::get({16}, ipType), vec1, vec2,
970 ArrayRef<int64_t>{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13,
971 29, 14, 30, 15, 31});
972 }
973
974 if (blockingFactor == 4) {
975 shuffle1 = vector::ShuffleOp::create(
976 rewriter, loc, VectorType::get({32}, ipType), vec1, vec2,
977 ArrayRef<int64_t>{0, 16, 32, 48, 1, 17, 33, 49,
978 2, 18, 34, 50, 3, 19, 35, 51,
979 4, 20, 36, 52, 5, 21, 37, 53,
980 6, 22, 38, 54, 7, 23, 39, 55});
981
982 shuffle2 = vector::ShuffleOp::create(
983 rewriter, loc, VectorType::get({32}, ipType), vec1, vec2,
984 ArrayRef<int64_t>{8, 24, 40, 56, 9, 25, 41, 57,
985 10, 26, 42, 58, 11, 27, 43, 59,
986 12, 28, 44, 60, 13, 29, 45, 61,
987 14, 30, 46, 62, 15, 31, 47, 63});
988 }
989
990 auto rem = arith::DivUIOp::create(
991 rewriter, loc, rewriter.getIndexType(), iv, step);
992
993 vector::StoreOp::create(rewriter, loc, shuffle1, packedBuffer,
994 ValueRange{rem, c0});
995 vector::StoreOp::create(rewriter, loc, shuffle2, packedBuffer,
996 ValueRange{rem, nextStoreIndx});
997
998 scf::YieldOp::create(nestedBuilder, loc);
999 });
1000 loadRhs = amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
1001 ValueRange{c0, c0});
1002 } else {
1003
1004 loadRhs = amx::TileLoadOp::create(rewriter, loc, tileType, srcBuffRhs,
1005 indicesRhs);
1006 }
1007
1008 auto tileTypeAcc = amx::TileType::get({16, 16}, opType);
1009 auto loadAcc = amx::TileLoadOp::create(rewriter, loc, tileTypeAcc,
1010 srcBuffAcc, indicesAcc);
1011
1012 // Tiled dot-product.
1013 Value dp;
1014 if (ipType.isBF16() || ipType.isF8E5M2() || ipType.isF8E4M3FN())
1015 dp = amx::TileMulFOp::create(rewriter, loc, tileTypeAcc, loadLhs,
1016 loadRhs, loadAcc);
1017
1018 if (ipType.isSignlessInteger(8))
1019 dp = amx::TileMulIOp::create(rewriter, loc, tileTypeAcc, loadLhs,
1020 loadRhs, loadAcc);
1021
1022 auto bufferType = MemRefType::get({16, 16}, opType);
1023 auto resultBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
1024
1025 amx::TileStoreOp::create(rewriter, loc, resultBuffer, ValueRange{c0, c0},
1026 dp);
1027
1028 auto vectorType = mlir::VectorType::get({16, 16}, opType);
1029 int64_t srcRank =
1030 (dyn_cast<ShapedType>(resultBuffer.getType())).getRank();
1031 Value padding = ub::PoisonOp::create(rewriter, loc, opType);
1032 auto map = AffineMap::getMinorIdentityMap(srcRank, vectorType.getRank(),
1033 rewriter.getContext());
1034 SmallVector<bool> inBounds(vectorType.getRank(), true);
1035
1036 Value vecRow = vector::TransferReadOp::create(
1037 rewriter, loc, vectorType, resultBuffer, ValueRange{c0, c0}, padding,
1038 map, inBounds);
1039
1040 Value resultOp = contractionUsersAfterYield(contractOp.getResult());
1041 if (auto vecType = llvm::dyn_cast<VectorType>(resultOp.getType()))
1042 vecRow = vector::ShapeCastOp::create(rewriter, loc, vecType, vecRow);
1043
1044 rewriter.replaceAllUsesWith(resultOp, vecRow);
1045 return success();
1046 }
1047
1048 // Case 2: The acc are passed as iter args through the reduction loop.
1049 // We support, reduction loop depth until 2. TODO: Support for n-depth
1050 // reduction loop.
1051 // TODOs: Re-factor 2a and 2b.
1052 SmallVector<scf::ForOp> loopLists;
1053 Operation *current = contractOp;
1054 while (true) {
1055 Operation *parent = current->getParentOfType<scf::ForOp>();
1056
1057 if (!parent) {
1058 // The accumulator initialization can be hoisted above an enclosing
1059 // parallel region (scf.parallel/scf.forall) when the register tile
1060 // matches the problem size and the M/N register loops fold away. In
1061 // that case the reduction loop(s) collected so far are still valid to
1062 // rewrite, so stop climbing instead of bailing out.
1063 if (!loopLists.empty())
1064 break;
1065 return rewriter.notifyMatchFailure(
1066 contractOp,
1067 "Accumulator read and contract op not within scf.for op");
1068 }
1069
1070 loopLists.push_back(dyn_cast<scf::ForOp>(parent));
1071
1072 if (accReadOp->getBlock() == parent->getBlock()) {
1073 break;
1074 }
1075
1076 current = parent;
1077 }
1078 if (loopLists.size() > 2 || loopLists.size() == 0)
1079 return rewriter.notifyMatchFailure(
1080 contractOp, "Rewrite is supported until reduction loop depth of 2.");
1081
1082 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1083 contractOp.getLhs(), false);
1084 if (failed(srcIndxLhs))
1085 return rewriter.notifyMatchFailure(contractOp,
1086 "Failed to get the LHS src.");
1087 auto [srcBuffLhs, indicesLhs] = *srcIndxLhs;
1088
1089 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1090 contractOp.getRhs(), false);
1091 if (failed(srcIndxRhs))
1092 return rewriter.notifyMatchFailure(contractOp,
1093 "Failed to get the RHS src.");
1094 auto [srcBuffRhs, indicesRhs] = *srcIndxRhs;
1095 Operation *vectorOpLhs;
1096 llvm::TypeSwitch<Operation *>(contractOp.getLhs().getDefiningOp())
1097 .Case<TransferReadOp, LoadOp>([&](auto readOp) {
1098 vectorOpLhs = readOp.getBase().getDefiningOp();
1099 });
1100
1101 Operation *vectorOpRhs;
1102 llvm::TypeSwitch<Operation *>(contractOp.getRhs().getDefiningOp())
1103 .Case<TransferReadOp, LoadOp>([&](auto readOp) {
1104 vectorOpRhs = readOp.getBase().getDefiningOp();
1105 });
1106
1107 if (!vectorOpLhs || !vectorOpRhs)
1108 return rewriter.notifyMatchFailure(
1109 contractOp, "Failed to find LHS or RHS read source operation");
1110
1111 // Retrive all the contaction operation within the loop.
1112 SmallVector<vector::ContractionOp> ops;
1113 for (mlir::Operation &op : loopLists[0].getBody()->getOperations()) {
1114
1115 if (auto contract = llvm::dyn_cast<mlir::vector::ContractionOp>(op)) {
1116
1117 LogicalResult validate = validateContractOps(
1118 rewriter, contract, dimValue, srcBuffLhs, srcBuffRhs, true);
1119
1120 if (failed(validate))
1121 return rewriter.notifyMatchFailure(
1122 contractOp,
1123 "The associated contract operations doesn't satisfy "
1124 "the re-write conditions either the dimensions are "
1125 "wrong or MemRef source are different or many users.");
1126
1127 ops.push_back(contract);
1128 }
1129 }
1130
1131 if (!isVnni) {
1132 unsigned int pairCount = 0;
1133 for (size_t j = 0; j < ops.size(); j++) {
1134 for (size_t i = j; i < ops.size(); i++) {
1135 if (i != j && validatePairVectorContract(ops[j], ops[i], true, 16))
1136 pairCount = pairCount + 2;
1137 }
1138 }
1139
1140 if (pairCount != ops.size())
1141 return rewriter.notifyMatchFailure(
1142 contractOp, "Coudn't find the pair vector contract ");
1143 }
1144
1145 scf::ForOp innerLoop;
1146 scf::ForOp outerLoop;
1147
1148 scf::ForOp newLoop;
1149 // Case 2a: Reduction loop depth is 2.
1150 if (loopLists.size() == 2) {
1151 outerLoop = loopLists[1];
1152 innerLoop = loopLists[0];
1153
1154 LogicalResult validateOuterLoopStep =
1155 validateLoopStep(rewriter, outerLoop.getStep(), 1);
1156 if (failed(validateOuterLoopStep))
1157 return rewriter.notifyMatchFailure(contractOp, "Invalid loop step.");
1158
1159 int64_t stepValue = 16;
1160 if (!isVnni)
1161 stepValue = stepValue * blockingFactor;
1162 LogicalResult validateInnerLoopStep =
1163 validateLoopStep(rewriter, innerLoop.getStep(), stepValue);
1164 if (failed(validateInnerLoopStep))
1165 return rewriter.notifyMatchFailure(
1166 contractOp, "Invalid loop step. The step should be 32 for BF16 and "
1167 "64 for Int8/F8.");
1168
1169 SmallVector<Value> loopItrArgs = createTileZeros(
1170 rewriter, outerLoop.getLoc(), opType, outerLoop, ops.size());
1171
1172 if (isVnni) {
1173 newLoop = scf::ForOp::create(
1174 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1175 outerLoop.getUpperBound(), outerLoop.getStep(), loopItrArgs,
1176 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1177 Value ivOuterLoop, ValueRange iterArgsOuterLoop) {
1178 auto newInnerLoop = createLoops(
1179 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1180 innerLoop.getUpperBound(), innerLoop.getStep(),
1181 iterArgsOuterLoop, ipType, opType, blockingFactor, isVnni,
1182 vectorOpLhs, vectorOpRhs, contractOp, outerLoop, innerLoop,
1183 ops, ivOuterLoop, nullptr, true, nullptr, false, false);
1184
1185 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1186 newInnerLoop.getResults());
1187 });
1188
1189 } else {
1190
1191 bool isInnerLoopUBLarger = false;
1192 bool isInnerLoopUBHasOddQuot = false;
1193
1194 int64_t ubVal = 16 * blockingFactor;
1195 mlir::Value ub = innerLoop.getUpperBound();
1196 if (auto constOp = ub.getDefiningOp<mlir::arith::ConstantOp>()) {
1197 if (auto intAttr =
1198 llvm::dyn_cast<mlir::IntegerAttr>(constOp.getValue())) {
1199 ubVal = intAttr.getInt();
1200 }
1201 }
1202
1203 isInnerLoopUBLarger = ubVal > 16 * blockingFactor;
1204 isInnerLoopUBHasOddQuot =
1205 (((ubVal / (16 * blockingFactor)) % 2) == 1) && isInnerLoopUBLarger;
1206
1207 rewriter.setInsertionPoint(outerLoop);
1208
1209 auto c0 =
1210 arith::ConstantIndexOp::create(rewriter, outerLoop.getLoc(), 0);
1211 auto c1 =
1212 arith::ConstantIndexOp::create(rewriter, outerLoop.getLoc(), 1);
1213 auto spillLoopBound = arith::ConstantIndexOp::create(
1214 rewriter, outerLoop.getLoc(), 16 * blockingFactor);
1215
1216 Value spillOuterLoop = arith::SubIOp::create(
1217 rewriter, outerLoop.getLoc(), outerLoop.getUpperBound(), c1);
1218 Value spillInnerLoop =
1219 arith::SubIOp::create(rewriter, innerLoop.getLoc(),
1220 innerLoop.getUpperBound(), spillLoopBound);
1221 auto bufferType =
1222 MemRefType::get({2, 32, (blockingFactor * 16)}, ipType);
1223 auto packedBuffer =
1224 memref::AllocaOp::create(rewriter, outerLoop.getLoc(), bufferType);
1225
1226 // First Shuffling outside the reduction loops
1227 IRMapping rhsMapping;
1228 rhsMapping.map(
1229 vectorOpRhs->getOperand(
1230 getIndexPosition(contractOp.getRhs(), outerLoop) + 1),
1231 outerLoop.getLowerBound());
1232 rhsMapping.map(
1233 vectorOpRhs->getOperand(
1234 getIndexPosition(contractOp.getRhs(), innerLoop) + 1),
1235 innerLoop.getLowerBound());
1236 auto rhsClone = rewriter.clone(*vectorOpRhs, rhsMapping);
1237
1238 Value quotient_batch = arith::DivUIOp::create(
1239 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1240 outerLoop.getStep());
1241 Value quotient_k = arith::DivUIOp::create(rewriter, outerLoop.getLoc(),
1242 innerLoop.getLowerBound(),
1243 innerLoop.getStep());
1244
1245 Value quotient_add = arith::AddIOp::create(rewriter, outerLoop.getLoc(),
1246 quotient_batch, quotient_k);
1247 Value c2 =
1248 arith::ConstantIndexOp::create(rewriter, outerLoop.getLoc(), 2);
1249 Value rem = arith::RemUIOp::create(rewriter, outerLoop.getLoc(),
1250 quotient_add, c2);
1251
1252 performShuffle(rewriter, outerLoop.getLoc(), rhsClone->getResult(0),
1253 ipType, blockingFactor, packedBuffer, rem);
1254
1255 // First Set of Loops
1256 auto newLoopNonSpill = scf::ForOp::create(
1257 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1258 spillOuterLoop, outerLoop.getStep(), loopItrArgs,
1259 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1260 Value ivOuterLoop, ValueRange iterArgsOuterLoop) {
1261 auto newInnerLoop1 = createLoops(
1262 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1263 spillInnerLoop, innerLoop.getStep(), iterArgsOuterLoop,
1264 ipType, opType, blockingFactor, isVnni, vectorOpLhs,
1265 vectorOpRhs, contractOp, outerLoop, innerLoop, ops,
1266 ivOuterLoop, packedBuffer, true, spillLoopBound,
1267 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1268
1269 auto newInnerLoop = createLoops(
1270 rewriter, innerLoop.getLoc(), spillInnerLoop,
1271 innerLoop.getUpperBound(), innerLoop.getStep(),
1272 newInnerLoop1.getResults(), ipType, opType, blockingFactor,
1273 isVnni, vectorOpLhs, vectorOpRhs, contractOp, outerLoop,
1274 innerLoop, ops, ivOuterLoop, packedBuffer, true, c0,
1275 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1276
1277 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1278 newInnerLoop.getResults());
1279 });
1280
1281 // Last set of Loops
1282 newLoop = scf::ForOp::create(
1283 rewriter, outerLoop.getLoc(), spillOuterLoop,
1284 outerLoop.getUpperBound(), outerLoop.getStep(),
1285 newLoopNonSpill.getResults(),
1286 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1287 Value ivOuterLoop, ValueRange iterArgsOuterLoop) {
1288 auto newInnerLoop1 = createLoops(
1289 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1290 spillInnerLoop, innerLoop.getStep(), iterArgsOuterLoop,
1291 ipType, opType, blockingFactor, isVnni, vectorOpLhs,
1292 vectorOpRhs, contractOp, outerLoop, innerLoop, ops,
1293 ivOuterLoop, packedBuffer, true, spillLoopBound,
1294 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1295
1296 auto newInnerLoop = createLoops(
1297 rewriter, innerLoop.getLoc(), spillInnerLoop,
1298 innerLoop.getUpperBound(), innerLoop.getStep(),
1299 newInnerLoop1.getResults(), ipType, opType, blockingFactor,
1300 isVnni, vectorOpLhs, vectorOpRhs, contractOp, outerLoop,
1301 innerLoop, ops, ivOuterLoop, packedBuffer, false, c0,
1302 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1303
1304 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1305 newInnerLoop.getResults());
1306 });
1307 }
1308 }
1309
1310 // Case 2b: Reduction loop depth is 1.
1311 if (loopLists.size() == 1) {
1312
1313 innerLoop = loopLists[0];
1314 int64_t stepValue = 16;
1315 if (!isVnni)
1316 stepValue = stepValue * blockingFactor;
1317
1318 LogicalResult validateInnerLoopStep =
1319 validateLoopStep(rewriter, innerLoop.getStep(), stepValue);
1320 if (failed(validateInnerLoopStep))
1321 return rewriter.notifyMatchFailure(
1322 contractOp,
1323 "Invalid loop step. The step should be 32 for BF16 and "
1324 "64 for Int8/F8 or 1 if it is rduction loop other than K.");
1325
1326 SmallVector<Value> loopItrArgs = createTileZeros(
1327 rewriter, innerLoop.getLoc(), opType, innerLoop, ops.size());
1328
1329 if (isVnni) {
1330 newLoop = createLoops(
1331 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1332 innerLoop.getUpperBound(), innerLoop.getStep(), loopItrArgs, ipType,
1333 opType, blockingFactor, isVnni, vectorOpLhs, vectorOpRhs,
1334 contractOp, nullptr, innerLoop, ops, nullptr, nullptr, true,
1335 nullptr, false, false);
1336
1337 } else {
1338
1339 bool isInnerLoopUBLarger = false;
1340 bool isInnerLoopUBHasOddQuot = false;
1341
1342 int64_t ubVal = 16 * blockingFactor;
1343 mlir::Value ub = innerLoop.getUpperBound();
1344 if (auto constOp = ub.getDefiningOp<mlir::arith::ConstantOp>()) {
1345 if (auto intAttr =
1346 llvm::dyn_cast<mlir::IntegerAttr>(constOp.getValue())) {
1347 ubVal = intAttr.getInt();
1348 }
1349 }
1350
1351 isInnerLoopUBLarger = ubVal > 16 * blockingFactor;
1352 isInnerLoopUBHasOddQuot =
1353 (((ubVal / (16 * blockingFactor)) % 2) == 1) && isInnerLoopUBLarger;
1354
1355 rewriter.setInsertionPoint(innerLoop);
1356
1357 auto c0 =
1358 arith::ConstantIndexOp::create(rewriter, innerLoop.getLoc(), 0);
1359 int64_t offset = 16 * blockingFactor;
1360 if (auto cst =
1361 innerLoop.getStep().getDefiningOp<arith::ConstantIndexOp>())
1362 offset = cst.value();
1363
1364 auto spillLoopBound = arith::ConstantIndexOp::create(
1365 rewriter, innerLoop.getLoc(), offset);
1366 Value spillInnerLoop =
1367 arith::SubIOp::create(rewriter, innerLoop.getLoc(),
1368 innerLoop.getUpperBound(), spillLoopBound);
1369
1370 auto bufferType =
1371 MemRefType::get({2, 32, (blockingFactor * 16)}, ipType);
1372 auto packedBuffer =
1373 memref::AllocaOp::create(rewriter, innerLoop.getLoc(), bufferType);
1374
1375 // First Shuffling outside the reduction loops
1376 IRMapping rhsMapping;
1377 rhsMapping.map(
1378 vectorOpRhs->getOperand(
1379 getIndexPosition(contractOp.getRhs(), innerLoop) + 1),
1380 innerLoop.getLowerBound());
1381 auto rhsClone = rewriter.clone(*vectorOpRhs, rhsMapping);
1382
1383 Value quotient_k = arith::DivUIOp::create(rewriter, innerLoop.getLoc(),
1384 innerLoop.getLowerBound(),
1385 innerLoop.getStep());
1386 Value c2 =
1387 arith::ConstantIndexOp::create(rewriter, innerLoop.getLoc(), 2);
1388 Value rem = arith::RemUIOp::create(rewriter, innerLoop.getLoc(),
1389 quotient_k, c2);
1390
1391 performShuffle(rewriter, innerLoop.getLoc(), rhsClone->getResult(0),
1392 ipType, blockingFactor, packedBuffer, rem);
1393
1394 auto newLoopNonSpill = createLoops(
1395 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1396 spillInnerLoop, innerLoop.getStep(), loopItrArgs, ipType, opType,
1397 blockingFactor, isVnni, vectorOpLhs, vectorOpRhs, contractOp,
1398 nullptr, innerLoop, ops, nullptr, packedBuffer, true,
1399 spillLoopBound, isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1400
1401 newLoop = createLoops(rewriter, innerLoop.getLoc(), spillInnerLoop,
1402 innerLoop.getUpperBound(), innerLoop.getStep(),
1403 newLoopNonSpill.getResults(), ipType, opType,
1404 blockingFactor, isVnni, vectorOpLhs, vectorOpRhs,
1405 contractOp, nullptr, innerLoop, ops, nullptr,
1406 packedBuffer, false, c0, isInnerLoopUBLarger,
1407 isInnerLoopUBHasOddQuot);
1408 }
1409
1410 // This helps the final store back to the acc uses the same code for
1411 // the both reduction loop depth 1 or 2.
1412 outerLoop = innerLoop;
1413 }
1414
1415 // Copy the amx tile accumulation results to a MemRef buffer, add the
1416 // initial accumulation value, and store back to the C-Matrix
1417 Location loc = outerLoop.getLoc();
1418 Value srcBuffAcc;
1419 SmallVector<Value> indicesAcc;
1420
1421 llvm::TypeSwitch<Operation *>(accReadOp).Case<TransferReadOp, LoadOp>(
1422 [&](auto readOp) {
1423 srcBuffAcc = readOp.getOperand(0);
1424
1425 auto indices = readOp.getIndices();
1426 indicesAcc.reserve(indices.size());
1427
1428 llvm::transform(indices, std::back_inserter(indicesAcc),
1429 [&](OpFoldResult ofr) {
1431 rewriter, loc, ofr);
1432 });
1433 });
1434
1435 auto outputShapes =
1436 mlir::cast<mlir::MemRefType>(srcBuffAcc.getType()).getShape();
1437 unsigned int M = outputShapes[outputShapes.size() - 2];
1438 unsigned int N = outputShapes[outputShapes.size() - 1];
1439
1440 SmallVector<Value> dps = newLoop.getResults();
1441 auto bufferType = MemRefType::get({M, N}, opType);
1442 auto resultBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
1443
1444 // Store the amx tiled-dot product output into an MxN memref.
1445 for (unsigned int i = 0, k = 0; i < M; i = i + 16) {
1446 for (unsigned int j = 0; j < N; j = j + 16) {
1447 Value indexOp_i = arith::ConstantIndexOp::create(rewriter, loc, i);
1448 Value indexOp_j = arith::ConstantIndexOp::create(rewriter, loc, j);
1449 amx::TileStoreOp::create(rewriter, loc, resultBuffer,
1450 ValueRange{indexOp_i, indexOp_j}, dps[k]);
1451 k++;
1452 }
1453 }
1454 auto c0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
1455 auto c16 = arith::ConstantIndexOp::create(rewriter, loc, 16);
1456 auto one = arith::ConstantIndexOp::create(rewriter, loc, 1);
1457 auto nBound = arith::ConstantIndexOp::create(rewriter, loc, N);
1458
1459 // Create a loop that iterates over the MxN memerf, retrives two rows +
1460 // shuffle them, add up the C element values and stores them to temp buffer.
1461 scf::ForOp::create(
1462 rewriter, loc, c0, nBound, one, ValueRange{},
1463 [&](OpBuilder &nestedBuilder, Location loc, Value iv,
1464 ValueRange iterArgs) {
1465 auto row =
1466 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1467 resultBuffer, ValueRange{iv, c0});
1468
1469 auto row2 =
1470 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1471 resultBuffer, ValueRange{iv, c16});
1472
1473 Value shuffle1 = row;
1474 Value shuffle2 = row2;
1475
1476 if (!isVnni) {
1477 shuffle1 = vector::ShuffleOp::create(
1478 rewriter, loc, VectorType::get(16, opType), row, row2,
1479 ArrayRef<int64_t>{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20,
1480 21, 22, 23});
1481
1482 shuffle2 = vector::ShuffleOp::create(
1483 rewriter, loc, VectorType::get(16, opType), row, row2,
1484 ArrayRef<int64_t>{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15,
1485 28, 29, 30, 31});
1486 }
1487 indicesAcc[indicesAcc.size() - 2] = iv;
1488 indicesAcc[indicesAcc.size() - 1] = c0;
1489
1490 Value valueCRow1 =
1491 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1492 srcBuffAcc, indicesAcc);
1493 indicesAcc[indicesAcc.size() - 1] = c16;
1494
1495 Value valueCRow2 =
1496 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1497 srcBuffAcc, indicesAcc);
1498
1499 Value addOp;
1500 Value addOp2;
1501
1502 if (ipType.isBF16() || ipType.isF8E5M2() || ipType.isF8E4M3FN()) {
1503 addOp = arith::AddFOp::create(rewriter, loc, shuffle1, valueCRow1);
1504
1505 addOp2 = arith::AddFOp::create(rewriter, loc, shuffle2, valueCRow2);
1506 }
1507
1508 if (ipType.isSignlessInteger(8)) {
1509 addOp = arith::AddIOp::create(rewriter, loc, shuffle1, valueCRow1);
1510
1511 addOp2 = arith::AddIOp::create(rewriter, loc, shuffle2, valueCRow2);
1512 }
1513
1514 vector::StoreOp::create(rewriter, loc, addOp, resultBuffer,
1515 ValueRange{iv, c0});
1516 vector::StoreOp::create(rewriter, loc, addOp2, resultBuffer,
1517 ValueRange{iv, c16});
1518
1519 scf::YieldOp::create(nestedBuilder, loc);
1520 });
1521
1522 SmallVector<Value> writeResults;
1523 for (unsigned int i = 0; i < M; i = i + 16) {
1524 for (unsigned int j = 0; j < N; j = j + 16) {
1525 Value indexOp_i = arith::ConstantIndexOp::create(rewriter, loc, i);
1526 Value indexOp_j = arith::ConstantIndexOp::create(rewriter, loc, j);
1527
1528 auto vectorType = mlir::VectorType::get({16, 16}, opType);
1529
1530 int64_t srcRank =
1531 (dyn_cast<ShapedType>(resultBuffer.getType())).getRank();
1532 Value padding = ub::PoisonOp::create(rewriter, loc, opType);
1533 auto map = AffineMap::getMinorIdentityMap(srcRank, vectorType.getRank(),
1534 rewriter.getContext());
1535 SmallVector<bool> inBounds(vectorType.getRank(), true);
1536
1537 auto vec1 = vector::TransferReadOp::create(
1538 rewriter, loc, vectorType, resultBuffer,
1539 ValueRange{indexOp_i, indexOp_j}, padding, map, inBounds);
1540 writeResults.push_back(vec1);
1541 }
1542 }
1543
1544 // Replace use of vector.contract with dot-products.
1545 for (size_t i = 0; i < ops.size(); i++) {
1546 vector::ContractionOp contOp = ops[i];
1547 Value vecRow = writeResults[i];
1548
1549 Value resultWriteOp = contractionUsersAfterYield(contOp.getResult());
1550 if (auto vecType = llvm::dyn_cast<VectorType>(resultWriteOp.getType()))
1551 vecRow = mlir::vector::ShapeCastOp::create(rewriter, loc, vecType,
1552 writeResults[i]);
1553
1554 rewriter.replaceAllUsesWith(resultWriteOp, vecRow);
1555 }
1556
1557 return success();
1558 }
1559};
1560
1561} // namespace
1562
1564 RewritePatternSet &patterns) {
1565 patterns.add<VectorContractToAMXDotProduct>(patterns.getContext());
1566}
return success()
static void contract(RootOrderingGraph &graph, ArrayRef< Value > cycle, const DenseMap< Value, unsigned > &parentDepths, DenseMap< Value, Value > &actualSource, DenseMap< Value, Value > &actualTarget)
Contracts the specified cycle in the given graph in-place.
static Value collapseInnerDims(PatternRewriter &rewriter, mlir::Location loc, Value input, int64_t firstDimToCollapse)
Creates a memref.collapse_shape collapsing all inner dimensions of the input starting at firstDimToCo...
#define rem(a, b)
static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context)
Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:116
FloatType getF32Type()
Definition Builders.cpp:51
FloatType getF8E5M2Type()
Definition Builders.cpp:43
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:75
FloatType getBF16Type()
Definition Builders.cpp:45
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:59
FloatType getF8E4M3FNType()
Definition Builders.cpp:41
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
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:571
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
This class represents a single result from folding an operation.
This class represents an operand of an operation.
Definition Value.h:254
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Definition Value.cpp:226
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Value getOperand(unsigned idx)
Definition Operation.h:375
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:432
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
unsigned getNumOperands()
Definition Operation.h:371
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition Operation.h:255
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.
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
bool isF8E5M2() const
Definition Types.cpp:45
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
Definition Types.cpp:66
bool isF8E4M3FN() const
Definition Types.cpp:44
bool isBF16() const
Definition Types.cpp:37
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_iterator user_begin() const
Definition Value.h:216
unsigned getNumUses() const
This method computes the number of uses of this Value.
Definition Value.cpp:52
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
use_iterator use_begin() const
Definition Value.h:184
Specialization of arith.constant op that returns an integer of index type.
Definition Arith.h:114
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:398
Operation * getOwner() const
Return the owner of this operand.
Definition UseDefLists.h:38
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
mlir::x86::AMXTileType TileType
Definition X86Dialect.h:40
bool isInVnniLayout(Operation *op, llvm::ArrayRef< AffineMap > indexingMaps, std::optional< unsigned > blockingFactor=std::nullopt)
Definition X86Utils.cpp:42
Value contractionUsersAfterYield(Value v)
Definition X86Utils.cpp:149
Operation * traceToVectorReadLikeParentOperation(Value v)
Definition X86Utils.cpp:173
bool validatePairVectorContract(vector::ContractionOp contractOp, vector::ContractionOp pairContOp, bool rhsHasMultipleNonUnitDims, int64_t nonUnitDimValue)
Definition X86Utils.cpp:386
void populateVectorContractToAMXDotProductPatterns(RewritePatternSet &patterns)
Include the generated interface declarations.
OpFoldResult getAsIndexOpFoldResult(MLIRContext *ctx, int64_t val)
Convert int64_t to integer attributes of index type and return them as OpFoldResult.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition Utils.cpp:114
SmallVector< int64_t, 2 > ReassociationIndices
Definition Utils.h:27
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.