14#include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
15#include "mlir/Dialect/Affine/Analysis/LoopAnalysis.h"
16#include "mlir/Dialect/Affine/IR/AffineOps.h"
17#include "mlir/Dialect/Func/IR/FuncOps.h"
18#include "mlir/Dialect/Math/IR/Math.h"
19#include "mlir/Dialect/MemRef/IR/MemRef.h"
20#include "mlir/Dialect/SCF/IR/SCF.h"
21#include "mlir/Dialect/UB/IR/UBOps.h"
22#include "mlir/Dialect/Utils/ReshapeOpsUtils.h"
23#include "mlir/Dialect/Vector/Transforms/LoweringPatterns.h"
24#include "mlir/IR/Matchers.h"
25#include "mlir/IR/PatternMatch.h"
26#include "mlir/Pass/PassManager.h"
27#include "mlir/Transforms/DialectConversion.h"
28#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
29#include "llvm/ADT/STLExtras.h"
33#define DEBUG_TYPE "aievec-canonicalization"
45static AIEArch decodeAIETarget(
const std::string &target) {
46 if (!target.empty()) {
47 if (target ==
"aieml" || target ==
"aie2" || target ==
"aie2p")
64 :
public OpConversionPattern<vector::TransferReadOp> {
65 using OpConversionPattern<vector::TransferReadOp>::OpConversionPattern;
69 : OpConversionPattern<
vector::TransferReadOp>(context),
74 ConversionPatternRewriter &rewriter)
const override {
76 if (adaptor.getPermutationMap().isConstant())
80 auto vType = readOp.getVectorType();
88 auto vLen = vType.getShape().back();
89 auto longVecTy = VectorType::get(2 * vLen, vType.getElementType());
97 auto loc = readOp.getLoc();
98 Value oldInnerMostIdx = adaptor.getIndices().back();
99 auto offsetCorrectionMap =
100 AffineMap::get(1, 0, getAffineDimExpr(0, readOp.getContext()) - offset);
101 Value newInnerMostIdx = affine::AffineApplyOp::create(
102 rewriter, readOp.getLoc(), offsetCorrectionMap,
103 SmallVector<Value, 1>({oldInnerMostIdx}))
105 SmallVector<Value, 8> alignedIdx;
106 alignedIdx.append(adaptor.getIndices().begin(), adaptor.getIndices().end());
107 alignedIdx[alignedIdx.size() - 1] = newInnerMostIdx;
111 auto newReadOp = vector::TransferReadOp::create(
112 rewriter, loc, longVecTy, adaptor.getBase(), alignedIdx,
113 adaptor.getPadding());
116 rewriter.replaceOpWithNewOp<vector::ExtractStridedSliceOp>(
117 readOp, newReadOp.getResult(), offset, vLen, 1);
131 :
public OpConversionPattern<vector::TransferReadOp> {
132 using OpConversionPattern<vector::TransferReadOp>::OpConversionPattern;
135 : OpConversionPattern<
vector::TransferReadOp>(context) {}
139 ConversionPatternRewriter &rewriter)
const override {
140 AffineMap map = readOp.getPermutationMap();
141 if (!map.isConstant())
144 Value srcMemRef = adaptor.getBase();
145 SmallVector<Value, 8> indices;
149 if (cast<MemRefType>(srcMemRef.getType()).getRank() == 0) {
150 srcMemRef = memref::ExpandShapeOp::create(
151 rewriter, readOp.getLoc(), SmallVector<int64_t, 1>({1}),
152 srcMemRef, SmallVector<ReassociationIndices, 1>({}))
154 newIdx = arith::ConstantOp::create(rewriter, readOp.getLoc(),
155 rewriter.getIndexAttr(0L));
156 indices.push_back(newIdx);
158 indices.append(adaptor.getIndices().begin(), adaptor.getIndices().end());
159 newIdx = indices[indices.size() - 1];
163 if (
auto applyOp = newIdx.getDefiningOp<affine::AffineApplyOp>())
164 if (applyOp.getAffineMap().getNumDims() == 1) {
165 newIdx = applyOp.getMapOperands()[0];
166 offset = applyOp.getAffineMap().compose(ArrayRef<int64_t>{0})[0];
170 int64_t vlen = readOp.getVector().getType().getShape()[0];
171 if (offset >= vlen) {
174 int64_t numElemsToSkip = vlen * (offset / vlen);
175 offset = offset % vlen;
176 auto newAddrMap = AffineMap::get(
177 1, 0, getAffineDimExpr(0, readOp.getContext()) + numElemsToSkip);
179 affine::AffineApplyOp::create(rewriter, readOp.getLoc(), newAddrMap,
180 SmallVector<Value, 1>({newIdx}))
183 indices[indices.size() - 1] = newIdx;
184 auto newReadOp = vector::TransferReadOp::create(
185 rewriter, readOp.getLoc(), readOp.getVector().getType(), srcMemRef,
186 indices, adaptor.getPadding());
187 auto extractOp = vector::ExtractOp::create(rewriter, readOp.getLoc(),
188 newReadOp.getResult(),
189 ArrayRef<int64_t>{offset});
190 rewriter.replaceOpWithNewOp<vector::BroadcastOp>(
191 readOp, newReadOp.getVector().getType(), extractOp.getResult());
199template <
class UnaryOpA,
class UnaryOpB>
212 PatternRewriter &rewriter)
const override {
214 UnaryOpA::template hasTrait<OpTrait::OneOperand>(),
215 "SwapUnaryOps can only be instantiated for single-operand ops");
217 UnaryOpB::template hasTrait<OpTrait::OneOperand>(),
218 "SwapUnaryOps can only be instantiated for single-operand ops");
219 UnaryOpA aOp = bOp.getOperand().template getDefiningOp<UnaryOpA>();
221 return rewriter.notifyMatchFailure(bOp, UnaryOpB::getOperationName() +
222 " not preceeded by " +
223 UnaryOpA::getOperationName());
228 UnaryOpB::create(rewriter, bOp->getLoc(), SmallVector<Type>({newA2BTy}),
229 aOp->getOperands(), bOp->getAttrs());
230 auto newB = UnaryOpA::create(rewriter, bOp->getLoc(),
231 SmallVector<Type>({bOp.getResult().getType()}),
232 newA->getResults(), aOp->getAttrs());
233 rewriter.replaceOp(bOp, newB.getResult());
238static SmallVector<Value> collapseInnerMostDimIndices(PatternRewriter &b,
239 Location loc,
int numDims,
241 ArrayRef<int64_t> shape,
244 auto newIdxExpr = b.getAffineDimExpr(numDims - 1);
246 for (int64_t dim = numDims - 2; dim >= 0; dim--) {
247 stride *= shape[shape.size() - (numDims - dim - 1)];
248 newIdxExpr = newIdxExpr + b.getAffineDimExpr(dim) * stride;
250 auto newIndexMap = AffineMap::get(numDims, 0, newIdxExpr);
251 Value newInnerMostIdxValue =
252 affine::AffineApplyOp::create(b, loc, newIndexMap,
253 indices.take_back(numDims))
255 SmallVector<Value> newIdxRange;
256 for (
auto idx : indices.drop_back(numDims))
257 newIdxRange.push_back(idx);
258 newIdxRange.push_back(newInnerMostIdxValue);
262static Value collapseInnerMostShapeDims(PatternRewriter &b, Location loc,
263 int numDims, Value val) {
264 auto memRefTy = cast<MemRefType>(val.getType());
265 auto shape = memRefTy.getShape();
266 int64_t newInnerMostDim = std::accumulate(shape.end() - numDims, shape.end(),
267 1, std::multiplies<>());
268 SmallVector<int64_t, 4> newShape{shape.begin(), shape.end() - numDims + 1};
269 newShape[shape.size() - numDims] = newInnerMostDim;
272 auto reassocIndicesOpt = getReassociationIndicesForCollapse(shape, newShape);
273 assert(reassocIndicesOpt &&
"newShape must be a valid collapse of shape");
274 const auto &reassocIndices = *reassocIndicesOpt;
278 memref::CollapseShapeOp::computeCollapsedType(memRefTy, reassocIndices);
279 return memref::CollapseShapeOp::create(b, loc, newMemRefTy, val,
287static bool hasContiguousRowMajorStrides(MemRefType memRefTy) {
288 SmallVector<int64_t> strides;
290 if (failed(memRefTy.getStridesAndOffset(strides, offset)))
292 auto shape = memRefTy.getShape();
293 int64_t expected = 1;
294 for (
int i = shape.size() - 1; i >= 0; --i) {
295 if (strides[i] != ShapedType::kDynamic && strides[i] != expected)
297 if (shape[i] != ShapedType::kDynamic)
298 expected *= shape[i];
300 expected = ShapedType::kDynamic;
309 :
public OpConversionPattern<vector::TransferReadOp> {
310 using OpConversionPattern<vector::TransferReadOp>::OpConversionPattern;
314 ConversionPatternRewriter &rewriter)
const override {
317 if (!adaptor.getPermutationMap().isMinorIdentity() || adaptor.getMask())
319 VectorType vectorTy = readOp.getVector().getType();
320 if (vectorTy.getRank() < 2)
323 MemRefType memRefTy = dyn_cast<MemRefType>(adaptor.getBase().getType());
326 auto memRefShape = memRefTy.getShape();
327 auto vecShape = vectorTy.getShape();
330 VectorType::get({std::accumulate(vecShape.begin(), vecShape.end(), 1,
331 std::multiplies<>())},
332 vectorTy.getElementType());
333 if (!hasContiguousRowMajorStrides(memRefTy))
336 AffineMap layout = memRefTy.getLayout().getAffineMap();
338 collapseInnerMostDimIndices(rewriter, readOp.getLoc(), vecShape.size(),
339 adaptor.getIndices(), memRefShape, layout);
340 auto newSource = collapseInnerMostShapeDims(
341 rewriter, readOp.getLoc(), vecShape.size(), adaptor.getBase());
342 auto newVector = vector::TransferReadOp::create(
343 rewriter, readOp.getLoc(), newVectorTy, newSource, newIndices,
345 arith::getZeroConstant(rewriter, readOp.getLoc(),
346 newVectorTy.getElementType()));
348 auto inBoundsArrayAttrOpt = adaptor.getInBounds();
349 if (inBoundsArrayAttrOpt) {
350 SmallVector<bool> inBounds =
351 llvm::to_vector(inBoundsArrayAttrOpt.getAsValueRange<BoolAttr>());
352 SmallVector<bool> newInBounds({
false});
353 newInBounds[0] = llvm::all_of(inBounds, [](
bool v) {
return v; });
354 newVector.getProperties().setInBounds(
355 rewriter.getBoolArrayAttr(newInBounds));
358 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(readOp, vectorTy,
369 :
public OpConversionPattern<vector::TransferWriteOp> {
370 using OpConversionPattern<vector::TransferWriteOp>::OpConversionPattern;
374 ConversionPatternRewriter &rewriter)
const override {
377 if (!adaptor.getPermutationMap().isMinorIdentity() || adaptor.getMask())
379 VectorType vectorTy = cast<VectorType>(adaptor.getValueToStore().getType());
380 if (vectorTy.getRank() < 2)
383 MemRefType memRefTy = dyn_cast<MemRefType>(adaptor.getBase().getType());
386 auto memRefShape = memRefTy.getShape();
387 auto vecShape = vectorTy.getShape();
389 if (!hasContiguousRowMajorStrides(memRefTy))
393 VectorType::get({std::accumulate(vecShape.begin(), vecShape.end(), 1,
394 std::multiplies<>())},
395 vectorTy.getElementType());
396 AffineMap layout = memRefTy.getLayout().getAffineMap();
398 vector::ShapeCastOp::create(rewriter, writeOp.getLoc(), newVectorTy,
399 adaptor.getValueToStore())
402 collapseInnerMostDimIndices(rewriter, writeOp.getLoc(), vecShape.size(),
403 adaptor.getIndices(), memRefShape, layout);
404 auto newSource = collapseInnerMostShapeDims(
405 rewriter, writeOp.getLoc(), vecShape.size(), adaptor.getBase());
407 auto newOp = rewriter.replaceOpWithNewOp<vector::TransferWriteOp>(
408 writeOp, newVector, newSource, newIndices);
410 auto inBoundsArrayAttrOpt = adaptor.getInBounds();
411 if (inBoundsArrayAttrOpt) {
412 SmallVector<bool> inBounds =
413 llvm::to_vector(inBoundsArrayAttrOpt.getAsValueRange<BoolAttr>());
414 SmallVector<bool> newInBounds({
false});
415 newInBounds[0] = llvm::all_of(inBounds, [](
bool v) {
return v; });
416 newOp.getProperties().setInBounds(rewriter.getBoolArrayAttr(newInBounds));
425static LogicalResult isAllZeroOffsetAccess(mlir::OperandRange indices) {
426 if (!llvm::all_of(indices, [](Value val) {
428 if (!matchPattern(val, m_Constant(&attr)))
430 return attr.getInt() == 0;
439static SmallVector<Value> opFoldResultsToValues(PatternRewriter &rewriter,
441 memref::SubViewOp subViewOp) {
442 OpBuilder::InsertionGuard g(rewriter);
443 rewriter.setInsertionPoint(subViewOp);
444 SmallVector<Value> newIndices;
445 for (OpFoldResult offset : subViewOp.getMixedOffsets()) {
447 if (
auto attr = dyn_cast<Attribute>(offset)) {
448 indexVal = arith::ConstantIndexOp::create(
449 rewriter, loc, cast<IntegerAttr>(attr).getInt());
451 indexVal = cast<Value>(offset);
453 newIndices.push_back(indexVal);
478 PatternRewriter &rewriter)
const override {
480 auto subViewOp = dyn_cast_if_present<memref::SubViewOp>(
481 readOp.getBase().getDefiningOp());
486 if (failed(isAllZeroOffsetAccess(readOp.getIndices())))
490 SmallVector<Value> newIndices =
491 opFoldResultsToValues(rewriter, readOp.getLoc(), subViewOp);
494 rewriter.replaceOpWithNewOp<vector::TransferReadOp>(
495 readOp, readOp.getType(), subViewOp.getSource(), newIndices,
496 readOp.getPadding(), readOp.getInBoundsValues());
522 PatternRewriter &rewriter)
const override {
524 auto subViewOp = dyn_cast_if_present<memref::SubViewOp>(
525 writeOp.getBase().getDefiningOp());
530 if (failed(isAllZeroOffsetAccess(writeOp.getIndices())))
534 SmallVector<Value> newIndices =
535 opFoldResultsToValues(rewriter, writeOp.getLoc(), subViewOp);
538 vector::TransferWriteOp::create(rewriter, writeOp.getLoc(),
539 writeOp.getVector(), subViewOp.getSource(),
540 newIndices, writeOp.getInBoundsValues());
543 rewriter.eraseOp(writeOp);
555 using OpRewritePattern::OpRewritePattern;
558 PatternRewriter &rewriter)
const override {
559 for (OpOperand &init : forOp.getInitArgsMutable()) {
560 auto vecType = dyn_cast<VectorType>(init.get().getType());
561 if (!vecType || vecType.getRank() < 2 || vecType.isScalable())
563 Value flat = vector::ShapeCastOp::create(rewriter, forOp.getLoc(),
566 auto shapeCast = [](OpBuilder &builder, Location loc, Type type,
567 Value source) -> Value {
568 return vector::ShapeCastOp::create(builder, loc, type, source);
570 rewriter.replaceOp(forOp, scf::replaceAndCastForOpIterArg(
571 rewriter, forOp, init, flat, shapeCast));
588 PatternRewriter &rewriter)
const override {
589 auto insSrcTy = dyn_cast<VectorType>(insOp.getValueToStoreType());
593 auto srcShape = insSrcTy.getShape();
594 auto dstShape = insOp.getDestVectorType().getShape();
596 unsigned long numLeadUnitDimDst = 0;
597 while (numLeadUnitDimDst < dstShape.size() &&
598 dstShape[numLeadUnitDimDst] == 1)
601 if (!numLeadUnitDimDst)
604 unsigned long numLeadUnitDimSrc = 0;
605 while (numLeadUnitDimSrc < srcShape.size() &&
606 srcShape[numLeadUnitDimSrc] == 1)
609 SmallVector<int64_t> nonLeadUnitDimDstShape(
610 dstShape.begin() + numLeadUnitDimDst, dstShape.end());
611 SmallVector<int64_t> nonLeadUnitDimSrcShape(
612 srcShape.begin() + numLeadUnitDimSrc, srcShape.end());
614 if (nonLeadUnitDimSrcShape != nonLeadUnitDimDstShape)
617 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(
618 insOp, insOp.getDestVectorType(), insOp.getValueToStore());
627configureCommonAIECanonicalizeLegalizations(ConversionTarget &target) {
628 target.addLegalDialect<arith::ArithDialect, affine::AffineDialect,
629 memref::MemRefDialect, vector::VectorDialect,
634populateCommonAIECanonicalizeConversionPatterns(RewritePatternSet &patterns) {
636 patterns.getContext());
643static void configureAIEv1CanonicalizeLegalizations(ConversionTarget &target) {
644 target.addDynamicallyLegalOp<vector::TransferReadOp>(
645 [](vector::TransferReadOp op) {
646 return !op.getPermutationMap().isConstant() &&
653populateAIEv1CanonicalizeConversionPatterns(RewritePatternSet &patterns) {
662static void configureAIE2CanonicalizeLegalizations(ConversionTarget &target) {
663 target.addDynamicallyLegalOp<vector::TransferReadOp>(
664 [](vector::TransferReadOp op) {
665 return !op.getPermutationMap().isConstant() &&
668 op.getVector().getType().getRank() < 2;
670 target.addDynamicallyLegalOp<vector::TransferWriteOp>(
671 [](vector::TransferWriteOp op) {
672 return cast<VectorType>(op.getVector().getType()).getRank() < 2;
677populateAIE2CanonicalizeConversionPatterns(RewritePatternSet &patterns) {
694static Value smartTruncF32ToBF16(PatternRewriter &rewriter, Location loc,
695 Value val, Type bf16Type) {
696 if (
auto extfOp = val.getDefiningOp<arith::ExtFOp>()) {
697 if (extfOp.getIn().getType() == bf16Type)
698 return extfOp.getIn();
700 return arith::TruncFOp::create(rewriter, loc, bf16Type, val);
704static APFloat roundedToBF16(APFloat v) {
705 bool losesInfo =
false;
706 const llvm::fltSemantics &sem = v.getSemantics();
707 v.convert(APFloat::BFloat(), APFloat::rmNearestTiesToEven, &losesInfo);
708 v.convert(sem, APFloat::rmNearestTiesToEven, &losesInfo);
713static bool holdsBF16(Value val) {
714 if (
auto extfOp = val.getDefiningOp<arith::ExtFOp>())
715 return getElementTypeOrSelf(extfOp.getIn().getType()).isBF16();
716 DenseFPElementsAttr dense;
717 return matchPattern(val, m_Constant(&dense)) &&
718 llvm::all_of(dense.getValues<APFloat>(), [](
const APFloat &v) {
719 return roundedToBF16(v).bitwiseIsEqual(v);
724static Value roundToBF16(PatternRewriter &rewriter, Location loc, Value val) {
727 auto vecType = cast<VectorType>(val.getType());
728 DenseFPElementsAttr dense;
729 if (matchPattern(val, m_Constant(&dense))) {
730 SmallVector<APFloat> values;
731 for (
const APFloat &v : dense.getValues<APFloat>())
732 values.push_back(roundedToBF16(v));
733 return arith::ConstantOp::create(rewriter, loc,
734 DenseElementsAttr::get(vecType, values));
737 VectorType::get(vecType.getShape(), rewriter.getBF16Type());
738 Value bf16 = smartTruncF32ToBF16(rewriter, loc, val, bf16VecType);
739 return arith::ExtFOp::create(rewriter, loc, vecType, bf16,
743static bool isF32Vector(Type type) {
744 auto vecType = dyn_cast<VectorType>(type);
745 return vecType && vecType.getElementType().isF32();
756 using OpRewritePattern::OpRewritePattern;
759 PatternRewriter &rewriter)
const override {
760 auto resultType = dyn_cast<VectorType>(op.getType());
761 auto intermediateType = dyn_cast<VectorType>(op.getIn().getType());
762 if (!resultType || !intermediateType ||
763 !resultType.getElementType().isBF16() ||
764 !intermediateType.getElementType().isF32())
767 auto extfOp = op.getIn().getDefiningOp<arith::ExtFOp>();
768 if (!extfOp || extfOp.getIn().getType() != resultType)
771 rewriter.replaceOp(op, extfOp.getIn());
783template <
typename OpTy>
788 PatternRewriter &rewriter)
const override {
789 auto resultType = dyn_cast<VectorType>(op.getType());
790 if (!resultType || !resultType.getElementType().isF32())
793 Location loc = op.getLoc();
795 VectorType::get(resultType.getShape(), rewriter.getBF16Type());
798 smartTruncF32ToBF16(rewriter, loc, op.getLhs(), bf16VecType);
800 smartTruncF32ToBF16(rewriter, loc, op.getRhs(), bf16VecType);
803 OpTy::create(rewriter, loc, bf16VecType, lhsBF16, rhsBF16);
804 auto extOp = arith::ExtFOp::create(rewriter, loc, resultType, newResult,
806 rewriter.replaceOp(op, extOp);
814 using OpRewritePattern::OpRewritePattern;
817 PatternRewriter &rewriter)
const override {
818 auto lhsType = dyn_cast<VectorType>(op.getLhs().getType());
819 if (!lhsType || !lhsType.getElementType().isF32())
822 Location loc = op.getLoc();
824 VectorType::get(lhsType.getShape(), rewriter.getBF16Type());
827 smartTruncF32ToBF16(rewriter, loc, op.getLhs(), bf16VecType);
829 smartTruncF32ToBF16(rewriter, loc, op.getRhs(), bf16VecType);
831 rewriter.replaceOpWithNewOp<arith::CmpFOp>(op, op.getPredicate(), lhsBF16,
841 using OpRewritePattern::OpRewritePattern;
844 PatternRewriter &rewriter)
const override {
845 auto resultType = dyn_cast<VectorType>(op.getType());
846 if (!resultType || !resultType.getElementType().isF32())
849 Location loc = op.getLoc();
851 VectorType::get(resultType.getShape(), rewriter.getBF16Type());
854 smartTruncF32ToBF16(rewriter, loc, op.getTrueValue(), bf16VecType);
856 smartTruncF32ToBF16(rewriter, loc, op.getFalseValue(), bf16VecType);
858 Value newResult = arith::SelectOp::create(rewriter, loc, op.getCondition(),
859 trueValBF16, falseValBF16);
860 auto extOp = arith::ExtFOp::create(rewriter, loc, resultType, newResult,
862 rewriter.replaceOp(op, extOp);
874 using OpRewritePattern::OpRewritePattern;
877 PatternRewriter &rewriter)
const override {
878 if (!isF32Vector(op.getType()) ||
879 (holdsBF16(op.getLhs()) && holdsBF16(op.getRhs())))
881 Location loc = op.getLoc();
882 Value lhs = roundToBF16(rewriter, loc, op.getLhs());
883 Value rhs = roundToBF16(rewriter, loc, op.getRhs());
884 rewriter.replaceOpWithNewOp<arith::MulFOp>(op, lhs, rhs,
885 op.getFastmathAttr());
894 using OpRewritePattern::OpRewritePattern;
897 PatternRewriter &rewriter)
const override {
898 auto contracts = [](arith::FastMathFlagsAttr flags) {
899 return flags && bitEnumContainsAll(flags.getValue(),
900 arith::FastMathFlags::contract);
902 if (!isF32Vector(op.getType()) || !contracts(op.getFastmathAttr()))
904 for (
auto [mulOperand, acc] : {std::pair(op.getLhs(), op.getRhs()),
905 std::pair(op.getRhs(), op.getLhs())}) {
906 auto mulOp = mulOperand.getDefiningOp<arith::MulFOp>();
907 if (!mulOp || !mulOp->hasOneUse() || !contracts(mulOp.getFastmathAttr()))
909 Location loc = op.getLoc();
910 Value lhs = roundToBF16(rewriter, loc, mulOp.getLhs());
911 Value rhs = roundToBF16(rewriter, loc, mulOp.getRhs());
912 rewriter.replaceOpWithNewOp<vector::FMAOp>(op, lhs, rhs, acc);
913 rewriter.eraseOp(mulOp);
923 using OpRewritePattern::OpRewritePattern;
926 PatternRewriter &rewriter)
const override {
927 if (!isF32Vector(op.getType()) ||
928 (holdsBF16(op.getLhs()) && holdsBF16(op.getRhs())))
930 Location loc = op.getLoc();
931 Value lhs = roundToBF16(rewriter, loc, op.getLhs());
932 Value rhs = roundToBF16(rewriter, loc, op.getRhs());
933 rewriter.replaceOpWithNewOp<vector::FMAOp>(op, lhs, rhs, op.getAcc());
939template <
typename OpTy>
944 PatternRewriter &rewriter)
const override {
945 auto resultType = dyn_cast<VectorType>(op.getType());
946 if (!resultType || !resultType.getElementType().isF32())
949 Location loc = op.getLoc();
951 VectorType::get(resultType.getShape(), rewriter.getBF16Type());
954 smartTruncF32ToBF16(rewriter, loc, op->getOperand(0), bf16VecType);
956 Value newResult = OpTy::create(rewriter, loc, bf16VecType, inputBF16);
957 auto extOp = arith::ExtFOp::create(rewriter, loc, resultType, newResult,
959 rewriter.replaceOp(op, extOp);
965 :
public PassWrapper<BF16EmulationPass, OperationPass<>> {
968 auto *op = getOperation();
969 MLIRContext *context = &getContext();
970 RewritePatternSet patterns(context);
996 (void)applyPatternsGreedily(op, std::move(patterns));
1001 return std::make_unique<BF16EmulationPass>();
1009 using OpRewritePattern::OpRewritePattern;
1012 PatternRewriter &rewriter)
const override {
1013 auto srcType = dyn_cast<VectorType>(op.getSourceType());
1014 VectorType resType = op.getResultVectorType();
1015 if (!srcType || srcType.getRank() != 1 || resType.getRank() < 2 ||
1016 resType.getShape().back() != srcType.getNumElements() ||
1017 !srcType.getElementType().isIntOrFloat() || resType.isScalable())
1019 int64_t n = srcType.getNumElements();
1020 SmallVector<int64_t> mask;
1021 for (int64_t i = 0; i < resType.getNumElements(); ++i)
1022 mask.push_back(i % n);
1023 Value src = op.getSource();
1025 vector::ShuffleOp::create(rewriter, op.getLoc(), src, src, mask);
1026 rewriter.replaceOpWithNewOp<vector::ShapeCastOp>(op, resType, flat);
1032 :
public PassWrapper<VectorBroadcastLoweringPass, OperationPass<>> {
1036 auto *op = getOperation();
1037 MLIRContext *context = &getContext();
1038 RewritePatternSet patterns(context);
1039 populateVectorBroadcastLoweringPatterns(patterns);
1041 patterns.getContext());
1047 (void)applyPatternsGreedily(op, std::move(patterns));
1054static std::unique_ptr<::mlir::Pass>
1055createVectorBroadcastLoweringPass(
bool aie2p =
false) {
1056 return std::make_unique<VectorBroadcastLoweringPass>(aie2p);
1067 :
public PassWrapper<CanonicalizeVectorForAIEVecPass, OperationPass<>> {
1083 return "test-canonicalize-vector-for-aievec";
1087 return "Canonicalize vector operations for AIEVec conversion";
1092 .insert<arith::ArithDialect, memref::MemRefDialect,
1093 vector::VectorDialect, affine::AffineDialect, ub::UBDialect>();
1097 *
this,
"aie-target",
1099 "Select AIE version: \"aie\", \"aie2\", or \"aie2p\". This will "
1100 "determine the vector size and available operations."),
1101 llvm::cl::init(
"aie")};
1104 auto *op = getOperation();
1105 MLIRContext *context = &getContext();
1106 RewritePatternSet patterns(context);
1107 ConversionTarget target(*context);
1111 op->emitError() <<
"unknown AIE target '" <<
aieTarget <<
"'";
1112 signalPassFailure();
1116 populateCommonAIECanonicalizeConversionPatterns(patterns);
1117 configureCommonAIECanonicalizeLegalizations(target);
1119 populateAIEv1CanonicalizeConversionPatterns(patterns);
1120 configureAIEv1CanonicalizeLegalizations(target);
1122 populateAIE2CanonicalizeConversionPatterns(patterns);
1123 configureAIE2CanonicalizeLegalizations(target);
1127 RewritePatternSet patterns(context);
1132 (void)applyPatternsGreedily(op, std::move(patterns));
1135 if (failed(applyPartialConversion(op, target, std::move(patterns)))) {
1136 signalPassFailure();
1141static std::unique_ptr<::mlir::Pass> createCanonicalizeVectorForAIEVecPass(
1143 return std::make_unique<CanonicalizeVectorForAIEVecPass>(options);
1147 :
public PassWrapper<ReorderOperationsPass, OperationPass<>> {
1150 auto *op = getOperation();
1151 MLIRContext *context = &getContext();
1152 RewritePatternSet patterns(context);
1155 patterns.getContext(),
1156 [](arith::ExtSIOp extOp, vector::BroadcastOp bcastOp) -> Type {
1157 Type extInElemTy = extOp.getIn().getType();
1158 auto extInVecTy = dyn_cast<VectorType>(extInElemTy);
1160 extInElemTy = extInVecTy.getElementType();
1161 return VectorType::get(bcastOp.getResultVectorType().getShape(),
1165 (void)applyPatternsGreedily(op, std::move(patterns));
1169static std::unique_ptr<::mlir::Pass> createReorderOperationsPass() {
1170 return std::make_unique<ReorderOperationsPass>();
1187 pm.addPass(createReorderOperationsPass());
1189 pm.addPass(createVectorBroadcastLoweringPass(options.
aieTarget ==
"aie2p"));
1190 pm.addPass(createCanonicalizeVectorForAIEVecPass(options));
mlir::VectorType getFlattenedVectorType(mlir::VectorType vecTy)
std::unique_ptr<::mlir::Pass > createCopyRemovalPass()
Create a pass that removes unnecessary Copy operations.
std::optional< int64_t > getTransferReadAlignmentOffset(TransferReadLikeOp readOp, mlir::VectorType vType, int64_t alignment)
int32_t getElementSizeInBits(mlir::VectorType type)
std::unique_ptr<::mlir::Pass > createBF16EmulationPass()
Create a pass that runs f32 vector arithmetic on the bf16 datapath: multiplies and transcendentals ta...
void buildCanonicalizeVectorForAIEVec(mlir::OpPassManager &pm, const CanonicalizeVectorForAIEVecOptions &options)
void runOnOperation() override
LogicalResult matchAndRewrite(vector::BroadcastOp op, PatternRewriter &rewriter) const override
Pattern to canonicalize trivial vector.transfer_read operations on subviews.
LogicalResult matchAndRewrite(vector::TransferReadOp readOp, PatternRewriter &rewriter) const override
Pattern to canonicalize trivial vector.transfer_write operations on subviews.
LogicalResult matchAndRewrite(vector::TransferWriteOp writeOp, PatternRewriter &rewriter) const override
StringRef getArgument() const final
void getDependentDialects(DialectRegistry ®istry) const override
StringRef getDescription() const final
Option< std::string > aieTarget
CanonicalizeVectorForAIEVecPass(const CanonicalizeVectorForAIEVecOptions &options)
void runOnOperation() override
CanonicalizeVectorForAIEVecPass()=default
LogicalResult matchAndRewrite(vector::InsertOp insOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(vector::TransferReadOp readOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
ConvertSplatTransferReadToBroadcastPattern(MLIRContext *context)
Pattern to emulate f32 binary vector arithmetic ops in bf16.
LogicalResult matchAndRewrite(OpTy op, PatternRewriter &rewriter) const override
Pattern to emulate f32 comparison ops in bf16.
LogicalResult matchAndRewrite(arith::CmpFOp op, PatternRewriter &rewriter) const override
Pattern to emulate f32 vector.fma with bf16 inputs.
LogicalResult matchAndRewrite(vector::FMAOp op, PatternRewriter &rewriter) const override
Pattern to emulate an f32 multiply with bf16 inputs.
LogicalResult matchAndRewrite(arith::MulFOp op, PatternRewriter &rewriter) const override
Pattern to emulate f32 select ops in bf16.
LogicalResult matchAndRewrite(arith::SelectOp op, PatternRewriter &rewriter) const override
Pattern to emulate f32 unary vector ops in bf16.
LogicalResult matchAndRewrite(OpTy op, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(vector::TransferReadOp readOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(vector::TransferWriteOp writeOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(scf::ForOp forOp, PatternRewriter &rewriter) const override
Pattern to drop bf16 -> f32 -> bf16 round trips.
LogicalResult matchAndRewrite(arith::TruncFOp op, PatternRewriter &rewriter) const override
Pattern to fold an f32 multiply into the add that is its only user, as a vector.fma with bf16 inputs.
LogicalResult matchAndRewrite(arith::AddFOp op, PatternRewriter &rewriter) const override
void runOnOperation() override
SplitUnalignedTransferReadPattern(MLIRContext *context, int64_t maxVectorSize, int64_t alignment)
LogicalResult matchAndRewrite(vector::TransferReadOp readOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
InferTypeB2AFnTy inferTypeB2A
LogicalResult matchAndRewrite(UnaryOpB bOp, PatternRewriter &rewriter) const override
std::function< Type(UnaryOpA aOp, UnaryOpB bOp)> InferTypeB2AFnTy
SwapUnaryOpsPattern(MLIRContext *context, InferTypeB2AFnTy inferType)
void runOnOperation() override
VectorBroadcastLoweringPass(bool aie2p=false)
Options for the "canonicalize-vector-for-aievec" pipeline.
PassOptions::Option< bool > enableBF16Emulation
PassOptions::Option< std::string > aieTarget