1 //===- LoadsTest.cpp - local load analysis unit tests ---------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Analysis/Loads.h"
10 #include "llvm/Analysis/AssumptionCache.h"
11 #include "llvm/Analysis/LoopInfo.h"
12 #include "llvm/Analysis/ScalarEvolution.h"
13 #include "llvm/Analysis/TargetLibraryInfo.h"
14 #include "llvm/AsmParser/Parser.h"
15 #include "llvm/IR/Constants.h"
16 #include "llvm/IR/Dominators.h"
17 #include "llvm/IR/Instructions.h"
18 #include "llvm/IR/LLVMContext.h"
19 #include "llvm/IR/Module.h"
20 #include "llvm/Support/SourceMgr.h"
21 #include "gtest/gtest.h"
25 static std::unique_ptr
<Module
> parseIR(LLVMContext
&C
, const char *IR
) {
27 std::unique_ptr
<Module
> Mod
= parseAssemblyString(IR
, Err
, C
);
29 Err
.print("AnalysisTests", errs());
33 TEST(LoadsTest
, FindAvailableLoadedValueSameBasePtrConstantOffsetsNullAA
) {
35 std::unique_ptr
<Module
> M
= parseIR(C
,
37 target datalayout = "p
:64:64:64:32"
38 %class = type <{ i32, i32 }>
43 %f1 = getelementptr inbounds %class, %class* %o, i32 0, i32 0
44 store i32 42, i32* %f1
45 %f2 = getelementptr inbounds %class, %class* %o, i32 0, i32 1
46 store i32 43, i32* %f2
47 %v = load i32, i32* %f1
51 auto *GV
= M
->getNamedValue("f");
53 auto *F
= dyn_cast
<Function
>(GV
);
55 Instruction
*Inst
= &F
->front().front();
56 auto *AI
= dyn_cast
<AllocaInst
>(Inst
);
58 Inst
= &*++F
->front().rbegin();
59 auto *LI
= dyn_cast
<LoadInst
>(Inst
);
61 BasicBlock::iterator
BBI(LI
);
62 Value
*Loaded
= FindAvailableLoadedValue(
63 LI
, LI
->getParent(), BBI
, 0, nullptr, nullptr);
65 auto *CI
= dyn_cast
<ConstantInt
>(Loaded
);
67 ASSERT_TRUE(CI
->equalsInt(42));
70 TEST(LoadsTest
, CanReplacePointersIfEqual
) {
72 std::unique_ptr
<Module
> M
= parseIR(C
,
74 @y = common global [1 x i32] zeroinitializer, align 4
75 @x = common global [1 x i32] zeroinitializer, align 4
76 declare void @use(i32*)
78 define void @f(i32* %p1, i32* %p2, i64 %i) {
79 call void @use(i32* getelementptr inbounds ([1 x i32], [1 x i32]* @y, i64 0, i64 0))
81 %p1_idx = getelementptr inbounds i32, i32* %p1, i64 %i
82 call void @use(i32* %p1_idx)
84 %icmp = icmp eq i32* %p1, getelementptr inbounds ([1 x i32], [1 x i32]* @y, i64 0, i64 0)
85 %ptrInt = ptrtoint i32* %p1 to i64
89 const DataLayout
&DL
= M
->getDataLayout();
90 auto *GV
= M
->getNamedValue("f");
92 auto *F
= dyn_cast
<Function
>(GV
);
95 Value
*P1
= &*F
->arg_begin();
96 Value
*P2
= F
->getArg(1);
97 Value
*NullPtr
= Constant::getNullValue(P1
->getType());
98 auto InstIter
= F
->front().begin();
99 CallInst
*UserOfY
= cast
<CallInst
>(&*InstIter
);
100 Value
*ConstDerefPtr
= UserOfY
->getArgOperand(0);
101 // We cannot replace two pointers in arbitrary instructions unless we are
102 // replacing with null, a constant dereferencable pointer or they have the
103 // same underlying object.
104 EXPECT_FALSE(canReplacePointersIfEqual(ConstDerefPtr
, P1
, DL
));
105 EXPECT_FALSE(canReplacePointersIfEqual(P1
, P2
, DL
));
106 EXPECT_TRUE(canReplacePointersIfEqual(P1
, ConstDerefPtr
, DL
));
107 EXPECT_TRUE(canReplacePointersIfEqual(P1
, NullPtr
, DL
));
109 GetElementPtrInst
*BasedOnP1
= cast
<GetElementPtrInst
>(&*++InstIter
);
110 EXPECT_TRUE(canReplacePointersIfEqual(BasedOnP1
, P1
, DL
));
111 EXPECT_FALSE(canReplacePointersIfEqual(BasedOnP1
, P2
, DL
));
113 // We can replace two arbitrary pointers in icmp and ptrtoint instructions.
114 auto P1UseIter
= P1
->use_begin();
115 const Use
&PtrToIntUse
= *P1UseIter
;
116 const Use
&IcmpUse
= *++P1UseIter
;
117 const Use
&GEPUse
= *++P1UseIter
;
118 EXPECT_FALSE(canReplacePointersInUseIfEqual(GEPUse
, P2
, DL
));
119 EXPECT_TRUE(canReplacePointersInUseIfEqual(PtrToIntUse
, P2
, DL
));
120 EXPECT_TRUE(canReplacePointersInUseIfEqual(IcmpUse
, P2
, DL
));
123 TEST(LoadsTest
, IsDerefReadOnlyLoop
) {
125 std::unique_ptr
<Module
> M
= parseIR(C
,
129 %p1 = alloca [1024 x i8]
130 %p2 = alloca [1024 x i8]
134 %index = phi i64 [ %index.next, %loop.inc ], [ 3, %entry ]
135 %arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index
136 %ld1 = load i8, ptr %arrayidx, align 1
137 %arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index
138 %ld2 = load i8, ptr %arrayidx1, align 1
139 %cmp3 = icmp eq i8 %ld1, %ld2
140 br i1 %cmp3, label %loop.inc, label %loop.end
143 %index.next = add i64 %index, 1
144 %exitcond = icmp ne i64 %index.next, 67
145 br i1 %exitcond, label %loop, label %loop.end
148 %retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ]
152 define i64 @f2(ptr %p1) {
154 %p2 = alloca [1024 x i8]
158 %index = phi i64 [ %index.next, %loop.inc ], [ 3, %entry ]
159 %arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index
160 %ld1 = load i8, ptr %arrayidx, align 1
161 %arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index
162 %ld2 = load i8, ptr %arrayidx1, align 1
163 %cmp3 = icmp eq i8 %ld1, %ld2
164 br i1 %cmp3, label %loop.inc, label %loop.end
167 %index.next = add i64 %index, 1
168 %exitcond = icmp ne i64 %index.next, 67
169 br i1 %exitcond, label %loop, label %loop.end
172 %retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ]
176 auto *GV1
= M
->getNamedValue("f1");
177 auto *GV2
= M
->getNamedValue("f2");
178 ASSERT_TRUE(GV1
&& GV2
);
179 auto *F1
= dyn_cast
<Function
>(GV1
);
180 auto *F2
= dyn_cast
<Function
>(GV2
);
181 ASSERT_TRUE(F1
&& F2
);
183 TargetLibraryInfoImpl TLII
;
184 TargetLibraryInfo
TLI(TLII
);
186 auto IsDerefReadOnlyLoop
= [&TLI
](Function
*F
) -> bool {
187 AssumptionCache
AC(*F
);
188 DominatorTree
DT(*F
);
190 ScalarEvolution
SE(*F
, TLI
, AC
, DT
, LI
);
192 Function::iterator FI
= F
->begin();
193 // First basic block is entry - skip it.
194 BasicBlock
*Header
= &*(++FI
);
195 assert(Header
->getName() == "loop");
196 Loop
*L
= LI
.getLoopFor(Header
);
198 return isDereferenceableReadOnlyLoop(L
, &SE
, &DT
, &AC
);
201 ASSERT_TRUE(IsDerefReadOnlyLoop(F1
));
202 ASSERT_FALSE(IsDerefReadOnlyLoop(F2
));