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.
32static Value contractionUsersAfterYield(Value v) {
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(
496 OpBuilder &rewriter, Location loc, Value ivInnerLoop, Value ivOuterLoop,
497 bool isInnerLoopUBHasOddQuot, bool isInnerLoopUBLarger, bool pack,
498 unsigned int blockingFactor) {
499
500 Value c2 = arith::ConstantIndexOp::create(rewriter, loc, 2);
501 Value packOffset =
502 arith::ConstantIndexOp::create(rewriter, loc, (16 * blockingFactor));
503
504 Value quotientInnerLoop =
505 arith::DivUIOp::create(rewriter, loc, ivInnerLoop, packOffset);
506 Value remInnerLoop = arith::RemUIOp::create(
507 rewriter, loc, rewriter.getIndexType(), quotientInnerLoop, c2);
508
509 if (!isInnerLoopUBLarger && !pack) {
510 remInnerLoop = arith::RemUIOp::create(
511 rewriter, loc, rewriter.getIndexType(), ivOuterLoop, c2);
512 }
513
514 if (isInnerLoopUBHasOddQuot) {
515 auto remOuterLoop = arith::RemUIOp::create(
516 rewriter, loc, rewriter.getIndexType(), ivOuterLoop, c2);
517 auto remAdd = arith::AddIOp::create(rewriter, loc, rewriter.getIndexType(),
518 remInnerLoop, remOuterLoop);
519 remInnerLoop = arith::RemUIOp::create(rewriter, loc,
520 rewriter.getIndexType(), remAdd, c2);
521 }
522
523 return remInnerLoop;
524}
525
526static scf::ForOp
527createLoops(OpBuilder &rewriter, Location loc, Value lowerBound,
528 Value upperBound, Value step, SmallVector<Value> loopItrArgs,
529 Type ipType, Type opType, unsigned int blockingFactor, bool isVnni,
530 Operation *vectorOpLhs, Operation *vectorOpRhs,
531 vector::ContractionOp contractOp, scf::ForOp outerLoop,
532 scf::ForOp innerLoop, SmallVector<vector::ContractionOp> ops,
533 Value ivOuterLoop, Value packedBuffer, bool pack,
534 arith::ConstantIndexOp innerLoopIndex, bool isInnerLoopUBLarger,
535 bool isInnerLoopUBHasOddQuot) {
536
537 Value c0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
538 Value c1 = arith::ConstantIndexOp::create(rewriter, loc, 1);
539 Value c2 = arith::ConstantIndexOp::create(rewriter, loc, 2);
540
541 int64_t offset = 16 * blockingFactor;
542 if (auto cst = step.getDefiningOp<arith::ConstantIndexOp>())
543 offset = cst.value();
544
545 auto newLoop = scf::ForOp::create(
546 rewriter, loc, lowerBound, upperBound, step, loopItrArgs,
547 [&](OpBuilder &rewriterNewInnerLoop, Location locNewInnerLoop,
548 Value ivNewInnerLoop, ValueRange iterArgsNewInnerLoop) {
549 IRMapping mapping;
550 if (outerLoop)
551 mapping.map(vectorOpLhs->getOperand(
552 getIndexPosition(contractOp.getLhs(), outerLoop) + 1),
553 ivOuterLoop);
554
555 mapping.map(vectorOpLhs->getOperand(
556 getIndexPosition(contractOp.getLhs(), innerLoop) + 1),
557 ivNewInnerLoop);
558 auto lhsClone = rewriterNewInnerLoop.clone(*vectorOpLhs, mapping);
559
560 Value indxToStoreInBuffer = c0;
561 Value indxToLoadFromBuffer = c0;
562 if (!isVnni) {
563 if (outerLoop) {
564 if (innerLoopIndex.value() == 0) {
565 if (pack) {
566 ivNewInnerLoop = c0;
567 ivOuterLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
568 c1, ivOuterLoop);
569
570 if (!isInnerLoopUBLarger || isInnerLoopUBHasOddQuot) {
571 indxToStoreInBuffer = arith::RemUIOp::create(
572 rewriter, locNewInnerLoop, rewriter.getIndexType(),
573 ivOuterLoop, c2);
574 }
575
576 Value indxToLoadFromMatB = arith::AddIOp::create(
577 rewriter, loc, indxToStoreInBuffer, c1);
578 indxToLoadFromBuffer = arith::RemUIOp::create(
579 rewriter, loc, rewriter.getIndexType(), indxToLoadFromMatB,
580 c2);
581 }
582
583 } else {
585 rewriter, locNewInnerLoop, offset);
586 ivNewInnerLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
587 nLoadIndx, ivNewInnerLoop);
588 indxToStoreInBuffer = getIndxToLoadStoreFromPckBuffer(
589 rewriter, loc, ivNewInnerLoop, ivOuterLoop,
590 isInnerLoopUBHasOddQuot, isInnerLoopUBLarger, pack,
591 blockingFactor);
592 Value indxToLoadFromMatB =
593 arith::AddIOp::create(rewriter, loc, indxToStoreInBuffer, c1);
594 indxToLoadFromBuffer =
595 arith::RemUIOp::create(rewriter, loc, rewriter.getIndexType(),
596 indxToLoadFromMatB, c2);
597 }
598 } else {
599 if (pack) {
601 rewriter, locNewInnerLoop, offset);
602 ivNewInnerLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
603 nLoadIndx, ivNewInnerLoop);
604 Value quotient_K = arith::DivUIOp::create(
605 rewriter, loc, ivNewInnerLoop, nLoadIndx);
606 indxToStoreInBuffer = arith::RemUIOp::create(
607 rewriter, loc, rewriter.getIndexType(), quotient_K, c2);
608
609 Value indxToLoadFromMatB =
610 arith::AddIOp::create(rewriter, loc, indxToStoreInBuffer, c1);
611 indxToLoadFromBuffer =
612 arith::RemUIOp::create(rewriter, loc, rewriter.getIndexType(),
613 indxToLoadFromMatB, c2);
614 }
615 }
616 }
617 IRMapping rhsMapping;
618
619 Value matB;
620 Operation *rhsOp = vectorOpRhs;
621
622 // Clone for the subview type operations
623 if (rhsOp->getNumOperands() > 0) {
624
625 if (outerLoop) {
626 int64_t outerPos = getIndexPosition(contractOp.getRhs(), outerLoop);
627
628 if (outerPos >= 0) {
629 unsigned operandIdx = static_cast<unsigned>(outerPos + 1);
630
631 if (operandIdx < rhsOp->getNumOperands())
632 rhsMapping.map(rhsOp->getOperand(operandIdx), ivOuterLoop);
633 }
634 }
635
636 int64_t innerPos = getIndexPosition(contractOp.getRhs(), innerLoop);
637
638 if (innerPos >= 0) {
639 unsigned operandIdx = static_cast<unsigned>(innerPos + 1);
640
641 if (operandIdx < rhsOp->getNumOperands())
642 rhsMapping.map(rhsOp->getOperand(operandIdx), ivNewInnerLoop);
643 }
644
645 auto rhsClone = rewriterNewInnerLoop.clone(*rhsOp, rhsMapping);
646 matB = rhsClone->getResult(0);
647
648 } else {
649 // The mat B is of kind 'memref.get_global @__constant'
650 matB = rhsOp->getResult(0);
651 }
652
653 if (!isVnni) {
654 if (outerLoop) {
655 if (!pack) {
657 rewriter, locNewInnerLoop, offset);
658 matB = Value();
659 indxToLoadFromBuffer = c0;
660 indxToLoadFromBuffer = getIndxToLoadStoreFromPckBuffer(
661 rewriter, loc, nLoadIndx, ivOuterLoop,
662 isInnerLoopUBHasOddQuot, isInnerLoopUBLarger, pack,
663 blockingFactor);
664 }
665 } else {
666 if (!pack) {
668 rewriter, locNewInnerLoop, offset);
669 matB = Value();
670 Value quotient_K = arith::DivUIOp::create(
671 rewriter, loc, ivNewInnerLoop, nLoadIndx);
672 indxToLoadFromBuffer = arith::RemUIOp::create(
673 rewriter, loc, rewriter.getIndexType(), quotient_K, c2);
674 }
675 }
676 }
677 // compute tiled dot-product
678 SmallVector<Value> accumulators = createTiledDp(
679 rewriter, locNewInnerLoop, ops, lhsClone->getResult(0), matB,
680 ipType, opType, iterArgsNewInnerLoop, blockingFactor, isVnni,
681 packedBuffer, pack, indxToStoreInBuffer, indxToLoadFromBuffer);
682
683 scf::YieldOp::create(rewriterNewInnerLoop, locNewInnerLoop,
684 accumulators);
685 });
686
687 return newLoop;
688}
689
690// Implements tiled dot-product operation for a vector.contract operation or a
691// sequence of vector.contracts inside the reduction loops.
692//
693// For example:
694// Case 1: register blocked vector.contract with prepacked input
695// ```
696// vector.transfer_read %arg0 {{.}*} : memref<16x32x4xi8>, vector<16x16x4xi8>
697// vector.transfer_read %arg1 {{.}*} : memref<16x32x4xi8>, vector<16x16x4xi8>
698// vector.contract <16x16x4xi8>, <16x16x4xi8> into <16x16xi32>
699// vector.transfer_write arg2 {{.}*} : vector<16x16xi32>, memref<32x32xi32>
700// ```
701// to
702// ```
703// amx.tile_load %arg0 {{.}*} : memref<16x32x4xi8> into !amx.tile<16x64xi8>
704// amx.tile_load %arg1 {{.}*} : memref<16x32x4xi8> into !amx.tile<16x64xi8>
705// amx.tile_muli !amx.tile<16x64xi8> -> !amx.tile<16x16xi32>
706// amx.tile_store %arg2{{.}*} : memref<32x32xi32>, !amx.tile<16x16xi32>
707// ```
708//
709//
710// Case2: vector.contract with register blocked
711//
712// Output IR with online packing (with s/w pipeline advantage):
713// s/w pipeline: load, pack to VNNI, and store the B sub matrix
714// of the 0th batch-reduce and K iteration.
715// scf.for (0 to 31) {
716// - load 0th and 1st vector<32xbf16>, pack into VNNI, store the
717// first shuffle in 0th and 2nd shuffle in 16th index of the
718// buffer.
719// }
720// scf.for (0 to br-2) { batch-reduce loop
721// scf.for (0 to k-2) { K loop
722// - load A matrix
723// - scf.loop for s/w pipeline: load, pack to VNNI, and store the B sub
724// matrix for the next K loop iteration (c) load VNNI pack B matrix of K
725// iteration from the buffer (d) compute the tiled dot-product
726// }
727// Last iteration of the the K Loop (k-1) {
728// - load A matrix
729// - scf.loop for s/w pipeline: load, pack to VNNI, and store the B sub
730// matrix for the next batch-reduce + K loop iteration (c) load VNNI pack B
731// matrix of K iteration from the buffer (d) compute the tiled dot-product
732// }
733// }
734// Last iteration of the batch-reduce loop (br-1) {
735// scf.for (0 to k-2) { K loop
736// - load A matrix
737// - scf.loop for s/w pipeline: load, pack to VNNI, and store the B sub
738// matrix for the next K loop iteration (c) load VNNI pack B matrix of K
739// iteration from the buffer (d) compute the tiled dot-product
740// }
741// Last iteration of the the K Loop (k-1) {
742// - load A matrix
743// - load VNNI pack B matrix of K iteration from the buffer
744// - compute the tiled dot-product
745// }
746// }
747//
748// scf.for (0 to M)
749// scf.for (0 to N)
750// - Load the ith and i+1th acc
751// - Shuffle them as we packed using vpunpack
752// - Load C matrix and do arith.add with the shuffle
753// - Store back into C matrix
754struct VectorContractToAMXDotProduct
755 : public OpRewritePattern<vector::ContractionOp> {
756 using OpRewritePattern<vector::ContractionOp>::OpRewritePattern;
757
758 LogicalResult matchAndRewrite(vector::ContractionOp contractOp,
759 PatternRewriter &rewriter) const override {
760
761 if (contractOp.getKind() != vector::CombiningKind::ADD)
762 return rewriter.notifyMatchFailure(contractOp,
763 "Expects add combining kind.");
764
765 unsigned int blockingFactor =
766 contractOp.getLhsType().getElementType().isBF16() ? 2 : 4;
767 bool isVnni =
768 isInVnniLayout(contractOp.getOperation(),
769 contractOp.getIndexingMapsArray(), blockingFactor);
770
771 VectorType lhsTy = contractOp.getLhsType();
772 if (!lhsTy.getElementType().isBF16() &&
773 !lhsTy.getElementType().isSignlessInteger(8) &&
774 !lhsTy.getElementType().isF8E4M3FN() &&
775 !lhsTy.getElementType().isF8E5M2())
776 return rewriter.notifyMatchFailure(
777 contractOp, "Only BF16/Int8/F8 lowering is supported.");
778
779 if (lhsTy.getElementType() != contractOp.getRhsType().getElementType())
780 return rewriter.notifyMatchFailure(
781 contractOp, "Contraction should have same lhs and rhs type.");
782
783 VectorType accTy = dyn_cast<VectorType>(contractOp.getAccType());
784 if (!accTy)
785 return rewriter.notifyMatchFailure(contractOp, "Wrong accmulator type.");
786
787 if (((lhsTy.getElementType().isBF16() ||
788 lhsTy.getElementType().isF8E4M3FN() ||
789 lhsTy.getElementType().isF8E5M2()) &&
790 !accTy.getElementType().isF32()) ||
791 (lhsTy.getElementType().isSignlessInteger(8) &&
792 !accTy.getElementType().isSignlessInteger(32)))
793 return rewriter.notifyMatchFailure(contractOp,
794 "Only F32 for BF16 or Int32 for Int8 "
795 "accumulation type is supported.");
796
797 Operation *accReadOp =
798 traceToVectorReadLikeParentOperation(contractOp.getAcc());
799
800 Operation *resultWriteOp =
801 traceToVectorWriteLikeUserOperation(contractOp.getResult());
802
803 if (!accReadOp || !resultWriteOp)
804 return rewriter.notifyMatchFailure(
805 contractOp, "The ACC operand of the vector.contract should be a "
806 "transfer_read or a load. And, the result should be "
807 "stored using transfer_write or store.");
808
809 Type ipType = rewriter.getBF16Type();
810 Type opType = rewriter.getF32Type();
811
812 if (lhsTy.getElementType().isSignlessInteger(8)) {
813 ipType = rewriter.getIntegerType(8);
814 opType = rewriter.getIntegerType(32);
815 }
816
817 if (lhsTy.getElementType().isF8E4M3FN())
818 ipType = rewriter.getF8E4M3FNType();
819
820 if (lhsTy.getElementType().isF8E5M2())
821 ipType = rewriter.getF8E5M2Type();
822
823 if (accReadOp->getBlock() == contractOp->getBlock() &&
824 resultWriteOp->getBlock() != contractOp->getBlock())
825 return rewriter.notifyMatchFailure(
826 contractOp, "The accumulator store is in different block.");
827
828 if (accReadOp->getBlock() != contractOp->getBlock() &&
829 resultWriteOp->getBlock() == contractOp->getBlock())
830 return rewriter.notifyMatchFailure(
831 contractOp, "The accumulator read is in different block.");
832
833 if (!(isReadSrcMemref(contractOp.getLhs()) &&
834 isReadSrcMemref(contractOp.getRhs())))
835 return rewriter.notifyMatchFailure(
836 contractOp, "The LHS or RHS src is not a MemRef type.");
837
838 unsigned int dimValue = blockingFactor;
839 if (!isVnni)
840 dimValue = 16 * blockingFactor;
841
842 // Case 1: For just one VC rewrite. Where all accumulator read/write
843 // within the same block.
844 if (accReadOp->getBlock() == contractOp->getBlock() &&
845 resultWriteOp->getBlock() == contractOp->getBlock()) {
846
847 if (!isReadSrcMemref(contractOp.getAcc()))
848 return rewriter.notifyMatchFailure(contractOp,
849 "The ACC src is not a MemRef type.");
850
851 bool collapse = false;
852 if (isVnni)
853 collapse = true;
854
855 LogicalResult validate = validateContractOps(
856 rewriter, contractOp, dimValue, Value(), Value(), false);
857
858 if (failed(validate))
859 return rewriter.notifyMatchFailure(
860 contractOp, "The contract operation doesn't satisfy the operands "
861 "dimensions. M, N, and vnni dims are 16, 16, and 2/4. "
862 "The rest dims should be 1. Op should have one user.");
863
864 Location loc = contractOp.getLoc();
865
866 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
867 contractOp.getLhs(), collapse);
868 if (failed(srcIndxLhs))
869 return rewriter.notifyMatchFailure(contractOp,
870 "Failed to get the LHS src.");
871 auto [srcBuffLhs, indicesLhs] = *srcIndxLhs;
872
873 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
874 contractOp.getRhs(), collapse);
875 if (failed(srcIndxRhs))
876 return rewriter.notifyMatchFailure(contractOp,
877 "Failed to get the RHS src.");
878 auto rhsSrc = *srcIndxRhs;
879 auto srcBuffRhs = rhsSrc.first;
880 auto indicesRhs = rhsSrc.second;
881
882 auto srcIndxAcc = getSrcIndxValue(rewriter, contractOp.getLoc(),
883 contractOp.getAcc(), false);
884 if (failed(srcIndxAcc))
885 return rewriter.notifyMatchFailure(contractOp,
886 "Failed to get the ACC src.");
887 auto [srcBuffAcc, indicesAcc] = *srcIndxAcc;
888
889 Value c0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
890
891 // amx.tile_loads
892 auto tileType = amx::TileType::get({16, (16 * blockingFactor)}, ipType);
893 auto loadLhs = amx::TileLoadOp::create(rewriter, loc, tileType,
894 srcBuffLhs, indicesLhs);
895
896 // Create the subview and then load.
897 amx::TileLoadOp loadRhs;
898 if (!isVnni) {
899 VectorType vecTy;
900 SmallVector<OpFoldResult> indexVals;
901 llvm::TypeSwitch<Operation *>(contractOp.getRhs().getDefiningOp())
902 .Case<TransferReadOp, LoadOp>([&](auto readOp) {
903 indexVals = SmallVector<OpFoldResult>(readOp.getIndices().begin(),
904 readOp.getIndices().end());
905 vecTy = readOp.getType();
906 });
907 auto one = rewriter.getIndexAttr(1);
908 SmallVector<OpFoldResult> strides(indexVals.size(), one);
909 SmallVector<OpFoldResult> sizes = getAsIndexOpFoldResult(
910 contractOp.getRhs().getDefiningOp()->getContext(),
911 vecTy.getShape());
912 auto subview = memref::SubViewOp::create(rewriter, loc, srcBuffRhs,
913 indexVals, sizes, strides);
914 auto bufferType = MemRefType::get({16, (16 * blockingFactor)}, ipType);
915 auto packedBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
916
917 // create a loop that does online packing.
918 Value step =
919 arith::ConstantIndexOp::create(rewriter, loc, blockingFactor);
920 Value uBound = arith::ConstantIndexOp::create(rewriter, loc,
921 (blockingFactor * 16));
922 Value nextLoadIndx =
923 arith::ConstantIndexOp::create(rewriter, loc, (blockingFactor / 2));
924 Value nextStoreIndx = arith::ConstantIndexOp::create(
925 rewriter, loc, 16 * (blockingFactor / 2));
926
927 scf::ForOp::create(
928 rewriter, loc, c0, uBound, step, ValueRange{},
929 [&](OpBuilder &nestedBuilder, Location loc, Value iv,
930 ValueRange iterArgs) {
931 Value i1_load =
932 arith::AddIOp::create(rewriter, loc, nextLoadIndx, iv);
933
934 indicesRhs[indicesRhs.size() - 2] = iv;
935 indicesRhs[indicesRhs.size() - 1] = c0;
936 ValueRange range1(indicesRhs);
937 auto vec1 = vector::LoadOp::create(
938 rewriter, loc,
939 VectorType::get(16 * (blockingFactor / 2), ipType), subview,
940 range1);
941
942 indicesRhs[indicesRhs.size() - 2] = i1_load;
943 ValueRange range2(indicesRhs);
944 auto vec2 = vector::LoadOp::create(
945 rewriter, loc,
946 VectorType::get(16 * (blockingFactor / 2), ipType), subview,
947 range2);
948
949 vector::ShuffleOp shuffle1;
950 vector::ShuffleOp shuffle2;
951
952 if (blockingFactor == 2) {
953
954 shuffle1 = vector::ShuffleOp::create(
955 rewriter, loc, VectorType::get({16}, ipType), vec1, vec2,
956 ArrayRef<int64_t>{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21,
957 6, 22, 7, 23});
958
959 shuffle2 = vector::ShuffleOp::create(
960 rewriter, loc, VectorType::get({16}, ipType), vec1, vec2,
961 ArrayRef<int64_t>{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13,
962 29, 14, 30, 15, 31});
963 }
964
965 if (blockingFactor == 4) {
966 shuffle1 = vector::ShuffleOp::create(
967 rewriter, loc, VectorType::get({32}, ipType), vec1, vec2,
968 ArrayRef<int64_t>{0, 16, 32, 48, 1, 17, 33, 49,
969 2, 18, 34, 50, 3, 19, 35, 51,
970 4, 20, 36, 52, 5, 21, 37, 53,
971 6, 22, 38, 54, 7, 23, 39, 55});
972
973 shuffle2 = vector::ShuffleOp::create(
974 rewriter, loc, VectorType::get({32}, ipType), vec1, vec2,
975 ArrayRef<int64_t>{8, 24, 40, 56, 9, 25, 41, 57,
976 10, 26, 42, 58, 11, 27, 43, 59,
977 12, 28, 44, 60, 13, 29, 45, 61,
978 14, 30, 46, 62, 15, 31, 47, 63});
979 }
980
981 auto rem = arith::DivUIOp::create(
982 rewriter, loc, rewriter.getIndexType(), iv, step);
983
984 vector::StoreOp::create(rewriter, loc, shuffle1, packedBuffer,
985 ValueRange{rem, c0});
986 vector::StoreOp::create(rewriter, loc, shuffle2, packedBuffer,
987 ValueRange{rem, nextStoreIndx});
988
989 scf::YieldOp::create(nestedBuilder, loc);
990 });
991 loadRhs = amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
992 ValueRange{c0, c0});
993 } else {
994
995 loadRhs = amx::TileLoadOp::create(rewriter, loc, tileType, srcBuffRhs,
996 indicesRhs);
997 }
998
999 auto tileTypeAcc = amx::TileType::get({16, 16}, opType);
1000 auto loadAcc = amx::TileLoadOp::create(rewriter, loc, tileTypeAcc,
1001 srcBuffAcc, indicesAcc);
1002
1003 // Tiled dot-product.
1004 Value dp;
1005 if (ipType.isBF16() || ipType.isF8E5M2() || ipType.isF8E4M3FN())
1006 dp = amx::TileMulFOp::create(rewriter, loc, tileTypeAcc, loadLhs,
1007 loadRhs, loadAcc);
1008
1009 if (ipType.isSignlessInteger(8))
1010 dp = amx::TileMulIOp::create(rewriter, loc, tileTypeAcc, loadLhs,
1011 loadRhs, loadAcc);
1012
1013 auto bufferType = MemRefType::get({16, 16}, opType);
1014 auto resultBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
1015
1016 amx::TileStoreOp::create(rewriter, loc, resultBuffer, ValueRange{c0, c0},
1017 dp);
1018
1019 auto vectorType = mlir::VectorType::get({16, 16}, opType);
1020 int64_t srcRank =
1021 (dyn_cast<ShapedType>(resultBuffer.getType())).getRank();
1022 Value padding = ub::PoisonOp::create(rewriter, loc, opType);
1023 auto map = AffineMap::getMinorIdentityMap(srcRank, vectorType.getRank(),
1024 rewriter.getContext());
1025 SmallVector<bool> inBounds(vectorType.getRank(), true);
1026
1027 Value vecRow = vector::TransferReadOp::create(
1028 rewriter, loc, vectorType, resultBuffer, ValueRange{c0, c0}, padding,
1029 map, inBounds);
1030
1031 Value resultOp = contractionUsersAfterYield(contractOp.getResult());
1032 if (auto vecType = llvm::dyn_cast<VectorType>(resultOp.getType()))
1033 vecRow = vector::ShapeCastOp::create(rewriter, loc, vecType, vecRow);
1034
1035 rewriter.replaceAllUsesWith(resultOp, vecRow);
1036 return success();
1037 }
1038
1039 // Case 2: The acc are passed as iter args through the reduction loop.
1040 // We support, reduction loop depth until 2. TODO: Support for n-depth
1041 // reduction loop.
1042 // TODOs: Re-factor 2a and 2b.
1043 SmallVector<scf::ForOp> loopLists;
1044 Operation *current = contractOp;
1045 while (true) {
1046 Operation *parent = current->getParentOfType<scf::ForOp>();
1047
1048 if (!parent)
1049 return rewriter.notifyMatchFailure(
1050 contractOp,
1051 "Accumulator read and contract op not within scf.for op");
1052
1053 loopLists.push_back(dyn_cast<scf::ForOp>(parent));
1054
1055 if (accReadOp->getBlock() == parent->getBlock()) {
1056 break;
1057 }
1058
1059 current = parent;
1060 }
1061 if (loopLists.size() > 2 || loopLists.size() == 0)
1062 return rewriter.notifyMatchFailure(
1063 contractOp, "Rewrite is supported until reduction loop depth of 2.");
1064
1065 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1066 contractOp.getLhs(), false);
1067 if (failed(srcIndxLhs))
1068 return rewriter.notifyMatchFailure(contractOp,
1069 "Failed to get the LHS src.");
1070 auto [srcBuffLhs, indicesLhs] = *srcIndxLhs;
1071
1072 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1073 contractOp.getRhs(), false);
1074 if (failed(srcIndxRhs))
1075 return rewriter.notifyMatchFailure(contractOp,
1076 "Failed to get the RHS src.");
1077 auto [srcBuffRhs, indicesRhs] = *srcIndxRhs;
1078 Operation *vectorOpLhs;
1079 llvm::TypeSwitch<Operation *>(contractOp.getLhs().getDefiningOp())
1080 .Case<TransferReadOp, LoadOp>([&](auto readOp) {
1081 vectorOpLhs = readOp.getBase().getDefiningOp();
1082 });
1083
1084 Operation *vectorOpRhs;
1085 llvm::TypeSwitch<Operation *>(contractOp.getRhs().getDefiningOp())
1086 .Case<TransferReadOp, LoadOp>([&](auto readOp) {
1087 vectorOpRhs = readOp.getBase().getDefiningOp();
1088 });
1089
1090 if (!vectorOpLhs || !vectorOpRhs)
1091 return rewriter.notifyMatchFailure(
1092 contractOp, "Failed to find LHS or RHS read source operation");
1093
1094 // Retrive all the contaction operation within the loop.
1095 SmallVector<vector::ContractionOp> ops;
1096 for (mlir::Operation &op : loopLists[0].getBody()->getOperations()) {
1097
1098 if (auto contract = llvm::dyn_cast<mlir::vector::ContractionOp>(op)) {
1099
1100 LogicalResult validate = validateContractOps(
1101 rewriter, contract, dimValue, srcBuffLhs, srcBuffRhs, true);
1102
1103 if (failed(validate))
1104 return rewriter.notifyMatchFailure(
1105 contractOp,
1106 "The associated contract operations doesn't satisfy "
1107 "the re-write conditions either the dimensions are "
1108 "wrong or MemRef source are different or many users.");
1109
1110 ops.push_back(contract);
1111 }
1112 }
1113
1114 if (!isVnni) {
1115 unsigned int pairCount = 0;
1116 for (size_t j = 0; j < ops.size(); j++) {
1117 for (size_t i = j; i < ops.size(); i++) {
1118 if (i != j && validatePairVectorContract(ops[j], ops[i], true, 16))
1119 pairCount = pairCount + 2;
1120 }
1121 }
1122
1123 if (pairCount != ops.size())
1124 return rewriter.notifyMatchFailure(
1125 contractOp, "Coudn't find the pair vector contract ");
1126 }
1127
1128 scf::ForOp innerLoop;
1129 scf::ForOp outerLoop;
1130
1131 scf::ForOp newLoop;
1132 // Case 2a: Reduction loop depth is 2.
1133 if (loopLists.size() == 2) {
1134 outerLoop = loopLists[1];
1135 innerLoop = loopLists[0];
1136
1137 LogicalResult validateOuterLoopStep =
1138 validateLoopStep(rewriter, outerLoop.getStep(), 1);
1139 if (failed(validateOuterLoopStep))
1140 return rewriter.notifyMatchFailure(contractOp, "Invalid loop step.");
1141
1142 int64_t stepValue = 16;
1143 if (!isVnni)
1144 stepValue = stepValue * blockingFactor;
1145 LogicalResult validateInnerLoopStep =
1146 validateLoopStep(rewriter, innerLoop.getStep(), stepValue);
1147 if (failed(validateInnerLoopStep))
1148 return rewriter.notifyMatchFailure(
1149 contractOp, "Invalid loop step. The step should be 32 for BF16 and "
1150 "64 for Int8/F8.");
1151
1152 SmallVector<Value> loopItrArgs = createTileZeros(
1153 rewriter, outerLoop.getLoc(), opType, outerLoop, ops.size());
1154
1155 if (isVnni) {
1156 newLoop = scf::ForOp::create(
1157 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1158 outerLoop.getUpperBound(), outerLoop.getStep(), loopItrArgs,
1159 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1160 Value ivOuterLoop, ValueRange iterArgsOuterLoop) {
1161 auto newInnerLoop = createLoops(
1162 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1163 innerLoop.getUpperBound(), innerLoop.getStep(),
1164 iterArgsOuterLoop, ipType, opType, blockingFactor, isVnni,
1165 vectorOpLhs, vectorOpRhs, contractOp, outerLoop, innerLoop,
1166 ops, ivOuterLoop, nullptr, true, nullptr, false, false);
1167
1168 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1169 newInnerLoop.getResults());
1170 });
1171
1172 } else {
1173
1174 bool isInnerLoopUBLarger = false;
1175 bool isInnerLoopUBHasOddQuot = false;
1176
1177 int64_t ubVal = 16 * blockingFactor;
1178 mlir::Value ub = innerLoop.getUpperBound();
1179 if (auto constOp = ub.getDefiningOp<mlir::arith::ConstantOp>()) {
1180 if (auto intAttr =
1181 llvm::dyn_cast<mlir::IntegerAttr>(constOp.getValue())) {
1182 ubVal = intAttr.getInt();
1183 }
1184 }
1185
1186 isInnerLoopUBLarger = ubVal > 16 * blockingFactor;
1187 isInnerLoopUBHasOddQuot =
1188 (((ubVal / (16 * blockingFactor)) % 2) == 1) && isInnerLoopUBLarger;
1189
1190 rewriter.setInsertionPoint(outerLoop);
1191
1192 auto c0 =
1193 arith::ConstantIndexOp::create(rewriter, outerLoop.getLoc(), 0);
1194 auto c1 =
1195 arith::ConstantIndexOp::create(rewriter, outerLoop.getLoc(), 1);
1196 auto spillLoopBound = arith::ConstantIndexOp::create(
1197 rewriter, outerLoop.getLoc(), 16 * blockingFactor);
1198
1199 Value spillOuterLoop = arith::SubIOp::create(
1200 rewriter, outerLoop.getLoc(), outerLoop.getUpperBound(), c1);
1201 Value spillInnerLoop =
1202 arith::SubIOp::create(rewriter, innerLoop.getLoc(),
1203 innerLoop.getUpperBound(), spillLoopBound);
1204 auto bufferType =
1205 MemRefType::get({2, 32, (blockingFactor * 16)}, ipType);
1206 auto packedBuffer =
1207 memref::AllocaOp::create(rewriter, outerLoop.getLoc(), bufferType);
1208
1209 // First Shuffling outside the reduction loops
1210 IRMapping rhsMapping;
1211 rhsMapping.map(
1212 vectorOpRhs->getOperand(
1213 getIndexPosition(contractOp.getRhs(), outerLoop) + 1),
1214 outerLoop.getLowerBound());
1215 rhsMapping.map(
1216 vectorOpRhs->getOperand(
1217 getIndexPosition(contractOp.getRhs(), innerLoop) + 1),
1218 innerLoop.getLowerBound());
1219 auto rhsClone = rewriter.clone(*vectorOpRhs, rhsMapping);
1220
1221 Value quotient_batch = arith::DivUIOp::create(
1222 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1223 outerLoop.getStep());
1224 Value quotient_k = arith::DivUIOp::create(rewriter, outerLoop.getLoc(),
1225 innerLoop.getLowerBound(),
1226 innerLoop.getStep());
1227
1228 Value quotient_add = arith::AddIOp::create(rewriter, outerLoop.getLoc(),
1229 quotient_batch, quotient_k);
1230 Value c2 =
1231 arith::ConstantIndexOp::create(rewriter, outerLoop.getLoc(), 2);
1232 Value rem = arith::RemUIOp::create(rewriter, outerLoop.getLoc(),
1233 quotient_add, c2);
1234
1235 performShuffle(rewriter, outerLoop.getLoc(), rhsClone->getResult(0),
1236 ipType, blockingFactor, packedBuffer, rem);
1237
1238 // First Set of Loops
1239 auto newLoopNonSpill = scf::ForOp::create(
1240 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1241 spillOuterLoop, outerLoop.getStep(), loopItrArgs,
1242 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1243 Value ivOuterLoop, ValueRange iterArgsOuterLoop) {
1244 auto newInnerLoop1 = createLoops(
1245 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1246 spillInnerLoop, innerLoop.getStep(), iterArgsOuterLoop,
1247 ipType, opType, blockingFactor, isVnni, vectorOpLhs,
1248 vectorOpRhs, contractOp, outerLoop, innerLoop, ops,
1249 ivOuterLoop, packedBuffer, true, spillLoopBound,
1250 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1251
1252 auto newInnerLoop = createLoops(
1253 rewriter, innerLoop.getLoc(), spillInnerLoop,
1254 innerLoop.getUpperBound(), innerLoop.getStep(),
1255 newInnerLoop1.getResults(), ipType, opType, blockingFactor,
1256 isVnni, vectorOpLhs, vectorOpRhs, contractOp, outerLoop,
1257 innerLoop, ops, ivOuterLoop, packedBuffer, true, c0,
1258 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1259
1260 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1261 newInnerLoop.getResults());
1262 });
1263
1264 // Last set of Loops
1265 newLoop = scf::ForOp::create(
1266 rewriter, outerLoop.getLoc(), spillOuterLoop,
1267 outerLoop.getUpperBound(), outerLoop.getStep(),
1268 newLoopNonSpill.getResults(),
1269 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1270 Value ivOuterLoop, ValueRange iterArgsOuterLoop) {
1271 auto newInnerLoop1 = createLoops(
1272 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1273 spillInnerLoop, innerLoop.getStep(), iterArgsOuterLoop,
1274 ipType, opType, blockingFactor, isVnni, vectorOpLhs,
1275 vectorOpRhs, contractOp, outerLoop, innerLoop, ops,
1276 ivOuterLoop, packedBuffer, true, spillLoopBound,
1277 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1278
1279 auto newInnerLoop = createLoops(
1280 rewriter, innerLoop.getLoc(), spillInnerLoop,
1281 innerLoop.getUpperBound(), innerLoop.getStep(),
1282 newInnerLoop1.getResults(), ipType, opType, blockingFactor,
1283 isVnni, vectorOpLhs, vectorOpRhs, contractOp, outerLoop,
1284 innerLoop, ops, ivOuterLoop, packedBuffer, false, c0,
1285 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1286
1287 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1288 newInnerLoop.getResults());
1289 });
1290 }
1291 }
1292
1293 // Case 2b: Reduction loop depth is 1.
1294 if (loopLists.size() == 1) {
1295
1296 innerLoop = loopLists[0];
1297 int64_t stepValue = 16;
1298 if (!isVnni)
1299 stepValue = stepValue * blockingFactor;
1300
1301 LogicalResult validateInnerLoopStep =
1302 validateLoopStep(rewriter, innerLoop.getStep(), stepValue);
1303 if (failed(validateInnerLoopStep))
1304 return rewriter.notifyMatchFailure(
1305 contractOp,
1306 "Invalid loop step. The step should be 32 for BF16 and "
1307 "64 for Int8/F8 or 1 if it is rduction loop other than K.");
1308
1309 SmallVector<Value> loopItrArgs = createTileZeros(
1310 rewriter, innerLoop.getLoc(), opType, innerLoop, ops.size());
1311
1312 if (isVnni) {
1313 newLoop = createLoops(
1314 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1315 innerLoop.getUpperBound(), innerLoop.getStep(), loopItrArgs, ipType,
1316 opType, blockingFactor, isVnni, vectorOpLhs, vectorOpRhs,
1317 contractOp, nullptr, innerLoop, ops, nullptr, nullptr, true,
1318 nullptr, false, false);
1319
1320 } else {
1321
1322 bool isInnerLoopUBLarger = false;
1323 bool isInnerLoopUBHasOddQuot = false;
1324
1325 int64_t ubVal = 16 * blockingFactor;
1326 mlir::Value ub = innerLoop.getUpperBound();
1327 if (auto constOp = ub.getDefiningOp<mlir::arith::ConstantOp>()) {
1328 if (auto intAttr =
1329 llvm::dyn_cast<mlir::IntegerAttr>(constOp.getValue())) {
1330 ubVal = intAttr.getInt();
1331 }
1332 }
1333
1334 isInnerLoopUBLarger = ubVal > 16 * blockingFactor;
1335 isInnerLoopUBHasOddQuot =
1336 (((ubVal / (16 * blockingFactor)) % 2) == 1) && isInnerLoopUBLarger;
1337
1338 rewriter.setInsertionPoint(innerLoop);
1339
1340 auto c0 =
1341 arith::ConstantIndexOp::create(rewriter, innerLoop.getLoc(), 0);
1342 int64_t offset = 16 * blockingFactor;
1343 if (auto cst =
1344 innerLoop.getStep().getDefiningOp<arith::ConstantIndexOp>())
1345 offset = cst.value();
1346
1347 auto spillLoopBound = arith::ConstantIndexOp::create(
1348 rewriter, innerLoop.getLoc(), offset);
1349 Value spillInnerLoop =
1350 arith::SubIOp::create(rewriter, innerLoop.getLoc(),
1351 innerLoop.getUpperBound(), spillLoopBound);
1352
1353 auto bufferType =
1354 MemRefType::get({2, 32, (blockingFactor * 16)}, ipType);
1355 auto packedBuffer =
1356 memref::AllocaOp::create(rewriter, innerLoop.getLoc(), bufferType);
1357
1358 // First Shuffling outside the reduction loops
1359 IRMapping rhsMapping;
1360 rhsMapping.map(
1361 vectorOpRhs->getOperand(
1362 getIndexPosition(contractOp.getRhs(), innerLoop) + 1),
1363 innerLoop.getLowerBound());
1364 auto rhsClone = rewriter.clone(*vectorOpRhs, rhsMapping);
1365
1366 Value quotient_k = arith::DivUIOp::create(rewriter, innerLoop.getLoc(),
1367 innerLoop.getLowerBound(),
1368 innerLoop.getStep());
1369 Value c2 =
1370 arith::ConstantIndexOp::create(rewriter, innerLoop.getLoc(), 2);
1371 Value rem = arith::RemUIOp::create(rewriter, innerLoop.getLoc(),
1372 quotient_k, c2);
1373
1374 performShuffle(rewriter, innerLoop.getLoc(), rhsClone->getResult(0),
1375 ipType, blockingFactor, packedBuffer, rem);
1376
1377 auto newLoopNonSpill = createLoops(
1378 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1379 spillInnerLoop, innerLoop.getStep(), loopItrArgs, ipType, opType,
1380 blockingFactor, isVnni, vectorOpLhs, vectorOpRhs, contractOp,
1381 nullptr, innerLoop, ops, nullptr, packedBuffer, true,
1382 spillLoopBound, isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1383
1384 newLoop = createLoops(rewriter, innerLoop.getLoc(), spillInnerLoop,
1385 innerLoop.getUpperBound(), innerLoop.getStep(),
1386 newLoopNonSpill.getResults(), ipType, opType,
1387 blockingFactor, isVnni, vectorOpLhs, vectorOpRhs,
1388 contractOp, nullptr, innerLoop, ops, nullptr,
1389 packedBuffer, false, c0, isInnerLoopUBLarger,
1390 isInnerLoopUBHasOddQuot);
1391 }
1392
1393 // This helps the final store back to the acc uses the same code for
1394 // the both reduction loop depth 1 or 2.
1395 outerLoop = innerLoop;
1396 }
1397
1398 // Copy the amx tile accumulation results to a MemRef buffer, add the
1399 // initial accumulation value, and store back to the C-Matrix
1400 Location loc = outerLoop.getLoc();
1401 Value srcBuffAcc;
1402 SmallVector<Value> indicesAcc;
1403
1404 llvm::TypeSwitch<Operation *>(accReadOp).Case<TransferReadOp, LoadOp>(
1405 [&](auto readOp) {
1406 srcBuffAcc = readOp.getOperand(0);
1407
1408 auto indices = readOp.getIndices();
1409 indicesAcc.reserve(indices.size());
1410
1411 llvm::transform(indices, std::back_inserter(indicesAcc),
1412 [&](OpFoldResult ofr) {
1414 rewriter, loc, ofr);
1415 });
1416 });
1417
1418 auto outputShapes =
1419 mlir::cast<mlir::MemRefType>(srcBuffAcc.getType()).getShape();
1420 unsigned int M = outputShapes[outputShapes.size() - 2];
1421 unsigned int N = outputShapes[outputShapes.size() - 1];
1422
1423 SmallVector<Value> dps = newLoop.getResults();
1424 auto bufferType = MemRefType::get({M, N}, opType);
1425 auto resultBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
1426
1427 // Store the amx tiled-dot product output into an MxN memref.
1428 for (unsigned int i = 0, k = 0; i < M; i = i + 16) {
1429 for (unsigned int j = 0; j < N; j = j + 16) {
1430 Value indexOp_i = arith::ConstantIndexOp::create(rewriter, loc, i);
1431 Value indexOp_j = arith::ConstantIndexOp::create(rewriter, loc, j);
1432 amx::TileStoreOp::create(rewriter, loc, resultBuffer,
1433 ValueRange{indexOp_i, indexOp_j}, dps[k]);
1434 k++;
1435 }
1436 }
1437 auto c0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
1438 auto c16 = arith::ConstantIndexOp::create(rewriter, loc, 16);
1439 auto one = arith::ConstantIndexOp::create(rewriter, loc, 1);
1440 auto nBound = arith::ConstantIndexOp::create(rewriter, loc, N);
1441
1442 // Create a loop that iterates over the MxN memerf, retrives two rows +
1443 // shuffle them, add up the C element values and stores them to temp buffer.
1444 scf::ForOp::create(
1445 rewriter, loc, c0, nBound, one, ValueRange{},
1446 [&](OpBuilder &nestedBuilder, Location loc, Value iv,
1447 ValueRange iterArgs) {
1448 auto row =
1449 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1450 resultBuffer, ValueRange{iv, c0});
1451
1452 auto row2 =
1453 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1454 resultBuffer, ValueRange{iv, c16});
1455
1456 Value shuffle1 = row;
1457 Value shuffle2 = row2;
1458
1459 if (!isVnni) {
1460 shuffle1 = vector::ShuffleOp::create(
1461 rewriter, loc, VectorType::get(16, opType), row, row2,
1462 ArrayRef<int64_t>{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20,
1463 21, 22, 23});
1464
1465 shuffle2 = vector::ShuffleOp::create(
1466 rewriter, loc, VectorType::get(16, opType), row, row2,
1467 ArrayRef<int64_t>{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15,
1468 28, 29, 30, 31});
1469 }
1470 indicesAcc[indicesAcc.size() - 2] = iv;
1471 indicesAcc[indicesAcc.size() - 1] = c0;
1472
1473 Value valueCRow1 =
1474 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1475 srcBuffAcc, indicesAcc);
1476 indicesAcc[indicesAcc.size() - 1] = c16;
1477
1478 Value valueCRow2 =
1479 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1480 srcBuffAcc, indicesAcc);
1481
1482 Value addOp;
1483 Value addOp2;
1484
1485 if (ipType.isBF16() || ipType.isF8E5M2() || ipType.isF8E4M3FN()) {
1486 addOp = arith::AddFOp::create(rewriter, loc, shuffle1, valueCRow1);
1487
1488 addOp2 = arith::AddFOp::create(rewriter, loc, shuffle2, valueCRow2);
1489 }
1490
1491 if (ipType.isSignlessInteger(8)) {
1492 addOp = arith::AddIOp::create(rewriter, loc, shuffle1, valueCRow1);
1493
1494 addOp2 = arith::AddIOp::create(rewriter, loc, shuffle2, valueCRow2);
1495 }
1496
1497 vector::StoreOp::create(rewriter, loc, addOp, resultBuffer,
1498 ValueRange{iv, c0});
1499 vector::StoreOp::create(rewriter, loc, addOp2, resultBuffer,
1500 ValueRange{iv, c16});
1501
1502 scf::YieldOp::create(nestedBuilder, loc);
1503 });
1504
1505 SmallVector<Value> writeResults;
1506 for (unsigned int i = 0; i < M; i = i + 16) {
1507 for (unsigned int j = 0; j < N; j = j + 16) {
1508 Value indexOp_i = arith::ConstantIndexOp::create(rewriter, loc, i);
1509 Value indexOp_j = arith::ConstantIndexOp::create(rewriter, loc, j);
1510
1511 auto vectorType = mlir::VectorType::get({16, 16}, opType);
1512
1513 int64_t srcRank =
1514 (dyn_cast<ShapedType>(resultBuffer.getType())).getRank();
1515 Value padding = ub::PoisonOp::create(rewriter, loc, opType);
1516 auto map = AffineMap::getMinorIdentityMap(srcRank, vectorType.getRank(),
1517 rewriter.getContext());
1518 SmallVector<bool> inBounds(vectorType.getRank(), true);
1519
1520 auto vec1 = vector::TransferReadOp::create(
1521 rewriter, loc, vectorType, resultBuffer,
1522 ValueRange{indexOp_i, indexOp_j}, padding, map, inBounds);
1523 writeResults.push_back(vec1);
1524 }
1525 }
1526
1527 // Replace use of vector.contract with dot-products.
1528 for (size_t i = 0; i < ops.size(); i++) {
1529 vector::ContractionOp contOp = ops[i];
1530 Value vecRow = writeResults[i];
1531
1532 Value resultWriteOp = contractionUsersAfterYield(contOp.getResult());
1533 if (auto vecType = llvm::dyn_cast<VectorType>(resultWriteOp.getType()))
1534 vecRow = mlir::vector::ShapeCastOp::create(rewriter, loc, vecType,
1535 writeResults[i]);
1536
1537 rewriter.replaceAllUsesWith(resultWriteOp, vecRow);
1538 }
1539
1540 return success();
1541 }
1542};
1543
1544} // namespace
1545
1547 RewritePatternSet &patterns) {
1548 patterns.add<VectorContractToAMXDotProduct>(patterns.getContext());
1549}
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:112
FloatType getF32Type()
Definition Builders.cpp:47
FloatType getF8E5M2Type()
Definition Builders.cpp:39
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:71
FloatType getBF16Type()
Definition Builders.cpp:41
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:55
FloatType getF8E4M3FNType()
Definition Builders.cpp:37
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:209
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:567
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:400
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
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:384
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
Operation * traceToVectorWriteLikeUserOperation(Value v)
Definition X86Utils.cpp:194
bool isInVnniLayout(Operation *op, llvm::ArrayRef< AffineMap > indexingMaps, std::optional< unsigned > blockingFactor=std::nullopt)
Definition X86Utils.cpp:42
Operation * traceToVectorReadLikeParentOperation(Value v)
Definition X86Utils.cpp:154
bool validatePairVectorContract(vector::ContractionOp contractOp, vector::ContractionOp pairContOp, bool rhsHasMultipleNonUnitDims, int64_t nonUnitDimValue)
Definition X86Utils.cpp:352
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.