diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp --- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp +++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp @@ -460,7 +460,8 @@ return false; CallInst *CI = dyn_cast(Inst); - if (!CI || !CI->onlyReadsMemory()) + // Handle readnone, readonly and writeonly. + if (!CI || !(CI->doesNotReadMemory() || CI->onlyReadsMemory())) return false; return true; } @@ -1455,13 +1456,22 @@ !(MemInst.isValid() && !MemInst.mayReadFromMemory())) LastStore = nullptr; - // If this is a read-only call, process it. + // If this is a read-only or write-ony call, process it. if (CallValue::canHandle(&Inst)) { + // Write-only calls can be CSE'd. Increment the generation to prevent + // replacement if there are intervening stores. + auto PreStoreGeneration = CurrentGeneration; + if (Inst.mayWriteToMemory()) { + assert(!Inst.mayReadFromMemory()); + ++CurrentGeneration; + LastStore = nullptr; + } + // If we have an available version of this call, and if it is the right // generation, replace this instruction. std::pair InVal = AvailableCalls.lookup(&Inst); if (InVal.first != nullptr && - isSameMemGeneration(InVal.second, CurrentGeneration, InVal.first, + isSameMemGeneration(InVal.second, PreStoreGeneration, InVal.first, &Inst)) { LLVM_DEBUG(dbgs() << "EarlyCSE CSE CALL: " << Inst << " to: " << *InVal.first << '\n'); @@ -1480,7 +1490,7 @@ } // Otherwise, remember that we have this instruction. - AvailableCalls.insert(&Inst, std::make_pair(&Inst, CurrentGeneration)); + AvailableCalls.insert(&Inst, std::make_pair(&Inst, PreStoreGeneration)); continue; } diff --git a/llvm/test/Transforms/EarlyCSE/memoryssa.ll b/llvm/test/Transforms/EarlyCSE/memoryssa.ll --- a/llvm/test/Transforms/EarlyCSE/memoryssa.ll +++ b/llvm/test/Transforms/EarlyCSE/memoryssa.ll @@ -148,3 +148,33 @@ declare void @llvm.lifetime.end.p0i8(i64, i32*) declare void @llvm.lifetime.start.p0i8(i64, i32*) + +;; Check that writeonly call gets CSE'd when using MSSA +declare i32 @write_only() writeonly + +; CHECK-LABEL: @test_write_only +; CHECK-NOMEMSSA-LABEL: @test_write_only +define void @test_write_only(i1 %c, i32* %p) { +; CHECK: call i32 @write_only() +; CHECK-NOMEMSSA: call i32 @write_only() + %call = call i32 @write_only() +; CHECK: %v1 = load i32, i32* @G1 +; CHECK-NOMEMSSA: %v1 = load i32, i32* @G1 + %v1 = load i32, i32* @G1 +; CHECK-NOT: call i32 @write_only() +; CHECK-NOMEMSSA: call i32 @write_only() + %call2 = call i32 @write_only() +; CHECK: store +; CHECK-NOMEMSSA: store + store i32 %v1, i32* @G2 +; CHECK: call i32 @write_only() +; CHECK-NOMEMSSA: call i32 @write_only() + %call3 = call i32 @write_only() +; CHECK: store +; CHECK-NOMEMSSA: store + store i32 %v1, i32* @G2 +; CHECK: call i32 @write_only() +; CHECK-NOMEMSSA: call i32 @write_only() + %call4 = call i32 @write_only() + ret void +}