[llvm] ac6061e - [Analysis] Add new function isDereferenceableReadOnlyLoop (#97292)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 19 02:06:26 PDT 2024
Author: David Sherwood
Date: 2024-07-19T10:06:23+01:00
New Revision: ac6061e084250a377baa552842261797aa6da6a8
URL: https://github.com/llvm/llvm-project/commit/ac6061e084250a377baa552842261797aa6da6a8
DIFF: https://github.com/llvm/llvm-project/commit/ac6061e084250a377baa552842261797aa6da6a8.diff
LOG: [Analysis] Add new function isDereferenceableReadOnlyLoop (#97292)
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.
Added:
Modified:
llvm/include/llvm/Analysis/Loads.h
llvm/lib/Analysis/Loads.cpp
llvm/unittests/Analysis/LoadsTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/Loads.h b/llvm/include/llvm/Analysis/Loads.h
index 6da1c5596b10e..33e817828b754 100644
--- a/llvm/include/llvm/Analysis/Loads.h
+++ b/llvm/include/llvm/Analysis/Loads.h
@@ -86,6 +86,11 @@ bool isDereferenceableAndAlignedInLoop(LoadInst *LI, Loop *L,
ScalarEvolution &SE, DominatorTree &DT,
AssumptionCache *AC = nullptr);
+/// Return true if the loop \p L cannot fault on any iteration and only
+/// contains read-only memory accesses.
+bool isDereferenceableReadOnlyLoop(Loop *L, ScalarEvolution *SE,
+ DominatorTree *DT, AssumptionCache *AC);
+
/// Return true if we know that executing a load from this value cannot trap.
///
/// If DT and ScanFrom are specified this method performs context-sensitive
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 1d54a66705a2a..255a7ae56dd3b 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -769,3 +769,18 @@ bool llvm::canReplacePointersIfEqual(const Value *From, const Value *To,
return isPointerAlwaysReplaceable(From, To, DL);
}
+
+bool llvm::isDereferenceableReadOnlyLoop(Loop *L, ScalarEvolution *SE,
+ DominatorTree *DT,
+ AssumptionCache *AC) {
+ for (BasicBlock *BB : L->blocks()) {
+ for (Instruction &I : *BB) {
+ if (auto *LI = dyn_cast<LoadInst>(&I)) {
+ if (!isDereferenceableAndAlignedInLoop(LI, L, *SE, *DT, AC))
+ return false;
+ } else if (I.mayReadFromMemory() || I.mayWriteToMemory() || I.mayThrow())
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/llvm/unittests/Analysis/LoadsTest.cpp b/llvm/unittests/Analysis/LoadsTest.cpp
index 5da3feaf762f3..13377a8082096 100644
--- a/llvm/unittests/Analysis/LoadsTest.cpp
+++ b/llvm/unittests/Analysis/LoadsTest.cpp
@@ -7,8 +7,13 @@
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/Loads.h"
+#include "llvm/Analysis/AssumptionCache.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/ScalarEvolution.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
@@ -114,3 +119,85 @@ define void @f(i32* %p1, i32* %p2, i64 %i) {
EXPECT_TRUE(canReplacePointersInUseIfEqual(PtrToIntUse, P2, DL));
EXPECT_TRUE(canReplacePointersInUseIfEqual(IcmpUse, P2, DL));
}
+
+TEST(LoadsTest, IsDerefReadOnlyLoop) {
+ LLVMContext C;
+ std::unique_ptr<Module> M = parseIR(C,
+ R"IR(
+define i64 @f1() {
+entry:
+ %p1 = alloca [1024 x i8]
+ %p2 = alloca [1024 x i8]
+ br label %loop
+
+loop:
+ %index = phi i64 [ %index.next, %loop.inc ], [ 3, %entry ]
+ %arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index
+ %ld1 = load i8, ptr %arrayidx, align 1
+ %arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index
+ %ld2 = load i8, ptr %arrayidx1, align 1
+ %cmp3 = icmp eq i8 %ld1, %ld2
+ br i1 %cmp3, label %loop.inc, label %loop.end
+
+loop.inc:
+ %index.next = add i64 %index, 1
+ %exitcond = icmp ne i64 %index.next, 67
+ br i1 %exitcond, label %loop, label %loop.end
+
+loop.end:
+ %retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ]
+ ret i64 %retval
+}
+
+define i64 @f2(ptr %p1) {
+entry:
+ %p2 = alloca [1024 x i8]
+ br label %loop
+
+loop:
+ %index = phi i64 [ %index.next, %loop.inc ], [ 3, %entry ]
+ %arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index
+ %ld1 = load i8, ptr %arrayidx, align 1
+ %arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index
+ %ld2 = load i8, ptr %arrayidx1, align 1
+ %cmp3 = icmp eq i8 %ld1, %ld2
+ br i1 %cmp3, label %loop.inc, label %loop.end
+
+loop.inc:
+ %index.next = add i64 %index, 1
+ %exitcond = icmp ne i64 %index.next, 67
+ br i1 %exitcond, label %loop, label %loop.end
+
+loop.end:
+ %retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ]
+ ret i64 %retval
+}
+)IR");
+ auto *GV1 = M->getNamedValue("f1");
+ auto *GV2 = M->getNamedValue("f2");
+ ASSERT_TRUE(GV1 && GV2);
+ auto *F1 = dyn_cast<Function>(GV1);
+ auto *F2 = dyn_cast<Function>(GV2);
+ ASSERT_TRUE(F1 && F2);
+
+ TargetLibraryInfoImpl TLII;
+ TargetLibraryInfo TLI(TLII);
+
+ auto IsDerefReadOnlyLoop = [&TLI](Function *F) -> bool {
+ AssumptionCache AC(*F);
+ DominatorTree DT(*F);
+ LoopInfo LI(DT);
+ ScalarEvolution SE(*F, TLI, AC, DT, LI);
+
+ Function::iterator FI = F->begin();
+ // First basic block is entry - skip it.
+ BasicBlock *Header = &*(++FI);
+ assert(Header->getName() == "loop");
+ Loop *L = LI.getLoopFor(Header);
+
+ return isDereferenceableReadOnlyLoop(L, &SE, &DT, &AC);
+ };
+
+ ASSERT_TRUE(IsDerefReadOnlyLoop(F1));
+ ASSERT_FALSE(IsDerefReadOnlyLoop(F2));
+}
More information about the llvm-commits
mailing list