Skip to content

Commit 158819e

Browse files
committed
[Analysis] Add new function isDereferenceableReadOnlyLoop
I created this patch due to a reviewer request on PR #88385 to split off the analysis changes, however without the other code in that PR I can only test the new function with unit tests.
1 parent de528ff commit 158819e

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

llvm/include/llvm/Analysis/Loads.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ bool isDereferenceableAndAlignedInLoop(LoadInst *LI, Loop *L,
8686
ScalarEvolution &SE, DominatorTree &DT,
8787
AssumptionCache *AC = nullptr);
8888

89+
/// Return true if the loop \p L cannot fault on any iteration and only
90+
/// contains read-only memory accesses.
91+
bool isDereferenceableReadOnlyLoop(Loop *L, ScalarEvolution *SE,
92+
DominatorTree *DT, AssumptionCache *AC);
93+
8994
/// Return true if we know that executing a load from this value cannot trap.
9095
///
9196
/// If DT and ScanFrom are specified this method performs context-sensitive

llvm/lib/Analysis/Loads.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,3 +769,19 @@ bool llvm::canReplacePointersIfEqual(const Value *From, const Value *To,
769769

770770
return isPointerAlwaysReplaceable(From, To, DL);
771771
}
772+
773+
bool llvm::isDereferenceableReadOnlyLoop(Loop *L, ScalarEvolution *SE,
774+
DominatorTree *DT,
775+
AssumptionCache *AC) {
776+
for (BasicBlock *BB : L->blocks()) {
777+
for (Instruction &I : *BB) {
778+
LoadInst *LI = dyn_cast<LoadInst>(&I);
779+
if (LI) {
780+
if (!isDereferenceableAndAlignedInLoop(LI, L, *SE, *DT, AC))
781+
return false;
782+
} else if (I.mayReadFromMemory() || I.mayWriteToMemory() || I.mayThrow())
783+
return false;
784+
}
785+
}
786+
return true;
787+
}

llvm/unittests/Analysis/LoadsTest.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@
77
//===----------------------------------------------------------------------===//
88

99
#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"
1014
#include "llvm/AsmParser/Parser.h"
1115
#include "llvm/IR/Constants.h"
16+
#include "llvm/IR/Dominators.h"
1217
#include "llvm/IR/Instructions.h"
1318
#include "llvm/IR/LLVMContext.h"
1419
#include "llvm/IR/Module.h"
@@ -114,3 +119,86 @@ define void @f(i32* %p1, i32* %p2, i64 %i) {
114119
EXPECT_TRUE(canReplacePointersInUseIfEqual(PtrToIntUse, P2, DL));
115120
EXPECT_TRUE(canReplacePointersInUseIfEqual(IcmpUse, P2, DL));
116121
}
122+
123+
TEST(LoadsTest, IsDerefReadOnlyLoop) {
124+
LLVMContext C;
125+
std::unique_ptr<Module> M = parseIR(C,
126+
R"IR(
127+
define i64 @f1() {
128+
entry:
129+
%p1 = alloca [1024 x i8]
130+
%p2 = alloca [1024 x i8]
131+
br label %loop
132+
133+
loop:
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
141+
142+
loop.inc:
143+
%index.next = add i64 %index, 1
144+
%exitcond = icmp ne i64 %index.next, 67
145+
br i1 %exitcond, label %loop, label %loop.end
146+
147+
loop.end:
148+
%retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ]
149+
ret i64 %retval
150+
}
151+
152+
define i64 @f2(ptr %p1) {
153+
entry:
154+
%p2 = alloca [1024 x i8]
155+
br label %loop
156+
157+
loop:
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
165+
166+
loop.inc:
167+
%index.next = add i64 %index, 1
168+
%exitcond = icmp ne i64 %index.next, 67
169+
br i1 %exitcond, label %loop, label %loop.end
170+
171+
loop.end:
172+
%retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ]
173+
ret i64 %retval
174+
}
175+
)IR");
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);
182+
Function *FNs[2] = { F1, F2 };
183+
184+
TargetLibraryInfoImpl TLII;
185+
TargetLibraryInfo TLI(TLII);
186+
for (unsigned I = 0; I < 2; I++) {
187+
Function *F = FNs[I];
188+
AssumptionCache AC(*F);
189+
DominatorTree DT(*F);
190+
LoopInfo LI(DT);
191+
ScalarEvolution SE(*F, TLI, AC, DT, LI);
192+
193+
Function::iterator FI = F->begin();
194+
// First basic block is entry - skip it.
195+
BasicBlock *Header = &*(++FI);
196+
assert(Header->getName() == "loop");
197+
Loop *L = LI.getLoopFor(Header);
198+
199+
if (I == 0)
200+
ASSERT_TRUE(isDereferenceableReadOnlyLoop(L, &SE, &DT, &AC));
201+
else
202+
ASSERT_FALSE(isDereferenceableReadOnlyLoop(L, &SE, &DT, &AC));
203+
}
204+
}

0 commit comments

Comments
 (0)