This patch teaches the caching MemorySSA walker a few things:
1. Not to walk Phis we've walked before. It seems that we tried to do
this before, but it didn't work so well in cases like:
define void @foo() {
%1 = alloca i8
%2 = alloca i8
br label %begin
begin:
; 3 = MemoryPhi({%0,liveOnEntry},{%end,2})
; 1 = MemoryDef(3)
store i8 0, i8* %2
br label %end
end:
; MemoryUse(?)
load i8, i8* %1
; 2 = MemoryDef(1)
store i8 0, i8* %2
br label %begin
}
Because we wouldn't put Phis in Q.Visited until we tried to visit them.
So, when trying to optimize MemoryUse(?):
- We would visit 3 above
- ...Which would make us put {%0,liveOnEntry} in Q.Visited
- ...Which would make us visit {%0,liveOnEntry}
- ...Which would make us put {%end,2} in Q.Visited
- ...Which would make us visit {%end,2}
- ...Which would make us visit 3
- ...Which would realize we've already visited everything in 3
- ...Which would make us conservatively return 3.
In the added test-case, (@looped_visitedonlyonce) this behavior would
cause us to give incorrect results. Specifically, we'd visit 4 twice
in the same query, but on the second visit, we'd skip while.cond because
it had been visited, visit if.then/if.then2, and cache "1" as the
clobbering def on the way back.
2. If we try to walk the defs of a {Phi,MemLoc} and see it has been
visited before, just hand back the Phi we're trying to optimize.
I promise this isn't as terrible as it seems. :)
We now insert {Phi,MemLoc} pairs just before walking the Phi's upward
defs. So, we check the cache for the {Phi,MemLoc} pair before checking
if we've already walked the Phi.
The {Phi,MemLoc} pair is (almost?) always guaranteed to have a cache
entry if we've already fully walked it, because we cache as we go.
So, if the {Phi,MemLoc} pair isn't in cache, either:
(a) we must be in the process of visiting it (in which case, we can't
give a better answer in a cache-as-we-go DFS walker)
(b) we visited it, but didn't cache it on the way back (...which seems
to require `ModifyingAccess` to not dominate `StartingAccess`,
so I'm 99% sure that would be an error. If it's not an error, I
haven't been able to get it to happen locally, so I suspect it's
rare.)
- - - - -
As a consequence of this change, we no longer skip upward defs of phis,
so we can kill the `VisitedOnlyOne` check. This gives us better accuracy
than we had before, at the cost of potentially doing a bit more work
when we have a loop.
llvm-svn: 264814
124 lines
4.7 KiB
LLVM
124 lines
4.7 KiB
LLVM
; RUN: opt -basicaa -print-memoryssa -verify-memoryssa -analyze < %s 2>&1 | FileCheck %s
|
|
|
|
%struct.hoge = type { i32, %struct.widget }
|
|
%struct.widget = type { i64 }
|
|
|
|
define hidden void @quux(%struct.hoge *%f) align 2 {
|
|
%tmp = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1, i32 0
|
|
%tmp24 = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1
|
|
%tmp25 = bitcast %struct.widget* %tmp24 to i64**
|
|
br label %bb26
|
|
|
|
bb26: ; preds = %bb77, %0
|
|
; CHECK: 3 = MemoryPhi({%0,liveOnEntry},{bb77,2})
|
|
; CHECK-NEXT: br i1 undef, label %bb68, label %bb77
|
|
br i1 undef, label %bb68, label %bb77
|
|
|
|
bb68: ; preds = %bb26
|
|
; CHECK: MemoryUse(liveOnEntry)
|
|
; CHECK-NEXT: %tmp69 = load i64, i64* null, align 8
|
|
%tmp69 = load i64, i64* null, align 8
|
|
; CHECK: 1 = MemoryDef(3)
|
|
; CHECK-NEXT: store i64 %tmp69, i64* %tmp, align 8
|
|
store i64 %tmp69, i64* %tmp, align 8
|
|
br label %bb77
|
|
|
|
bb77: ; preds = %bb68, %bb26
|
|
; CHECK: 2 = MemoryPhi({bb26,3},{bb68,1})
|
|
; CHECK: MemoryUse(2)
|
|
; CHECK-NEXT: %tmp78 = load i64*, i64** %tmp25, align 8
|
|
%tmp78 = load i64*, i64** %tmp25, align 8
|
|
%tmp79 = getelementptr inbounds i64, i64* %tmp78, i64 undef
|
|
br label %bb26
|
|
}
|
|
|
|
; CHECK-LABEL: define void @quux_skip
|
|
define void @quux_skip(%struct.hoge* noalias %f, i64* noalias %g) align 2 {
|
|
%tmp = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1, i32 0
|
|
%tmp24 = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1
|
|
%tmp25 = bitcast %struct.widget* %tmp24 to i64**
|
|
br label %bb26
|
|
|
|
bb26: ; preds = %bb77, %0
|
|
; CHECK: 3 = MemoryPhi({%0,liveOnEntry},{bb77,2})
|
|
; CHECK-NEXT: br i1 undef, label %bb68, label %bb77
|
|
br i1 undef, label %bb68, label %bb77
|
|
|
|
bb68: ; preds = %bb26
|
|
; CHECK: MemoryUse(3)
|
|
; CHECK-NEXT: %tmp69 = load i64, i64* %g, align 8
|
|
%tmp69 = load i64, i64* %g, align 8
|
|
; CHECK: 1 = MemoryDef(3)
|
|
; CHECK-NEXT: store i64 %tmp69, i64* %g, align 8
|
|
store i64 %tmp69, i64* %g, align 8
|
|
br label %bb77
|
|
|
|
bb77: ; preds = %bb68, %bb26
|
|
; CHECK: 2 = MemoryPhi({bb26,3},{bb68,1})
|
|
; FIXME: This should be MemoryUse(liveOnEntry)
|
|
; CHECK: MemoryUse(3)
|
|
; CHECK-NEXT: %tmp78 = load i64*, i64** %tmp25, align 8
|
|
%tmp78 = load i64*, i64** %tmp25, align 8
|
|
br label %bb26
|
|
}
|
|
|
|
; CHECK-LABEL: define void @quux_dominated
|
|
define void @quux_dominated(%struct.hoge* noalias %f, i64* noalias %g) align 2 {
|
|
%tmp = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1, i32 0
|
|
%tmp24 = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1
|
|
%tmp25 = bitcast %struct.widget* %tmp24 to i64**
|
|
br label %bb26
|
|
|
|
bb26: ; preds = %bb77, %0
|
|
; CHECK: 4 = MemoryPhi({%0,liveOnEntry},{bb77,2})
|
|
; CHECK: MemoryUse(4)
|
|
; CHECK-NEXT: load i64*, i64** %tmp25, align 8
|
|
load i64*, i64** %tmp25, align 8
|
|
br i1 undef, label %bb68, label %bb77
|
|
|
|
bb68: ; preds = %bb26
|
|
; CHECK: MemoryUse(4)
|
|
; CHECK-NEXT: %tmp69 = load i64, i64* %g, align 8
|
|
%tmp69 = load i64, i64* %g, align 8
|
|
; CHECK: 1 = MemoryDef(4)
|
|
; CHECK-NEXT: store i64 %tmp69, i64* %g, align 8
|
|
store i64 %tmp69, i64* %g, align 8
|
|
br label %bb77
|
|
|
|
bb77: ; preds = %bb68, %bb26
|
|
; CHECK: 3 = MemoryPhi({bb26,4},{bb68,1})
|
|
; CHECK: 2 = MemoryDef(3)
|
|
; CHECK-NEXT: store i64* null, i64** %tmp25, align 8
|
|
store i64* null, i64** %tmp25, align 8
|
|
br label %bb26
|
|
}
|
|
|
|
; CHECK-LABEL: define void @quux_nodominate
|
|
define void @quux_nodominate(%struct.hoge* noalias %f, i64* noalias %g) align 2 {
|
|
%tmp = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1, i32 0
|
|
%tmp24 = getelementptr inbounds %struct.hoge, %struct.hoge* %f, i64 0, i32 1
|
|
%tmp25 = bitcast %struct.widget* %tmp24 to i64**
|
|
br label %bb26
|
|
|
|
bb26: ; preds = %bb77, %0
|
|
; CHECK: 3 = MemoryPhi({%0,liveOnEntry},{bb77,2})
|
|
; CHECK: MemoryUse(liveOnEntry)
|
|
; CHECK-NEXT: load i64*, i64** %tmp25, align 8
|
|
load i64*, i64** %tmp25, align 8
|
|
br i1 undef, label %bb68, label %bb77
|
|
|
|
bb68: ; preds = %bb26
|
|
; CHECK: MemoryUse(3)
|
|
; CHECK-NEXT: %tmp69 = load i64, i64* %g, align 8
|
|
%tmp69 = load i64, i64* %g, align 8
|
|
; CHECK: 1 = MemoryDef(3)
|
|
; CHECK-NEXT: store i64 %tmp69, i64* %g, align 8
|
|
store i64 %tmp69, i64* %g, align 8
|
|
br label %bb77
|
|
|
|
bb77: ; preds = %bb68, %bb26
|
|
; CHECK: 2 = MemoryPhi({bb26,3},{bb68,1})
|
|
; CHECK-NEXT: br label %bb26
|
|
br label %bb26
|
|
}
|