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 unsigned idx = use.getOperandNumber();
283 vector::ContractionOp contractA,
284 vector::ContractionOp contractB,
285 int64_t nonUnitDimAcc, VectorType accTy) {
287 if (!isa<vector::TransferReadOp, vector::LoadOp>(opA) ||
288 !isa<vector::TransferReadOp, vector::LoadOp>(opB)) {
297 auto elemTy = accTy.getElementType();
298 auto flatTy = VectorType::get(nonUnitDimAcc, elemTy);
301 vector::ShapeCastOp::create(rewriter, loc, flatTy, opA->
getResult(0));
303 vector::ShapeCastOp::create(rewriter, loc, flatTy, opB->
getResult(0));
306 nonUnitDimAcc, (elemTy.isSignlessInteger(32) && nonUnitDimAcc == 8));
308 auto shuffleLo = vector::ShuffleOp::create(rewriter, loc, flatTy, castA,
309 castB, masks.maskLo);
310 auto shuffleHi = vector::ShuffleOp::create(rewriter, loc, flatTy, castA,
311 castB, masks.maskHi);
313 auto newAccA = vector::ShapeCastOp::create(rewriter, loc, accTy, shuffleLo);
314 auto newAccB = vector::ShapeCastOp::create(rewriter, loc, accTy, shuffleHi);
318 return isa<vector::ContractionOp, scf::ForOp>(use.getOwner());
323 return isa<vector::ContractionOp, scf::ForOp>(use.getOwner());
353 auto elemTy = accTy.getElementType();
354 auto flatTy = VectorType::get(nonUnitDimAcc, elemTy);
357 auto castA = vector::ShapeCastOp::create(rewriter, loc, flatTy, vecA);
358 auto castB = vector::ShapeCastOp::create(rewriter, loc, flatTy, vecB);
362 nonUnitDimAcc, (elemTy.isSignlessInteger(32) && nonUnitDimAcc == 8));
364 auto shuffledLo = vector::ShuffleOp::create(rewriter, loc, flatTy, castA,
365 castB, masks.maskLo);
366 auto shuffledHi = vector::ShuffleOp::create(rewriter, loc, flatTy, castA,
367 castB, masks.maskHi);
370 auto newVecA = vector::ShapeCastOp::create(rewriter, loc, accTy, shuffledLo);
371 auto newVecB = vector::ShapeCastOp::create(rewriter, loc, accTy, shuffledHi);
387 vector::ContractionOp pairContOp,
388 bool rhsHasMultipleNonUnitDims,
390 if (contractOp == pairContOp)
393 if (rhsHasMultipleNonUnitDims &&
394 !(contractOp.getLhs() == pairContOp.getLhs()))
397 if (!rhsHasMultipleNonUnitDims &&
398 !(contractOp.getRhs() == pairContOp.getRhs()))
401 auto nonUnitOperand =
402 rhsHasMultipleNonUnitDims ? contractOp.getRhs() : contractOp.getLhs();
403 auto nonUnitOperandPairContOp =
404 rhsHasMultipleNonUnitDims ? pairContOp.getRhs() : pairContOp.getLhs();
409 .Case<vector::TransferReadOp, vector::LoadOp>([&](
auto readOp) {
410 srcBuff = readOp.getOperand(0);
412 readOp.getIndices().end());
414 .Case<vector::ShapeCastOp>([&](vector::ShapeCastOp op) {
415 srcBuff = op.getSource();
419 Value srcBuffPairContOp;
422 .Case<vector::TransferReadOp, vector::LoadOp>([&](
auto readOp) {
423 srcBuffPairContOp = readOp.getOperand(0);
425 readOp.getIndices().begin(), readOp.getIndices().end());
427 .Case<vector::ShapeCastOp>([&](vector::ShapeCastOp op) {
428 srcBuffPairContOp = op.getSource();
432 if (!srcBuff || !srcBuffPairContOp)
436 auto shuffleHw = srcBuffPairContOp.getDefiningOp<vector::ShuffleOp>();
438 if (shuffleLw && shuffleHw)
439 return shuffleLw.getV1() == shuffleHw.getV1() &&
440 shuffleLw.getV2() == shuffleHw.getV2();
442 if (srcBuff != srcBuffPairContOp)
445 bool oneConstantOffset =
false;
446 for (
size_t i = 0; i < indexVals.size(); i++) {
448 if (indexVals[i] == indexValsPairContOp[i])
457 if ((*v1 - *v0) != nonUnitDimValue)
460 oneConstantOffset =
true;
463 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.
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.
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