19#include "llvm/ADT/TypeSwitch.h"
20#include "llvm/Support/Casting.h"
22#include "llvm/ADT/ArrayRef.h"
28static FailureOr<SmallVector<mlir::utils::IteratorType>>
33 map.
getNumDims(), mlir::utils::IteratorType::reduction);
35 if (
auto dim = dyn_cast<AffineDimExpr>(expr))
36 iterators[dim.getPosition()] = mlir::utils::IteratorType::parallel;
43 std::optional<unsigned> blockingFactor) {
45 FailureOr<linalg::ContractionDimensions> dims =
52 auto typeA = dyn_cast<ShapedType>(matA.getType());
53 auto typeB = dyn_cast<ShapedType>(matB.getType());
54 unsigned rankA = typeA.getRank();
55 unsigned rankB = typeB.getRank();
57 if (rankA < 3 || rankB < 3)
62 if (dims->k.size() < 2)
71 if (failed(maybeIters))
77 auto vnniDimA = dyn_cast<AffineDimExpr>(mapA.
getResult(rankA - 1));
78 auto vnniDimB = dyn_cast<AffineDimExpr>(mapB.
getResult(rankB - 1));
79 if (!vnniDimA || !vnniDimB || vnniDimA != vnniDimB ||
80 iteratorTypes[vnniDimA.getPosition()] !=
81 mlir::utils::IteratorType::reduction)
83 auto redDimA = dyn_cast<AffineDimExpr>(mapA.
getResult(rankA - 2));
84 auto redDimB = dyn_cast<AffineDimExpr>(mapB.
getResult(rankB - 3));
85 if (!redDimA || !redDimB || redDimA != redDimB ||
86 iteratorTypes[redDimA.getPosition()] !=
87 mlir::utils::IteratorType::reduction)
89 auto parallelDimB = dyn_cast<AffineDimExpr>(mapB.
getResult(rankB - 2));
90 if (!parallelDimB || iteratorTypes[parallelDimB.getPosition()] !=
91 mlir::utils::IteratorType::parallel)
98 auto vnniDimSize = typeB.getShape().back();
99 if (vnniDimSize == ShapedType::kDynamic || vnniDimSize == 0 ||
100 vnniDimSize % 2 != 0)
102 if (typeA.getShape().back() != vnniDimSize)
104 if (blockingFactor && vnniDimSize != *blockingFactor)
108 if (typeA.getShape().end()[-2] != typeB.getShape().end()[-3])
121 assert((nonUnitDimAcc == 8 || nonUnitDimAcc == 16) &&
122 "Unsupported nonUnitDimAcc value");
125 static constexpr int64_t maskLo8[] = {0, 8, 1, 9, 2, 10, 3, 11};
126 static constexpr int64_t maskHi8[] = {4, 12, 5, 13, 6, 14, 7, 15};
129 static constexpr int64_t maskLo8_avx2_int8[] = {0, 1, 2, 3, 8, 9, 10, 11};
130 static constexpr int64_t maskHi8_avx2_int8[] = {4, 5, 6, 7, 12, 13, 14, 15};
133 static constexpr int64_t maskLo16[] = {0, 1, 2, 3, 16, 17, 18, 19,
134 4, 5, 6, 7, 20, 21, 22, 23};
135 static constexpr int64_t maskHi16[] = {8, 9, 10, 11, 24, 25, 26, 27,
136 12, 13, 14, 15, 28, 29, 30, 31};
138 if (nonUnitDimAcc == 16)
139 return {maskLo16, maskHi16};
142 return {maskLo8_avx2_int8, maskHi8_avx2_int8};
144 return {maskLo8, maskHi8};
156 if (!isa<scf::YieldOp>(user))
159 auto yield = cast<scf::YieldOp>(user);
177 if (isa<vector::TransferReadOp, vector::LoadOp, arith::ConstantOp>(
180 if (
auto constOp = dyn_cast<arith::ConstantOp>(defOp)) {
182 dyn_cast<DenseElementsAttr>(constOp.getValue())) {
183 if (!denseAttr.isSplat())
188 if (
auto floatAttr = dyn_cast<FloatAttr>(splat))
189 return floatAttr.getValue().isZero() ? defOp :
nullptr;
191 if (
auto intAttr = dyn_cast<IntegerAttr>(splat))
192 return intAttr.getValue().isZero() ? defOp :
nullptr;
204 if (
auto barg = dyn_cast<BlockArgument>(v)) {
205 auto *parentOp = barg.getOwner()->getParentOp();
207 if (
auto forOp = dyn_cast<scf::ForOp>(parentOp)) {
208 unsigned argNum = barg.getArgNumber();
214 unsigned iterIdx = argNum - 1;
215 v = forOp.getInitArgs()[iterIdx];
242 if (isa<vector::TransferWriteOp>(user) || isa<vector::StoreOp>(user))
245 if (isa<vector::ShuffleOp>(user))
249 if (
auto yield = dyn_cast<scf::YieldOp>(user)) {
251 unsigned idx = use.getOperandNumber();
259 if (
auto forOp = dyn_cast<scf::ForOp>(user)) {
260 OpResult loopResult = forOp.getTiedLoopResult(&use);
285 vector::ContractionOp contractA,
286 vector::ContractionOp contractB,
287 int64_t nonUnitDimAcc, VectorType accTy) {
289 if (!isa<vector::TransferReadOp, vector::LoadOp>(opA) ||
290 !isa<vector::TransferReadOp, vector::LoadOp>(opB)) {
299 auto elemTy = accTy.getElementType();
300 auto flatTy = VectorType::get(nonUnitDimAcc, elemTy);
303 vector::ShapeCastOp::create(rewriter, loc, flatTy, opA->
getResult(0));
305 vector::ShapeCastOp::create(rewriter, loc, flatTy, opB->
getResult(0));
308 nonUnitDimAcc, (elemTy.isSignlessInteger(32) && nonUnitDimAcc == 8));
310 auto shuffleLo = vector::ShuffleOp::create(rewriter, loc, flatTy, castA,
311 castB, masks.maskLo);
312 auto shuffleHi = vector::ShuffleOp::create(rewriter, loc, flatTy, castA,
313 castB, masks.maskHi);
315 auto newAccA = vector::ShapeCastOp::create(rewriter, loc, accTy, shuffleLo);
316 auto newAccB = vector::ShapeCastOp::create(rewriter, loc, accTy, shuffleHi);
320 return isa<vector::ContractionOp, scf::ForOp>(use.getOwner());
325 return isa<vector::ContractionOp, scf::ForOp>(use.getOwner());
355 auto elemTy = accTy.getElementType();
356 auto flatTy = VectorType::get(nonUnitDimAcc, elemTy);
359 auto castA = vector::ShapeCastOp::create(rewriter, loc, flatTy, vecA);
360 auto castB = vector::ShapeCastOp::create(rewriter, loc, flatTy, vecB);
364 nonUnitDimAcc, (elemTy.isSignlessInteger(32) && nonUnitDimAcc == 8));
366 auto shuffledLo = vector::ShuffleOp::create(rewriter, loc, flatTy, castA,
367 castB, masks.maskLo);
368 auto shuffledHi = vector::ShuffleOp::create(rewriter, loc, flatTy, castA,
369 castB, masks.maskHi);
372 auto newVecA = vector::ShapeCastOp::create(rewriter, loc, accTy, shuffledLo);
373 auto newVecB = vector::ShapeCastOp::create(rewriter, loc, accTy, shuffledHi);
392 vector::ContractionOp pairContOp,
393 bool rhsHasMultipleNonUnitDims,
395 if (contractOp == pairContOp)
398 if (rhsHasMultipleNonUnitDims &&
399 !(contractOp.getLhs() == pairContOp.getLhs()))
402 if (!rhsHasMultipleNonUnitDims &&
403 !(contractOp.getRhs() == pairContOp.getRhs()))
406 auto nonUnitOperand =
407 rhsHasMultipleNonUnitDims ? contractOp.getRhs() : contractOp.getLhs();
408 auto nonUnitOperandPairContOp =
409 rhsHasMultipleNonUnitDims ? pairContOp.getRhs() : pairContOp.getLhs();
414 .Case<vector::TransferReadOp, vector::LoadOp>([&](
auto readOp) {
415 srcBuff = readOp.getOperand(0);
417 readOp.getIndices().end());
419 .Case<vector::ShapeCastOp>([&](vector::ShapeCastOp op) {
420 srcBuff = op.getSource();
424 Value srcBuffPairContOp;
427 .Case<vector::TransferReadOp, vector::LoadOp>([&](
auto readOp) {
428 srcBuffPairContOp = readOp.getOperand(0);
430 readOp.getIndices().begin(), readOp.getIndices().end());
432 .Case<vector::ShapeCastOp>([&](vector::ShapeCastOp op) {
433 srcBuffPairContOp = op.getSource();
437 if (!srcBuff || !srcBuffPairContOp)
441 auto shuffleHw = srcBuffPairContOp.getDefiningOp<vector::ShuffleOp>();
443 if (shuffleLw && shuffleHw)
444 return shuffleLw.getV1() == shuffleHw.getV1() &&
445 shuffleLw.getV2() == shuffleHw.getV2();
447 if (srcBuff != srcBuffPairContOp)
450 bool oneConstantOffset =
false;
451 for (
size_t i = 0; i < indexVals.size(); i++) {
453 if (indexVals[i] == indexValsPairContOp[i])
462 if ((*v1 - *v0) != nonUnitDimValue)
465 oneConstantOffset =
true;
468 return oneConstantOffset;
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
AffineExpr getResult(unsigned idx) const
Attributes are known-constant values of operations.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
This is a value defined by a result of an operation.
Operation is the basic unit of execution within MLIR.
void replaceUsesOfWith(Value from, Value to)
Replace any uses of 'from' with 'to' within this operation.
Value getOperand(unsigned idx)
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
result_range getResults()
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
virtual void replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
unsigned getNumUses() const
This method computes the number of uses of this Value.
user_range getUsers() const
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
use_iterator use_begin() const
Operation * getOwner() const
Return the owner of this operand.
FailureOr< ContractionDimensions > inferContractionDims(LinalgOp linalgOp)
Find at least 2 parallel (m and n) and 1 reduction (k) dimension candidates that form a matmul subcom...
LogicalResult shuffleBeforeWriteLikeOp(PatternRewriter &rewriter, Value contractARes, Value contractBRes, int64_t nonUnitDimAcc, VectorType accTy)
Operation * traceToVectorWriteLikeUserOperation(Value v)
static FailureOr< SmallVector< mlir::utils::IteratorType > > inferIteratorsFromOutMap(AffineMap map)
bool isInVnniLayout(Operation *op, llvm::ArrayRef< AffineMap > indexingMaps, std::optional< unsigned > blockingFactor=std::nullopt)
Value contractionUsersAfterYield(Value v)
Operation * traceToVectorReadLikeParentOperation(Value v)
ShuffleMasks getShuffleMasks(int64_t nonUnitDimAcc, bool isInt8Avx2)
LogicalResult shuffleAfterReadLikeOp(PatternRewriter &rewriter, Operation *opA, Operation *opB, vector::ContractionOp contractA, vector::ContractionOp contractB, int64_t nonUnitDimAcc, VectorType accTy)
bool validatePairVectorContract(vector::ContractionOp contractOp, vector::ContractionOp pairContOp, bool rhsHasMultipleNonUnitDims, int64_t nonUnitDimValue)
Include the generated interface declarations.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
llvm::ArrayRef< int64_t > maskHi
llvm::ArrayRef< int64_t > maskLo