@@ -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
163169end
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
172178const 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 )
174182const 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
185193const ⊥, ⊤ = NotAnalyzed (), AllEscape ()
186194
187195# Convenience names for some ⊑ₑ queries
188196has_no_escape (x:: EscapeInfo ) = ! x. ReturnEscape && isempty (x. ThrownEscape) && 0 ∉ x. Liveness
189197has_arg_escape (x:: EscapeInfo ) = 0 in x. Liveness
190198has_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)
192200has_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
194202has_all_escape (x:: EscapeInfo ) = ⊤ ⊑ ₑ x
195203
196204# utility lattice constructors
197205ignore_argescape (x:: EscapeInfo ) = EscapeInfo (x; Liveness= delete! (copy (x. Liveness), 0 ))
198206ignore_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 )
200208ignore_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))
10971105end
10981106
10991107@noinline function unexpected_assignment! (ir:: IRCode , pc:: Int )
@@ -1164,7 +1172,7 @@ function add_alias_escapes!(astate::AnalysisState, @nospecialize(v), ainfo::AInf
11641172end
11651173
11661174function 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
11701178end
0 commit comments