19#include "mlir/Dialect/Affine/IR/AffineOps.h"
20#include "mlir/Dialect/Func/IR/FuncOps.h"
21#include "mlir/Dialect/MemRef/IR/MemRef.h"
22#include "mlir/Dialect/SCF/IR/SCF.h"
23#include "mlir/IR/PatternMatch.h"
24#include "mlir/Pass/PassManager.h"
25#include "mlir/Transforms/DialectConversion.h"
26#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
27#include "mlir/Transforms/Passes.h"
29#define DEBUG_TYPE "aievec-optimize"
44SmallVector<NamedAttribute>
51static bool canFoldAIEShiftAndBroadcast(aievec::BroadcastOp op,
52 aievec::ShiftOp &shiftOp,
54 if (!op.getSource().getDefiningOp())
57 shiftOp = dyn_cast<aievec::ShiftOp>(op.getSource().getDefiningOp());
62 VectorType vType = cast<VectorType>(shiftOp->getResult(0).getType());
64 auto constOp = cast<arith::ConstantOp>(shiftOp.getShift().getDefiningOp());
65 int32_t shiftBytes = cast<IntegerAttr>(constOp.getValue()).getInt();
66 idx = shiftBytes * 8 / elemSize + op.getIdx();
71template <
typename AIEv1MACLikeOp,
72 typename = std::enable_if_t<
73 std::is_same_v<AIEv1MACLikeOp, aievec::aie1::FMAOp> ||
74 std::is_same_v<AIEv1MACLikeOp, aievec::aie1::FMAOp::Adaptor>>>
75static bool isSingleColumnInt16VectorTimesScalarMac(AIEv1MACLikeOp fmaOp) {
77 VectorType lhsVTy = cast<VectorType>(fmaOp.getLhs().getType());
78 auto intTy = dyn_cast<IntegerType>(lhsVTy.getElementType());
79 if (!intTy || intTy.getWidth() != 16)
81 if (lhsVTy.getShape()[0] != 32)
84 if (fmaOp.getXoffsets() !=
"0x73727170" ||
85 fmaOp.getXoffsetsHi() !=
"0x77767574" || fmaOp.getXstart() !=
"0" ||
86 fmaOp.getXsquare() !=
"0x3120" || fmaOp.getZoffsets() !=
"0" ||
87 fmaOp.getZoffsetsHi() !=
"0" || fmaOp.getZstep() !=
"1")
90 if (!fmaOp.getLhs().getDefiningOp())
92 aievec::ConcatOp concatOp =
93 dyn_cast<aievec::ConcatOp>(fmaOp.getLhs().getDefiningOp());
96 auto tailVec = concatOp.getSources()[1];
97 if (!tailVec.getDefiningOp())
99 auto constOp = dyn_cast<arith::ConstantOp>(tailVec.getDefiningOp());
102 auto cstDense = dyn_cast<DenseIntElementsAttr>(constOp.getValue());
105 return llvm::all_of(cstDense, [](
const APInt &val) {
return val == 0; });
108static bool singleColumnFMAOpCanFold(aievec::aie1::FMAOp fmaOp) {
109 auto *accProdOp = fmaOp.getAcc().getDefiningOp();
112 auto accFmaOp = dyn_cast<aievec::aie1::FMAOp>(accProdOp);
115 if (!isSingleColumnInt16VectorTimesScalarMac(accFmaOp))
117 return fmaOp.getRhs() == accFmaOp.getRhs() &&
118 !singleColumnFMAOpCanFold(accFmaOp);
125 :
public OpConversionPattern<aievec::aie1::FMAOp> {
126 using OpConversionPattern<aievec::aie1::FMAOp>::OpConversionPattern;
130 ConversionPatternRewriter &rewriter)
const override {
131 if (!isSingleColumnInt16VectorTimesScalarMac(adaptor))
133 auto *accProdOp = adaptor.getAcc().getDefiningOp();
136 auto accFmaOp = dyn_cast<aievec::aie1::FMAOp>(accProdOp);
139 if (!isSingleColumnInt16VectorTimesScalarMac(accFmaOp))
141 if (adaptor.getRhs() != accFmaOp.getRhs())
144 cast<aievec::ConcatOp>(accFmaOp.getLhs().getDefiningOp());
145 auto fmaConcatOp = cast<aievec::ConcatOp>(adaptor.getLhs().getDefiningOp());
146 unsigned fmaZstart, accFmaZstart;
147 if (adaptor.getZstart().getAsInteger(10, fmaZstart) ||
148 accFmaOp.getZstart().getAsInteger(10, accFmaZstart))
150 auto start = std::min(fmaZstart, accFmaZstart);
151 auto step = std::max(fmaZstart, accFmaZstart) - start;
152 auto lowV = accConcatOp.getSources()[0];
153 auto hiV = fmaConcatOp.getSources()[0];
154 if (accFmaZstart > fmaZstart)
155 std::swap(lowV, hiV);
156 auto newConcatOp = aievec::ConcatOp::create(
157 rewriter, fmaOp.getLoc(), adaptor.getLhs().getType(),
158 SmallVector<Value, 2>({lowV, hiV}));
160 rewriter.replaceOpWithNewOp<aievec::aie1::FMAOp>(
161 fmaOp, TypeRange({fmaOp.getResult().getType()}),
162 ValueRange({newConcatOp, adaptor.getRhs(), accFmaOp.getAcc()}),
169 :
public OpConversionPattern<aievec::BroadcastOp> {
170 using OpConversionPattern<aievec::BroadcastOp>::OpConversionPattern;
174 ConversionPatternRewriter &rewriter)
const override {
175 aievec::ShiftOp shiftOp =
nullptr;
178 if (!canFoldAIEShiftAndBroadcast(bcastOp, shiftOp, idx)) {
182 VectorType resultType = cast<VectorType>(bcastOp.getResult().getType());
184 rewriter.replaceOpWithNewOp<aievec::BroadcastOp>(bcastOp, resultType,
185 shiftOp.getLhs(), idx);
195populateAIEVecV1TransformationPatterns(RewritePatternSet &patterns) {
200populateAIEVecV2TransformationPatterns(RewritePatternSet &patterns) {
209configureAIEVecV1TransformationLegalizations(ConversionTarget &target) {
210 target.addLegalDialect<aievec::AIEVecDialect,
211 aievec::aie1::AIEVecAIE1Dialect>();
212 target.addDynamicallyLegalOp<aievec::aie1::FMAOp>(
213 [](aievec::aie1::FMAOp fmaOp) {
214 if (isSingleColumnInt16VectorTimesScalarMac(fmaOp))
215 return !singleColumnFMAOpCanFold(fmaOp);
221configureAIEVecV2TransformationLegalizations(ConversionTarget &target) {
222 target.addDynamicallyLegalOp<xilinx::aievec::BroadcastOp>(
223 [](xilinx::aievec::BroadcastOp op) {
224 aievec::ShiftOp shiftOp =
nullptr;
226 return !canFoldAIEShiftAndBroadcast(op, shiftOp, idx);
234 :
public PassWrapper<AIEVecTransformationPass, OperationPass<>> {
248 StringRef
getArgument() const final {
return "test-aievec-optimize"; }
250 return "Optimize groups of simple aievec ops into complex aievec ops.";
254 registry.insert<affine::AffineDialect, xilinx::aievec::AIEVecDialect,
255 aievec::aie1::AIEVecAIE1Dialect, arith::ArithDialect,
256 memref::MemRefDialect, scf::SCFDialect,
257 vector::VectorDialect>();
262 llvm::cl::desc(
"Select AIE version: \"aie\" or \"aie2\". This will "
263 "determine the vector size and available operations."),
264 llvm::cl::init(
"aie")};
267 auto *op = getOperation();
268 MLIRContext *context = &getContext();
269 RewritePatternSet patterns(context);
270 ConversionTarget target(*context);
274 if (target ==
"aieml" || target ==
"aie2" || target ==
"aie2p") {
276 }
else if (target !=
"aie") {
277 op->emitError() <<
"unknown AIE target '" <<
aieTarget <<
"'";
284 populateAIEVecV1TransformationPatterns(patterns);
285 configureAIEVecV1TransformationLegalizations(target);
287 populateAIEVecV2TransformationPatterns(patterns);
288 configureAIEVecV2TransformationLegalizations(target);
291 if (failed(applyPartialConversion(op, target, std::move(patterns)))) {
297static std::unique_ptr<::mlir::Pass>
299 return std::make_unique<AIEVecTransformationPass>(options);
303 :
public PassWrapper<AIEVecConvOpTransformationPass, OperationPass<>> {
319 return "test-aievec-convolution-optimize";
322 return "Optimize chains of macs into AIE2 conv ops.";
326 registry.insert<affine::AffineDialect, xilinx::aievec::AIEVecDialect,
327 aievec::aie1::AIEVecAIE1Dialect, arith::ArithDialect,
328 memref::MemRefDialect, scf::SCFDialect,
329 vector::VectorDialect>();
334 llvm::cl::desc(
"Select AIE version: \"aie\" or \"aie2\". This will "
335 "determine the vector size and available operations."),
336 llvm::cl::init(
"aie")};
340 llvm::cl::desc(
"Shift parameter for rounding and saturation."),
344 auto *op = getOperation();
345 MLIRContext *context = &getContext();
346 RewritePatternSet patterns(context);
347 ConversionTarget target(*context);
351 if (target ==
"aieml" || target ==
"aie2" || target ==
"aie2p") {
353 }
else if (target !=
"aie") {
354 op->emitError() <<
"unknown AIE target '" <<
aieTarget <<
"'";
360 AnalysisManager am = getAnalysisManager();
366 if (failed(applyPartialConversion(op, target, std::move(patterns)))) {
372static std::unique_ptr<::mlir::Pass>
374 return std::make_unique<AIEVecConvOpTransformationPass>(options);
384 pm.addPass(createAIEVecTransformationPass(options));
386 pm.addPass(createCSEPass());
387 pm.addPass(createCanonicalizerPass());
393 pm.addPass(createAIEVecConvOpTransformationPass(options));
397 pm.addPass(createCSEPass());
398 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()
int32_t getElementSizeInBits(mlir::VectorType type)
void configureAIEVecConvOpTransformationLegalizations(ConversionTarget &target, AnalysisManager &am)
void populateAIEVecConvOpTransformationPatterns(RewritePatternSet &patterns, AnalysisManager &am, unsigned shiftParam)
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 > aieTarget