aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzGoldthorpe <zgoldtho@ualberta.ca>2025-05-28 04:29:34 -0600
committerGitHub <noreply@github.com>2025-05-28 12:29:34 +0200
commit0291f495dbea64231212a8d51ecef653e10aed33 (patch)
tree04756ce8769d26c83114a0d9d2098f57eb37acfc
parentc4d0d95a4fb92d65594f3575814a027815b5182f (diff)
downloadllvm-0291f495dbea64231212a8d51ecef653e10aed33.zip
llvm-0291f495dbea64231212a8d51ecef653e10aed33.tar.gz
llvm-0291f495dbea64231212a8d51ecef653e10aed33.tar.bz2
[EarlyCSE] Correcting assertion for DSE with invariant loads (#141495)
This patch corrects an assertion to handle an edge case where there is a dead store into an `!invariant.load`'s pointer, but there is an interleaving store to a different (non-aliasing) address. In this case we know that the interleaved store cannot modify the address even without MemorySSA, so the assert does not hold. This bug was found through the AMD fuzzing project.
-rw-r--r--llvm/lib/Transforms/Scalar/EarlyCSE.cpp12
-rw-r--r--llvm/test/Transforms/EarlyCSE/invariant-loads.ll27
2 files changed, 29 insertions, 10 deletions
diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index 1e37836..09cb2f4 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -1698,16 +1698,8 @@ bool EarlyCSE::processNode(DomTreeNode *Node) {
if (MemInst.isValid() && MemInst.isStore()) {
LoadValue InVal = AvailableLoads.lookup(MemInst.getPointerOperand());
if (InVal.DefInst &&
- InVal.DefInst == getMatchingValue(InVal, MemInst, CurrentGeneration)) {
- // It is okay to have a LastStore to a different pointer here if MemorySSA
- // tells us that the load and store are from the same memory generation.
- // In that case, LastStore should keep its present value since we're
- // removing the current store.
- assert((!LastStore ||
- ParseMemoryInst(LastStore, TTI).getPointerOperand() ==
- MemInst.getPointerOperand() ||
- MSSA) &&
- "can't have an intervening store if not using MemorySSA!");
+ InVal.DefInst ==
+ getMatchingValue(InVal, MemInst, CurrentGeneration)) {
LLVM_DEBUG(dbgs() << "EarlyCSE DSE (writeback): " << Inst << '\n');
if (!DebugCounter::shouldExecute(CSECounter)) {
LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n");
diff --git a/llvm/test/Transforms/EarlyCSE/invariant-loads.ll b/llvm/test/Transforms/EarlyCSE/invariant-loads.ll
index 88454660..96a8fc3 100644
--- a/llvm/test/Transforms/EarlyCSE/invariant-loads.ll
+++ b/llvm/test/Transforms/EarlyCSE/invariant-loads.ll
@@ -4,6 +4,7 @@
; RUN: opt -S -passes='early-cse<memssa>' --enable-knowledge-retention < %s | FileCheck %s --check-prefixes=CHECK,USE_ASSUME
declare void @clobber_and_use(i32)
+declare void @clobber_and_combine(i32, i32)
define void @f_0(ptr %ptr) {
; NO_ASSUME-LABEL: @f_0(
@@ -164,6 +165,32 @@ define void @test_dse1(ptr %p) {
ret void
}
+; Extending @test_dse1, the call can't change the contents of p.invariant.
+; Moreover, the contents of p.invariant cannot change from the store to p.
+define void @test_dse2(ptr noalias %p, ptr noalias %p.invariant) {
+; NO_ASSUME-LABEL: @test_dse2(
+; NO_ASSUME-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
+; NO_ASSUME-NEXT: [[V_INVARIANT:%.*]] = load i32, ptr [[P_INVARIANT:%.*]], align 4, !invariant.load !0
+; NO_ASSUME-NEXT: [[V_COMB:%.*]] = call i32 @clobber_and_combine(i32 [[V]], i32 [[V_INVARIANT]])
+; NO_ASSUME-NEXT: store i32 [[V_COMB]], ptr [[P]], align 4
+; NO_ASSUME-NEXT: ret void
+;
+; USE_ASSUME-LABEL: @test_dse2(
+; USE_ASSUME-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
+; USE_ASSUME-NEXT: [[V_INVARIANT:%.*]] = load i32, ptr [[P_INVARIANT:%.*]], align 4, !invariant.load !0
+; USE_ASSUME-NEXT: [[V_COMB:%.*]] = call i32 @clobber_and_combine(i32 [[V]], i32 [[V_INVARIANT]])
+; USE_ASSUME-NEXT: store i32 [[V_COMB]], ptr [[P]], align 4
+; USE_ASSUME-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[P_INVARIANT]], i64 4), "nonnull"(ptr [[P_INVARIANT]]), "align"(ptr [[P_INVARIANT]], i64 4) ]
+; USE_ASSUME-NEXT: ret void
+;
+ %v = load i32, ptr %p
+ %v.invariant = load i32, ptr %p.invariant, !invariant.load !{}
+ %v.comb = call i32 @clobber_and_combine(i32 %v, i32 %v.invariant)
+ store i32 %v.comb, ptr %p
+ store i32 %v.invariant, ptr %p.invariant
+ ret void
+}
+
; By assumption, v1 must equal v2 (TODO)
define void @test_false_negative_dse2(ptr %p, i32 %v2) {
; CHECK-LABEL: @test_false_negative_dse2(