31#define DEBUG_TYPE "lower-contract-to-arm-sve-i8mm"
47std::optional<Value> getExtOperand(
Value v) {
49 static_assert(llvm::is_one_of<Op, arith::ExtSIOp, arith::ExtUIOp>::value,
50 "Must be instantiated with either sign- or zero- extension op");
56 if constexpr (std::is_same<Op, arith::ExtSIOp>::value) {
57 auto vTy = cast<VectorType>(v.
getType());
58 if (!vTy.getElementType().isSignlessInteger(8))
67 auto inOp = extOp.getIn();
68 auto inTy = dyn_cast<VectorType>(inOp.getType());
69 if (!inTy || !inTy.getElementType().isSignlessInteger(8))
72 auto outTy = dyn_cast<VectorType>(extOp.getType());
73 if (!outTy || !outTy.getElementType().isSignlessInteger(32))
159class VectorContractRewriter {
172 MMLA mmlaOp = MMLA::Nop;
177 bool swapOperands =
false;
198 LogicalResult match(vector::ContractionOp op,
PatternRewriter &rewriter);
201 VectorContractRewriter() =
default;
217 case MMLA::SignedInt:
218 return arm_sve::SmmlaOp::create(rewriter, loc, resTy,
acc,
lhs,
rhs);
219 case MMLA::UnsignedInt:
220 return arm_sve::UmmlaOp::create(rewriter, loc, resTy,
acc,
lhs,
rhs);
222 return arm_sve::UsmmlaOp::create(rewriter, loc, resTy,
acc,
lhs,
rhs);
224 return arm_sve::BfmmlaOp::create(rewriter, loc, resTy,
acc,
lhs,
rhs);
226 llvm_unreachable(
"Uninitialized operation kind");
230LogicalResult VectorContractRewriter::match(vector::ContractionOp op,
233 auto itTypes = op.getIteratorTypesArray();
234 if (itTypes.size() != 3 || itTypes[0] != vector::IteratorType::parallel ||
235 itTypes[1] != vector::IteratorType::parallel ||
236 itTypes[2] != vector::IteratorType::reduction)
238 op,
"iterator types do not correspond to matrix multiplication");
245 if (op.getIndexingMapsArray()[0] !=
248 op.getIndexingMapsArray()[1] !=
252 3,
ArrayRef{0u, 1u}, op.getContext()))
256 if (op.getKind() != vector::CombiningKind::ADD)
262Value VectorContractRewriter::lower(vector::ContractionOp op,
266 Type operandEltType = cast<VectorType>(
lhs.getType()).getElementType();
267 Type resultEltType = cast<VectorType>(op.getResultType()).getElementType();
269 const int64_t numOperandSubTileElts =
273 "Only implemented for i32 or f32 output");
274 const int64_t numResultSubTileElts = 4;
279 VectorType::get(numOperandSubTileElts, operandEltType,
282 VectorType::get(numOperandSubTileElts, operandEltType,
285 VectorType::get(numResultSubTileElts, resultEltType,
290 auto flatRhsTileType = VectorType::get(k * n, operandEltType,
295 auto accRowTy = VectorType::get(n, resultEltType,
300 auto accRowX2Ty = VectorType::get(2 * n, resultEltType,
305 auto accRow64Ty = VectorType::get(n / 2, rewriter.
getI64Type(),
310 auto accRowX264Ty = VectorType::get(n, rewriter.
getI64Type(),
317 for (
int64_t i = 0; i < m; i += 2) {
325 std::iota(shuffleIdx.begin(), shuffleIdx.end(), 0);
326 auto t = vector::ShuffleOp::create(rewriter, loc, r0, r1, shuffleIdx);
328 auto s = vector::ScalableInsertOp::create(
329 rewriter, loc, t, ub::PoisonOp::create(rewriter, loc, flatLhsType), 0);
331 auto r = arm_sve::DupQLaneOp::create(rewriter, loc, s, 0);
332 lhsTile.push_back(r);
336 auto rhs = vector::ShapeCastOp::create(rewriter, this->rhs.
getLoc(),
337 flatRhsTileType, this->rhs);
342 rhsTile.push_back(vector::ScalableExtractOp::create(
343 rewriter, loc, flatRhsType,
rhs,
j * k));
347 for (
int64_t i = 0; i < m; i += 2) {
349 auto r0 = vector::ExtractOp::create(rewriter, loc, op.getAcc(),
351 auto r1 = vector::ExtractOp::create(rewriter, loc, op.getAcc(),
358 accTileVec = vector::InterleaveOp::create(rewriter, loc, r0, r1);
362 auto r0I64 = vector::BitCastOp::create(rewriter, loc, accRow64Ty, r0);
363 auto r1I64 = vector::BitCastOp::create(rewriter, loc, accRow64Ty, r1);
367 auto intrI64 = vector::InterleaveOp::create(rewriter, loc, r0I64, r1I64);
371 vector::BitCastOp::create(rewriter, loc, accRowX2Ty, intrI64);
375 accTile.push_back(vector::ScalableExtractOp::create(
376 rewriter, loc, flatAccType, accTileVec,
j * 2));
381 for (
int64_t i = 0; i < m / 2; ++i)
383 Value mmla = createMMLA(rewriter, loc, accTile[i * n / 2 +
j], lhsTile[i],
385 outTile.push_back(mmla);
389 Value result = ub::PoisonOp::create(rewriter, loc, op.getResultType());
390 for (
int64_t i = 0; i < m / 2; ++i) {
392 Value row = ub::PoisonOp::create(rewriter, loc, accRowX2Ty);
394 row = vector::ScalableInsertOp::create(
395 rewriter, loc, outTile[i * n / 2 +
j], row,
j * 4);
403 auto tmp = vector::DeinterleaveOp::create(rewriter, loc, row);
404 out0 = tmp.getRes1();
405 out1 = tmp.getRes2();
408 auto row64 = vector::BitCastOp::create(rewriter, loc, accRowX264Ty, row);
409 auto deintr64 = vector::DeinterleaveOp::create(rewriter, loc, row64);
412 out0 = vector::BitCastOp::create(rewriter, loc, accRowTy,
414 out1 = vector::BitCastOp::create(rewriter, loc, accRowTy,
417 result = vector::InsertOp::create(rewriter, loc, out0,
result, i * 2);
418 result = vector::InsertOp::create(rewriter, loc, out1,
result, i * 2 + 1);
424class VectorContractRewriterI8MM :
public VectorContractRewriter {
428 LogicalResult matchAndInit(vector::ContractionOp op,
430 if (failed(match(op, rewriter)))
433 VectorType lhsType = op.getLhsType();
434 VectorType rhsType = op.getRhsType();
436 m = lhsType.getDimSize(0);
437 n = rhsType.getDimSize(0);
438 k = rhsType.getDimSize(1);
445 if (lhsType.isScalable() || !rhsType.getScalableDims()[0] ||
446 rhsType.getScalableDims()[1] || lhsType.getDimSize(1) != k || k != 8 ||
447 m < 2 || m % 2 != 0 || n < 2 || n % 2 != 0 ||
448 !rhsType.getScalableDims()[0])
452 auto outTy = dyn_cast<VectorType>(op.getResultType());
453 if (!outTy || outTy.getElementType() != rewriter.
getI32Type())
455 "output type is not a vector of i32");
461 mmlaOp = MMLA::SignedInt;
462 swapOperands =
false;
463 auto maybeLhs = getExtOperand<arith::ExtSIOp>(op.getLhs());
465 mmlaOp = MMLA::UnsignedInt;
466 maybeLhs = getExtOperand<arith::ExtUIOp>(op.getLhs());
470 op,
"LHS is not a sign- or zero- extended i8");
472 auto maybeRhs = getExtOperand<arith::ExtSIOp>(op.getRhs());
474 if (mmlaOp == MMLA::UnsignedInt)
475 mmlaOp = MMLA::MixedInt;
477 if (mmlaOp == MMLA::SignedInt) {
478 mmlaOp = MMLA::MixedInt;
481 maybeRhs = getExtOperand<arith::ExtUIOp>(op.getRhs());
485 op,
"RHS is not a sign- or zero- extended i8");
496class VectorContractRewriterBfloat :
public VectorContractRewriter {
500 LogicalResult matchAndInit(vector::ContractionOp op,
502 if (failed(match(op, rewriter)))
505 VectorType lhsType = op.getLhsType();
506 VectorType rhsType = op.getRhsType();
508 m = lhsType.getDimSize(0);
509 n = rhsType.getDimSize(0);
510 k = rhsType.getDimSize(1);
517 if (lhsType.isScalable() || !rhsType.getScalableDims()[0] ||
518 rhsType.getScalableDims()[1] || lhsType.getDimSize(1) != k || k != 4 ||
519 m < 2 || m % 2 != 0 || n < 2 || n % 2 != 0 ||
520 !rhsType.getScalableDims()[0])
524 auto outTy = dyn_cast<VectorType>(op.getResultType());
525 if (!outTy || outTy.getElementType() != rewriter.
getF32Type())
527 "output type is not a vector of f32");
530 if (lhsType.getElementType() != rewriter.
getBF16Type())
532 "input type is not a vector of bf16");
535 mmlaOp = MMLA::Bfloat;
536 swapOperands =
false;
545class LowerContractionToSVEI8MMPattern
549 LogicalResult matchAndRewrite(vector::ContractionOp op,
553 VectorContractRewriterI8MM vcr;
554 if (failed(vcr.matchAndInit(op, rewriter)))
564class LowerContractionToSVEBFMMLAPattern
568 LogicalResult matchAndRewrite(vector::ContractionOp op,
572 VectorContractRewriterBfloat vcr;
573 if (failed(vcr.matchAndInit(op, rewriter)))
588 patterns.
add<LowerContractionToSVEI8MMPattern>(context, 2);
594 patterns.
add<LowerContractionToSVEBFMMLAPattern>(context, 2);
static AffineMap getMultiDimMapWithTargets(unsigned numDims, ArrayRef< unsigned > targets, MLIRContext *context)
Returns an affine map with numDims input dimensions and results specified by targets.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This provides public APIs that all operations should have.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
void populateLowerContractionToSVEBFMMLAPatterns(RewritePatternSet &patterns)
void populateLowerContractionToSVEI8MMPatterns(RewritePatternSet &patterns)
Include the generated interface declarations.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.