github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/test/lib/Transforms/TestVectorizationUtils.cpp (about) 1 //===- VectorizerTestPass.cpp - VectorizerTestPass Pass Impl --------------===// 2 // 3 // Copyright 2019 The MLIR Authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 // ============================================================================= 17 // 18 // This file implements a simple testing pass for vectorization functionality. 19 // 20 //===----------------------------------------------------------------------===// 21 22 #include "mlir/Analysis/AffineAnalysis.h" 23 #include "mlir/Analysis/NestedMatcher.h" 24 #include "mlir/Analysis/SliceAnalysis.h" 25 #include "mlir/Analysis/VectorAnalysis.h" 26 #include "mlir/Dialect/AffineOps/AffineOps.h" 27 #include "mlir/IR/Builders.h" 28 #include "mlir/IR/Diagnostics.h" 29 #include "mlir/IR/StandardTypes.h" 30 #include "mlir/Pass/Pass.h" 31 #include "mlir/Support/Functional.h" 32 #include "mlir/Support/STLExtras.h" 33 #include "mlir/Transforms/Passes.h" 34 35 #include "llvm/ADT/STLExtras.h" 36 #include "llvm/Support/CommandLine.h" 37 #include "llvm/Support/Debug.h" 38 39 #define DEBUG_TYPE "affine-vectorizer-test" 40 41 using namespace mlir; 42 43 using llvm::SetVector; 44 45 using functional::map; 46 47 static llvm::cl::OptionCategory clOptionsCategory(DEBUG_TYPE " options"); 48 49 static llvm::cl::list<int> clTestVectorShapeRatio( 50 "vector-shape-ratio", 51 llvm::cl::desc("Specify the HW vector size for vectorization"), 52 llvm::cl::ZeroOrMore, llvm::cl::cat(clOptionsCategory)); 53 static llvm::cl::opt<bool> clTestForwardSlicingAnalysis( 54 "forward-slicing", 55 llvm::cl::desc("Enable testing forward static slicing and topological sort " 56 "functionalities"), 57 llvm::cl::cat(clOptionsCategory)); 58 static llvm::cl::opt<bool> clTestBackwardSlicingAnalysis( 59 "backward-slicing", 60 llvm::cl::desc("Enable testing backward static slicing and " 61 "topological sort functionalities"), 62 llvm::cl::cat(clOptionsCategory)); 63 static llvm::cl::opt<bool> clTestSlicingAnalysis( 64 "slicing", 65 llvm::cl::desc("Enable testing static slicing and topological sort " 66 "functionalities"), 67 llvm::cl::cat(clOptionsCategory)); 68 static llvm::cl::opt<bool> clTestComposeMaps( 69 "compose-maps", 70 llvm::cl::desc( 71 "Enable testing the composition of AffineMap where each " 72 "AffineMap in the composition is specified as the affine_map attribute " 73 "in a constant op."), 74 llvm::cl::cat(clOptionsCategory)); 75 static llvm::cl::opt<bool> clTestNormalizeMaps( 76 "normalize-maps", 77 llvm::cl::desc( 78 "Enable testing the normalization of AffineAffineApplyOp " 79 "where each AffineAffineApplyOp in the composition is a single output " 80 "operation."), 81 llvm::cl::cat(clOptionsCategory)); 82 83 namespace { 84 struct VectorizerTestPass : public FunctionPass<VectorizerTestPass> { 85 static constexpr auto kTestAffineMapOpName = "test_affine_map"; 86 static constexpr auto kTestAffineMapAttrName = "affine_map"; 87 88 void runOnFunction() override; 89 void testVectorShapeRatio(llvm::raw_ostream &outs); 90 void testForwardSlicing(llvm::raw_ostream &outs); 91 void testBackwardSlicing(llvm::raw_ostream &outs); 92 void testSlicing(llvm::raw_ostream &outs); 93 void testComposeMaps(llvm::raw_ostream &outs); 94 void testNormalizeMaps(); 95 }; 96 97 } // end anonymous namespace 98 99 void VectorizerTestPass::testVectorShapeRatio(llvm::raw_ostream &outs) { 100 auto f = getFunction(); 101 using matcher::Op; 102 SmallVector<int64_t, 8> shape(clTestVectorShapeRatio.begin(), 103 clTestVectorShapeRatio.end()); 104 auto subVectorType = 105 VectorType::get(shape, FloatType::getF32(f.getContext())); 106 // Only filter operations that operate on a strict super-vector and have one 107 // return. This makes testing easier. 108 auto filter = [&](Operation &op) { 109 assert(subVectorType.getElementType().isF32() && 110 "Only f32 supported for now"); 111 if (!matcher::operatesOnSuperVectorsOf(op, subVectorType)) { 112 return false; 113 } 114 if (op.getNumResults() != 1) { 115 return false; 116 } 117 return true; 118 }; 119 auto pat = Op(filter); 120 SmallVector<NestedMatch, 8> matches; 121 pat.match(f, &matches); 122 for (auto m : matches) { 123 auto *opInst = m.getMatchedOperation(); 124 // This is a unit test that only checks and prints shape ratio. 125 // As a consequence we write only Ops with a single return type for the 126 // purpose of this test. If we need to test more intricate behavior in the 127 // future we can always extend. 128 auto superVectorType = opInst->getResult(0)->getType().cast<VectorType>(); 129 auto ratio = shapeRatio(superVectorType, subVectorType); 130 if (!ratio.hasValue()) { 131 opInst->emitRemark("NOT MATCHED"); 132 } else { 133 outs << "\nmatched: " << *opInst << " with shape ratio: "; 134 interleaveComma(MutableArrayRef<unsigned>(*ratio), outs); 135 } 136 } 137 } 138 139 static NestedPattern patternTestSlicingOps() { 140 using functional::map; 141 using matcher::Op; 142 // Match all operations with the kTestSlicingOpName name. 143 auto filter = [](Operation &op) { 144 // Just use a custom op name for this test, it makes life easier. 145 return op.getName().getStringRef() == "slicing-test-op"; 146 }; 147 return Op(filter); 148 } 149 150 void VectorizerTestPass::testBackwardSlicing(llvm::raw_ostream &outs) { 151 auto f = getFunction(); 152 outs << "\n" << f.getName(); 153 154 SmallVector<NestedMatch, 8> matches; 155 patternTestSlicingOps().match(f, &matches); 156 for (auto m : matches) { 157 SetVector<Operation *> backwardSlice; 158 getBackwardSlice(m.getMatchedOperation(), &backwardSlice); 159 outs << "\nmatched: " << *m.getMatchedOperation() 160 << " backward static slice: "; 161 for (auto *op : backwardSlice) 162 outs << "\n" << *op; 163 } 164 } 165 166 void VectorizerTestPass::testForwardSlicing(llvm::raw_ostream &outs) { 167 auto f = getFunction(); 168 outs << "\n" << f.getName(); 169 170 SmallVector<NestedMatch, 8> matches; 171 patternTestSlicingOps().match(f, &matches); 172 for (auto m : matches) { 173 SetVector<Operation *> forwardSlice; 174 getForwardSlice(m.getMatchedOperation(), &forwardSlice); 175 outs << "\nmatched: " << *m.getMatchedOperation() 176 << " forward static slice: "; 177 for (auto *op : forwardSlice) 178 outs << "\n" << *op; 179 } 180 } 181 182 void VectorizerTestPass::testSlicing(llvm::raw_ostream &outs) { 183 auto f = getFunction(); 184 outs << "\n" << f.getName(); 185 186 SmallVector<NestedMatch, 8> matches; 187 patternTestSlicingOps().match(f, &matches); 188 for (auto m : matches) { 189 SetVector<Operation *> staticSlice = getSlice(m.getMatchedOperation()); 190 outs << "\nmatched: " << *m.getMatchedOperation() << " static slice: "; 191 for (auto *op : staticSlice) 192 outs << "\n" << *op; 193 } 194 } 195 196 static bool customOpWithAffineMapAttribute(Operation &op) { 197 return op.getName().getStringRef() == 198 VectorizerTestPass::kTestAffineMapOpName; 199 } 200 201 void VectorizerTestPass::testComposeMaps(llvm::raw_ostream &outs) { 202 auto f = getFunction(); 203 204 using matcher::Op; 205 auto pattern = Op(customOpWithAffineMapAttribute); 206 SmallVector<NestedMatch, 8> matches; 207 pattern.match(f, &matches); 208 SmallVector<AffineMap, 4> maps; 209 maps.reserve(matches.size()); 210 for (auto m : llvm::reverse(matches)) { 211 auto *opInst = m.getMatchedOperation(); 212 auto map = opInst->getAttr(VectorizerTestPass::kTestAffineMapAttrName) 213 .cast<AffineMapAttr>() 214 .getValue(); 215 maps.push_back(map); 216 } 217 AffineMap res; 218 for (auto m : maps) { 219 res = res ? res.compose(m) : m; 220 } 221 simplifyAffineMap(res).print(outs << "\nComposed map: "); 222 } 223 224 static bool affineApplyOp(Operation &op) { return isa<AffineApplyOp>(op); } 225 226 static bool singleResultAffineApplyOpWithoutUses(Operation &op) { 227 auto app = dyn_cast<AffineApplyOp>(op); 228 return app && app.use_empty(); 229 } 230 231 void VectorizerTestPass::testNormalizeMaps() { 232 using matcher::Op; 233 234 auto f = getFunction(); 235 236 // Save matched AffineApplyOp that all need to be erased in the end. 237 auto pattern = Op(affineApplyOp); 238 SmallVector<NestedMatch, 8> toErase; 239 pattern.match(f, &toErase); 240 { 241 // Compose maps. 242 auto pattern = Op(singleResultAffineApplyOpWithoutUses); 243 SmallVector<NestedMatch, 8> matches; 244 pattern.match(f, &matches); 245 for (auto m : matches) { 246 auto app = cast<AffineApplyOp>(m.getMatchedOperation()); 247 OpBuilder b(m.getMatchedOperation()); 248 SmallVector<Value *, 8> operands(app.getOperands()); 249 makeComposedAffineApply(b, app.getLoc(), app.getAffineMap(), operands); 250 } 251 } 252 // We should now be able to erase everything in reverse order in this test. 253 for (auto m : llvm::reverse(toErase)) { 254 m.getMatchedOperation()->erase(); 255 } 256 } 257 258 void VectorizerTestPass::runOnFunction() { 259 // Thread-safe RAII local context, BumpPtrAllocator freed on exit. 260 NestedPatternContext mlContext; 261 262 // Only support single block functions at this point. 263 FuncOp f = getFunction(); 264 if (f.getBlocks().size() != 1) 265 return; 266 267 std::string str; 268 llvm::raw_string_ostream outs(str); 269 270 if (!clTestVectorShapeRatio.empty()) 271 testVectorShapeRatio(outs); 272 273 if (clTestForwardSlicingAnalysis) 274 testForwardSlicing(outs); 275 276 if (clTestBackwardSlicingAnalysis) 277 testBackwardSlicing(outs); 278 279 if (clTestSlicingAnalysis) 280 testSlicing(outs); 281 282 if (clTestComposeMaps) 283 testComposeMaps(outs); 284 285 if (clTestNormalizeMaps) 286 testNormalizeMaps(); 287 288 if (!outs.str().empty()) { 289 emitRemark(UnknownLoc::get(&getContext()), outs.str()); 290 } 291 } 292 293 std::unique_ptr<FunctionPassBase> mlir::createVectorizerTestPass() { 294 return std::make_unique<VectorizerTestPass>(); 295 } 296 297 static PassRegistration<VectorizerTestPass> 298 pass("affine-vectorizer-test", 299 "Tests vectorizer standalone functionality."); 300 301 #undef DEBUG_TYPE