@@ -236,7 +236,7 @@ mutable struct InferenceState
236
236
slottypes:: Vector{Any}
237
237
src:: CodeInfo
238
238
cfg:: CFG
239
- method_info :: MethodInfo
239
+ spec_info :: SpecInfo
240
240
241
241
#= intermediate states for local abstract interpretation =#
242
242
currbb:: Int
@@ -251,6 +251,7 @@ mutable struct InferenceState
251
251
stmt_info:: Vector{CallInfo}
252
252
253
253
#= intermediate states for interprocedural abstract interpretation =#
254
+ tasks:: Vector{WorkThunk}
254
255
pclimitations:: IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on currpc ssavalue
255
256
limitations:: IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on return
256
257
cycle_backedges:: Vector{Tuple{InferenceState, Int}} # call-graph backedges connecting from callee to caller
@@ -293,7 +294,7 @@ mutable struct InferenceState
293
294
sptypes = sptypes_from_meth_instance (mi)
294
295
code = src. code:: Vector{Any}
295
296
cfg = compute_basic_blocks (code)
296
- method_info = MethodInfo (src)
297
+ spec_info = SpecInfo (src)
297
298
298
299
currbb = currpc = 1
299
300
ip = BitSet (1 ) # TODO BitSetBoundedMinPrioritySet(1)
@@ -328,6 +329,7 @@ mutable struct InferenceState
328
329
limitations = IdSet {InferenceState} ()
329
330
cycle_backedges = Vector {Tuple{InferenceState,Int}} ()
330
331
callstack = AbsIntState[]
332
+ tasks = WorkThunk[]
331
333
332
334
valid_worlds = WorldRange (1 , get_world_counter ())
333
335
bestguess = Bottom
@@ -349,9 +351,9 @@ mutable struct InferenceState
349
351
restrict_abstract_call_sites = isa (def, Module)
350
352
351
353
this = new (
352
- mi, world, mod, sptypes, slottypes, src, cfg, method_info ,
354
+ mi, world, mod, sptypes, slottypes, src, cfg, spec_info ,
353
355
currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info,
354
- pclimitations, limitations, cycle_backedges, callstack, 0 , 0 , 0 ,
356
+ tasks, pclimitations, limitations, cycle_backedges, callstack, 0 , 0 , 0 ,
355
357
result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects,
356
358
restrict_abstract_call_sites, cache_mode, insert_coverage,
357
359
interp)
789
791
790
792
# TODO add `result::InferenceResult` and put the irinterp result into the inference cache?
791
793
mutable struct IRInterpretationState
792
- const method_info :: MethodInfo
794
+ const spec_info :: SpecInfo
793
795
const ir:: IRCode
794
796
const mi:: MethodInstance
795
797
const world:: UInt
@@ -800,13 +802,14 @@ mutable struct IRInterpretationState
800
802
const ssa_refined:: BitSet
801
803
const lazyreachability:: LazyCFGReachability
802
804
valid_worlds:: WorldRange
805
+ const tasks:: Vector{WorkThunk}
803
806
const edges:: Vector{Any}
804
807
callstack # ::Vector{AbsIntState}
805
808
frameid:: Int
806
809
parentid:: Int
807
810
808
811
function IRInterpretationState (interp:: AbstractInterpreter ,
809
- method_info :: MethodInfo , ir:: IRCode , mi:: MethodInstance , argtypes:: Vector{Any} ,
812
+ spec_info :: SpecInfo , ir:: IRCode , mi:: MethodInstance , argtypes:: Vector{Any} ,
810
813
world:: UInt , min_world:: UInt , max_world:: UInt )
811
814
curridx = 1
812
815
given_argtypes = Vector {Any} (undef, length (argtypes))
@@ -825,10 +828,11 @@ mutable struct IRInterpretationState
825
828
ssa_refined = BitSet ()
826
829
lazyreachability = LazyCFGReachability (ir)
827
830
valid_worlds = WorldRange (min_world, max_world == typemax (UInt) ? get_world_counter () : max_world)
831
+ tasks = WorkThunk[]
828
832
edges = Any[]
829
833
callstack = AbsIntState[]
830
- return new (method_info , ir, mi, world, curridx, argtypes_refined, ir. sptypes, tpdum,
831
- ssa_refined, lazyreachability, valid_worlds, edges, callstack, 0 , 0 )
834
+ return new (spec_info , ir, mi, world, curridx, argtypes_refined, ir. sptypes, tpdum,
835
+ ssa_refined, lazyreachability, valid_worlds, tasks, edges, callstack, 0 , 0 )
832
836
end
833
837
end
834
838
@@ -841,14 +845,13 @@ function IRInterpretationState(interp::AbstractInterpreter,
841
845
else
842
846
isa (src, CodeInfo) || return nothing
843
847
end
844
- method_info = MethodInfo (src)
848
+ spec_info = SpecInfo (src)
845
849
ir = inflate_ir (src, mi)
846
850
argtypes = va_process_argtypes (optimizer_lattice (interp), argtypes, src. nargs, src. isva)
847
- return IRInterpretationState (interp, method_info , ir, mi, argtypes, world,
851
+ return IRInterpretationState (interp, spec_info , ir, mi, argtypes, world,
848
852
codeinst. min_world, codeinst. max_world)
849
853
end
850
854
851
-
852
855
# AbsIntState
853
856
# ===========
854
857
@@ -870,6 +873,7 @@ function print_callstack(frame::AbsIntState)
870
873
print (frame_instance (sv))
871
874
is_cached (sv) || print (" [uncached]" )
872
875
sv. parentid == idx - 1 || print (" [parent=" , sv. parentid, " ]" )
876
+ isempty (callers_in_cycle (sv)) || print (" [cycle=" , sv. cycleid, " ]" )
873
877
println ()
874
878
@assert sv. frameid == idx
875
879
end
@@ -922,11 +926,11 @@ is_constproped(::IRInterpretationState) = true
922
926
is_cached (sv:: InferenceState ) = ! iszero (sv. cache_mode & CACHE_MODE_GLOBAL)
923
927
is_cached (:: IRInterpretationState ) = false
924
928
925
- method_info (sv:: InferenceState ) = sv. method_info
926
- method_info (sv:: IRInterpretationState ) = sv. method_info
929
+ spec_info (sv:: InferenceState ) = sv. spec_info
930
+ spec_info (sv:: IRInterpretationState ) = sv. spec_info
927
931
928
- propagate_inbounds (sv:: AbsIntState ) = method_info (sv). propagate_inbounds
929
- method_for_inference_limit_heuristics (sv:: AbsIntState ) = method_info (sv). method_for_inference_limit_heuristics
932
+ propagate_inbounds (sv:: AbsIntState ) = spec_info (sv). propagate_inbounds
933
+ method_for_inference_limit_heuristics (sv:: AbsIntState ) = spec_info (sv). method_for_inference_limit_heuristics
930
934
931
935
frame_world (sv:: InferenceState ) = sv. world
932
936
frame_world (sv:: IRInterpretationState ) = sv. world
@@ -994,7 +998,10 @@ of the same cycle, only if it is part of a cycle with multiple frames.
994
998
function callers_in_cycle (sv:: InferenceState )
995
999
callstack = sv. callstack:: Vector{AbsIntState}
996
1000
cycletop = cycleid = sv. cycleid
997
- while cycletop < length (callstack) && (callstack[cycletop + 1 ]:: InferenceState ). cycleid == cycleid
1001
+ while cycletop < length (callstack)
1002
+ frame = callstack[cycletop + 1 ]
1003
+ frame isa InferenceState || break
1004
+ frame. cycleid == cycleid || break
998
1005
cycletop += 1
999
1006
end
1000
1007
return AbsIntCycle (callstack, cycletop == cycleid ? 0 : cycleid, cycletop)
@@ -1054,6 +1061,7 @@ function merge_effects!(::AbstractInterpreter, caller::InferenceState, effects::
1054
1061
effects = Effects (effects; effect_free= ALWAYS_TRUE)
1055
1062
end
1056
1063
caller. ipo_effects = merge_effects (caller. ipo_effects, effects)
1064
+ nothing
1057
1065
end
1058
1066
merge_effects! (:: AbstractInterpreter , :: IRInterpretationState , :: Effects ) = return
1059
1067
@@ -1116,3 +1124,90 @@ function get_max_methods_for_module(mod::Module)
1116
1124
max_methods < 0 && return nothing
1117
1125
return max_methods
1118
1126
end
1127
+
1128
+ """
1129
+ Future{T}
1130
+
1131
+ Delayed return value for a value of type `T`, similar to RefValue{T}, but
1132
+ explicitly represents completed as a `Bool` rather than as `isdefined`.
1133
+ Set once with `f[] = v` and accessed with `f[]` afterwards.
1134
+
1135
+ Can also be constructed with the `completed` flag value and a closure to
1136
+ produce `x`, as well as the additional arguments to avoid always capturing the
1137
+ same couple of values.
1138
+ """
1139
+ struct Future{T}
1140
+ later:: Union{Nothing,RefValue{T}}
1141
+ now:: Union{Nothing,T}
1142
+ Future {T} () where {T} = new {T} (RefValue {T} (), nothing )
1143
+ Future {T} (x) where {T} = new {T} (nothing , x)
1144
+ Future (x:: T ) where {T} = new {T} (nothing , x)
1145
+ end
1146
+ isready (f:: Future ) = f. later === nothing
1147
+ getindex (f:: Future{T} ) where {T} = (later = f. later; later === nothing ? f. now:: T : later[])
1148
+ setindex! (f:: Future , v) = something (f. later)[] = v
1149
+ convert (:: Type{Future{T}} , x) where {T} = Future {T} (x) # support return type conversion
1150
+ convert (:: Type{Future{T}} , x:: Future ) where {T} = x:: Future{T}
1151
+ function Future {T} (f, immediate:: Bool , interp:: AbstractInterpreter , sv:: AbsIntState ) where {T}
1152
+ if immediate
1153
+ return Future {T} (f (interp, sv))
1154
+ else
1155
+ @assert applicable (f, interp, sv)
1156
+ result = Future {T} ()
1157
+ push! (sv. tasks, function (interp, sv)
1158
+ result[] = f (interp, sv)
1159
+ return true
1160
+ end )
1161
+ return result
1162
+ end
1163
+ end
1164
+ function Future {T} (f, prev:: Future{S} , interp:: AbstractInterpreter , sv:: AbsIntState ) where {T, S}
1165
+ later = prev. later
1166
+ if later === nothing
1167
+ return Future {T} (f (prev[], interp, sv))
1168
+ else
1169
+ @assert Core. _hasmethod (Tuple{Core. Typeof (f), S, typeof (interp), typeof (sv)})
1170
+ result = Future {T} ()
1171
+ push! (sv. tasks, function (interp, sv)
1172
+ result[] = f (later[], interp, sv) # capture just later, instead of all of prev
1173
+ return true
1174
+ end )
1175
+ return result
1176
+ end
1177
+ end
1178
+
1179
+
1180
+ """
1181
+ doworkloop(args...)
1182
+
1183
+ Run a tasks inside the abstract interpreter, returning false if there are none.
1184
+ Tasks will be run in DFS post-order tree order, such that all child tasks will
1185
+ be run in the order scheduled, prior to running any subsequent tasks. This
1186
+ allows tasks to generate more child tasks, which will be run before anything else.
1187
+ Each task will be run repeatedly when returning `false`, until it returns `true`.
1188
+ """
1189
+ function doworkloop (interp:: AbstractInterpreter , sv:: AbsIntState )
1190
+ tasks = sv. tasks
1191
+ prev = length (tasks)
1192
+ prev == 0 && return false
1193
+ task = pop! (tasks)
1194
+ completed = task (interp, sv)
1195
+ tasks = sv. tasks # allow dropping gc root over the previous call
1196
+ completed isa Bool || throw (TypeError (:return , " " , Bool, task)) # print the task on failure as part of the error message, instead of just "@ workloop:line"
1197
+ completed || push! (tasks, task)
1198
+ # efficient post-order visitor: items pushed are executed in reverse post order such
1199
+ # that later items are executed before earlier ones, but are fully executed
1200
+ # (including any dependencies scheduled by them) before going on to the next item
1201
+ reverse! (tasks, #= start=# prev)
1202
+ return true
1203
+ end
1204
+
1205
+
1206
+ # macro workthunk(name::Symbol, body)
1207
+ # name = esc(name)
1208
+ # body = esc(body)
1209
+ # return replace_linenums!(
1210
+ # :(function $name($(esc(interp)), $(esc(sv)))
1211
+ # $body
1212
+ # end), __source__)
1213
+ # end
0 commit comments