blob: 97dbd18af51d52137ea153f2750eaa0d063e84a3 [file] [log] [blame]
Nicolai Haehnle981ceb82018-10-18 09:38:44 +00001//===- DivergenceAnalysisTest.cpp - DivergenceAnalysis unit tests ---------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "llvm/ADT/SmallVector.h"
11#include "llvm/Analysis/AssumptionCache.h"
12#include "llvm/Analysis/DivergenceAnalysis.h"
13#include "llvm/Analysis/LoopInfo.h"
14#include "llvm/Analysis/PostDominators.h"
15#include "llvm/Analysis/SyncDependenceAnalysis.h"
16#include "llvm/Analysis/TargetLibraryInfo.h"
17#include "llvm/AsmParser/Parser.h"
18#include "llvm/IR/Constants.h"
19#include "llvm/IR/Dominators.h"
20#include "llvm/IR/GlobalVariable.h"
21#include "llvm/IR/IRBuilder.h"
22#include "llvm/IR/InstIterator.h"
23#include "llvm/IR/LLVMContext.h"
24#include "llvm/IR/LegacyPassManager.h"
25#include "llvm/IR/Module.h"
26#include "llvm/IR/Verifier.h"
27#include "llvm/Support/SourceMgr.h"
28#include "gtest/gtest.h"
29
30namespace llvm {
31namespace {
32
33BasicBlock *GetBlockByName(StringRef BlockName, Function &F) {
34 for (auto &BB : F) {
35 if (BB.getName() != BlockName)
36 continue;
37 return &BB;
38 }
39 return nullptr;
40}
41
42// We use this fixture to ensure that we clean up DivergenceAnalysis before
43// deleting the PassManager.
44class DivergenceAnalysisTest : public testing::Test {
45protected:
46 LLVMContext Context;
47 Module M;
48 TargetLibraryInfoImpl TLII;
49 TargetLibraryInfo TLI;
50
51 std::unique_ptr<DominatorTree> DT;
52 std::unique_ptr<PostDominatorTree> PDT;
53 std::unique_ptr<LoopInfo> LI;
54 std::unique_ptr<SyncDependenceAnalysis> SDA;
55
56 DivergenceAnalysisTest() : M("", Context), TLII(), TLI(TLII) {}
57
58 DivergenceAnalysis buildDA(Function &F, bool IsLCSSA) {
59 DT.reset(new DominatorTree(F));
60 PDT.reset(new PostDominatorTree(F));
61 LI.reset(new LoopInfo(*DT));
62 SDA.reset(new SyncDependenceAnalysis(*DT, *PDT, *LI));
63 return DivergenceAnalysis(F, nullptr, *DT, *LI, *SDA, IsLCSSA);
64 }
65
66 void runWithDA(
67 Module &M, StringRef FuncName, bool IsLCSSA,
68 function_ref<void(Function &F, LoopInfo &LI, DivergenceAnalysis &DA)>
69 Test) {
70 auto *F = M.getFunction(FuncName);
71 ASSERT_NE(F, nullptr) << "Could not find " << FuncName;
72 DivergenceAnalysis DA = buildDA(*F, IsLCSSA);
73 Test(*F, *LI, DA);
74 }
75};
76
77// Simple initial state test
78TEST_F(DivergenceAnalysisTest, DAInitialState) {
79 IntegerType *IntTy = IntegerType::getInt32Ty(Context);
80 FunctionType *FTy =
81 FunctionType::get(Type::getVoidTy(Context), {IntTy}, false);
82 Function *F = cast<Function>(M.getOrInsertFunction("f", FTy));
83 BasicBlock *BB = BasicBlock::Create(Context, "entry", F);
84 ReturnInst::Create(Context, nullptr, BB);
85
86 DivergenceAnalysis DA = buildDA(*F, false);
87
88 // Whole function region
89 EXPECT_EQ(DA.getRegionLoop(), nullptr);
90
91 // No divergence in initial state
92 EXPECT_FALSE(DA.hasDetectedDivergence());
93
94 // No spurious divergence
95 DA.compute();
96 EXPECT_FALSE(DA.hasDetectedDivergence());
97
98 // Detected divergence after marking
99 Argument &arg = *F->arg_begin();
100 DA.markDivergent(arg);
101
102 EXPECT_TRUE(DA.hasDetectedDivergence());
103 EXPECT_TRUE(DA.isDivergent(arg));
104
105 DA.compute();
106 EXPECT_TRUE(DA.hasDetectedDivergence());
107 EXPECT_TRUE(DA.isDivergent(arg));
108}
109
110TEST_F(DivergenceAnalysisTest, DANoLCSSA) {
111 LLVMContext C;
112 SMDiagnostic Err;
113
114 std::unique_ptr<Module> M = parseAssemblyString(
115 "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "
116 " "
117 "define i32 @f_1(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) "
118 " local_unnamed_addr { "
119 "entry: "
120 " br label %loop.ph "
121 " "
122 "loop.ph: "
123 " br label %loop "
124 " "
125 "loop: "
126 " %iv0 = phi i32 [ %iv0.inc, %loop ], [ 0, %loop.ph ] "
127 " %iv1 = phi i32 [ %iv1.inc, %loop ], [ -2147483648, %loop.ph ] "
128 " %iv0.inc = add i32 %iv0, 1 "
129 " %iv1.inc = add i32 %iv1, 3 "
130 " %cond.cont = icmp slt i32 %iv0, %n "
131 " br i1 %cond.cont, label %loop, label %for.end.loopexit "
132 " "
133 "for.end.loopexit: "
134 " ret i32 %iv0 "
135 "} ",
136 Err, C);
137
138 Function *F = M->getFunction("f_1");
139 DivergenceAnalysis DA = buildDA(*F, false);
140 EXPECT_FALSE(DA.hasDetectedDivergence());
141
142 auto ItArg = F->arg_begin();
143 ItArg++;
144 auto &NArg = *ItArg;
145
146 // Seed divergence in argument %n
147 DA.markDivergent(NArg);
148
149 DA.compute();
150 EXPECT_TRUE(DA.hasDetectedDivergence());
151
152 // Verify that "ret %iv.0" is divergent
153 auto ItBlock = F->begin();
154 std::advance(ItBlock, 3);
155 auto &ExitBlock = *GetBlockByName("for.end.loopexit", *F);
156 auto &RetInst = *cast<ReturnInst>(ExitBlock.begin());
157 EXPECT_TRUE(DA.isDivergent(RetInst));
158}
159
160TEST_F(DivergenceAnalysisTest, DALCSSA) {
161 LLVMContext C;
162 SMDiagnostic Err;
163
164 std::unique_ptr<Module> M = parseAssemblyString(
165 "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "
166 " "
167 "define i32 @f_lcssa(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) "
168 " local_unnamed_addr { "
169 "entry: "
170 " br label %loop.ph "
171 " "
172 "loop.ph: "
173 " br label %loop "
174 " "
175 "loop: "
176 " %iv0 = phi i32 [ %iv0.inc, %loop ], [ 0, %loop.ph ] "
177 " %iv1 = phi i32 [ %iv1.inc, %loop ], [ -2147483648, %loop.ph ] "
178 " %iv0.inc = add i32 %iv0, 1 "
179 " %iv1.inc = add i32 %iv1, 3 "
180 " %cond.cont = icmp slt i32 %iv0, %n "
181 " br i1 %cond.cont, label %loop, label %for.end.loopexit "
182 " "
183 "for.end.loopexit: "
184 " %val.ret = phi i32 [ %iv0, %loop ] "
185 " br label %detached.return "
186 " "
187 "detached.return: "
188 " ret i32 %val.ret "
189 "} ",
190 Err, C);
191
192 Function *F = M->getFunction("f_lcssa");
193 DivergenceAnalysis DA = buildDA(*F, true);
194 EXPECT_FALSE(DA.hasDetectedDivergence());
195
196 auto ItArg = F->arg_begin();
197 ItArg++;
198 auto &NArg = *ItArg;
199
200 // Seed divergence in argument %n
201 DA.markDivergent(NArg);
202
203 DA.compute();
204 EXPECT_TRUE(DA.hasDetectedDivergence());
205
206 // Verify that "ret %iv.0" is divergent
207 auto ItBlock = F->begin();
208 std::advance(ItBlock, 4);
209 auto &ExitBlock = *GetBlockByName("detached.return", *F);
210 auto &RetInst = *cast<ReturnInst>(ExitBlock.begin());
211 EXPECT_TRUE(DA.isDivergent(RetInst));
212}
213
214TEST_F(DivergenceAnalysisTest, DAJoinDivergence) {
215 LLVMContext C;
216 SMDiagnostic Err;
217
218 std::unique_ptr<Module> M = parseAssemblyString(
219 "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "
220 " "
221 "define void @f_1(i1 %a, i1 %b, i1 %c) "
222 " local_unnamed_addr { "
223 "A: "
224 " br i1 %a, label %B, label %C "
225 " "
226 "B: "
227 " br i1 %b, label %C, label %D "
228 " "
229 "C: "
230 " %c.join = phi i32 [ 0, %A ], [ 1, %B ] "
231 " br i1 %c, label %D, label %E "
232 " "
233 "D: "
234 " %d.join = phi i32 [ 0, %B ], [ 1, %C ] "
235 " br label %E "
236 " "
237 "E: "
238 " %e.join = phi i32 [ 0, %C ], [ 1, %D ] "
239 " ret void "
240 "} "
241 " "
242 "define void @f_2(i1 %a, i1 %b, i1 %c) "
243 " local_unnamed_addr { "
244 "A: "
245 " br i1 %a, label %B, label %E "
246 " "
247 "B: "
248 " br i1 %b, label %C, label %D "
249 " "
250 "C: "
251 " br label %D "
252 " "
253 "D: "
254 " %d.join = phi i32 [ 0, %B ], [ 1, %C ] "
255 " br label %E "
256 " "
257 "E: "
258 " %e.join = phi i32 [ 0, %A ], [ 1, %D ] "
259 " ret void "
260 "} "
261 " "
262 "define void @f_3(i1 %a, i1 %b, i1 %c)"
263 " local_unnamed_addr { "
264 "A: "
265 " br i1 %a, label %B, label %C "
266 " "
267 "B: "
268 " br label %C "
269 " "
270 "C: "
271 " %c.join = phi i32 [ 0, %A ], [ 1, %B ] "
272 " br i1 %c, label %D, label %E "
273 " "
274 "D: "
275 " br label %E "
276 " "
277 "E: "
278 " %e.join = phi i32 [ 0, %C ], [ 1, %D ] "
279 " ret void "
280 "} ",
281 Err, C);
282
283 // Maps divergent conditions to the basic blocks whose Phi nodes become
284 // divergent. Blocks need to be listed in IR order.
285 using SmallBlockVec = SmallVector<const BasicBlock *, 4>;
286 using InducedDivJoinMap = std::map<const Value *, SmallBlockVec>;
287
288 // Actual function performing the checks.
289 auto CheckDivergenceFunc = [this](Function &F,
290 InducedDivJoinMap &ExpectedDivJoins) {
291 for (auto &ItCase : ExpectedDivJoins) {
292 auto *DivVal = ItCase.first;
293 auto DA = buildDA(F, false);
294 DA.markDivergent(*DivVal);
295 DA.compute();
296
297 // List of basic blocks that shall host divergent Phi nodes.
298 auto ItDivJoins = ItCase.second.begin();
299
300 for (auto &BB : F) {
301 auto *Phi = dyn_cast<PHINode>(BB.begin());
302 if (!Phi)
303 continue;
304
Nicolai Haehnlec7fe2162018-10-18 12:54:39 +0000305 if (ItDivJoins != ItCase.second.end() && &BB == *ItDivJoins) {
Nicolai Haehnle981ceb82018-10-18 09:38:44 +0000306 EXPECT_TRUE(DA.isDivergent(*Phi));
307 // Advance to next block with expected divergent PHI node.
308 ++ItDivJoins;
309 } else {
310 EXPECT_FALSE(DA.isDivergent(*Phi));
311 }
312 }
313 }
314 };
315
316 {
317 auto *F = M->getFunction("f_1");
318 auto ItBlocks = F->begin();
319 ItBlocks++; // Skip A
320 ItBlocks++; // Skip B
321 auto *C = &*ItBlocks++;
322 auto *D = &*ItBlocks++;
323 auto *E = &*ItBlocks;
324
325 auto ItArg = F->arg_begin();
326 auto *AArg = &*ItArg++;
327 auto *BArg = &*ItArg++;
328 auto *CArg = &*ItArg;
329
330 InducedDivJoinMap DivJoins;
331 DivJoins.emplace(AArg, SmallBlockVec({C, D, E}));
332 DivJoins.emplace(BArg, SmallBlockVec({D, E}));
333 DivJoins.emplace(CArg, SmallBlockVec({E}));
334
335 CheckDivergenceFunc(*F, DivJoins);
336 }
337
338 {
339 auto *F = M->getFunction("f_2");
340 auto ItBlocks = F->begin();
341 ItBlocks++; // Skip A
342 ItBlocks++; // Skip B
343 ItBlocks++; // Skip C
344 auto *D = &*ItBlocks++;
345 auto *E = &*ItBlocks;
346
347 auto ItArg = F->arg_begin();
348 auto *AArg = &*ItArg++;
349 auto *BArg = &*ItArg++;
350 auto *CArg = &*ItArg;
351
352 InducedDivJoinMap DivJoins;
353 DivJoins.emplace(AArg, SmallBlockVec({E}));
354 DivJoins.emplace(BArg, SmallBlockVec({D}));
355 DivJoins.emplace(CArg, SmallBlockVec({}));
356
357 CheckDivergenceFunc(*F, DivJoins);
358 }
359
360 {
361 auto *F = M->getFunction("f_3");
362 auto ItBlocks = F->begin();
363 ItBlocks++; // Skip A
364 ItBlocks++; // Skip B
365 auto *C = &*ItBlocks++;
366 ItBlocks++; // Skip D
367 auto *E = &*ItBlocks;
368
369 auto ItArg = F->arg_begin();
370 auto *AArg = &*ItArg++;
371 auto *BArg = &*ItArg++;
372 auto *CArg = &*ItArg;
373
374 InducedDivJoinMap DivJoins;
375 DivJoins.emplace(AArg, SmallBlockVec({C}));
376 DivJoins.emplace(BArg, SmallBlockVec({}));
377 DivJoins.emplace(CArg, SmallBlockVec({E}));
378
379 CheckDivergenceFunc(*F, DivJoins);
380 }
381}
382
383TEST_F(DivergenceAnalysisTest, DASwitchUnreachableDefault) {
384 LLVMContext C;
385 SMDiagnostic Err;
386
387 std::unique_ptr<Module> M = parseAssemblyString(
388 "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "
389 " "
390 "define void @switch_unreachable_default(i32 %cond) local_unnamed_addr { "
391 "entry: "
392 " switch i32 %cond, label %sw.default [ "
393 " i32 0, label %sw.bb0 "
394 " i32 1, label %sw.bb1 "
395 " ] "
396 " "
397 "sw.bb0: "
398 " br label %sw.epilog "
399 " "
400 "sw.bb1: "
401 " br label %sw.epilog "
402 " "
403 "sw.default: "
404 " unreachable "
405 " "
406 "sw.epilog: "
407 " %div.dbl = phi double [ 0.0, %sw.bb0], [ -1.0, %sw.bb1 ] "
408 " ret void "
409 "}",
410 Err, C);
411
412 auto *F = M->getFunction("switch_unreachable_default");
413 auto &CondArg = *F->arg_begin();
414 auto DA = buildDA(*F, false);
415
416 EXPECT_FALSE(DA.hasDetectedDivergence());
417
418 DA.markDivergent(CondArg);
419 DA.compute();
420
421 // Still %CondArg is divergent.
422 EXPECT_TRUE(DA.hasDetectedDivergence());
423
424 // The join uni.dbl is not divergent (see D52221)
425 auto &ExitBlock = *GetBlockByName("sw.epilog", *F);
426 auto &DivDblPhi = *cast<PHINode>(ExitBlock.begin());
427 EXPECT_TRUE(DA.isDivergent(DivDblPhi));
428}
429
430} // end anonymous namespace
431} // end namespace llvm