diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2561,7 +2561,7 @@ } /// Given a call CB which uses an address UsedV, return true if we can prove the -/// calls only effect is storing to V. +/// call's only possible effect is storing to V. static bool isRemovableWrite(CallBase &CB, Value *UsedV) { if (!CB.use_empty()) // TODO: add recursion if returned attribute is present @@ -2580,18 +2580,15 @@ if (!CB.doesNotCapture(i)) // capture would allow the address to be read back in an untracked manner return false; - if (UsedV != CB.getArgOperand(i)) { - if (!CB.onlyReadsMemory(i)) - // A write to another memory location keeps the call live, and thus we - // must keep the alloca so that the call has somewhere to write to. - // TODO: This results in an inprecision when two values derived from the - // same alloca are passed as arguments to the same function. - return false; - continue; - } - if (!CB.paramHasAttr(i, Attribute::WriteOnly)) - // a read would hold the address live + if (UsedV != CB.getArgOperand(i) && !CB.onlyReadsMemory(i)) + // A write to another memory location keeps the call live, and thus we + // must keep the alloca so that the call has somewhere to write to. + // TODO: This results in an inprecision when two values derived from the + // same alloca are passed as arguments to the same function. return false; + // Note: Both reads from and writes to the alloca are fine. Since the + // result is unused nothing can observe the values read from the alloca + // without writing it to some other observable location (checked above). } return true; } diff --git a/llvm/test/Transforms/InstCombine/trivial-dse-calls.ll b/llvm/test/Transforms/InstCombine/trivial-dse-calls.ll --- a/llvm/test/Transforms/InstCombine/trivial-dse-calls.ll +++ b/llvm/test/Transforms/InstCombine/trivial-dse-calls.ll @@ -50,6 +50,18 @@ ret void } +; As long as the result is unused, we can even remove reads of the alloca +; itself since the write will be dropped. +define void @test_dead_readwrite() { +; CHECK-LABEL: @test_dead_readwrite( +; CHECK-NEXT: ret void +; + %a = alloca i32, align 4 + %bitcast = bitcast i32* %a to i8* + call void @f(i8* nocapture %bitcast) argmemonly nounwind willreturn + ret void +} + define i32 @test_neg_read_after() { ; CHECK-LABEL: @test_neg_read_after( ; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 @@ -198,3 +210,15 @@ ret void } +; As long as the result is unused, we can even remove reads of the alloca +; itself since the write will be dropped. +define void @test_self_read() { +; CHECK-LABEL: @test_self_read( +; CHECK-NEXT: ret void +; + %a = alloca i32, align 4 + %bitcast = bitcast i32* %a to i8* + call void @f2(i8* nocapture writeonly %bitcast, i8* nocapture readonly %bitcast) argmemonly nounwind willreturn + ret void +} +