xref: /llvm-project/llvm/unittests/Analysis/LoadsTest.cpp (revision ac6061e084250a377baa552842261797aa6da6a8)
124962cedSHiroshi Yamauchi //===- LoadsTest.cpp - local load analysis unit tests ---------------------===//
224962cedSHiroshi Yamauchi //
324962cedSHiroshi Yamauchi // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
424962cedSHiroshi Yamauchi // See https://llvm.org/LICENSE.txt for license information.
524962cedSHiroshi Yamauchi // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
624962cedSHiroshi Yamauchi //
724962cedSHiroshi Yamauchi //===----------------------------------------------------------------------===//
824962cedSHiroshi Yamauchi 
924962cedSHiroshi Yamauchi #include "llvm/Analysis/Loads.h"
10*ac6061e0SDavid Sherwood #include "llvm/Analysis/AssumptionCache.h"
11*ac6061e0SDavid Sherwood #include "llvm/Analysis/LoopInfo.h"
12*ac6061e0SDavid Sherwood #include "llvm/Analysis/ScalarEvolution.h"
13*ac6061e0SDavid Sherwood #include "llvm/Analysis/TargetLibraryInfo.h"
1424962cedSHiroshi Yamauchi #include "llvm/AsmParser/Parser.h"
15823b32fbSBill Wendling #include "llvm/IR/Constants.h"
16*ac6061e0SDavid Sherwood #include "llvm/IR/Dominators.h"
1724962cedSHiroshi Yamauchi #include "llvm/IR/Instructions.h"
1824962cedSHiroshi Yamauchi #include "llvm/IR/LLVMContext.h"
1924962cedSHiroshi Yamauchi #include "llvm/IR/Module.h"
2024962cedSHiroshi Yamauchi #include "llvm/Support/SourceMgr.h"
2124962cedSHiroshi Yamauchi #include "gtest/gtest.h"
2224962cedSHiroshi Yamauchi 
2324962cedSHiroshi Yamauchi using namespace llvm;
2424962cedSHiroshi Yamauchi 
2524962cedSHiroshi Yamauchi static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
2624962cedSHiroshi Yamauchi   SMDiagnostic Err;
2724962cedSHiroshi Yamauchi   std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
2824962cedSHiroshi Yamauchi   if (!Mod)
2924962cedSHiroshi Yamauchi     Err.print("AnalysisTests", errs());
3024962cedSHiroshi Yamauchi   return Mod;
3124962cedSHiroshi Yamauchi }
3224962cedSHiroshi Yamauchi 
3324962cedSHiroshi Yamauchi TEST(LoadsTest, FindAvailableLoadedValueSameBasePtrConstantOffsetsNullAA) {
3424962cedSHiroshi Yamauchi   LLVMContext C;
3524962cedSHiroshi Yamauchi   std::unique_ptr<Module> M = parseIR(C,
3624962cedSHiroshi Yamauchi                                       R"IR(
373c51b9e2SAlex Richardson target datalayout = "p:64:64:64:32"
3824962cedSHiroshi Yamauchi %class = type <{ i32, i32 }>
3924962cedSHiroshi Yamauchi 
4024962cedSHiroshi Yamauchi define i32 @f() {
4124962cedSHiroshi Yamauchi entry:
4224962cedSHiroshi Yamauchi   %o = alloca %class
4324962cedSHiroshi Yamauchi   %f1 = getelementptr inbounds %class, %class* %o, i32 0, i32 0
4424962cedSHiroshi Yamauchi   store i32 42, i32* %f1
4524962cedSHiroshi Yamauchi   %f2 = getelementptr inbounds %class, %class* %o, i32 0, i32 1
4624962cedSHiroshi Yamauchi   store i32 43, i32* %f2
4724962cedSHiroshi Yamauchi   %v = load i32, i32* %f1
4824962cedSHiroshi Yamauchi   ret i32 %v
4924962cedSHiroshi Yamauchi }
5024962cedSHiroshi Yamauchi )IR");
5124962cedSHiroshi Yamauchi   auto *GV = M->getNamedValue("f");
5224962cedSHiroshi Yamauchi   ASSERT_TRUE(GV);
5324962cedSHiroshi Yamauchi   auto *F = dyn_cast<Function>(GV);
5424962cedSHiroshi Yamauchi   ASSERT_TRUE(F);
5524962cedSHiroshi Yamauchi   Instruction *Inst = &F->front().front();
5624962cedSHiroshi Yamauchi   auto *AI = dyn_cast<AllocaInst>(Inst);
5724962cedSHiroshi Yamauchi   ASSERT_TRUE(AI);
5824962cedSHiroshi Yamauchi   Inst = &*++F->front().rbegin();
5924962cedSHiroshi Yamauchi   auto *LI = dyn_cast<LoadInst>(Inst);
6024962cedSHiroshi Yamauchi   ASSERT_TRUE(LI);
6124962cedSHiroshi Yamauchi   BasicBlock::iterator BBI(LI);
6224962cedSHiroshi Yamauchi   Value *Loaded = FindAvailableLoadedValue(
6324962cedSHiroshi Yamauchi       LI, LI->getParent(), BBI, 0, nullptr, nullptr);
6424962cedSHiroshi Yamauchi   ASSERT_TRUE(Loaded);
6524962cedSHiroshi Yamauchi   auto *CI = dyn_cast<ConstantInt>(Loaded);
6624962cedSHiroshi Yamauchi   ASSERT_TRUE(CI);
6724962cedSHiroshi Yamauchi   ASSERT_TRUE(CI->equalsInt(42));
6824962cedSHiroshi Yamauchi }
690d966ae4SFlorian Hahn 
700d966ae4SFlorian Hahn TEST(LoadsTest, CanReplacePointersIfEqual) {
710d966ae4SFlorian Hahn   LLVMContext C;
720d966ae4SFlorian Hahn   std::unique_ptr<Module> M = parseIR(C,
730d966ae4SFlorian Hahn                                       R"IR(
740d966ae4SFlorian Hahn @y = common global [1 x i32] zeroinitializer, align 4
750d966ae4SFlorian Hahn @x = common global [1 x i32] zeroinitializer, align 4
760d966ae4SFlorian Hahn declare void @use(i32*)
770d966ae4SFlorian Hahn 
78b10e4b82SUsman Nadeem define void @f(i32* %p1, i32* %p2, i64 %i) {
790d966ae4SFlorian Hahn   call void @use(i32* getelementptr inbounds ([1 x i32], [1 x i32]* @y, i64 0, i64 0))
80b10e4b82SUsman Nadeem 
81b10e4b82SUsman Nadeem   %p1_idx = getelementptr inbounds i32, i32* %p1, i64 %i
82b10e4b82SUsman Nadeem   call void @use(i32* %p1_idx)
83b10e4b82SUsman Nadeem 
84b10e4b82SUsman Nadeem   %icmp = icmp eq i32* %p1, getelementptr inbounds ([1 x i32], [1 x i32]* @y, i64 0, i64 0)
85b10e4b82SUsman Nadeem   %ptrInt = ptrtoint i32* %p1 to i64
860d966ae4SFlorian Hahn   ret void
870d966ae4SFlorian Hahn }
880d966ae4SFlorian Hahn )IR");
89b10e4b82SUsman Nadeem   const DataLayout &DL = M->getDataLayout();
900d966ae4SFlorian Hahn   auto *GV = M->getNamedValue("f");
910d966ae4SFlorian Hahn   ASSERT_TRUE(GV);
920d966ae4SFlorian Hahn   auto *F = dyn_cast<Function>(GV);
930d966ae4SFlorian Hahn   ASSERT_TRUE(F);
940d966ae4SFlorian Hahn 
95b10e4b82SUsman Nadeem   Value *P1 = &*F->arg_begin();
96b10e4b82SUsman Nadeem   Value *P2 = F->getArg(1);
97b10e4b82SUsman Nadeem   Value *NullPtr = Constant::getNullValue(P1->getType());
980d966ae4SFlorian Hahn   auto InstIter = F->front().begin();
99b10e4b82SUsman Nadeem   CallInst *UserOfY = cast<CallInst>(&*InstIter);
100b10e4b82SUsman Nadeem   Value *ConstDerefPtr = UserOfY->getArgOperand(0);
101b10e4b82SUsman Nadeem   // We cannot replace two pointers in arbitrary instructions unless we are
102b10e4b82SUsman Nadeem   // replacing with null, a constant dereferencable pointer or they have the
103b10e4b82SUsman Nadeem   // same underlying object.
104b10e4b82SUsman Nadeem   EXPECT_FALSE(canReplacePointersIfEqual(ConstDerefPtr, P1, DL));
105b10e4b82SUsman Nadeem   EXPECT_FALSE(canReplacePointersIfEqual(P1, P2, DL));
106b10e4b82SUsman Nadeem   EXPECT_TRUE(canReplacePointersIfEqual(P1, ConstDerefPtr, DL));
107b10e4b82SUsman Nadeem   EXPECT_TRUE(canReplacePointersIfEqual(P1, NullPtr, DL));
1080d966ae4SFlorian Hahn 
109b10e4b82SUsman Nadeem   GetElementPtrInst *BasedOnP1 = cast<GetElementPtrInst>(&*++InstIter);
110b10e4b82SUsman Nadeem   EXPECT_TRUE(canReplacePointersIfEqual(BasedOnP1, P1, DL));
111b10e4b82SUsman Nadeem   EXPECT_FALSE(canReplacePointersIfEqual(BasedOnP1, P2, DL));
112b10e4b82SUsman Nadeem 
113b10e4b82SUsman Nadeem   // We can replace two arbitrary pointers in icmp and ptrtoint instructions.
114b10e4b82SUsman Nadeem   auto P1UseIter = P1->use_begin();
115b10e4b82SUsman Nadeem   const Use &PtrToIntUse = *P1UseIter;
116b10e4b82SUsman Nadeem   const Use &IcmpUse = *++P1UseIter;
117b10e4b82SUsman Nadeem   const Use &GEPUse = *++P1UseIter;
118b10e4b82SUsman Nadeem   EXPECT_FALSE(canReplacePointersInUseIfEqual(GEPUse, P2, DL));
119b10e4b82SUsman Nadeem   EXPECT_TRUE(canReplacePointersInUseIfEqual(PtrToIntUse, P2, DL));
120b10e4b82SUsman Nadeem   EXPECT_TRUE(canReplacePointersInUseIfEqual(IcmpUse, P2, DL));
1210d966ae4SFlorian Hahn }
122*ac6061e0SDavid Sherwood 
123*ac6061e0SDavid Sherwood TEST(LoadsTest, IsDerefReadOnlyLoop) {
124*ac6061e0SDavid Sherwood   LLVMContext C;
125*ac6061e0SDavid Sherwood   std::unique_ptr<Module> M = parseIR(C,
126*ac6061e0SDavid Sherwood                                       R"IR(
127*ac6061e0SDavid Sherwood define i64 @f1() {
128*ac6061e0SDavid Sherwood entry:
129*ac6061e0SDavid Sherwood   %p1 = alloca [1024 x i8]
130*ac6061e0SDavid Sherwood   %p2 = alloca [1024 x i8]
131*ac6061e0SDavid Sherwood   br label %loop
132*ac6061e0SDavid Sherwood 
133*ac6061e0SDavid Sherwood loop:
134*ac6061e0SDavid Sherwood   %index = phi i64 [ %index.next, %loop.inc ], [ 3, %entry ]
135*ac6061e0SDavid Sherwood   %arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index
136*ac6061e0SDavid Sherwood   %ld1 = load i8, ptr %arrayidx, align 1
137*ac6061e0SDavid Sherwood   %arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index
138*ac6061e0SDavid Sherwood   %ld2 = load i8, ptr %arrayidx1, align 1
139*ac6061e0SDavid Sherwood   %cmp3 = icmp eq i8 %ld1, %ld2
140*ac6061e0SDavid Sherwood   br i1 %cmp3, label %loop.inc, label %loop.end
141*ac6061e0SDavid Sherwood 
142*ac6061e0SDavid Sherwood loop.inc:
143*ac6061e0SDavid Sherwood   %index.next = add i64 %index, 1
144*ac6061e0SDavid Sherwood   %exitcond = icmp ne i64 %index.next, 67
145*ac6061e0SDavid Sherwood   br i1 %exitcond, label %loop, label %loop.end
146*ac6061e0SDavid Sherwood 
147*ac6061e0SDavid Sherwood loop.end:
148*ac6061e0SDavid Sherwood   %retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ]
149*ac6061e0SDavid Sherwood   ret i64 %retval
150*ac6061e0SDavid Sherwood }
151*ac6061e0SDavid Sherwood 
152*ac6061e0SDavid Sherwood define i64 @f2(ptr %p1) {
153*ac6061e0SDavid Sherwood entry:
154*ac6061e0SDavid Sherwood   %p2 = alloca [1024 x i8]
155*ac6061e0SDavid Sherwood   br label %loop
156*ac6061e0SDavid Sherwood 
157*ac6061e0SDavid Sherwood loop:
158*ac6061e0SDavid Sherwood   %index = phi i64 [ %index.next, %loop.inc ], [ 3, %entry ]
159*ac6061e0SDavid Sherwood   %arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index
160*ac6061e0SDavid Sherwood   %ld1 = load i8, ptr %arrayidx, align 1
161*ac6061e0SDavid Sherwood   %arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index
162*ac6061e0SDavid Sherwood   %ld2 = load i8, ptr %arrayidx1, align 1
163*ac6061e0SDavid Sherwood   %cmp3 = icmp eq i8 %ld1, %ld2
164*ac6061e0SDavid Sherwood   br i1 %cmp3, label %loop.inc, label %loop.end
165*ac6061e0SDavid Sherwood 
166*ac6061e0SDavid Sherwood loop.inc:
167*ac6061e0SDavid Sherwood   %index.next = add i64 %index, 1
168*ac6061e0SDavid Sherwood   %exitcond = icmp ne i64 %index.next, 67
169*ac6061e0SDavid Sherwood   br i1 %exitcond, label %loop, label %loop.end
170*ac6061e0SDavid Sherwood 
171*ac6061e0SDavid Sherwood loop.end:
172*ac6061e0SDavid Sherwood   %retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ]
173*ac6061e0SDavid Sherwood   ret i64 %retval
174*ac6061e0SDavid Sherwood }
175*ac6061e0SDavid Sherwood )IR");
176*ac6061e0SDavid Sherwood   auto *GV1 = M->getNamedValue("f1");
177*ac6061e0SDavid Sherwood   auto *GV2 = M->getNamedValue("f2");
178*ac6061e0SDavid Sherwood   ASSERT_TRUE(GV1 && GV2);
179*ac6061e0SDavid Sherwood   auto *F1 = dyn_cast<Function>(GV1);
180*ac6061e0SDavid Sherwood   auto *F2 = dyn_cast<Function>(GV2);
181*ac6061e0SDavid Sherwood   ASSERT_TRUE(F1 && F2);
182*ac6061e0SDavid Sherwood 
183*ac6061e0SDavid Sherwood   TargetLibraryInfoImpl TLII;
184*ac6061e0SDavid Sherwood   TargetLibraryInfo TLI(TLII);
185*ac6061e0SDavid Sherwood 
186*ac6061e0SDavid Sherwood   auto IsDerefReadOnlyLoop = [&TLI](Function *F) -> bool {
187*ac6061e0SDavid Sherwood     AssumptionCache AC(*F);
188*ac6061e0SDavid Sherwood     DominatorTree DT(*F);
189*ac6061e0SDavid Sherwood     LoopInfo LI(DT);
190*ac6061e0SDavid Sherwood     ScalarEvolution SE(*F, TLI, AC, DT, LI);
191*ac6061e0SDavid Sherwood 
192*ac6061e0SDavid Sherwood     Function::iterator FI = F->begin();
193*ac6061e0SDavid Sherwood     // First basic block is entry - skip it.
194*ac6061e0SDavid Sherwood     BasicBlock *Header = &*(++FI);
195*ac6061e0SDavid Sherwood     assert(Header->getName() == "loop");
196*ac6061e0SDavid Sherwood     Loop *L = LI.getLoopFor(Header);
197*ac6061e0SDavid Sherwood 
198*ac6061e0SDavid Sherwood     return isDereferenceableReadOnlyLoop(L, &SE, &DT, &AC);
199*ac6061e0SDavid Sherwood   };
200*ac6061e0SDavid Sherwood 
201*ac6061e0SDavid Sherwood   ASSERT_TRUE(IsDerefReadOnlyLoop(F1));
202*ac6061e0SDavid Sherwood   ASSERT_FALSE(IsDerefReadOnlyLoop(F2));
203*ac6061e0SDavid Sherwood }
204