diff options
-rw-r--r-- | llvm/lib/Transforms/Scalar/EarlyCSE.cpp | 11 | ||||
-rw-r--r-- | llvm/test/Transforms/EarlyCSE/invariant-loads.ll | 21 | ||||
-rw-r--r-- | llvm/test/Transforms/EarlyCSE/invariant.start.ll | 12 |
3 files changed, 41 insertions, 3 deletions
diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp index 3f70ee49ad9..6f03023cab9 100644 --- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp +++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp @@ -799,7 +799,9 @@ bool EarlyCSE::processNode(DomTreeNode *Node) { continue; auto *CI = cast<CallInst>(Inst); MemoryLocation MemLoc = MemoryLocation::getForArgument(CI, 1, TLI); - AvailableInvariants.insert(MemLoc, CurrentGeneration); + // Don't start a scope if we already have a better one pushed + if (!AvailableInvariants.count(MemLoc)) + AvailableInvariants.insert(MemLoc, CurrentGeneration); continue; } @@ -888,9 +890,12 @@ bool EarlyCSE::processNode(DomTreeNode *Node) { if (MemInst.isInvariantLoad()) { // If we pass an invariant load, we know that memory location is // indefinitely constant from the moment of first dereferenceability. - // We conservatively treat the invariant_load as that moment. + // We conservatively treat the invariant_load as that moment. If we + // pass a invariant load after already establishing a scope, don't + // restart it since we want to preserve the earliest point seen. auto MemLoc = MemoryLocation::get(Inst); - AvailableInvariants.insert(MemLoc, CurrentGeneration); + if (!AvailableInvariants.count(MemLoc)) + AvailableInvariants.insert(MemLoc, CurrentGeneration); } // If we have an available version of this load, and if it is the right diff --git a/llvm/test/Transforms/EarlyCSE/invariant-loads.ll b/llvm/test/Transforms/EarlyCSE/invariant-loads.ll index 889e6cb8a5d..c3fa32d6fad 100644 --- a/llvm/test/Transforms/EarlyCSE/invariant-loads.ll +++ b/llvm/test/Transforms/EarlyCSE/invariant-loads.ll @@ -135,3 +135,24 @@ define void @test_scope_start_without_load(i32* %p) { call void @clobber_and_use(i32 %v3) ret void } + +; If we already have an invariant scope, don't want to start a new one +; with a potentially greater generation. This hides the earlier invariant +; load +define void @test_scope_restart(i32* %p) { +; CHECK-LABEL: @test_scope_restart +; CHECK: %v1 = load i32, i32* %p +; CHECK: call void @clobber_and_use(i32 %v1) +; CHECK: %add = add i32 %v1, %v1 +; CHECK: call void @clobber_and_use(i32 %add) +; CHECK: call void @clobber_and_use(i32 %v1) +; CHECK: ret void + %v1 = load i32, i32* %p, !invariant.load !{} + call void @clobber_and_use(i32 %v1) + %v2 = load i32, i32* %p, !invariant.load !{} + %add = add i32 %v1, %v2 + call void @clobber_and_use(i32 %add) + %v3 = load i32, i32* %p + call void @clobber_and_use(i32 %v3) + ret void +} diff --git a/llvm/test/Transforms/EarlyCSE/invariant.start.ll b/llvm/test/Transforms/EarlyCSE/invariant.start.ll index 5fabd0cee0d..b5dc9a6bff7 100644 --- a/llvm/test/Transforms/EarlyCSE/invariant.start.ll +++ b/llvm/test/Transforms/EarlyCSE/invariant.start.ll @@ -93,6 +93,18 @@ define i32 @test_before_clobber(i32* %p) { ret i32 %sub } +define i32 @test_duplicate_scope(i32* %p) { +; CHECK-LABEL: @test_duplicate_scope +; CHECK: ret i32 0 + %v1 = load i32, i32* %p + call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) + call void @clobber() + call {}* @llvm.invariant.start.p0i32(i64 4, i32* %p) + %v2 = load i32, i32* %p + %sub = sub i32 %v1, %v2 + ret i32 %sub +} + define i32 @test_unanalzyable_load(i32* %p) { ; CHECK-LABEL: @test_unanalzyable_load ; CHECK: ret i32 0 |