@@ -84,7 +84,10 @@ A lattice for escape information, which holds the following properties:
84
84
- `x.Analyzed::Bool`: not formally part of the lattice, only indicates `x` has not been analyzed or not
85
85
- `x.ReturnEscape::Bool`: indicates `x` can escape to the caller via return
86
86
- `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.
88
91
- `x.AliasInfo::Union{IndexableFields,Unindexable,Bool}`: maintains all possible values
89
92
that can be aliased to fields or array elements of `x`:
90
93
* `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:
95
98
* `x.AliasInfo::IndexableElements` records all the possible values that can be aliased to elements of array `x` with precise index information
96
99
* `x.AliasInfo::Unindexable` records all the possible values that can be aliased to fields/elements of `x` without precise index information
97
100
- `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)
101
107
- `x.ArgEscape::Int` (not implemented yet): indicates it will escape to the caller through
102
108
`setfield!` on argument(s)
103
109
* `-1` : no escape
@@ -163,40 +169,42 @@ struct EscapeInfo
163
169
end
164
170
165
171
# 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 )
168
172
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 )
171
177
172
178
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 )
174
182
const ARG_LIVENESS = LivenessSet (0 )
175
183
176
184
# 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)
184
192
185
193
const ⊥, ⊤ = NotAnalyzed (), AllEscape ()
186
194
187
195
# Convenience names for some ⊑ₑ queries
188
196
has_no_escape (x:: EscapeInfo ) = ! x. ReturnEscape && isempty (x. ThrownEscape) && 0 ∉ x. Liveness
189
197
has_arg_escape (x:: EscapeInfo ) = 0 in x. Liveness
190
198
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)
192
200
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
194
202
has_all_escape (x:: EscapeInfo ) = ⊤ ⊑ ₑ x
195
203
196
204
# utility lattice constructors
197
205
ignore_argescape (x:: EscapeInfo ) = EscapeInfo (x; Liveness= delete! (copy (x. Liveness), 0 ))
198
206
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 )
200
208
ignore_liveness (x:: EscapeInfo ) = EscapeInfo (x; Liveness= BOT_LIVENESS)
201
209
202
210
# 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
1025
1033
x = escapes[i]
1026
1034
xt = x. ThrownEscape
1027
1035
xt === TOP_THROWN_ESCAPE && @goto propagate_exception_escape # fast pass
1028
- for pc in x . ThrownEscape
1036
+ for pc in xt
1029
1037
for region in tryregions
1030
1038
pc in region && @goto propagate_exception_escape # early break because of AllEscape
1031
1039
end
@@ -1093,7 +1101,7 @@ function from_interprocedural(arginfo::ArgEscapeInfo, retinfo::EscapeInfo, pc::I
1093
1101
# it might be okay from the SROA point of view, since we can't remove the allocation
1094
1102
# as far as it's passed to a callee anyway, but still we may want some field analysis
1095
1103
# for e.g. stack allocation or some other IPO optimizations
1096
- #= AliasInfo=# TOP_ALIAS_INFO , #= Liveness=# LivenessSet (pc))
1104
+ #= AliasInfo=# true , #= Liveness=# LivenessSet (pc))
1097
1105
end
1098
1106
1099
1107
@noinline function unexpected_assignment! (ir:: IRCode , pc:: Int )
@@ -1164,7 +1172,7 @@ function add_alias_escapes!(astate::AnalysisState, @nospecialize(v), ainfo::AInf
1164
1172
end
1165
1173
1166
1174
function escape_unanalyzable_obj! (astate:: AnalysisState , @nospecialize (obj), objinfo:: EscapeInfo )
1167
- objinfo = EscapeInfo (objinfo, TOP_ALIAS_INFO )
1175
+ objinfo = EscapeInfo (objinfo, true )
1168
1176
add_escape_change! (astate, obj, objinfo)
1169
1177
return objinfo
1170
1178
end
0 commit comments