Skip to content

Commit 0dd274b

Browse files
committed
update EA: more efficient sentinels for ThrownEscape and Liveness properties
1 parent c30ff93 commit 0dd274b

File tree

1 file changed

+30
-22
lines changed

1 file changed

+30
-22
lines changed

base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ A lattice for escape information, which holds the following properties:
8484
- `x.Analyzed::Bool`: not formally part of the lattice, only indicates `x` has not been analyzed or not
8585
- `x.ReturnEscape::Bool`: indicates `x` can escape to the caller via return
8686
- `x.ThrownEscape::BitSet`: records SSA statements numbers where `x` can be thrown as exception:
87-
this information will be used by `escape_exception!` to propagate potential escapes via exception
87+
* `isempty(x.ThrownEscape)`: `x` will never be thrown in this call frame (the bottom)
88+
* `pc ∈ x.ThrownEscape`: `x` may be thrown at the SSA statement at `pc`
89+
* `-1 ∈ x.ThrownEscape`: `x` may be thrown at arbitrary points of this call frame (the top)
90+
This information will be used by `escape_exception!` to propagate potential escapes via exception.
8891
- `x.AliasInfo::Union{IndexableFields,Unindexable,Bool}`: maintains all possible values
8992
that can be aliased to fields or array elements of `x`:
9093
* `x.AliasInfo === false` indicates the fields/elements of `x` isn't analyzed yet
@@ -95,9 +98,12 @@ A lattice for escape information, which holds the following properties:
9598
* `x.AliasInfo::IndexableElements` records all the possible values that can be aliased to elements of array `x` with precise index information
9699
* `x.AliasInfo::Unindexable` records all the possible values that can be aliased to fields/elements of `x` without precise index information
97100
- `x.Liveness::BitSet`: records SSA statement numbers where `x` should be live, e.g.
98-
to be used as a call argument, to be returned to a caller, or preserved for `:foreigncall`.
99-
`0 ∈ x.Liveness` has the special meaning that it's a call argument of the currently analyzed
100-
call frame (and thus it's visible from the caller immediately).
101+
to be used as a call argument, to be returned to a caller, or preserved for `:foreigncall`:
102+
* `isempty(x.Liveness)`: `x` is never be used in this call frame (the bottom)
103+
* `0 ∈ x.Liveness` also has the special meaning that it's a call argument of the currently
104+
analyzed call frame (and thus it's visible from the caller immediately).
105+
* `pc ∈ x.Liveness`: `x` may be used at the SSA statement at `pc`
106+
* `-1 ∈ x.Liveness`: `x` may be used at arbitrary points of this call frame (the top)
101107
- `x.ArgEscape::Int` (not implemented yet): indicates it will escape to the caller through
102108
`setfield!` on argument(s)
103109
* `-1` : no escape
@@ -163,40 +169,42 @@ struct EscapeInfo
163169
end
164170

165171
# precomputed default values in order to eliminate computations at each callsite
166-
const BOT_THROWN_ESCAPE = LivenessSet()
167-
const TOP_THROWN_ESCAPE = LivenessSet(1:100_000)
168172

169-
const BOT_ALIAS_INFO = false
170-
const TOP_ALIAS_INFO = true
173+
const BOT_THROWN_ESCAPE = LivenessSet()
174+
# NOTE the lattice operations should try to avoid actual set computations on this top value,
175+
# and e.g. LivenessSet(0:1000000) should also work without incurring excessive computations
176+
const TOP_THROWN_ESCAPE = LivenessSet(-1)
171177

172178
const BOT_LIVENESS = LivenessSet()
173-
const TOP_LIVENESS = LivenessSet(0:100_000)
179+
# NOTE the lattice operations should try to avoid actual set computations on this top value,
180+
# and e.g. LivenessSet(0:1000000) should also work without incurring excessive computations
181+
const TOP_LIVENESS = LivenessSet(-1:0)
174182
const ARG_LIVENESS = LivenessSet(0)
175183

176184
# the constructors
177-
NotAnalyzed() = EscapeInfo(false, false, BOT_THROWN_ESCAPE, BOT_ALIAS_INFO, BOT_LIVENESS) # not formally part of the lattice
178-
NoEscape() = EscapeInfo(true, false, BOT_THROWN_ESCAPE, BOT_ALIAS_INFO, BOT_LIVENESS)
179-
ArgEscape() = EscapeInfo(true, false, BOT_THROWN_ESCAPE, TOP_ALIAS_INFO, ARG_LIVENESS) # TODO allow interprocedural alias analysis?
180-
ReturnEscape(pc::Int) = EscapeInfo(true, true, BOT_THROWN_ESCAPE, BOT_ALIAS_INFO, LivenessSet(pc))
181-
AllReturnEscape() = EscapeInfo(true, true, BOT_THROWN_ESCAPE, BOT_ALIAS_INFO, TOP_LIVENESS)
182-
ThrownEscape(pc::Int) = EscapeInfo(true, false, LivenessSet(pc), BOT_ALIAS_INFO, BOT_LIVENESS)
183-
AllEscape() = EscapeInfo(true, true, TOP_THROWN_ESCAPE, TOP_ALIAS_INFO, TOP_LIVENESS)
185+
NotAnalyzed() = EscapeInfo(false, false, BOT_THROWN_ESCAPE, false, BOT_LIVENESS) # not formally part of the lattice
186+
NoEscape() = EscapeInfo(true, false, BOT_THROWN_ESCAPE, false, BOT_LIVENESS)
187+
ArgEscape() = EscapeInfo(true, false, BOT_THROWN_ESCAPE, true, ARG_LIVENESS) # TODO allow interprocedural alias analysis?
188+
ReturnEscape(pc::Int) = EscapeInfo(true, true, BOT_THROWN_ESCAPE, false, LivenessSet(pc))
189+
AllReturnEscape() = EscapeInfo(true, true, BOT_THROWN_ESCAPE, false, TOP_LIVENESS)
190+
ThrownEscape(pc::Int) = EscapeInfo(true, false, LivenessSet(pc), false, BOT_LIVENESS)
191+
AllEscape() = EscapeInfo(true, true, TOP_THROWN_ESCAPE, true, TOP_LIVENESS)
184192

185193
const ⊥, ⊤ = NotAnalyzed(), AllEscape()
186194

187195
# Convenience names for some ⊑ₑ queries
188196
has_no_escape(x::EscapeInfo) = !x.ReturnEscape && isempty(x.ThrownEscape) && 0 x.Liveness
189197
has_arg_escape(x::EscapeInfo) = 0 in x.Liveness
190198
has_return_escape(x::EscapeInfo) = x.ReturnEscape
191-
has_return_escape(x::EscapeInfo, pc::Int) = x.ReturnEscape && pc in x.Liveness
199+
has_return_escape(x::EscapeInfo, pc::Int) = x.ReturnEscape && (-1 x.Liveness || pc in x.Liveness)
192200
has_thrown_escape(x::EscapeInfo) = !isempty(x.ThrownEscape)
193-
has_thrown_escape(x::EscapeInfo, pc::Int) = pc in x.ThrownEscape
201+
has_thrown_escape(x::EscapeInfo, pc::Int) = -1 x.ThrownEscape || pc in x.ThrownEscape
194202
has_all_escape(x::EscapeInfo) =ₑ x
195203

196204
# utility lattice constructors
197205
ignore_argescape(x::EscapeInfo) = EscapeInfo(x; Liveness=delete!(copy(x.Liveness), 0))
198206
ignore_thrownescapes(x::EscapeInfo) = EscapeInfo(x; ThrownEscape=BOT_THROWN_ESCAPE)
199-
ignore_aliasinfo(x::EscapeInfo) = EscapeInfo(x, BOT_ALIAS_INFO)
207+
ignore_aliasinfo(x::EscapeInfo) = EscapeInfo(x, false)
200208
ignore_liveness(x::EscapeInfo) = EscapeInfo(x; Liveness=BOT_LIVENESS)
201209

202210
# we need to make sure this `==` operator corresponds to lattice equality rather than object equality,
@@ -1025,7 +1033,7 @@ function escape_exception!(astate::AnalysisState, tryregions::Vector{UnitRange{I
10251033
x = escapes[i]
10261034
xt = x.ThrownEscape
10271035
xt === TOP_THROWN_ESCAPE && @goto propagate_exception_escape # fast pass
1028-
for pc in x.ThrownEscape
1036+
for pc in xt
10291037
for region in tryregions
10301038
pc in region && @goto propagate_exception_escape # early break because of AllEscape
10311039
end
@@ -1093,7 +1101,7 @@ function from_interprocedural(arginfo::ArgEscapeInfo, retinfo::EscapeInfo, pc::I
10931101
# it might be okay from the SROA point of view, since we can't remove the allocation
10941102
# as far as it's passed to a callee anyway, but still we may want some field analysis
10951103
# for e.g. stack allocation or some other IPO optimizations
1096-
#=AliasInfo=#TOP_ALIAS_INFO, #=Liveness=#LivenessSet(pc))
1104+
#=AliasInfo=#true, #=Liveness=#LivenessSet(pc))
10971105
end
10981106

10991107
@noinline function unexpected_assignment!(ir::IRCode, pc::Int)
@@ -1164,7 +1172,7 @@ function add_alias_escapes!(astate::AnalysisState, @nospecialize(v), ainfo::AInf
11641172
end
11651173

11661174
function escape_unanalyzable_obj!(astate::AnalysisState, @nospecialize(obj), objinfo::EscapeInfo)
1167-
objinfo = EscapeInfo(objinfo, TOP_ALIAS_INFO)
1175+
objinfo = EscapeInfo(objinfo, true)
11681176
add_escape_change!(astate, obj, objinfo)
11691177
return objinfo
11701178
end

0 commit comments

Comments
 (0)