Skip to content

Commit ca12688

Browse files
committed
update EA: add simple per-element alias analysis
1 parent b2c3938 commit ca12688

File tree

5 files changed

+691
-117
lines changed

5 files changed

+691
-117
lines changed

base/compiler/ssair/EscapeAnalysis/EAUtils.jl

+57-47
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ julia> Base.isassigned(x::SafeRef) = true;
5050
5151
julia> get′(x) = isassigned(x) ? x[] : throw(x);
5252
53-
julia> result = code_escapes((String,String,String)) do s1, s2, s3
53+
julia> result = code_escapes((String,String,String,String)) do s1, s2, s3, s4
5454
r1 = Ref(s1)
5555
r2 = Ref(s2)
5656
r3 = SafeRef(s3)
@@ -61,52 +61,58 @@ julia> result = code_escapes((String,String,String)) do s1, s2, s3
6161
global g = err # will definitely escape `r1`
6262
end
6363
s2 = get′(r2) # still `r2` doesn't escape fully
64-
s3 = get′(r3) # still `r2` doesn't escape fully
65-
return s2, s3
64+
s3 = get′(r3) # still `r3` doesn't escape fully
65+
s4 = sizeof(s4) # the argument `s4` doesn't escape here
66+
return s2, s3, s4
6667
end
67-
#3(X _2::String, ↑ _3::String, ↑ _4::String) in Main at REPL[7]:2
68-
2 X 1 ── %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String} │╻╷╷ Ref
69-
3 *′ │ %2 = %new(Base.RefValue{String}, _3)::Base.RefValue{String} │╻╷╷ Ref
70-
4 ✓′ └─── %3 = %new(SafeRef{String}, _4)::SafeRef{String} │╻╷ SafeRef
71-
5 ◌ 2 ── %4 = \$(Expr(:enter, #8)) │
72-
✓′ │ %5 = ϒ (%3)::SafeRef{String} │
73-
*′ └─── %6 = ϒ (%2)::Base.RefValue{String} │
74-
6 ◌ 3 ── %7 = Base.isdefined(%1, :x)::Bool │╻╷ get′
75-
◌ └─── goto #5 if not %7 ││
76-
X 4 ── Base.getfield(%1, :x)::String ││╻ getindex
77-
◌ └─── goto #6 ││
78-
◌ 5 ── Main.throw(%1)::Union{} ││
79-
◌ └─── unreachable ││
80-
7 ◌ 6 ── nothing::typeof(Core.sizeof) │╻ sizeof
81-
◌ │ nothing::Int64 ││
82-
◌ └─── \$(Expr(:leave, 1)) │
83-
◌ 7 ── goto #10 │
84-
✓′ 8 ── %17 = φᶜ (%5)::SafeRef{String} │
85-
*′ │ %18 = φᶜ (%6)::Base.RefValue{String} │
86-
◌ └─── \$(Expr(:leave, 1)) │
87-
X 9 ── %20 = \$(Expr(:the_exception))::Any │
88-
9 ◌ │ (Main.g = %20)::Any │
89-
◌ └─── \$(Expr(:pop_exception, :(%4)))::Any │
90-
11 ✓′ 10 ┄ %23 = φ (#7 => %3, #9 => %17)::SafeRef{String} │
91-
*′ │ %24 = φ (#7 => %2, #9 => %18)::Base.RefValue{String} │
92-
◌ │ %25 = Base.isdefined(%24, :x)::Bool ││╻ isassigned
93-
◌ └─── goto #12 if not %25 ││
94-
↑ 11 ─ %27 = Base.getfield(%24, :x)::String │││╻ getproperty
95-
◌ └─── goto #13 ││
96-
◌ 12 ─ Main.throw(%24)::Union{} ││
97-
◌ └─── unreachable ││
98-
12 ↑ 13 ─ %31 = Base.getfield(%23, :x)::String │╻╷╷ get′
99-
13 ↑ │ %32 = Core.tuple(%27, %31)::Tuple{String, String} │
100-
◌ └─── return %32 │
68+
#1(X _2::String, ↑ _3::String, ↑ _4::String, ✓ _5::String) in Main at REPL[6]:2
69+
2 X 1 ── %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String} │╻╷╷ Ref
70+
3 *′ │ %2 = %new(Base.RefValue{String}, _3)::Base.RefValue{String} │╻╷╷ Ref
71+
4 ✓′ └─── %3 = %new(SafeRef{String}, _4)::SafeRef{String} │╻╷ SafeRef
72+
5 ◌ 2 ── %4 = \$(Expr(:enter, #8)) │
73+
✓′ │ %5 = ϒ (%3)::SafeRef{String} │
74+
*′ │ %6 = ϒ (%2)::Base.RefValue{String} │
75+
✓ └─── %7 = ϒ (_5)::String │
76+
6 ◌ 3 ── %8 = Base.isdefined(%1, :x)::Bool │╻╷ get′
77+
◌ └─── goto #5 if not %8 ││
78+
X 4 ── Base.getfield(%1, :x)::String ││╻ getindex
79+
◌ └─── goto #6 ││
80+
◌ 5 ── Main.throw(%1)::Union{} ││
81+
◌ └─── unreachable ││
82+
7 ◌ 6 ── nothing::typeof(Core.sizeof) │╻ sizeof
83+
◌ │ nothing::Int64 ││
84+
◌ └─── \$(Expr(:leave, 1)) │
85+
◌ 7 ── goto #10 │
86+
✓′ 8 ── %18 = φᶜ (%5)::SafeRef{String} │
87+
*′ │ %19 = φᶜ (%6)::Base.RefValue{String} │
88+
✓ │ %20 = φᶜ (%7)::String │
89+
◌ └─── \$(Expr(:leave, 1)) │
90+
X 9 ── %22 = \$(Expr(:the_exception))::Any │
91+
9 ◌ │ (Main.g = %22)::Any │
92+
◌ └─── \$(Expr(:pop_exception, :(%4)))::Any │
93+
11 ✓′ 10 ┄ %25 = φ (#7 => %3, #9 => %18)::SafeRef{String} │
94+
*′ │ %26 = φ (#7 => %2, #9 => %19)::Base.RefValue{String} │
95+
✓ │ %27 = φ (#7 => _5, #9 => %20)::String │
96+
◌ │ %28 = Base.isdefined(%26, :x)::Bool ││╻ isassigned
97+
◌ └─── goto #12 if not %28 ││
98+
↑ 11 ─ %30 = Base.getfield(%26, :x)::String │││╻ getproperty
99+
◌ └─── goto #13 ││
100+
◌ 12 ─ Main.throw(%26)::Union{} ││
101+
◌ └─── unreachable ││
102+
12 ↑ 13 ─ %34 = Base.getfield(%25, :x)::String │╻╷╷ get′
103+
13 ◌ │ %35 = Core.sizeof::typeof(Core.sizeof) │╻ sizeof
104+
◌ │ %36 = (%35)(%27)::Int64 ││
105+
14 ↑ │ %37 = Core.tuple(%30, %34, %36)::Tuple{String, String, Int64} │
106+
◌ └─── return %37 │
101107
```
102108
103109
The symbols in the side of each call argument and SSA statements represents the following meaning:
104-
- `◌`: this value is not analyzed because escape information of it won't be used anyway (when the object is `isbitstype` for example)
105-
- `✓`: this value never escapes (`has_no_escape(result.state[x])` holds)
106-
- `↑`: this value can escape to the caller via return (`has_return_escape(result.state[x])` holds)
107-
- `X`: this value can escape to somewhere the escape analysis can't reason about like escapes to a global memory (`has_all_escape(result.state[x])` holds)
108-
- `*`: this value's escape state is between the `ReturnEscape` and `AllEscape` in the lattice of [`EscapeInfo`](@ref), e.g. it has unhandled `ThrownEscape`
109-
- `′`: this value has additional field/aliasing information in its `AliasInfo` property
110+
- `◌` (plain): this value is not analyzed because escape information of it won't be used anyway (when the object is `isbitstype` for example)
111+
- `✓` (green or cyan): this value never escapes (`has_no_escape(result.state[x])` holds), colored blue if it has arg escape also (`has_arg_escape(result.state[x])` holds)
112+
- `↑` (blue or yellow): this value can escape to the caller via return (`has_return_escape(result.state[x])` holds), colored yellow if it has unhandled thrown escape also (`has_thrown_escape(result.state[x])` holds)
113+
- `X` (red): this value can escape to somewhere the escape analysis can't reason about like escapes to a global memory (`has_all_escape(result.state[x])` holds)
114+
- `*` (bold): this value's escape state is between the `ReturnEscape` and `AllEscape` in the partial order of [`EscapeInfo`](@ref), colored yellow if it has unhandled thrown escape also (`has_thrown_escape(result.state[x])` holds)
115+
- `′`: this value has additional object field / array element information in its `AliasInfo` property
110116
111117
For testing, escape information of each call argument and SSA value can be inspected programmatically as like:
112118
```julia
@@ -292,13 +298,17 @@ function get_name_color(x::EscapeInfo, symbol::Bool = false)
292298
getname(x) = string(nameof(x))
293299
if x === EA.⊥
294300
name, color = (getname(EA.NotAnalyzed), ""), :plain
295-
elseif EA.has_no_escape(x)
296-
name, color = (getname(EA.NoEscape), ""), :green
301+
elseif EA.has_no_escape(EA.ignore_argescape(x))
302+
if EA.has_arg_escape(x)
303+
name, color = (getname(EA.ArgEscape), ""), :cyan
304+
else
305+
name, color = (getname(EA.NoEscape), ""), :green
306+
end
297307
elseif EA.has_all_escape(x)
298308
name, color = (getname(EA.AllEscape), "X"), :red
299-
elseif EA.NoEscape() (EA.ignore_thrownescapes EA.ignore_aliasinfo)(x) EA.AllReturnEscape()
309+
elseif EA.has_return_escape(x)
300310
name = (getname(EA.ReturnEscape), "")
301-
color = EA.has_thrown_escape(x) ? :yellow : :cyan
311+
color = EA.has_thrown_escape(x) ? :yellow : :blue
302312
else
303313
name = (nothing, "*")
304314
color = EA.has_thrown_escape(x) ? :yellow : :bold

0 commit comments

Comments
 (0)