xref: /llvm-project/llvm/unittests/SandboxIR/PassTest.cpp (revision 4b209c5d87c8b8eb4bbf2750ea9daa5927a13699)
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