1 //===- PassTest.cpp -------------------------------------------------------===// 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 #include "llvm/SandboxIR/Pass.h" 10 #include "llvm/Analysis/TargetTransformInfo.h" 11 #include "llvm/AsmParser/Parser.h" 12 #include "llvm/IR/Module.h" 13 #include "llvm/SandboxIR/Constant.h" 14 #include "llvm/SandboxIR/Context.h" 15 #include "llvm/SandboxIR/Function.h" 16 #include "llvm/SandboxIR/PassManager.h" 17 #include "llvm/SandboxIR/Region.h" 18 #include "llvm/Support/SourceMgr.h" 19 #include "gtest/gtest.h" 20 21 using namespace llvm::sandboxir; 22 23 struct PassTest : public testing::Test { 24 llvm::LLVMContext LLVMCtx; 25 std::unique_ptr<llvm::Module> LLVMM; 26 std::unique_ptr<Context> Ctx; 27 std::unique_ptr<llvm::TargetTransformInfo> TTI; 28 29 Function *parseFunction(const char *IR, const char *FuncName) { 30 llvm::SMDiagnostic Err; 31 LLVMM = parseAssemblyString(IR, Err, LLVMCtx); 32 TTI = std::make_unique<llvm::TargetTransformInfo>(LLVMM->getDataLayout()); 33 34 if (!LLVMM) 35 Err.print("PassTest", llvm::errs()); 36 Ctx = std::make_unique<Context>(LLVMCtx); 37 return Ctx->createFunction(LLVMM->getFunction(FuncName)); 38 } 39 }; 40 41 TEST_F(PassTest, FunctionPass) { 42 auto *F = parseFunction(R"IR( 43 define void @foo() { 44 ret void 45 } 46 )IR", 47 "foo"); 48 class TestPass final : public FunctionPass { 49 unsigned &BBCnt; 50 51 public: 52 TestPass(unsigned &BBCnt) : FunctionPass("test-pass"), BBCnt(BBCnt) {} 53 bool runOnFunction(Function &F, const Analyses &A) final { 54 for ([[maybe_unused]] auto &BB : F) 55 ++BBCnt; 56 return false; 57 } 58 }; 59 unsigned BBCnt = 0; 60 TestPass TPass(BBCnt); 61 // Check getName(), 62 EXPECT_EQ(TPass.getName(), "test-pass"); 63 // Check classof(). 64 EXPECT_TRUE(llvm::isa<FunctionPass>(TPass)); 65 // Check runOnFunction(); 66 TPass.runOnFunction(*F, Analyses::emptyForTesting()); 67 EXPECT_EQ(BBCnt, 1u); 68 #ifndef NDEBUG 69 { 70 // Check print(). 71 std::string Buff; 72 llvm::raw_string_ostream SS(Buff); 73 TPass.print(SS); 74 EXPECT_EQ(Buff, "test-pass"); 75 } 76 { 77 // Check operator<<(). 78 std::string Buff; 79 llvm::raw_string_ostream SS(Buff); 80 SS << TPass; 81 EXPECT_EQ(Buff, "test-pass"); 82 } 83 // Check pass name assertions. 84 class TestNamePass final : public FunctionPass { 85 public: 86 TestNamePass(llvm::StringRef Name) : FunctionPass(Name) {} 87 bool runOnFunction(Function &F, const Analyses &A) { return false; } 88 }; 89 EXPECT_DEATH(TestNamePass("white space"), ".*whitespace.*"); 90 EXPECT_DEATH(TestNamePass("-dash"), ".*start with.*"); 91 #endif 92 } 93 94 TEST_F(PassTest, RegionPass) { 95 auto *F = parseFunction(R"IR( 96 define i8 @foo(i8 %v0, i8 %v1) { 97 %t0 = add i8 %v0, 1 98 %t1 = add i8 %t0, %v1, !sandboxvec !0 99 %t2 = add i8 %t1, %v1, !sandboxvec !0 100 ret i8 %t1 101 } 102 103 !0 = distinct !{!"sandboxregion"} 104 )IR", 105 "foo"); 106 107 class TestPass final : public RegionPass { 108 unsigned &InstCount; 109 110 public: 111 TestPass(unsigned &InstCount) 112 : RegionPass("test-pass"), InstCount(InstCount) {} 113 bool runOnRegion(Region &R, const Analyses &A) final { 114 for ([[maybe_unused]] auto &Inst : R) { 115 ++InstCount; 116 } 117 return false; 118 } 119 }; 120 unsigned InstCount = 0; 121 TestPass TPass(InstCount); 122 // Check getName(), 123 EXPECT_EQ(TPass.getName(), "test-pass"); 124 // Check runOnRegion(); 125 llvm::SmallVector<std::unique_ptr<Region>> Regions = 126 Region::createRegionsFromMD(*F, *TTI); 127 ASSERT_EQ(Regions.size(), 1u); 128 TPass.runOnRegion(*Regions[0], Analyses::emptyForTesting()); 129 EXPECT_EQ(InstCount, 2u); 130 #ifndef NDEBUG 131 { 132 // Check print(). 133 std::string Buff; 134 llvm::raw_string_ostream SS(Buff); 135 TPass.print(SS); 136 EXPECT_EQ(Buff, "test-pass"); 137 } 138 { 139 // Check operator<<(). 140 std::string Buff; 141 llvm::raw_string_ostream SS(Buff); 142 SS << TPass; 143 EXPECT_EQ(Buff, "test-pass"); 144 } 145 // Check pass name assertions. 146 class TestNamePass final : public RegionPass { 147 public: 148 TestNamePass(llvm::StringRef Name) : RegionPass(Name) {} 149 bool runOnRegion(Region &F, const Analyses &A) { return false; } 150 }; 151 EXPECT_DEATH(TestNamePass("white space"), ".*whitespace.*"); 152 EXPECT_DEATH(TestNamePass("-dash"), ".*start with.*"); 153 #endif 154 } 155 156 TEST_F(PassTest, FunctionPassManager) { 157 auto *F = parseFunction(R"IR( 158 define void @foo() { 159 ret void 160 } 161 )IR", 162 "foo"); 163 class TestPass1 final : public FunctionPass { 164 unsigned &BBCnt; 165 166 public: 167 TestPass1(unsigned &BBCnt) : FunctionPass("test-pass1"), BBCnt(BBCnt) {} 168 bool runOnFunction(Function &F, const Analyses &A) final { 169 for ([[maybe_unused]] auto &BB : F) 170 ++BBCnt; 171 return false; 172 } 173 }; 174 class TestPass2 final : public FunctionPass { 175 unsigned &BBCnt; 176 177 public: 178 TestPass2(unsigned &BBCnt) : FunctionPass("test-pass2"), BBCnt(BBCnt) {} 179 bool runOnFunction(Function &F, const Analyses &A) final { 180 for ([[maybe_unused]] auto &BB : F) 181 ++BBCnt; 182 return false; 183 } 184 }; 185 unsigned BBCnt1 = 0; 186 unsigned BBCnt2 = 0; 187 188 FunctionPassManager FPM("test-fpm"); 189 FPM.addPass(std::make_unique<TestPass1>(BBCnt1)); 190 FPM.addPass(std::make_unique<TestPass2>(BBCnt2)); 191 // Check runOnFunction(). 192 FPM.runOnFunction(*F, Analyses::emptyForTesting()); 193 EXPECT_EQ(BBCnt1, 1u); 194 EXPECT_EQ(BBCnt2, 1u); 195 #ifndef NDEBUG 196 // Check dump(). 197 std::string Buff; 198 llvm::raw_string_ostream SS(Buff); 199 FPM.print(SS); 200 EXPECT_EQ(Buff, "test-fpm(test-pass1,test-pass2)"); 201 #endif // NDEBUG 202 } 203 204 TEST_F(PassTest, RegionPassManager) { 205 auto *F = parseFunction(R"IR( 206 define i8 @foo(i8 %v0, i8 %v1) { 207 %t0 = add i8 %v0, 1 208 %t1 = add i8 %t0, %v1, !sandboxvec !0 209 %t2 = add i8 %t1, %v1, !sandboxvec !0 210 ret i8 %t1 211 } 212 213 !0 = distinct !{!"sandboxregion"} 214 )IR", 215 "foo"); 216 217 class TestPass1 final : public RegionPass { 218 unsigned &InstCount; 219 220 public: 221 TestPass1(unsigned &InstCount) 222 : RegionPass("test-pass1"), InstCount(InstCount) {} 223 bool runOnRegion(Region &R, const Analyses &A) final { 224 for ([[maybe_unused]] auto &Inst : R) 225 ++InstCount; 226 return false; 227 } 228 }; 229 class TestPass2 final : public RegionPass { 230 unsigned &InstCount; 231 232 public: 233 TestPass2(unsigned &InstCount) 234 : RegionPass("test-pass2"), InstCount(InstCount) {} 235 bool runOnRegion(Region &R, const Analyses &A) final { 236 for ([[maybe_unused]] auto &Inst : R) 237 ++InstCount; 238 return false; 239 } 240 }; 241 unsigned InstCount1 = 0; 242 unsigned InstCount2 = 0; 243 244 RegionPassManager RPM("test-rpm"); 245 RPM.addPass(std::make_unique<TestPass1>(InstCount1)); 246 RPM.addPass(std::make_unique<TestPass2>(InstCount2)); 247 // Check runOnRegion(). 248 llvm::SmallVector<std::unique_ptr<Region>> Regions = 249 Region::createRegionsFromMD(*F, *TTI); 250 ASSERT_EQ(Regions.size(), 1u); 251 RPM.runOnRegion(*Regions[0], Analyses::emptyForTesting()); 252 EXPECT_EQ(InstCount1, 2u); 253 EXPECT_EQ(InstCount2, 2u); 254 #ifndef NDEBUG 255 // Check dump(). 256 std::string Buff; 257 llvm::raw_string_ostream SS(Buff); 258 RPM.print(SS); 259 EXPECT_EQ(Buff, "test-rpm(test-pass1,test-pass2)"); 260 #endif // NDEBUG 261 } 262 263 TEST_F(PassTest, SetPassPipeline) { 264 auto *F = parseFunction(R"IR( 265 define void @f() { 266 ret void 267 } 268 )IR", 269 "f"); 270 class FooPass final : public FunctionPass { 271 std::string &Str; 272 std::string Args; 273 274 public: 275 FooPass(std::string &Str, llvm::StringRef Args) 276 : FunctionPass("foo-pass"), Str(Str), Args(Args.str()) {} 277 bool runOnFunction(Function &F, const Analyses &A) final { 278 Str += "foo<" + Args + ">"; 279 return false; 280 } 281 }; 282 class BarPass final : public FunctionPass { 283 std::string &Str; 284 std::string Args; 285 286 public: 287 BarPass(std::string &Str, llvm::StringRef Args) 288 : FunctionPass("bar-pass"), Str(Str), Args(Args.str()) {} 289 bool runOnFunction(Function &F, const Analyses &A) final { 290 Str += "bar<" + Args + ">"; 291 return false; 292 } 293 }; 294 295 std::string Str; 296 auto CreatePass = 297 [&Str](llvm::StringRef Name, 298 llvm::StringRef Args) -> std::unique_ptr<FunctionPass> { 299 if (Name == "foo") 300 return std::make_unique<FooPass>(Str, Args); 301 if (Name == "bar") 302 return std::make_unique<BarPass>(Str, Args); 303 return nullptr; 304 }; 305 306 FunctionPassManager FPM("test-fpm"); 307 FPM.setPassPipeline("foo<abc>,bar<nested1<nested2<nested3>>>,foo", 308 CreatePass); 309 FPM.runOnFunction(*F, Analyses::emptyForTesting()); 310 EXPECT_EQ(Str, "foo<abc>bar<nested1<nested2<nested3>>>foo<>"); 311 312 // A second call to setPassPipeline will trigger an assertion in debug mode. 313 #ifndef NDEBUG 314 EXPECT_DEATH(FPM.setPassPipeline("bar,bar,foo", CreatePass), 315 "setPassPipeline called on a non-empty sandboxir::PassManager"); 316 #endif 317 318 // Fresh PM for the death tests so they die from bad pipeline strings, rather 319 // than from multiple setPassPipeline calls. 320 FunctionPassManager FPM2("test-fpm"); 321 // Bad/empty pass names. 322 EXPECT_DEATH(FPM2.setPassPipeline("bad-pass-name", CreatePass), 323 ".*not registered.*"); 324 EXPECT_DEATH(FPM2.setPassPipeline(",", CreatePass), ".*empty pass name.*"); 325 EXPECT_DEATH(FPM2.setPassPipeline("<>", CreatePass), ".*empty pass name.*"); 326 EXPECT_DEATH(FPM2.setPassPipeline("<>foo", CreatePass), 327 ".*empty pass name.*"); 328 EXPECT_DEATH(FPM2.setPassPipeline("foo,<>", CreatePass), 329 ".*empty pass name.*"); 330 331 // Mismatched argument brackets. 332 EXPECT_DEATH(FPM2.setPassPipeline("foo<", CreatePass), ".*Missing '>'.*"); 333 EXPECT_DEATH(FPM2.setPassPipeline("foo<bar", CreatePass), ".*Missing '>'.*"); 334 EXPECT_DEATH(FPM2.setPassPipeline("foo<bar<>", CreatePass), 335 ".*Missing '>'.*"); 336 EXPECT_DEATH(FPM2.setPassPipeline("foo>", CreatePass), ".*Unexpected '>'.*"); 337 EXPECT_DEATH(FPM2.setPassPipeline(">foo", CreatePass), ".*Unexpected '>'.*"); 338 // Extra garbage between args and next delimiter/end-of-string. 339 EXPECT_DEATH(FPM2.setPassPipeline("foo<bar<>>>", CreatePass), 340 ".*Expected delimiter.*"); 341 EXPECT_DEATH(FPM2.setPassPipeline("bar<>foo", CreatePass), 342 ".*Expected delimiter.*"); 343 EXPECT_DEATH(FPM2.setPassPipeline("bar<>foo,baz", CreatePass), 344 ".*Expected delimiter.*"); 345 EXPECT_DEATH(FPM2.setPassPipeline("foo<args><more-args>", CreatePass), 346 ".*Expected delimiter.*"); 347 EXPECT_DEATH(FPM2.setPassPipeline("foo<args>bar", CreatePass), 348 ".*Expected delimiter.*"); 349 } 350