diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -1088,7 +1088,8 @@ return I.first->second; } - bool isInvisibleToCallerBeforeRet(const Value *V) { + bool isInvisibleToCallerBeforeRet(const Value *V, + Instruction *CtxI = nullptr) { if (isa(V)) return true; auto I = InvisibleToCallerBeforeRet.insert({V, false}); @@ -1101,6 +1102,8 @@ // of stores removed on a large test set in practice. I.first->second = !PointerMayBeCaptured(V, false, true); } + if (!I.first->second && CtxI) + return notCapturedBeforeOrAt(V, CtxI); return I.first->second; } @@ -1456,7 +1459,8 @@ MemoryDef *CurrentDef = cast(Current); Instruction *CurrentI = CurrentDef->getMemoryInst(); - if (canSkipDef(CurrentDef, !isInvisibleToCallerBeforeRet(DefUO), TLI)) + if (canSkipDef(CurrentDef, !isInvisibleToCallerBeforeRet(DefUO, CurrentI), + TLI)) continue; // Before we try to remove anything, check for any extra throwing @@ -1609,7 +1613,8 @@ continue; } - if (UseInst->mayThrow() && !isInvisibleToCallerBeforeRet(DefUO)) { + if (UseInst->mayThrow() && + !isInvisibleToCallerBeforeRet(DefUO, UseInst)) { LLVM_DEBUG(dbgs() << " ... found throwing instruction\n"); return None; } @@ -1797,7 +1802,7 @@ // First see if we can ignore it by using the fact that SI is an // alloca/alloca like object that is not visible to the caller during // execution of the function. - if (SILocUnd && isInvisibleToCallerBeforeRet(SILocUnd)) + if (SILocUnd && isInvisibleToCallerBeforeRet(SILocUnd, NI)) return false; if (SI->getParent() == NI->getParent()) @@ -1813,7 +1818,7 @@ bool isDSEBarrier(const Value *SILocUnd, Instruction *NI) { // If NI may throw it acts as a barrier, unless we are to an alloca/alloca // like object that does not escape. - if (NI->mayThrow() && !isInvisibleToCallerBeforeRet(SILocUnd)) + if (NI->mayThrow() && !isInvisibleToCallerBeforeRet(SILocUnd, NI)) return true; // If NI is an atomic load/store stronger than monotonic, do not try to diff --git a/llvm/test/Transforms/DeadStoreElimination/multiblock-captures.ll b/llvm/test/Transforms/DeadStoreElimination/multiblock-captures.ll --- a/llvm/test/Transforms/DeadStoreElimination/multiblock-captures.ll +++ b/llvm/test/Transforms/DeadStoreElimination/multiblock-captures.ll @@ -111,7 +111,6 @@ define i8* @test_malloc_capture_4() { ; CHECK-LABEL: @test_malloc_capture_4( ; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24) -; CHECK-NEXT: store i8 0, i8* [[M]], align 1 ; CHECK-NEXT: call void @may_throw_readnone() ; CHECK-NEXT: br label [[EXIT:%.*]] ; CHECK: exit: @@ -164,7 +163,6 @@ define i8* @test_malloc_capture_6() { ; CHECK-LABEL: @test_malloc_capture_6( ; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24) -; CHECK-NEXT: store i8 0, i8* [[M]], align 1 ; CHECK-NEXT: call void @may_throw_readnone() ; CHECK-NEXT: br label [[EXIT:%.*]] ; CHECK: exit: @@ -193,7 +191,6 @@ define i8* @test_malloc_capture_7() { ; CHECK-LABEL: @test_malloc_capture_7( ; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24) -; CHECK-NEXT: store i8 0, i8* [[M]], align 1 ; CHECK-NEXT: call void @may_throw() ; CHECK-NEXT: br label [[EXIT:%.*]] ; CHECK: exit: diff --git a/llvm/test/Transforms/DeadStoreElimination/multiblock-malloc-free.ll b/llvm/test/Transforms/DeadStoreElimination/multiblock-malloc-free.ll --- a/llvm/test/Transforms/DeadStoreElimination/multiblock-malloc-free.ll +++ b/llvm/test/Transforms/DeadStoreElimination/multiblock-malloc-free.ll @@ -39,7 +39,7 @@ ; We cannot remove the store in the entry block, because @unknown_func could ; unwind and the stored value could be read by the caller. -define void @test17(i32* noalias %P) { +define void @test17(i32* %P) { ; CHECK-LABEL: @test17( ; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8* ; CHECK-NEXT: store i32 1, i32* [[P]], align 4 @@ -63,6 +63,31 @@ ret void } +; Same as @test17, but with the argument %P being noalias. This guarantees that +; @unknown_func cannot access it and the store can be removed. +define void @test17_2(i32* noalias %P) { +; CHECK-LABEL: @test17_2( +; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8* +; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB3:%.*]] +; CHECK: bb1: +; CHECK-NEXT: call void @unknown_func() +; CHECK-NEXT: br label [[BB3]] +; CHECK: bb3: +; CHECK-NEXT: call void @free(i8* [[P2]]) +; CHECK-NEXT: ret void +; + %P2 = bitcast i32* %P to i8* + store i32 1, i32* %P + br i1 true, label %bb1, label %bb3 +bb1: + call void @unknown_func() + store i32 1, i32* %P + br label %bb3 +bb3: + call void @free(i8* %P2) + ret void +} + define void @test17_read_after_free(i32* noalias %P) { ; CHECK-LABEL: @test17_read_after_free( ; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8* diff --git a/llvm/test/Transforms/DeadStoreElimination/simple.ll b/llvm/test/Transforms/DeadStoreElimination/simple.ll --- a/llvm/test/Transforms/DeadStoreElimination/simple.ll +++ b/llvm/test/Transforms/DeadStoreElimination/simple.ll @@ -479,7 +479,7 @@ ; We cannot remove any stores, because @unknown_func may unwind and the caller ; may read %p while unwinding. -define void @test34(i32* noalias %p) { +define void @test34(i32* %p) { ; CHECK-LABEL: @test34( ; CHECK-NEXT: store i32 1, i32* [[P:%.*]], align 4 ; CHECK-NEXT: call void @unknown_func() @@ -492,6 +492,20 @@ ret void } +; Like @test34 but with the argument %p being noalias. @unknown_func cannot +; access the object and the first store can be removed. +define void @test34_2(i32* noalias %p) { +; CHECK-LABEL: @test34_2( +; CHECK-NEXT: call void @unknown_func() +; CHECK-NEXT: store i32 0, i32* [[P:%.*]], align 4 +; CHECK-NEXT: ret void +; + store i32 1, i32* %p + call void @unknown_func() + store i32 0, i32* %p + ret void +} + ; Remove redundant store even with an unwinding function in the same block define void @test35(i32* noalias %p) { ; CHECK-LABEL: @test35( @@ -642,7 +656,7 @@ ; We cannot remove `store i32 1, i32* %p`, because @unknown_func may unwind ; and the caller may read %p while unwinding. -define void @test41(i32* noalias %P) { +define void @test41(i32* %P) { ; CHECK-LABEL: @test41( ; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8* ; CHECK-NEXT: store i32 1, i32* [[P]], align 4 @@ -658,6 +672,23 @@ ret void } +; Like @test41 but with the argument %p being noalias. @unknown_func cannot +; access the object and the first store can be removed. +define void @test41_2(i32* noalias %P) { +; CHECK-LABEL: @test41_2( +; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8* +; CHECK-NEXT: call void @unknown_func() +; CHECK-NEXT: call void @free(i8* [[P2]]) +; CHECK-NEXT: ret void +; + %P2 = bitcast i32* %P to i8* + store i32 1, i32* %P + call void @unknown_func() + store i32 2, i32* %P + call void @free(i8* %P2) + ret void +} + define void @test42(i32* %P, i32* %Q) { ; CHECK-LABEL: @test42( ; CHECK-NEXT: store i32 1, i32* [[P:%.*]], align 4