@@ -251,6 +251,7 @@ mutable struct InferenceState
251251 stmt_info:: Vector{CallInfo}
252252
253253 #= intermediate states for interprocedural abstract interpretation =#
254+ tasks:: Vector{WorkThunk}
254255 pclimitations:: IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on currpc ssavalue
255256 limitations:: IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on return
256257 cycle_backedges:: Vector{Tuple{InferenceState, Int}} # call-graph backedges connecting from callee to caller
@@ -328,6 +329,7 @@ mutable struct InferenceState
328329 limitations = IdSet {InferenceState} ()
329330 cycle_backedges = Vector {Tuple{InferenceState,Int}} ()
330331 callstack = AbsIntState[]
332+ tasks = WorkThunk[]
331333
332334 valid_worlds = WorldRange (1 , get_world_counter ())
333335 bestguess = Bottom
@@ -351,7 +353,7 @@ mutable struct InferenceState
351353 this = new (
352354 mi, world, mod, sptypes, slottypes, src, cfg, method_info,
353355 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 ,
355357 result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects,
356358 restrict_abstract_call_sites, cache_mode, insert_coverage,
357359 interp)
@@ -800,6 +802,7 @@ mutable struct IRInterpretationState
800802 const ssa_refined:: BitSet
801803 const lazyreachability:: LazyCFGReachability
802804 valid_worlds:: WorldRange
805+ const tasks:: Vector{WorkThunk}
803806 const edges:: Vector{Any}
804807 callstack # ::Vector{AbsIntState}
805808 frameid:: Int
@@ -825,10 +828,11 @@ mutable struct IRInterpretationState
825828 ssa_refined = BitSet ()
826829 lazyreachability = LazyCFGReachability (ir)
827830 valid_worlds = WorldRange (min_world, max_world == typemax (UInt) ? get_world_counter () : max_world)
831+ tasks = WorkThunk[]
828832 edges = Any[]
829833 callstack = AbsIntState[]
830834 return new (method_info, ir, mi, world, curridx, argtypes_refined, ir. sptypes, tpdum,
831- ssa_refined, lazyreachability, valid_worlds, edges, callstack, 0 , 0 )
835+ ssa_refined, lazyreachability, valid_worlds, tasks, edges, callstack, 0 , 0 )
832836 end
833837end
834838
@@ -870,6 +874,7 @@ function print_callstack(frame::AbsIntState)
870874 print (frame_instance (sv))
871875 is_cached (sv) || print (" [uncached]" )
872876 sv. parentid == idx - 1 || print (" [parent=" , sv. parentid, " ]" )
877+ isempty (callers_in_cycle (sv)) || print (" [cycle=" , sv. cycleid, " ]" )
873878 println ()
874879 @assert sv. frameid == idx
875880 end
@@ -994,7 +999,10 @@ of the same cycle, only if it is part of a cycle with multiple frames.
994999function callers_in_cycle (sv:: InferenceState )
9951000 callstack = sv. callstack:: Vector{AbsIntState}
9961001 cycletop = cycleid = sv. cycleid
997- while cycletop < length (callstack) && (callstack[cycletop + 1 ]:: InferenceState ). cycleid == cycleid
1002+ while cycletop < length (callstack)
1003+ frame = callstack[cycletop + 1 ]
1004+ frame isa InferenceState || break
1005+ frame. cycleid == cycleid || break
9981006 cycletop += 1
9991007 end
10001008 return AbsIntCycle (callstack, cycletop == cycleid ? 0 : cycleid, cycletop)
@@ -1054,6 +1062,7 @@ function merge_effects!(::AbstractInterpreter, caller::InferenceState, effects::
10541062 effects = Effects (effects; effect_free= ALWAYS_TRUE)
10551063 end
10561064 caller. ipo_effects = merge_effects (caller. ipo_effects, effects)
1065+ nothing
10571066end
10581067merge_effects! (:: AbstractInterpreter , :: IRInterpretationState , :: Effects ) = return
10591068
@@ -1116,3 +1125,90 @@ function get_max_methods_for_module(mod::Module)
11161125 max_methods < 0 && return nothing
11171126 return max_methods
11181127end
1128+
1129+ """
1130+ Future{T}
1131+
1132+ Delayed return value for a value of type `T`, similar to RefValue{T}, but
1133+ explicitly represents completed as a `Bool` rather than as `isdefined`.
1134+ Set once with `f[] = v` and accessed with `f[]` afterwards.
1135+
1136+ Can also be constructed with the `completed` flag value and a closure to
1137+ produce `x`, as well as the additional arguments to avoid always capturing the
1138+ same couple of values.
1139+ """
1140+ struct Future{T}
1141+ later:: Union{Nothing,RefValue{T}}
1142+ now:: Union{Nothing,T}
1143+ Future {T} () where {T} = new {T} (RefValue {T} (), nothing )
1144+ Future {T} (x) where {T} = new {T} (nothing , x)
1145+ Future (x:: T ) where {T} = new {T} (nothing , x)
1146+ end
1147+ isready (f:: Future ) = f. later === nothing
1148+ getindex (f:: Future{T} ) where {T} = (later = f. later; later === nothing ? f. now:: T : later[])
1149+ setindex! (f:: Future , v) = something (f. later)[] = v
1150+ convert (:: Type{Future{T}} , x) where {T} = Future {T} (x) # support return type conversion
1151+ convert (:: Type{Future{T}} , x:: Future ) where {T} = x:: Future{T}
1152+ function Future {T} (f, immediate:: Bool , interp:: AbstractInterpreter , sv:: AbsIntState ) where {T}
1153+ if immediate
1154+ return Future {T} (f (interp, sv))
1155+ else
1156+ @assert applicable (f, interp, sv)
1157+ result = Future {T} ()
1158+ push! (sv. tasks, function (interp, sv)
1159+ result[] = f (interp, sv)
1160+ return true
1161+ end )
1162+ return result
1163+ end
1164+ end
1165+ function Future {T} (f, prev:: Future{S} , interp:: AbstractInterpreter , sv:: AbsIntState ) where {T, S}
1166+ later = prev. later
1167+ if later === nothing
1168+ return Future {T} (f (prev[], interp, sv))
1169+ else
1170+ @assert Core. _hasmethod (Tuple{Core. Typeof (f), S, typeof (interp), typeof (sv)})
1171+ result = Future {T} ()
1172+ push! (sv. tasks, function (interp, sv)
1173+ result[] = f (later[], interp, sv) # capture just later, instead of all of prev
1174+ return true
1175+ end )
1176+ return result
1177+ end
1178+ end
1179+
1180+
1181+ """
1182+ doworkloop(args...)
1183+
1184+ Run a tasks inside the abstract interpreter, returning false if there are none.
1185+ Tasks will be run in DFS post-order tree order, such that all child tasks will
1186+ be run in the order scheduled, prior to running any subsequent tasks. This
1187+ allows tasks to generate more child tasks, which will be run before anything else.
1188+ Each task will be run repeatedly when returning `false`, until it returns `true`.
1189+ """
1190+ function doworkloop (interp:: AbstractInterpreter , sv:: AbsIntState )
1191+ tasks = sv. tasks
1192+ prev = length (tasks)
1193+ prev == 0 && return false
1194+ task = pop! (tasks)
1195+ completed = task (interp, sv)
1196+ tasks = sv. tasks # allow dropping gc root over the previous call
1197+ completed isa Bool || throw (TypeError (:return , " " , Bool, task)) # print the task on failure as part of the error message, instead of just "@ workloop:line"
1198+ completed || push! (tasks, task)
1199+ # efficient post-order visitor: items pushed are executed in reverse post order such
1200+ # that later items are executed before earlier ones, but are fully executed
1201+ # (including any dependencies scheduled by them) before going on to the next item
1202+ reverse! (tasks, #= start=# prev)
1203+ return true
1204+ end
1205+
1206+
1207+ # macro workthunk(name::Symbol, body)
1208+ # name = esc(name)
1209+ # body = esc(body)
1210+ # return replace_linenums!(
1211+ # :(function $name($(esc(interp)), $(esc(sv)))
1212+ # $body
1213+ # end), __source__)
1214+ # end
0 commit comments