aboutsummaryrefslogtreecommitdiff
path: root/unittests
diff options
context:
space:
mode:
authorNicolai Haehnle <nhaehnle@gmail.com>2018-10-18 09:38:44 +0000
committerNicolai Haehnle <nhaehnle@gmail.com>2018-10-18 09:38:44 +0000
commit981ceb83bd0a6e998dca372c3d0660604b21a974 (patch)
tree175cb4bcc41ec3de6cbc79dfcccde1807dbdfbe5 /unittests
parent6071e3bbc0027598049dbd9b6379d88491ca597d (diff)
[DA] DivergenceAnalysis for unstructured, reducible CFGs
Summary: This is patch 2 of the new DivergenceAnalysis (https://reviews.llvm.org/D50433). This patch contains a generic divergence analysis implementation for unstructured, reducible Control-Flow Graphs. It contains two new classes. The `SyncDependenceAnalysis` class lazily computes sync dependences, which relate divergent branches to points of joining divergent control. The `DivergenceAnalysis` class contains the generic divergence analysis implementation. Reviewers: nhaehnle Reviewed By: nhaehnle Subscribers: sameerds, kristina, nhaehnle, xbolva00, tschuett, mgorny, llvm-commits Differential Revision: https://reviews.llvm.org/D51491 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@344734 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'unittests')
-rw-r--r--unittests/Analysis/CMakeLists.txt1
-rw-r--r--unittests/Analysis/DivergenceAnalysisTest.cpp431
2 files changed, 432 insertions, 0 deletions
diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt
index cf1c072fdc3..7d4fd33716e 100644
--- a/unittests/Analysis/CMakeLists.txt
+++ b/unittests/Analysis/CMakeLists.txt
@@ -14,6 +14,7 @@ add_llvm_unittest(AnalysisTests
CallGraphTest.cpp
CFGTest.cpp
CGSCCPassManagerTest.cpp
+ DivergenceAnalysisTest.cpp
GlobalsModRefTest.cpp
ValueLatticeTest.cpp
LazyCallGraphTest.cpp
diff --git a/unittests/Analysis/DivergenceAnalysisTest.cpp b/unittests/Analysis/DivergenceAnalysisTest.cpp
new file mode 100644
index 00000000000..8afd4bf4e66
--- /dev/null
+++ b/unittests/Analysis/DivergenceAnalysisTest.cpp
@@ -0,0 +1,431 @@
+//===- DivergenceAnalysisTest.cpp - DivergenceAnalysis unit tests ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Analysis/AssumptionCache.h"
+#include "llvm/Analysis/DivergenceAnalysis.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/PostDominators.h"
+#include "llvm/Analysis/SyncDependenceAnalysis.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Support/SourceMgr.h"
+#include "gtest/gtest.h"
+
+namespace llvm {
+namespace {
+
+BasicBlock *GetBlockByName(StringRef BlockName, Function &F) {
+ for (auto &BB : F) {
+ if (BB.getName() != BlockName)
+ continue;
+ return &BB;
+ }
+ return nullptr;
+}
+
+// We use this fixture to ensure that we clean up DivergenceAnalysis before
+// deleting the PassManager.
+class DivergenceAnalysisTest : public testing::Test {
+protected:
+ LLVMContext Context;
+ Module M;
+ TargetLibraryInfoImpl TLII;
+ TargetLibraryInfo TLI;
+
+ std::unique_ptr<DominatorTree> DT;
+ std::unique_ptr<PostDominatorTree> PDT;
+ std::unique_ptr<LoopInfo> LI;
+ std::unique_ptr<SyncDependenceAnalysis> SDA;
+
+ DivergenceAnalysisTest() : M("", Context), TLII(), TLI(TLII) {}
+
+ DivergenceAnalysis buildDA(Function &F, bool IsLCSSA) {
+ DT.reset(new DominatorTree(F));
+ PDT.reset(new PostDominatorTree(F));
+ LI.reset(new LoopInfo(*DT));
+ SDA.reset(new SyncDependenceAnalysis(*DT, *PDT, *LI));
+ return DivergenceAnalysis(F, nullptr, *DT, *LI, *SDA, IsLCSSA);
+ }
+
+ void runWithDA(
+ Module &M, StringRef FuncName, bool IsLCSSA,
+ function_ref<void(Function &F, LoopInfo &LI, DivergenceAnalysis &DA)>
+ Test) {
+ auto *F = M.getFunction(FuncName);
+ ASSERT_NE(F, nullptr) << "Could not find " << FuncName;
+ DivergenceAnalysis DA = buildDA(*F, IsLCSSA);
+ Test(*F, *LI, DA);
+ }
+};
+
+// Simple initial state test
+TEST_F(DivergenceAnalysisTest, DAInitialState) {
+ IntegerType *IntTy = IntegerType::getInt32Ty(Context);
+ FunctionType *FTy =
+ FunctionType::get(Type::getVoidTy(Context), {IntTy}, false);
+ Function *F = cast<Function>(M.getOrInsertFunction("f", FTy));
+ BasicBlock *BB = BasicBlock::Create(Context, "entry", F);
+ ReturnInst::Create(Context, nullptr, BB);
+
+ DivergenceAnalysis DA = buildDA(*F, false);
+
+ // Whole function region
+ EXPECT_EQ(DA.getRegionLoop(), nullptr);
+
+ // No divergence in initial state
+ EXPECT_FALSE(DA.hasDetectedDivergence());
+
+ // No spurious divergence
+ DA.compute();
+ EXPECT_FALSE(DA.hasDetectedDivergence());
+
+ // Detected divergence after marking
+ Argument &arg = *F->arg_begin();
+ DA.markDivergent(arg);
+
+ EXPECT_TRUE(DA.hasDetectedDivergence());
+ EXPECT_TRUE(DA.isDivergent(arg));
+
+ DA.compute();
+ EXPECT_TRUE(DA.hasDetectedDivergence());
+ EXPECT_TRUE(DA.isDivergent(arg));
+}
+
+TEST_F(DivergenceAnalysisTest, DANoLCSSA) {
+ LLVMContext C;
+ SMDiagnostic Err;
+
+ std::unique_ptr<Module> M = parseAssemblyString(
+ "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "
+ " "
+ "define i32 @f_1(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) "
+ " local_unnamed_addr { "
+ "entry: "
+ " br label %loop.ph "
+ " "
+ "loop.ph: "
+ " br label %loop "
+ " "
+ "loop: "
+ " %iv0 = phi i32 [ %iv0.inc, %loop ], [ 0, %loop.ph ] "
+ " %iv1 = phi i32 [ %iv1.inc, %loop ], [ -2147483648, %loop.ph ] "
+ " %iv0.inc = add i32 %iv0, 1 "
+ " %iv1.inc = add i32 %iv1, 3 "
+ " %cond.cont = icmp slt i32 %iv0, %n "
+ " br i1 %cond.cont, label %loop, label %for.end.loopexit "
+ " "
+ "for.end.loopexit: "
+ " ret i32 %iv0 "
+ "} ",
+ Err, C);
+
+ Function *F = M->getFunction("f_1");
+ DivergenceAnalysis DA = buildDA(*F, false);
+ EXPECT_FALSE(DA.hasDetectedDivergence());
+
+ auto ItArg = F->arg_begin();
+ ItArg++;
+ auto &NArg = *ItArg;
+
+ // Seed divergence in argument %n
+ DA.markDivergent(NArg);
+
+ DA.compute();
+ EXPECT_TRUE(DA.hasDetectedDivergence());
+
+ // Verify that "ret %iv.0" is divergent
+ auto ItBlock = F->begin();
+ std::advance(ItBlock, 3);
+ auto &ExitBlock = *GetBlockByName("for.end.loopexit", *F);
+ auto &RetInst = *cast<ReturnInst>(ExitBlock.begin());
+ EXPECT_TRUE(DA.isDivergent(RetInst));
+}
+
+TEST_F(DivergenceAnalysisTest, DALCSSA) {
+ LLVMContext C;
+ SMDiagnostic Err;
+
+ std::unique_ptr<Module> M = parseAssemblyString(
+ "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "
+ " "
+ "define i32 @f_lcssa(i8* nocapture %arr, i32 %n, i32* %A, i32* %B) "
+ " local_unnamed_addr { "
+ "entry: "
+ " br label %loop.ph "
+ " "
+ "loop.ph: "
+ " br label %loop "
+ " "
+ "loop: "
+ " %iv0 = phi i32 [ %iv0.inc, %loop ], [ 0, %loop.ph ] "
+ " %iv1 = phi i32 [ %iv1.inc, %loop ], [ -2147483648, %loop.ph ] "
+ " %iv0.inc = add i32 %iv0, 1 "
+ " %iv1.inc = add i32 %iv1, 3 "
+ " %cond.cont = icmp slt i32 %iv0, %n "
+ " br i1 %cond.cont, label %loop, label %for.end.loopexit "
+ " "
+ "for.end.loopexit: "
+ " %val.ret = phi i32 [ %iv0, %loop ] "
+ " br label %detached.return "
+ " "
+ "detached.return: "
+ " ret i32 %val.ret "
+ "} ",
+ Err, C);
+
+ Function *F = M->getFunction("f_lcssa");
+ DivergenceAnalysis DA = buildDA(*F, true);
+ EXPECT_FALSE(DA.hasDetectedDivergence());
+
+ auto ItArg = F->arg_begin();
+ ItArg++;
+ auto &NArg = *ItArg;
+
+ // Seed divergence in argument %n
+ DA.markDivergent(NArg);
+
+ DA.compute();
+ EXPECT_TRUE(DA.hasDetectedDivergence());
+
+ // Verify that "ret %iv.0" is divergent
+ auto ItBlock = F->begin();
+ std::advance(ItBlock, 4);
+ auto &ExitBlock = *GetBlockByName("detached.return", *F);
+ auto &RetInst = *cast<ReturnInst>(ExitBlock.begin());
+ EXPECT_TRUE(DA.isDivergent(RetInst));
+}
+
+TEST_F(DivergenceAnalysisTest, DAJoinDivergence) {
+ LLVMContext C;
+ SMDiagnostic Err;
+
+ std::unique_ptr<Module> M = parseAssemblyString(
+ "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "
+ " "
+ "define void @f_1(i1 %a, i1 %b, i1 %c) "
+ " local_unnamed_addr { "
+ "A: "
+ " br i1 %a, label %B, label %C "
+ " "
+ "B: "
+ " br i1 %b, label %C, label %D "
+ " "
+ "C: "
+ " %c.join = phi i32 [ 0, %A ], [ 1, %B ] "
+ " br i1 %c, label %D, label %E "
+ " "
+ "D: "
+ " %d.join = phi i32 [ 0, %B ], [ 1, %C ] "
+ " br label %E "
+ " "
+ "E: "
+ " %e.join = phi i32 [ 0, %C ], [ 1, %D ] "
+ " ret void "
+ "} "
+ " "
+ "define void @f_2(i1 %a, i1 %b, i1 %c) "
+ " local_unnamed_addr { "
+ "A: "
+ " br i1 %a, label %B, label %E "
+ " "
+ "B: "
+ " br i1 %b, label %C, label %D "
+ " "
+ "C: "
+ " br label %D "
+ " "
+ "D: "
+ " %d.join = phi i32 [ 0, %B ], [ 1, %C ] "
+ " br label %E "
+ " "
+ "E: "
+ " %e.join = phi i32 [ 0, %A ], [ 1, %D ] "
+ " ret void "
+ "} "
+ " "
+ "define void @f_3(i1 %a, i1 %b, i1 %c)"
+ " local_unnamed_addr { "
+ "A: "
+ " br i1 %a, label %B, label %C "
+ " "
+ "B: "
+ " br label %C "
+ " "
+ "C: "
+ " %c.join = phi i32 [ 0, %A ], [ 1, %B ] "
+ " br i1 %c, label %D, label %E "
+ " "
+ "D: "
+ " br label %E "
+ " "
+ "E: "
+ " %e.join = phi i32 [ 0, %C ], [ 1, %D ] "
+ " ret void "
+ "} ",
+ Err, C);
+
+ // Maps divergent conditions to the basic blocks whose Phi nodes become
+ // divergent. Blocks need to be listed in IR order.
+ using SmallBlockVec = SmallVector<const BasicBlock *, 4>;
+ using InducedDivJoinMap = std::map<const Value *, SmallBlockVec>;
+
+ // Actual function performing the checks.
+ auto CheckDivergenceFunc = [this](Function &F,
+ InducedDivJoinMap &ExpectedDivJoins) {
+ for (auto &ItCase : ExpectedDivJoins) {
+ auto *DivVal = ItCase.first;
+ auto DA = buildDA(F, false);
+ DA.markDivergent(*DivVal);
+ DA.compute();
+
+ // List of basic blocks that shall host divergent Phi nodes.
+ auto ItDivJoins = ItCase.second.begin();
+
+ for (auto &BB : F) {
+ auto *Phi = dyn_cast<PHINode>(BB.begin());
+ if (!Phi)
+ continue;
+
+ if (&BB == *ItDivJoins) {
+ EXPECT_TRUE(DA.isDivergent(*Phi));
+ // Advance to next block with expected divergent PHI node.
+ ++ItDivJoins;
+ } else {
+ EXPECT_FALSE(DA.isDivergent(*Phi));
+ }
+ }
+ }
+ };
+
+ {
+ auto *F = M->getFunction("f_1");
+ auto ItBlocks = F->begin();
+ ItBlocks++; // Skip A
+ ItBlocks++; // Skip B
+ auto *C = &*ItBlocks++;
+ auto *D = &*ItBlocks++;
+ auto *E = &*ItBlocks;
+
+ auto ItArg = F->arg_begin();
+ auto *AArg = &*ItArg++;
+ auto *BArg = &*ItArg++;
+ auto *CArg = &*ItArg;
+
+ InducedDivJoinMap DivJoins;
+ DivJoins.emplace(AArg, SmallBlockVec({C, D, E}));
+ DivJoins.emplace(BArg, SmallBlockVec({D, E}));
+ DivJoins.emplace(CArg, SmallBlockVec({E}));
+
+ CheckDivergenceFunc(*F, DivJoins);
+ }
+
+ {
+ auto *F = M->getFunction("f_2");
+ auto ItBlocks = F->begin();
+ ItBlocks++; // Skip A
+ ItBlocks++; // Skip B
+ ItBlocks++; // Skip C
+ auto *D = &*ItBlocks++;
+ auto *E = &*ItBlocks;
+
+ auto ItArg = F->arg_begin();
+ auto *AArg = &*ItArg++;
+ auto *BArg = &*ItArg++;
+ auto *CArg = &*ItArg;
+
+ InducedDivJoinMap DivJoins;
+ DivJoins.emplace(AArg, SmallBlockVec({E}));
+ DivJoins.emplace(BArg, SmallBlockVec({D}));
+ DivJoins.emplace(CArg, SmallBlockVec({}));
+
+ CheckDivergenceFunc(*F, DivJoins);
+ }
+
+ {
+ auto *F = M->getFunction("f_3");
+ auto ItBlocks = F->begin();
+ ItBlocks++; // Skip A
+ ItBlocks++; // Skip B
+ auto *C = &*ItBlocks++;
+ ItBlocks++; // Skip D
+ auto *E = &*ItBlocks;
+
+ auto ItArg = F->arg_begin();
+ auto *AArg = &*ItArg++;
+ auto *BArg = &*ItArg++;
+ auto *CArg = &*ItArg;
+
+ InducedDivJoinMap DivJoins;
+ DivJoins.emplace(AArg, SmallBlockVec({C}));
+ DivJoins.emplace(BArg, SmallBlockVec({}));
+ DivJoins.emplace(CArg, SmallBlockVec({E}));
+
+ CheckDivergenceFunc(*F, DivJoins);
+ }
+}
+
+TEST_F(DivergenceAnalysisTest, DASwitchUnreachableDefault) {
+ LLVMContext C;
+ SMDiagnostic Err;
+
+ std::unique_ptr<Module> M = parseAssemblyString(
+ "target datalayout = \"e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128\" "
+ " "
+ "define void @switch_unreachable_default(i32 %cond) local_unnamed_addr { "
+ "entry: "
+ " switch i32 %cond, label %sw.default [ "
+ " i32 0, label %sw.bb0 "
+ " i32 1, label %sw.bb1 "
+ " ] "
+ " "
+ "sw.bb0: "
+ " br label %sw.epilog "
+ " "
+ "sw.bb1: "
+ " br label %sw.epilog "
+ " "
+ "sw.default: "
+ " unreachable "
+ " "
+ "sw.epilog: "
+ " %div.dbl = phi double [ 0.0, %sw.bb0], [ -1.0, %sw.bb1 ] "
+ " ret void "
+ "}",
+ Err, C);
+
+ auto *F = M->getFunction("switch_unreachable_default");
+ auto &CondArg = *F->arg_begin();
+ auto DA = buildDA(*F, false);
+
+ EXPECT_FALSE(DA.hasDetectedDivergence());
+
+ DA.markDivergent(CondArg);
+ DA.compute();
+
+ // Still %CondArg is divergent.
+ EXPECT_TRUE(DA.hasDetectedDivergence());
+
+ // The join uni.dbl is not divergent (see D52221)
+ auto &ExitBlock = *GetBlockByName("sw.epilog", *F);
+ auto &DivDblPhi = *cast<PHINode>(ExitBlock.begin());
+ EXPECT_TRUE(DA.isDivergent(DivDblPhi));
+}
+
+} // end anonymous namespace
+} // end namespace llvm