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/Func/IR/FuncOps.h" 15 #include "mlir/Dialect/Linalg/Transforms/Transforms.h" 16 #include "mlir/Pass/Pass.h" 17 #include "mlir/Pass/PassManager.h" 18 #include "mlir/Transforms/GreedyPatternRewriteDriver.h" 19 #include "llvm/ADT/TypeSwitch.h" 20 21 namespace mlir { 22 23 static void addOperands(Operation *op, SetVector<Value> &operandSet) { 24 if (!op) 25 return; 26 TypeSwitch<Operation *, void>(op) 27 .Case<linalg::LinalgOp>([&](linalg::LinalgOp linalgOp) { 28 SmallVector<Value> inputOperands = linalgOp.getInputOperands(); 29 operandSet.insert(inputOperands.begin(), inputOperands.end()); 30 }) 31 .Default([&](Operation *operation) { 32 operandSet.insert(operation->operand_begin(), operation->operand_end()); 33 }); 34 } 35 36 template <int limit = 3> 37 static bool setFusedOpOperandLimit(const OpResult &producer, 38 const OpOperand &consumer) { 39 SetVector<Value> fusedOpOperands; 40 if (producer.getOwner()->getNumResults() != 1) 41 return false; 42 addOperands(consumer.getOwner(), fusedOpOperands); 43 fusedOpOperands.remove(producer); 44 addOperands(producer.getOwner(), fusedOpOperands); 45 return fusedOpOperands.size() <= limit; 46 } 47 48 namespace { 49 struct TestLinalgElementwiseFusion 50 : public PassWrapper<TestLinalgElementwiseFusion, OperationPass<FuncOp>> { 51 TestLinalgElementwiseFusion() = default; 52 TestLinalgElementwiseFusion(const TestLinalgElementwiseFusion &pass) 53 : PassWrapper(pass) {} 54 void getDependentDialects(DialectRegistry ®istry) const override { 55 registry.insert<AffineDialect, linalg::LinalgDialect, memref::MemRefDialect, 56 tensor::TensorDialect>(); 57 } 58 StringRef getArgument() const final { 59 return "test-linalg-elementwise-fusion-patterns"; 60 } 61 StringRef getDescription() const final { 62 return "Test Linalg element wise operation fusion patterns"; 63 } 64 65 Option<bool> fuseGenericOps{ 66 *this, "fuse-generic-ops", 67 llvm::cl::desc("Test fusion of generic operations."), 68 llvm::cl::init(false)}; 69 70 Option<bool> controlFuseByExpansion{ 71 *this, "control-fusion-by-expansion", 72 llvm::cl::desc( 73 "Test controlling fusion of reshape with generic op by expansion"), 74 llvm::cl::init(false)}; 75 76 Option<bool> pushExpandingReshape{ 77 *this, "push-expanding-reshape", 78 llvm::cl::desc("Test linalg expand_shape -> generic " 79 "to generic -> expand_shape pattern"), 80 llvm::cl::init(false)}; 81 82 Option<bool> fuseWithReshapeByCollapsing{ 83 *this, "fuse-with-reshape-by-collapsing", 84 llvm::cl::desc("Test linalg expand_shape -> generic fusion patterns that " 85 "collapse the iteration space of the consumer"), 86 llvm::cl::init(false)}; 87 88 Option<bool> fuseWithReshapeByCollapsingWithControlFn{ 89 *this, "fuse-with-reshape-by-collapsing-control", 90 llvm::cl::desc("Test controlling the linalg expand_shape -> generic " 91 "fusion patterns that " 92 "collapse the iteration space of the consumer"), 93 llvm::cl::init(false)}; 94 95 void runOnOperation() override { 96 MLIRContext *context = &this->getContext(); 97 FuncOp funcOp = this->getOperation(); 98 99 if (fuseGenericOps) { 100 RewritePatternSet fusionPatterns(context); 101 linalg::populateElementwiseOpsFusionPatterns( 102 fusionPatterns, 103 linalg::LinalgElementwiseFusionOptions() 104 .setControlElementwiseOpsFusionFn(setFusedOpOperandLimit<4>)); 105 106 (void)applyPatternsAndFoldGreedily(funcOp.getBody(), 107 std::move(fusionPatterns)); 108 return; 109 } 110 111 if (controlFuseByExpansion) { 112 RewritePatternSet fusionPatterns(context); 113 114 linalg::ControlElementwiseOpsFusionFn controlReshapeFusionFn = 115 [](const OpResult &producer, OpOperand &consumer) { 116 if (auto collapseOp = 117 producer.getDefiningOp<tensor::CollapseShapeOp>()) { 118 if (!collapseOp.src().getDefiningOp<linalg::LinalgOp>()) { 119 return false; 120 } 121 } 122 if (auto expandOp = 123 dyn_cast<tensor::ExpandShapeOp>(consumer.getOwner())) { 124 if (expandOp->hasOneUse()) { 125 OpOperand &use = *expandOp->getUses().begin(); 126 auto linalgOp = dyn_cast<linalg::LinalgOp>(use.getOwner()); 127 if (linalgOp && linalgOp.isOutputTensor(&use)) 128 return true; 129 } 130 } 131 return linalg::skipUnitDimReshape(producer, consumer); 132 }; 133 134 linalg::populateFoldReshapeOpsByExpansionPatterns(fusionPatterns, 135 controlReshapeFusionFn); 136 (void)applyPatternsAndFoldGreedily(funcOp.getBody(), 137 std::move(fusionPatterns)); 138 return; 139 } 140 141 if (pushExpandingReshape) { 142 RewritePatternSet patterns(context); 143 linalg::populatePushReshapeOpsPatterns(patterns); 144 (void)applyPatternsAndFoldGreedily(funcOp.getBody(), std::move(patterns)); 145 } 146 147 if (fuseWithReshapeByCollapsing) { 148 RewritePatternSet patterns(context); 149 linalg::populateFoldReshapeOpsByCollapsingPatterns(patterns); 150 (void)applyPatternsAndFoldGreedily(funcOp.getBody(), std::move(patterns)); 151 } 152 153 if (fuseWithReshapeByCollapsingWithControlFn) { 154 RewritePatternSet patterns(context); 155 linalg::ControlElementwiseOpsFusionFn controlFn = 156 [](const OpResult &producer, OpOperand &consumer) -> bool { 157 if (isa<tensor::ExpandShapeOp>(producer.getDefiningOp())) { 158 // Skip fusing the first operand. 159 return consumer.getOperandNumber(); 160 } 161 return true; 162 }; 163 linalg::populateFoldReshapeOpsByCollapsingPatterns(patterns, controlFn); 164 (void)applyPatternsAndFoldGreedily(funcOp.getBody(), std::move(patterns)); 165 } 166 } 167 }; 168 169 } // namespace 170 171 namespace test { 172 void registerTestLinalgElementwiseFusion() { 173 PassRegistration<TestLinalgElementwiseFusion>(); 174 } 175 } // namespace test 176 177 } // namespace mlir 178