xref: /llvm-project/llvm/unittests/SandboxIR/RegionTest.cpp (revision 4b209c5d87c8b8eb4bbf2750ea9daa5927a13699)
1 //===- RegionTest.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/Region.h"
10 #include "llvm/Analysis/TargetTransformInfo.h"
11 #include "llvm/AsmParser/Parser.h"
12 #include "llvm/SandboxIR/Context.h"
13 #include "llvm/SandboxIR/Function.h"
14 #include "llvm/SandboxIR/Instruction.h"
15 #include "llvm/Support/SourceMgr.h"
16 #include "gmock/gmock-matchers.h"
17 #include "gtest/gtest.h"
18 
19 using namespace llvm;
20 
21 struct RegionTest : public testing::Test {
22   LLVMContext C;
23   std::unique_ptr<Module> M;
24   std::unique_ptr<TargetTransformInfo> TTI;
25 
26   void parseIR(LLVMContext &C, const char *IR) {
27     SMDiagnostic Err;
28     M = parseAssemblyString(IR, Err, C);
29     TTI = std::make_unique<TargetTransformInfo>(M->getDataLayout());
30     if (!M)
31       Err.print("RegionTest", errs());
32   }
33 };
34 
35 TEST_F(RegionTest, Basic) {
36   parseIR(C, R"IR(
37 define i8 @foo(i8 %v0, i8 %v1) {
38   %t0 = add i8 %v0, 1
39   %t1 = add i8 %t0, %v1
40   ret i8 %t1
41 }
42 )IR");
43   llvm::Function *LLVMF = &*M->getFunction("foo");
44   sandboxir::Context Ctx(C);
45   auto *F = Ctx.createFunction(LLVMF);
46   auto *BB = &*F->begin();
47   auto It = BB->begin();
48   auto *T0 = cast<sandboxir::Instruction>(&*It++);
49   auto *T1 = cast<sandboxir::Instruction>(&*It++);
50   auto *Ret = cast<sandboxir::Instruction>(&*It++);
51   sandboxir::Region Rgn(Ctx, *TTI);
52 
53   // Check getContext.
54   EXPECT_EQ(&Ctx, &Rgn.getContext());
55 
56   // Check add / remove / empty.
57   EXPECT_TRUE(Rgn.empty());
58   Rgn.add(T0);
59   EXPECT_FALSE(Rgn.empty());
60   Rgn.remove(T0);
61   EXPECT_TRUE(Rgn.empty());
62 
63   // Check iteration.
64   Rgn.add(T0);
65   Rgn.add(T1);
66   Rgn.add(Ret);
67   // Use an ordered matcher because we're supposed to preserve the insertion
68   // order for determinism.
69   EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, T1, Ret));
70 
71   // Check contains
72   EXPECT_TRUE(Rgn.contains(T0));
73   Rgn.remove(T0);
74   EXPECT_FALSE(Rgn.contains(T0));
75 
76 #ifndef NDEBUG
77   // Check equality comparison. Insert in reverse order into `Other` to check
78   // that comparison is order-independent.
79   sandboxir::Region Other(Ctx, *TTI);
80   Other.add(Ret);
81   EXPECT_NE(Rgn, Other);
82   Other.add(T1);
83   EXPECT_EQ(Rgn, Other);
84 #endif
85 }
86 
87 TEST_F(RegionTest, CallbackUpdates) {
88   parseIR(C, R"IR(
89 define i8 @foo(i8 %v0, i8 %v1, ptr %ptr) {
90   %t0 = add i8 %v0, 1
91   %t1 = add i8 %t0, %v1
92   ret i8 %t0
93 }
94 )IR");
95   llvm::Function *LLVMF = &*M->getFunction("foo");
96   sandboxir::Context Ctx(C);
97   auto *F = Ctx.createFunction(LLVMF);
98   auto *Ptr = F->getArg(2);
99   auto *BB = &*F->begin();
100   auto It = BB->begin();
101   auto *T0 = cast<sandboxir::Instruction>(&*It++);
102   auto *T1 = cast<sandboxir::Instruction>(&*It++);
103   auto *Ret = cast<sandboxir::Instruction>(&*It++);
104   sandboxir::Region Rgn(Ctx, *TTI);
105   Rgn.add(T0);
106   Rgn.add(T1);
107 
108   // Test creation.
109   auto *NewI = sandboxir::StoreInst::create(T0, Ptr, /*Align=*/std::nullopt,
110                                             Ret->getIterator(), Ctx);
111   EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, T1, NewI));
112 
113   // Test deletion.
114   T1->eraseFromParent();
115   EXPECT_THAT(Rgn.insts(), testing::ElementsAre(T0, NewI));
116 }
117 
118 TEST_F(RegionTest, MetadataFromIR) {
119   parseIR(C, R"IR(
120 define i8 @foo(i8 %v0, i8 %v1) {
121   %t0 = add i8 %v0, 1, !sandboxvec !0
122   %t1 = add i8 %t0, %v1, !sandboxvec !1
123   %t2 = add i8 %t1, %v1, !sandboxvec !1
124   ret i8 %t2
125 }
126 
127 !0 = distinct !{!"sandboxregion"}
128 !1 = distinct !{!"sandboxregion"}
129 )IR");
130   llvm::Function *LLVMF = &*M->getFunction("foo");
131   sandboxir::Context Ctx(C);
132   auto *F = Ctx.createFunction(LLVMF);
133   auto *BB = &*F->begin();
134   auto It = BB->begin();
135   auto *T0 = cast<sandboxir::Instruction>(&*It++);
136   auto *T1 = cast<sandboxir::Instruction>(&*It++);
137   auto *T2 = cast<sandboxir::Instruction>(&*It++);
138 
139   SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
140       sandboxir::Region::createRegionsFromMD(*F, *TTI);
141   EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0));
142   EXPECT_THAT(Regions[1]->insts(), testing::UnorderedElementsAre(T1, T2));
143 }
144 
145 TEST_F(RegionTest, NonContiguousRegion) {
146   parseIR(C, R"IR(
147 define i8 @foo(i8 %v0, i8 %v1) {
148   %t0 = add i8 %v0, 1, !sandboxvec !0
149   %t1 = add i8 %t0, %v1
150   %t2 = add i8 %t1, %v1, !sandboxvec !0
151   ret i8 %t2
152 }
153 
154 !0 = distinct !{!"sandboxregion"}
155 )IR");
156   llvm::Function *LLVMF = &*M->getFunction("foo");
157   sandboxir::Context Ctx(C);
158   auto *F = Ctx.createFunction(LLVMF);
159   auto *BB = &*F->begin();
160   auto It = BB->begin();
161   auto *T0 = cast<sandboxir::Instruction>(&*It++);
162   [[maybe_unused]] auto *T1 = cast<sandboxir::Instruction>(&*It++);
163   auto *T2 = cast<sandboxir::Instruction>(&*It++);
164 
165   SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
166       sandboxir::Region::createRegionsFromMD(*F, *TTI);
167   EXPECT_THAT(Regions[0]->insts(), testing::UnorderedElementsAre(T0, T2));
168 }
169 
170 TEST_F(RegionTest, DumpedMetadata) {
171   parseIR(C, R"IR(
172 define i8 @foo(i8 %v0, i8 %v1) {
173   %t0 = add i8 %v0, 1
174   %t1 = add i8 %t0, %v1
175   %t2 = add i8 %t1, %v1
176   ret i8 %t1
177 }
178 )IR");
179   llvm::Function *LLVMF = &*M->getFunction("foo");
180   sandboxir::Context Ctx(C);
181   auto *F = Ctx.createFunction(LLVMF);
182   auto *BB = &*F->begin();
183   auto It = BB->begin();
184   auto *T0 = cast<sandboxir::Instruction>(&*It++);
185   [[maybe_unused]] auto *T1 = cast<sandboxir::Instruction>(&*It++);
186   auto *T2 = cast<sandboxir::Instruction>(&*It++);
187   [[maybe_unused]] auto *Ret = cast<sandboxir::Instruction>(&*It++);
188   sandboxir::Region Rgn(Ctx, *TTI);
189   Rgn.add(T0);
190   sandboxir::Region Rgn2(Ctx, *TTI);
191   Rgn2.add(T2);
192 
193   std::string output;
194   llvm::raw_string_ostream RSO(output);
195   M->print(RSO, nullptr, /*ShouldPreserveUseListOrder=*/true,
196            /*IsForDebug=*/true);
197 
198   // TODO: Replace this with a lit test, which is more suitable for this kind
199   // of IR comparison.
200   std::string expected = R"(; ModuleID = '<string>'
201 source_filename = "<string>"
202 
203 define i8 @foo(i8 %v0, i8 %v1) {
204   %t0 = add i8 %v0, 1, !sandboxvec !0
205   %t1 = add i8 %t0, %v1
206   %t2 = add i8 %t1, %v1, !sandboxvec !1
207   ret i8 %t1
208 }
209 
210 !0 = distinct !{!"sandboxregion"}
211 !1 = distinct !{!"sandboxregion"}
212 )";
213   EXPECT_EQ(expected, output);
214 }
215 
216 TEST_F(RegionTest, MetadataRoundTrip) {
217   parseIR(C, R"IR(
218 define i8 @foo(i8 %v0, i8 %v1) {
219   %t0 = add i8 %v0, 1
220   %t1 = add i8 %t0, %v1
221   ret i8 %t1
222 }
223 )IR");
224   llvm::Function *LLVMF = &*M->getFunction("foo");
225   sandboxir::Context Ctx(C);
226   auto *F = Ctx.createFunction(LLVMF);
227   auto *BB = &*F->begin();
228   auto It = BB->begin();
229   auto *T0 = cast<sandboxir::Instruction>(&*It++);
230   auto *T1 = cast<sandboxir::Instruction>(&*It++);
231 
232   sandboxir::Region Rgn(Ctx, *TTI);
233   Rgn.add(T0);
234   Rgn.add(T1);
235 
236   SmallVector<std::unique_ptr<sandboxir::Region>> Regions =
237       sandboxir::Region::createRegionsFromMD(*F, *TTI);
238   ASSERT_EQ(1U, Regions.size());
239 #ifndef NDEBUG
240   EXPECT_EQ(Rgn, *Regions[0].get());
241 #endif
242 }
243 
244 TEST_F(RegionTest, RegionCost) {
245   parseIR(C, R"IR(
246 define void @foo(i8 %v0, i8 %v1, i8 %v2) {
247   %add0 = add i8 %v0, 1
248   %add1 = add i8 %v1, 2
249   %add2 = add i8 %v2, 3
250   ret void
251 }
252 )IR");
253   llvm::Function *LLVMF = &*M->getFunction("foo");
254   auto *LLVMBB = &*LLVMF->begin();
255   auto LLVMIt = LLVMBB->begin();
256   auto *LLVMAdd0 = &*LLVMIt++;
257   auto *LLVMAdd1 = &*LLVMIt++;
258   auto *LLVMAdd2 = &*LLVMIt++;
259 
260   sandboxir::Context Ctx(C);
261   auto *F = Ctx.createFunction(LLVMF);
262   auto *BB = &*F->begin();
263   auto It = BB->begin();
264   auto *Add0 = cast<sandboxir::Instruction>(&*It++);
265   auto *Add1 = cast<sandboxir::Instruction>(&*It++);
266   auto *Add2 = cast<sandboxir::Instruction>(&*It++);
267 
268   sandboxir::Region Rgn(Ctx, *TTI);
269   const auto &SB = Rgn.getScoreboard();
270   EXPECT_EQ(SB.getAfterCost(), 0);
271   EXPECT_EQ(SB.getBeforeCost(), 0);
272 
273   auto GetCost = [this](llvm::Instruction *LLVMI) {
274     constexpr static TTI::TargetCostKind CostKind = TTI::TCK_RecipThroughput;
275     SmallVector<const llvm::Value *> Operands(LLVMI->operands());
276     return TTI->getInstructionCost(LLVMI, Operands, CostKind);
277   };
278   // Add `Add0` to the region, should be counted in "After".
279   Rgn.add(Add0);
280   EXPECT_EQ(SB.getBeforeCost(), 0);
281   EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd0));
282   // Same for `Add1`.
283   Rgn.add(Add1);
284   EXPECT_EQ(SB.getBeforeCost(), 0);
285   EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd0) + GetCost(LLVMAdd1));
286   // Remove `Add0`, should be subtracted from "After".
287   Rgn.remove(Add0);
288   EXPECT_EQ(SB.getBeforeCost(), 0);
289   EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1));
290   // Remove `Add2` which was never in the region, should counted in "Before".
291   Rgn.remove(Add2);
292   EXPECT_EQ(SB.getBeforeCost(), GetCost(LLVMAdd2));
293   EXPECT_EQ(SB.getAfterCost(), GetCost(LLVMAdd1));
294 }
295