xref: /llvm-project/llvm/unittests/Analysis/DDGTest.cpp (revision 4169338e75cdce73d34063532db598c95ee82ae4)
1 //===- DDGTest.cpp - DDGAnalysis unit tests -------------------------------===//
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/Analysis/DDG.h"
10 #include "llvm/Analysis/AliasAnalysis.h"
11 #include "llvm/Analysis/AssumptionCache.h"
12 #include "llvm/Analysis/BasicAliasAnalysis.h"
13 #include "llvm/Analysis/LoopInfo.h"
14 #include "llvm/Analysis/ScalarEvolution.h"
15 #include "llvm/Analysis/TargetLibraryInfo.h"
16 #include "llvm/AsmParser/Parser.h"
17 #include "llvm/IR/Dominators.h"
18 #include "llvm/IR/Module.h"
19 #include "llvm/Support/SourceMgr.h"
20 #include "gtest/gtest.h"
21 
22 using namespace llvm;
23 
24 /// Build the DDG analysis for a loop and run the given test \p Test.
runTest(Module & M,StringRef FuncName,function_ref<void (Function & F,LoopInfo & LI,DependenceInfo & DI,ScalarEvolution & SE)> Test)25 static void runTest(Module &M, StringRef FuncName,
26                     function_ref<void(Function &F, LoopInfo &LI,
27                                       DependenceInfo &DI, ScalarEvolution &SE)>
28                         Test) {
29   auto *F = M.getFunction(FuncName);
30   ASSERT_NE(F, nullptr) << "Could not find " << FuncName;
31 
32   TargetLibraryInfoImpl TLII;
33   TargetLibraryInfo TLI(TLII);
34   AssumptionCache AC(*F);
35   DominatorTree DT(*F);
36   LoopInfo LI(DT);
37   ScalarEvolution SE(*F, TLI, AC, DT, LI);
38   AAResults AA(TLI);
39   DependenceInfo DI(F, &AA, &SE, &LI);
40   Test(*F, LI, DI, SE);
41 }
42 
makeLLVMModule(LLVMContext & Context,const char * ModuleStr)43 static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,
44                                               const char *ModuleStr) {
45   SMDiagnostic Err;
46   return parseAssemblyString(ModuleStr, Err, Context);
47 }
48 
TEST(DDGTest,getDependencies)49 TEST(DDGTest, getDependencies) {
50   const char *ModuleStr =
51       "target datalayout = \"e-m:e-i64:64-n32:64\"\n"
52       "target triple = \"powerpc64le-unknown-linux-gnu\"\n"
53       "\n"
54       "define dso_local void @foo(i32 signext %n, i32* noalias %A, i32* "
55       "noalias %B) {\n"
56       "entry:\n"
57       "   %cmp1 = icmp sgt i32 %n, 0\n"
58       "   br i1 %cmp1, label %for.body.preheader, label %for.end\n"
59       "\n"
60       "for.body.preheader:\n"
61       "   %wide.trip.count = zext i32 %n to i64\n"
62       "   br label %for.body\n"
63       " \n"
64       " for.body:\n"
65       "   %indvars.iv = phi i64 [ 0, %for.body.preheader ], [ "
66       "%indvars.iv.next, %for.body ]\n"
67       "   %arrayidx = getelementptr inbounds i32, i32* %A, i64 %indvars.iv\n"
68       "  %0 = trunc i64 %indvars.iv to i32\n"
69       "  store i32 %0, i32* %arrayidx, align 4\n"
70       "  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
71       "  %arrayidx2 = getelementptr inbounds i32, i32* %A, i64 "
72       "%indvars.iv.next\n"
73       "  %1 = load i32, i32* %arrayidx2, align 4\n"
74       "  %add3 = add nsw i32 %1, 1\n"
75       "  %arrayidx5 = getelementptr inbounds i32, i32* %B, i64 %indvars.iv\n"
76       "  store i32 %add3, i32* %arrayidx5, align 4\n"
77       "  %exitcond = icmp ne i64 %indvars.iv.next, %wide.trip.count\n"
78       "  br i1 %exitcond, label %for.body, label %for.end.loopexit\n"
79       "\n"
80       "for.end.loopexit:\n"
81       "  br label %for.end\n"
82       "\n"
83       "for.end:\n"
84       "  ret void\n"
85       "}\n";
86 
87   LLVMContext Context;
88   std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);
89 
90   runTest(
91       *M, "foo",
92       [&](Function &F, LoopInfo &LI, DependenceInfo &DI, ScalarEvolution &SE) {
93         Loop *L = *LI.begin();
94         assert(L && "expected the loop to be identified.");
95 
96         DataDependenceGraph DDG(*L, LI, DI);
97 
98         // Collect all the nodes that have an outgoing memory edge
99         // while collecting all memory edges as well. There should
100         // only be one node with an outgoing memory edge and there
101         // should only be one memory edge in the entire graph.
102         std::vector<DDGNode *> DependenceSourceNodes;
103         std::vector<DDGEdge *> MemoryEdges;
104         for (DDGNode *N : DDG) {
105           for (DDGEdge *E : *N) {
106             bool SourceAdded = false;
107             if (E->isMemoryDependence()) {
108               MemoryEdges.push_back(E);
109               if (!SourceAdded) {
110                 DependenceSourceNodes.push_back(N);
111                 SourceAdded = true;
112               }
113             }
114           }
115         }
116 
117         EXPECT_EQ(DependenceSourceNodes.size(), 1ull);
118         EXPECT_EQ(MemoryEdges.size(), 1ull);
119 
120         DataDependenceGraph::DependenceList DL;
121         DDG.getDependencies(*DependenceSourceNodes.back(),
122                             MemoryEdges.back()->getTargetNode(), DL);
123 
124         EXPECT_EQ(DL.size(), 1ull);
125         EXPECT_TRUE(DL.back()->isAnti());
126         EXPECT_EQ(DL.back()->getLevels(), 1u);
127         EXPECT_NE(DL.back()->getDistance(1), nullptr);
128         EXPECT_EQ(DL.back()->getDistance(1),
129                   SE.getOne(DL.back()->getDistance(1)->getType()));
130       });
131 }
132 
133 /// Test to make sure that when pi-blocks are formed, multiple edges of the same
134 /// kind and direction are collapsed into a single edge.
135 /// In the test below, %loadASubI belongs to an outside node, which has input
136 /// dependency with multiple load instructions in the pi-block containing
137 /// %loadBSubI. We expect a single memory dependence edge from the outside node
138 /// to this pi-block. The pi-block also contains %add and %add7 both of which
139 /// feed a phi in an outside node. We expect a single def-use edge from the
140 /// pi-block to the node containing that phi.
TEST(DDGTest,avoidDuplicateEdgesToFromPiBlocks)141 TEST(DDGTest, avoidDuplicateEdgesToFromPiBlocks) {
142   const char *ModuleStr =
143       "target datalayout = \"e-m:e-i64:64-n32:64-v256:256:256-v512:512:512\"\n"
144       "\n"
145       "define void @foo(float* noalias %A, float* noalias %B, float* noalias "
146       "%C, float* noalias %D, i32 signext %n) {\n"
147       "entry:\n"
148       "  %cmp1 = icmp sgt i32 %n, 0\n"
149       "  br i1 %cmp1, label %for.body.preheader, label %for.end\n"
150       "\n"
151       "for.body.preheader:                               ; preds = %entry\n"
152       "  %wide.trip.count = zext i32 %n to i64\n"
153       "  br label %for.body\n"
154       "\n"
155       "for.body:                                         ; preds = "
156       "%for.body.preheader, %if.end\n"
157       "  %indvars.iv = phi i64 [ 0, %for.body.preheader ], [ %indvars.iv.next, "
158       "%if.end ]\n"
159       "  %arrayidx = getelementptr inbounds float, float* %A, i64 %indvars.iv\n"
160       "  %loadASubI = load float, float* %arrayidx, align 4\n"
161       "  %arrayidx2 = getelementptr inbounds float, float* %B, i64 "
162       "%indvars.iv\n"
163       "  %loadBSubI = load float, float* %arrayidx2, align 4\n"
164       "  %add = fadd fast float %loadASubI, %loadBSubI\n"
165       "  %arrayidx4 = getelementptr inbounds float, float* %A, i64 "
166       "%indvars.iv\n"
167       "  store float %add, float* %arrayidx4, align 4\n"
168       "  %arrayidx6 = getelementptr inbounds float, float* %A, i64 "
169       "%indvars.iv\n"
170       "  %0 = load float, float* %arrayidx6, align 4\n"
171       "  %add7 = fadd fast float %0, 1.000000e+00\n"
172       "  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"
173       "  %arrayidx10 = getelementptr inbounds float, float* %B, i64 "
174       "%indvars.iv.next\n"
175       "  store float %add7, float* %arrayidx10, align 4\n"
176       "  %arrayidx12 = getelementptr inbounds float, float* %A, i64 "
177       "%indvars.iv\n"
178       "  %1 = load float, float* %arrayidx12, align 4\n"
179       "  %cmp13 = fcmp fast ogt float %1, 1.000000e+02\n"
180       "  br i1 %cmp13, label %if.then, label %if.else\n"
181       "\n"
182       "if.then:                                          ; preds = %for.body\n"
183       "  br label %if.end\n"
184       "\n"
185       "if.else:                                          ; preds = %for.body\n"
186       "  br label %if.end\n"
187       "\n"
188       "if.end:                                           ; preds = %if.else, "
189       "%if.then\n"
190       "  %ff.0 = phi float [ %add, %if.then ], [ %add7, %if.else ]\n"
191       "  store float %ff.0, float* %C, align 4\n"
192       "  %exitcond = icmp ne i64 %indvars.iv.next, %wide.trip.count\n"
193       "  br i1 %exitcond, label %for.body, label %for.end.loopexit\n"
194       "\n"
195       "for.end.loopexit:                                 ; preds = %if.end\n"
196       "  br label %for.end\n"
197       "\n"
198       "for.end:                                          ; preds = "
199       "%for.end.loopexit, %entry\n"
200       "  ret void\n"
201       "}\n";
202 
203   LLVMContext Context;
204   std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleStr);
205 
206   runTest(
207       *M, "foo",
208       [&](Function &F, LoopInfo &LI, DependenceInfo &DI, ScalarEvolution &SE) {
209         Loop *L = *LI.begin();
210         assert(L && "expected the loop to be identified.");
211 
212         DataDependenceGraph DDG(*L, LI, DI);
213 
214         const DDGNode *LoadASubI = nullptr;
215         for (DDGNode *N : DDG) {
216           if (!isa<SimpleDDGNode>(N))
217             continue;
218           SmallVector<Instruction *, 8> IList;
219           N->collectInstructions([](const Instruction *I) { return true; },
220                                  IList);
221           if (llvm::any_of(IList, [](Instruction *I) {
222                 return I->getName() == "loadASubI";
223               })) {
224             LoadASubI = N;
225             break;
226           }
227         }
228         assert(LoadASubI && "Did not find load of A[i]");
229 
230         const PiBlockDDGNode *PiBlockWithBSubI = nullptr;
231         for (DDGNode *N : DDG) {
232           if (!isa<PiBlockDDGNode>(N))
233             continue;
234           for (DDGNode *M : cast<PiBlockDDGNode>(N)->getNodes()) {
235             SmallVector<Instruction *, 8> IList;
236             M->collectInstructions([](const Instruction *I) { return true; },
237                                    IList);
238             if (llvm::any_of(IList, [](Instruction *I) {
239                   return I->getName() == "loadBSubI";
240                 })) {
241               PiBlockWithBSubI = static_cast<PiBlockDDGNode *>(N);
242               break;
243             }
244           }
245           if (PiBlockWithBSubI)
246             break;
247         }
248         assert(PiBlockWithBSubI &&
249                "Did not find pi-block containing load of B[i]");
250 
251         const DDGNode *FFPhi = nullptr;
252         for (DDGNode *N : DDG) {
253           if (!isa<SimpleDDGNode>(N))
254             continue;
255           SmallVector<Instruction *, 8> IList;
256           N->collectInstructions([](const Instruction *I) { return true; },
257                                  IList);
258           if (llvm::any_of(IList, [](Instruction *I) {
259                 return I->getName() == "ff.0";
260               })) {
261             FFPhi = N;
262             break;
263           }
264         }
265         assert(FFPhi && "Did not find ff.0 phi instruction");
266 
267         // Expect a single memory edge from '%0 = A[i]' to the pi-block. This
268         // means the duplicate incoming memory edges are removed during pi-block
269         // formation.
270         SmallVector<DDGEdge *, 4> EL;
271         LoadASubI->findEdgesTo(*PiBlockWithBSubI, EL);
272         unsigned NumMemoryEdges = llvm::count_if(
273             EL, [](DDGEdge *Edge) { return Edge->isMemoryDependence(); });
274         EXPECT_EQ(NumMemoryEdges, 1ull);
275 
276         /// Expect a single def-use edge from the pi-block to '%ff.0 = phi...`.
277         /// This means the duplicate outgoing def-use edges are removed during
278         /// pi-block formation.
279         EL.clear();
280         PiBlockWithBSubI->findEdgesTo(*FFPhi, EL);
281         NumMemoryEdges =
282             llvm::count_if(EL, [](DDGEdge *Edge) { return Edge->isDefUse(); });
283         EXPECT_EQ(NumMemoryEdges, 1ull);
284       });
285 }
286