22#include "mlir/Dialect/Affine/IR/AffineOps.h"
23#include "mlir/Dialect/Func/IR/FuncOps.h"
24#include "mlir/Dialect/MemRef/IR/MemRef.h"
25#include "mlir/Dialect/SCF/IR/SCF.h"
26#include "mlir/IR/PatternMatch.h"
27#include "mlir/Pass/PassManager.h"
28#include "mlir/Transforms/DialectConversion.h"
29#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
30#include "mlir/Transforms/Passes.h"
32#define DEBUG_TYPE "aievec-optimize"
37using namespace vector;
47SmallVector<NamedAttribute>
54static bool canFoldAIEShiftAndBroadcast(aievec::BroadcastOp op,
55 aievec::ShiftOp &shiftOp,
57 if (!op.getSource().getDefiningOp())
60 shiftOp = dyn_cast<aievec::ShiftOp>(op.getSource().getDefiningOp());
65 VectorType vType = cast<VectorType>(shiftOp->getResult(0).getType());
67 auto constOp = cast<arith::ConstantOp>(shiftOp.getShift().getDefiningOp());
68 int32_t shiftBytes = cast<IntegerAttr>(constOp.getValue()).getInt();
69 idx = shiftBytes * 8 / elemSize + op.getIdx();
78template <
typename AIEv1MACLikeOp,
79 typename = std::enable_if_t<
80 std::is_same_v<AIEv1MACLikeOp, aievec::aie1::FMAOp> ||
81 std::is_same_v<AIEv1MACLikeOp, aievec::aie1::FMAOp::Adaptor>>>
82static bool isSingleColumnInt16VectorTimesScalarMac(AIEv1MACLikeOp fmaOp) {
84 VectorType lhsVTy = cast<VectorType>(fmaOp.getLhs().getType());
85 auto intTy = dyn_cast<IntegerType>(lhsVTy.getElementType());
86 if (!intTy || intTy.getWidth() != 16)
88 if (lhsVTy.getShape()[0] != 32)
91 if (fmaOp.getXoffsets() !=
"0x73727170" ||
92 fmaOp.getXoffsetsHi() !=
"0x77767574" || fmaOp.getXstart() !=
"0" ||
93 fmaOp.getXsquare() !=
"0x3120" || fmaOp.getZoffsets() !=
"0" ||
94 fmaOp.getZoffsetsHi() !=
"0" || fmaOp.getZstep() !=
"1")
97 if (!fmaOp.getLhs().getDefiningOp())
99 aievec::ConcatOp concatOp =
100 dyn_cast<aievec::ConcatOp>(fmaOp.getLhs().getDefiningOp());
103 auto tailVec = concatOp.getSources()[1];
104 if (!tailVec.getDefiningOp())
106 auto constOp = dyn_cast<arith::ConstantOp>(tailVec.getDefiningOp());
109 auto cstDense = dyn_cast<DenseIntElementsAttr>(constOp.getValue());
112 return llvm::all_of(cstDense, [](
const APInt &val) {
return val == 0; });
115static bool singleColumnFMAOpCanFold(aievec::aie1::FMAOp fmaOp) {
116 auto accProdOp = fmaOp.getAcc().getDefiningOp();
119 auto accFmaOp = dyn_cast<aievec::aie1::FMAOp>(accProdOp);
122 if (!isSingleColumnInt16VectorTimesScalarMac(accFmaOp))
124 return fmaOp.getRhs() == accFmaOp.getRhs() &&
125 !singleColumnFMAOpCanFold(accFmaOp);
137 ConversionPatternRewriter &rewriter)
const override {
138 if (!isSingleColumnInt16VectorTimesScalarMac(adaptor))
140 auto accProdOp = adaptor.getAcc().getDefiningOp();
143 auto accFmaOp = dyn_cast<aievec::aie1::FMAOp>(accProdOp);
146 if (!isSingleColumnInt16VectorTimesScalarMac(accFmaOp))
148 if (adaptor.getRhs() != accFmaOp.getRhs())
151 cast<aievec::ConcatOp>(accFmaOp.getLhs().getDefiningOp());
152 auto fmaConcatOp = cast<aievec::ConcatOp>(adaptor.getLhs().getDefiningOp());
153 unsigned fmaZstart, accFmaZstart;
154 if (adaptor.getZstart().getAsInteger(10, fmaZstart) ||
155 accFmaOp.getZstart().getAsInteger(10, accFmaZstart))
157 auto start = std::min(fmaZstart, accFmaZstart);
158 auto step = std::max(fmaZstart, accFmaZstart) - start;
159 auto lowV = accConcatOp.getSources()[0];
160 auto hiV = fmaConcatOp.getSources()[0];
161 if (accFmaZstart > fmaZstart)
162 std::swap(lowV, hiV);
163 auto newConcatOp = rewriter.create<aievec::ConcatOp>(
164 fmaOp.getLoc(), adaptor.getLhs().getType(),
165 SmallVector<Value, 2>({lowV, hiV}));
167 rewriter.replaceOpWithNewOp<aievec::aie1::FMAOp>(
168 fmaOp, TypeRange({fmaOp.getResult().getType()}),
169 ValueRange({newConcatOp, adaptor.getRhs(), accFmaOp.getAcc()}),
181 ConversionPatternRewriter &rewriter)
const override {
182 aievec::ShiftOp shiftOp =
nullptr;
185 if (!canFoldAIEShiftAndBroadcast(bcastOp, shiftOp, idx)) {
189 VectorType resultType = cast<VectorType>(bcastOp.getResult().getType());
191 rewriter.replaceOpWithNewOp<aievec::BroadcastOp>(bcastOp, resultType,
192 shiftOp.getLhs(), idx);
201static void populateAIEVecV1TransformationPatterns(RewritePatternSet &patterns,
206static void populateAIEVecV2TransformationPatterns(RewritePatternSet &patterns,
216configureAIEVecV1TransformationLegalizations(ConversionTarget &target,
218 target.addLegalDialect<aievec::AIEVecDialect,
219 aievec::aie1::AIEVecAIE1Dialect>();
220 target.addDynamicallyLegalOp<aievec::aie1::FMAOp>(
221 [](aievec::aie1::FMAOp fmaOp) {
222 if (isSingleColumnInt16VectorTimesScalarMac(fmaOp))
223 return !singleColumnFMAOpCanFold(fmaOp);
229configureAIEVecV2TransformationLegalizations(ConversionTarget &target,
231 target.addDynamicallyLegalOp<xilinx::aievec::BroadcastOp>(
232 [](xilinx::aievec::BroadcastOp op) {
233 aievec::ShiftOp shiftOp =
nullptr;
235 return !canFoldAIEShiftAndBroadcast(op, shiftOp, idx);
243 :
public PassWrapper<AIEVecTransformationPass, OperationPass<>> {
258 StringRef
getArgument() const final {
return "test-aievec-optimize"; }
260 return "Optimize groups of simple aievec ops into complex aievec ops.";
264 registry.insert<affine::AffineDialect, xilinx::aievec::AIEVecDialect,
265 aievec::aie1::AIEVecAIE1Dialect, arith::ArithDialect,
266 memref::MemRefDialect, scf::SCFDialect,
267 vector::VectorDialect>();
272 llvm::cl::desc(
"Select AIE version: \"aie\" or \"aie2\". This will "
273 "determine the vector size and available operations."),
274 llvm::cl::init(
"aie")};
277 *
this,
"target-backend",
278 llvm::cl::desc(
"Select translation backend: \"cpp\" or \"llvmir\". This "
279 "will determine the aievec operations used to convert "
280 "from vector dialect."),
281 llvm::cl::init(
"cpp")};
284 auto op = getOperation();
285 MLIRContext *context = &getContext();
286 RewritePatternSet patterns(context);
287 ConversionTarget target(*context);
288 AIEArch aieVersion = AIEArch::AIE;
291 if (target ==
"aieml" || target ==
"aie2") {
292 aieVersion = AIEArch::AIE2;
293 }
else if (target !=
"aie") {
294 op->emitError() <<
"unknown AIE target '" <<
aieTarget <<
"'";
303 if (backendStr ==
"llvmir") {
304 backend = TargetBackend::LLVMIR;
305 if (aieVersion == AIEArch::AIE) {
306 op->emitError() <<
"targetting LLVM IR is not supported for AIEv1";
310 }
else if (backendStr !=
"cpp") {
311 op->emitError() <<
"unknown target backend'" <<
targetBackend <<
"'";
317 if (aieVersion == AIEArch::AIE) {
318 populateAIEVecV1TransformationPatterns(patterns, backend);
319 configureAIEVecV1TransformationLegalizations(target, backend);
321 populateAIEVecV2TransformationPatterns(patterns, backend);
322 configureAIEVecV2TransformationLegalizations(target, backend);
325 if (failed(applyPartialConversion(op, target, std::move(patterns)))) {
331static std::unique_ptr<::mlir::Pass>
333 return std::make_unique<AIEVecTransformationPass>(options);
337 :
public PassWrapper<AIEVecConvOpTransformationPass, OperationPass<>> {
354 return "test-aievec-convolution-optimize";
357 return "Optimize chains of macs into AIE2 conv ops.";
361 registry.insert<affine::AffineDialect, xilinx::aievec::AIEVecDialect,
362 aievec::aie1::AIEVecAIE1Dialect, arith::ArithDialect,
363 memref::MemRefDialect, scf::SCFDialect,
364 vector::VectorDialect>();
369 llvm::cl::desc(
"Select AIE version: \"aie\" or \"aie2\". This will "
370 "determine the vector size and available operations."),
371 llvm::cl::init(
"aie")};
374 *
this,
"target-backend",
375 llvm::cl::desc(
"Select translation backend: \"cpp\" or \"llvmir\". This "
376 "will determine the aievec operations used to convert "
377 "from vector dialect."),
378 llvm::cl::init(
"cpp")};
382 llvm::cl::desc(
"Shift parameter for rounding and saturation."),
386 auto op = getOperation();
387 MLIRContext *context = &getContext();
388 RewritePatternSet patterns(context);
389 ConversionTarget target(*context);
390 AIEArch aieVersion = AIEArch::AIE;
393 if (target ==
"aieml" || target ==
"aie2") {
394 aieVersion = AIEArch::AIE2;
395 }
else if (target !=
"aie") {
396 op->emitError() <<
"unknown AIE target '" <<
aieTarget <<
"'";
405 if (backendStr ==
"llvmir") {
406 backend = TargetBackend::LLVMIR;
407 if (aieVersion == AIEArch::AIE) {
408 op->emitError() <<
"targetting LLVM IR is not supported for AIEv1";
412 }
else if (backendStr !=
"cpp") {
413 op->emitError() <<
"unknown target backend'" <<
targetBackend <<
"'";
419 AnalysisManager am = getAnalysisManager();
420 if (aieVersion == AIEArch::AIE2) {
426 if (failed(applyPartialConversion(op, target, std::move(patterns)))) {
432static std::unique_ptr<::mlir::Pass>
434 return std::make_unique<AIEVecConvOpTransformationPass>(options);
444 pm.addPass(createAIEVecTransformationPass(options));
446 pm.addPass(createCSEPass());
447 pm.addPass(createCanonicalizerPass());
452 pm.addPass(createAIEVecConvOpTransformationPass(options));
456 pm.addPass(createCSEPass());
457 pm.addPass(createCanonicalizerPass());
unsigned getVectorLaneSize(mlir::VectorType type)
SmallVector< NamedAttribute > buildFMAOpSplatAttrForElemTy(aievec::aie1::FMAOp fmaOp, int64_t bcastPos, int64_t step=1)
std::unique_ptr< mlir::Pass > createAIEVecConvolutionAnalysisPass()
void populateAIEVecConvOpTransformationPatterns(RewritePatternSet &patterns, AnalysisManager &am, unsigned shiftParam, TargetBackend backend)
int32_t getElementSizeInBits(mlir::VectorType type)
void configureAIEVecConvOpTransformationLegalizations(ConversionTarget &target, AnalysisManager &am, TargetBackend backend)
void buildOptimizeAIEVec(mlir::OpPassManager &pm, const OptimizeAIEVecOptions &options)
LogicalResult matchAndRewrite(aievec::BroadcastOp bcastOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(aievec::aie1::FMAOp fmaOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
Options for the "optimize-aievec" pipeline.
PassOptions::Option< unsigned > shiftParam
PassOptions::Option< std::string > targetBackend
PassOptions::Option< std::string > aieTarget