10#include "mlir/Dialect/Affine/IR/AffineOps.h"
11#include "mlir/Dialect/Bufferization/IR/Bufferization.h"
12#include "mlir/Dialect/Linalg/IR/Linalg.h"
13#include "mlir/Dialect/Linalg/Utils/Utils.h"
14#include "mlir/Dialect/Transform/IR/TransformTypes.h"
15#include "mlir/Dialect/Transform/Interfaces/TransformInterfaces.h"
16#include "mlir/Dialect/Transform/Utils/Utils.h"
17#include "mlir/Dialect/Vector/IR/VectorOps.h"
19#include "llvm/ADT/DenseMap.h"
20#include "llvm/ADT/TypeSwitch.h"
24#define DEBUG_TYPE "aievec-transforms"
40static Value vectorizeTensor(OpBuilder &rewriter, Location loc, Value tensor) {
41 auto opTy = tensor.getType();
42 auto shapeTy = cast<ShapedType>(opTy);
43 auto shape = shapeTy.getShape();
44 auto elemTy = shapeTy.getElementType();
45 auto ToBufferOp = bufferization::ToBufferOp::create(
46 rewriter, loc, MemRefType::get(shape, elemTy), tensor);
47 auto rank = shape.size();
48 auto newShape = shape.slice(0, rank - 2);
49 auto opVecElemTy = VectorType::get(shape.slice(rank - 2, 2), elemTy);
50 auto opMemrefVecTy = MemRefType::get(newShape, opVecElemTy);
52 vector::TypeCastOp::create(rewriter, loc, opMemrefVecTy, ToBufferOp);
53 auto toTensorOp = bufferization::ToTensorOp::create(
54 rewriter, loc, RankedTensorType::get(newShape, opVecElemTy), typeCastOp);
55 toTensorOp.setRestrict(
true);
56 return toTensorOp.getResult();
62static Value scalarizeTensor(OpBuilder &rewriter, Location loc, Value tensor) {
63 auto opTy = tensor.getType();
64 auto shapeTy = cast<ShapedType>(opTy);
66 auto vecShape = shapeTy.getShape();
67 auto vecElemTy = cast<VectorType>(shapeTy.getElementType());
68 auto elemTy = vecElemTy.getElementType();
69 auto toBufferVecTyOp = bufferization::ToBufferOp::create(
70 rewriter, loc, MemRefType::get(vecShape, vecElemTy), tensor);
72 SmallVector<int64_t> scalShape;
73 for (
auto d : shapeTy.getShape())
74 scalShape.push_back(d);
75 for (
auto d : vecElemTy.getShape())
76 scalShape.push_back(d);
77 auto opMemrefScalTy = MemRefType::get(scalShape, elemTy);
78 auto typeCastOp = vector::TypeCastOp::create(rewriter, loc, opMemrefScalTy,
81 auto toTensorOp = bufferization::ToTensorOp::create(
82 rewriter, loc, RankedTensorType::get(scalShape, elemTy), typeCastOp);
83 toTensorOp.setRestrict(
true);
84 return toTensorOp.getResult();
87static bool vectorizeContractionOpBlock(OpBuilder &rewriter, Location loc,
88 Block &srcBlock, Block &dstBlock) {
89 auto *ctx = rewriter.getContext();
90 OpBuilder::InsertionGuard g(rewriter);
91 rewriter.setInsertionPointToStart(&dstBlock);
92 auto baA =
static_cast<Value
>(dstBlock.getArgument(0));
93 auto baB =
static_cast<Value
>(dstBlock.getArgument(1));
94 auto baC =
static_cast<Value
>(dstBlock.getArgument(2));
96 llvm::DenseMap<Value, Value> convertedValues;
97 convertedValues.try_emplace(srcBlock.getArgument(0), baA);
98 convertedValues.try_emplace(srcBlock.getArgument(1), baB);
99 convertedValues.try_emplace(srcBlock.getArgument(2), baC);
100 auto indexingMaps = rewriter.getAffineMapArrayAttr(
101 {AffineMap::getPermutationMap(ArrayRef<unsigned>{1, 0, 2}, ctx)
103 AffineMap::getPermutationMap(ArrayRef<unsigned>{0, 2, 1}, ctx)
105 AffineMap::getPermutationMap(ArrayRef<unsigned>{2, 0, 1}, ctx)
107 auto iteratorTypes = rewriter.getArrayAttr(
108 {vector::IteratorTypeAttr::get(ctx, vector::IteratorType::parallel),
109 vector::IteratorTypeAttr::get(ctx, vector::IteratorType::parallel),
110 vector::IteratorTypeAttr::get(ctx, vector::IteratorType::reduction)});
111 bool addOpFound =
false, mulOpFound =
false;
112 WalkResult walkResult = srcBlock.walk([&](Operation *op) {
113 return llvm::TypeSwitch<Operation *, WalkResult>(op)
114 .Case<arith::AddIOp, arith::AddFOp>([&](
auto addOp) {
116 return WalkResult::interrupt();
118 auto lhs = addOp->getOperand(0);
119 auto rhs = addOp->getOperand(1);
121 auto lhsDefOp = lhs.getDefiningOp();
122 auto rhsDefOp = rhs.getDefiningOp();
123 if (isa_and_nonnull<arith::MulIOp, arith::MulFOp>(lhsDefOp)) {
124 opA = convertedValues[lhsDefOp->getOperand(0)];
125 opB = convertedValues[lhsDefOp->getOperand(1)];
126 opC = convertedValues[rhs];
127 }
else if (isa_and_nonnull<arith::MulIOp, arith::MulFOp>(rhsDefOp)) {
128 opA = convertedValues[rhsDefOp->getOperand(0)];
129 opB = convertedValues[rhsDefOp->getOperand(1)];
130 opC = convertedValues[lhs];
132 return WalkResult::interrupt();
133 auto conOp = vector::ContractionOp::create(
134 rewriter, loc, opA, opB, opC, indexingMaps, iteratorTypes);
135 convertedValues.try_emplace(op->getResult(0), conOp.getResult());
136 return WalkResult::advance();
138 .Case<arith::MulIOp, arith::MulFOp>([&](
auto) {
140 return WalkResult::interrupt();
142 return WalkResult::skip();
144 .Case<linalg::YieldOp>([&](linalg::YieldOp yieldOp) {
145 linalg::YieldOp::create(rewriter, loc,
146 convertedValues[yieldOp.getValues()[0]]);
147 return WalkResult::advance();
149 .Default([&](Operation *unaryOp) {
150 if (unaryOp->getNumResults() != 1 || unaryOp->getNumOperands() != 1)
151 return WalkResult::interrupt();
152 auto srcOpIn = unaryOp->getOperand(0);
153 auto srcOpInTy = srcOpIn.getType();
154 auto srcOpTy = unaryOp->getResultTypes()[0];
155 auto dstOpIn = convertedValues[srcOpIn];
156 Type dstOpTy = dstOpIn.getType();
157 if (srcOpInTy != srcOpTy) {
158 auto vecElemTy = dyn_cast<VectorType>(dstOpTy);
160 return WalkResult::interrupt();
161 dstOpTy = VectorType::get(vecElemTy.getShape(), srcOpTy);
164 rewriter.create(loc, unaryOp->getName().getIdentifier(),
165 {dstOpIn}, {dstOpTy}, unaryOp->getAttrs());
166 convertedValues.try_emplace(unaryOp->getResult(0),
167 newOp->getResult(0));
168 return WalkResult::advance();
171 return mulOpFound && addOpFound && !walkResult.wasInterrupted();
174DiagnosedSilenceableFailure transform::VectorizeContractionOp::applyToOne(
175 TransformRewriter &rewriter, linalg::GenericOp target,
176 ApplyToEachResultList &results, TransformState &state) {
178 auto *ctx = target.getContext();
179 SmallVector<Value> inputs = target.getInputs();
180 if (SmallVector<Value> outputs = target.getOutputs();
181 inputs.size() != 2 || outputs.size() != 1)
182 return emitSilenceableError() <<
"payload is not a contraction.";
185 SmallVector<utils::IteratorType> iterators = target.getIteratorTypesArray();
186 auto innerMostIterators =
187 SmallVector<utils::IteratorType>(iterators.end() - 3, iterators.end());
188 auto outerMostIterators =
189 SmallVector<utils::IteratorType>(iterators.begin(), iterators.end() - 3);
191 if (!linalg::isParallelIterator(innerMostIterators[0]) ||
192 !linalg::isParallelIterator(innerMostIterators[1]) ||
193 !linalg::isReductionIterator(innerMostIterators[2]))
194 return emitSilenceableError()
195 <<
"linalg.generic op innermost iterators don't correspond with a "
196 "gemm-like contraction.";
198 auto indexingMaps = target.getIndexingMapsArray();
205 AffineMap::getPermutationMap(ArrayRef<unsigned>{1, 0, 2}, ctx)
208 AffineMap::getPermutationMap(ArrayRef<unsigned>{0, 2, 1}, ctx)
211 AffineMap::getPermutationMap(ArrayRef<unsigned>{2, 0, 1}, ctx)
215 SmallVector<int64_t> outerMostResults;
216 for (int64_t i = 0; i < indexingMaps[0].getNumResults() - 2; i++)
217 outerMostResults.push_back(i);
219 auto innerMostA = indexingMaps[0].dropResults(outerMostResults);
220 auto innerMostB = indexingMaps[1].dropResults(outerMostResults);
221 auto innerMostC = indexingMaps[2].dropResults(outerMostResults);
225 int64_t numOuterMostDims = indexingMaps[0].getNumDims() - 3;
226 if (innerMostA != mmAidxMap.shiftDims(numOuterMostDims) ||
227 innerMostB != mmBidxMap.shiftDims(numOuterMostDims) ||
228 innerMostC != mmCidxMap.shiftDims(numOuterMostDims))
229 return emitSilenceableError()
230 <<
"linalg.generic op innermost indexing maps don't correspond with "
231 "a gemm-like contraction.";
237 SmallVector<AffineExpr> remOuterDims;
238 for (
unsigned i = 0; i < numOuterMostDims; i++)
239 remOuterDims.push_back(getAffineDimExpr(i, ctx));
240 unsigned numResults = indexingMaps[0].getNumResults();
241 SmallVector<int64_t> positions = {numResults - 2, numResults - 1};
242 auto outerMostAidxMap =
243 indexingMaps[0].dropResults(positions).replaceDimsAndSymbols(
244 remOuterDims, {}, numOuterMostDims, 0);
245 auto outerMostBidxMap =
246 indexingMaps[1].dropResults(positions).replaceDimsAndSymbols(
247 remOuterDims, {}, numOuterMostDims, 0);
248 auto outerMostCidxMap =
249 indexingMaps[2].dropResults(positions).replaceDimsAndSymbols(
250 remOuterDims, {}, numOuterMostDims, 0);
252 rewriter.setInsertionPoint(target);
253 Location loc = target.getLoc();
255 auto opA = vectorizeTensor(rewriter, loc, target.getInputs()[0]);
256 auto opB = vectorizeTensor(rewriter, loc, target.getInputs()[1]);
257 auto opC = vectorizeTensor(rewriter, loc, target.getOutputs()[0]);
260 auto newOp = linalg::GenericOp::create(
261 rewriter, loc, TypeRange({opC.getType()}), ValueRange({opA, opB}),
263 SmallVector<AffineMap>(
264 {outerMostAidxMap, outerMostBidxMap, outerMostCidxMap}),
266 auto &opBody = newOp->getRegion(0);
267 opBody.push_back(
new Block());
268 auto &opBlock = opBody.front();
269 opBlock.addArguments({cast<TensorType>(opA.getType()).getElementType(),
270 cast<TensorType>(opB.getType()).getElementType(),
271 cast<TensorType>(opC.getType()).getElementType()},
273 if (!vectorizeContractionOpBlock(rewriter, loc, target->getRegion(0).front(),
275 return emitSilenceableError()
276 <<
"linalg.generic op payload does not correspond with a "
277 "vectorizable contraction.";
280 auto res = scalarizeTensor(rewriter, loc, newOp.getResults()[0]);
281 rewriter.replaceOp(target, res);
283 results.push_back(newOp);
285 return DiagnosedSilenceableFailure::success();
288#define GET_OP_CLASSES
289#include "aie/Dialect/AIEVec/TransformOps/AIEVecTransformOps.cpp.inc"