1 //===- TestLinalgElementwiseFusion.cpp - Test Linalg elementwise fusion ---===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements a pass for testing fusion of elementwise operations in 10 // Linalg, mainly linalg options. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "mlir/Dialect/Linalg/Transforms/Transforms.h" 15 #include "mlir/Pass/Pass.h" 16 #include "mlir/Pass/PassManager.h" 17 #include "mlir/Transforms/GreedyPatternRewriteDriver.h" 18 #include "llvm/ADT/TypeSwitch.h" 19 20 namespace mlir { 21 22 static void addOperands(Operation *op, SetVector<Value> &operandSet) { 23 if (!op) 24 return; 25 TypeSwitch<Operation *, void>(op) 26 .Case<linalg::LinalgOp>([&](linalg::LinalgOp linalgOp) { 27 SmallVector<Value> inputOperands = linalgOp.getInputOperands(); 28 operandSet.insert(inputOperands.begin(), inputOperands.end()); 29 }) 30 .Default([&](Operation *operation) { 31 operandSet.insert(operation->operand_begin(), operation->operand_end()); 32 }); 33 } 34 35 template <int limit = 3> 36 static bool setFusedOpOperandLimit(const OpResult &producer, 37 const OpOperand &consumer) { 38 SetVector<Value> fusedOpOperands; 39 if (producer.getOwner()->getNumResults() != 1) 40 return false; 41 addOperands(consumer.getOwner(), fusedOpOperands); 42 fusedOpOperands.remove(producer); 43 addOperands(producer.getOwner(), fusedOpOperands); 44 return fusedOpOperands.size() <= limit; 45 } 46 47 namespace { 48 struct TestLinalgElementwiseFusion 49 : public PassWrapper<TestLinalgElementwiseFusion, OperationPass<FuncOp>> { 50 TestLinalgElementwiseFusion() = default; 51 TestLinalgElementwiseFusion(const TestLinalgElementwiseFusion &pass) 52 : PassWrapper(pass) {} 53 void getDependentDialects(DialectRegistry ®istry) const override { 54 registry.insert<AffineDialect, linalg::LinalgDialect, memref::MemRefDialect, 55 tensor::TensorDialect>(); 56 } 57 StringRef getArgument() const final { 58 return "test-linalg-elementwise-fusion-patterns"; 59 } 60 StringRef getDescription() const final { 61 return "Test Linalg element wise operation fusion patterns"; 62 } 63 64 Option<bool> fuseGenericOps{ 65 *this, "fuse-generic-ops", 66 llvm::cl::desc("Test fusion of generic operations."), 67 llvm::cl::init(false)}; 68 69 Option<bool> controlFuseByExpansion{ 70 *this, "control-fusion-by-expansion", 71 llvm::cl::desc( 72 "Test controlling fusion of reshape with generic op by expansion"), 73 llvm::cl::init(false)}; 74 75 Option<bool> pushExpandingReshape{ 76 *this, "push-expanding-reshape", 77 llvm::cl::desc("Test linalg expand_shape -> generic " 78 "to generic -> expand_shape pattern"), 79 llvm::cl::init(false)}; 80 81 void runOnOperation() override { 82 MLIRContext *context = &this->getContext(); 83 FuncOp funcOp = this->getOperation(); 84 85 if (fuseGenericOps) { 86 RewritePatternSet fusionPatterns(context); 87 linalg::populateElementwiseOpsFusionPatterns( 88 fusionPatterns, 89 linalg::LinalgElementwiseFusionOptions() 90 .setControlElementwiseOpsFusionFn(setFusedOpOperandLimit<4>)); 91 92 (void)applyPatternsAndFoldGreedily(funcOp.getBody(), 93 std::move(fusionPatterns)); 94 return; 95 } 96 97 if (controlFuseByExpansion) { 98 RewritePatternSet fusionPatterns(context); 99 100 linalg::ControlElementwiseOpsFusionFn controlReshapeFusionFn = 101 [](const OpResult &producer, OpOperand &consumer) { 102 if (auto collapseOp = 103 producer.getDefiningOp<tensor::CollapseShapeOp>()) { 104 if (!collapseOp.src().getDefiningOp<linalg::LinalgOp>()) { 105 return false; 106 } 107 } 108 if (auto expandOp = 109 dyn_cast<tensor::ExpandShapeOp>(consumer.getOwner())) { 110 if (expandOp->hasOneUse()) { 111 OpOperand &use = *expandOp->getUses().begin(); 112 auto linalgOp = dyn_cast<linalg::LinalgOp>(use.getOwner()); 113 if (linalgOp && linalgOp.isOutputTensor(&use)) 114 return true; 115 } 116 } 117 return linalg::skipUnitDimReshape(producer, consumer); 118 }; 119 120 linalg::populateFoldReshapeOpsByExpansionPatterns(fusionPatterns, 121 controlReshapeFusionFn); 122 (void)applyPatternsAndFoldGreedily(funcOp.getBody(), 123 std::move(fusionPatterns)); 124 return; 125 } 126 127 if (pushExpandingReshape) { 128 RewritePatternSet patterns(context); 129 linalg::populatePushReshapeOpsPatterns(patterns); 130 (void)applyPatternsAndFoldGreedily(funcOp.getBody(), std::move(patterns)); 131 } 132 } 133 }; 134 135 } // namespace 136 137 namespace test { 138 void registerTestLinalgElementwiseFusion() { 139 PassRegistration<TestLinalgElementwiseFusion>(); 140 } 141 } // namespace test 142 143 } // namespace mlir 144