1- const dynamic_scope = ScopedValue {Any} (0 )
2-
31"""
4- get_dynamic_scope()
2+ get_dynamic_scope(T::Type )
53
64Returns the dynamic scope associated to `Libtask`. If called from inside a `TapedTask`, this
75will return whatever is contained in its `dynamic_scope` field.
86
7+ The type `T` is required for optimal performance. If you know that the result of this
8+ operation must return a specific type, specific `T`. If you do not know what type it will
9+ return, pass `Any` -- this will typically yield type instabilities, but will run correctly.
10+
911See also [`set_dynamic_scope!`](@ref).
1012"""
11- get_dynamic_scope () = dynamic_scope[]
13+ get_dynamic_scope (:: Type{T} ) where {T} = typeassert ( task_local_storage ( :task_variable ), T)
1214
1315__v:: Int = 5
1416
@@ -25,16 +27,21 @@ See also: [`Libtask.consume`](@ref)
2527 return x
2628end
2729
28- function callable_ret_type (sig)
29- return Union{Base. code_ircode_by_type (sig)[1 ][2 ],ProducedValue}
30+ function callable_ret_type (sig, types)
31+ produce_type = Union{}
32+ for t in types
33+ p = isconcretetype (t) ? ProducedValue{t} : ProducedValue{T} where {T<: t }
34+ produce_type = CC. tmerge (p, produce_type)
35+ end
36+ return Union{Base. code_ircode_by_type (sig)[1 ][2 ],produce_type}
3037end
3138
3239function build_callable (sig:: Type{<:Tuple} )
3340 ir = Base. code_ircode_by_type (sig)[1 ][1 ]
34- bb, refs = derive_copyable_task_ir (BBCode (ir))
41+ bb, refs, types = derive_copyable_task_ir (BBCode (ir))
3542 unoptimised_ir = IRCode (bb)
3643 optimised_ir = Mooncake. optimise_ir! (unoptimised_ir)
37- mc_ret_type = callable_ret_type (sig)
44+ mc_ret_type = callable_ret_type (sig, types )
3845 mc = Mooncake. misty_closure (mc_ret_type, optimised_ir, refs... ; do_compile= true )
3946 return mc, refs[end ]
4047end
@@ -200,7 +207,8 @@ called, it start execution from the entry point. If `consume` has previously bee
200207`nothing` will be returned.
201208"""
202209@inline function consume (t:: TapedTask )
203- v = with (() -> t. mc (t. fargs... ), dynamic_scope => t. dynamic_scope)
210+ task_local_storage (:task_variable , t. dynamic_scope)
211+ v = t. mc. oc (t. fargs... )
204212 return v isa ProducedValue ? v[] : nothing
205213end
206214
285293struct ProducedValue{T}
286294 x:: T
287295end
296+ ProducedValue (:: Type{T} ) where {T} = ProducedValue {Type{T}} (T)
288297
289298@inline Base. getindex (x:: ProducedValue ) = x. x
290299
@@ -318,7 +327,35 @@ inc_args(x::Core.PiNode) = Core.PiNode(__inc(x.val), __inc(x.typ))
318327__inc (x:: Argument ) = Argument (x. n + 1 )
319328__inc (x) = x
320329
321- function derive_copyable_task_ir (ir:: BBCode ):: Tuple{BBCode,Tuple}
330+ const TypeInfo = Tuple{Vector{Any},Dict{ID,Type}}
331+
332+ """
333+ _typeof(x)
334+
335+ Central definition of typeof, which is specific to the use-required in this package.
336+ """
337+ _typeof (x) = Base. _stable_typeof (x)
338+ _typeof (x:: Tuple ) = Tuple{tuple_map (_typeof, x)... }
339+ _typeof (x:: NamedTuple{names} ) where {names} = NamedTuple{names,_typeof (Tuple (x))}
340+
341+ """
342+ get_type(info::ADInfo, x)
343+
344+ Returns the static / inferred type associated to `x`.
345+ """
346+ get_type (info:: TypeInfo , x:: Argument ) = info[1 ][x. n - 1 ]
347+ get_type (info:: TypeInfo , x:: ID ) = CC. widenconst (info[2 ][x])
348+ get_type (:: TypeInfo , x:: QuoteNode ) = _typeof (x. value)
349+ get_type (:: TypeInfo , x) = _typeof (x)
350+ function get_type (:: TypeInfo , x:: GlobalRef )
351+ return isconst (x) ? _typeof (getglobal (x. mod, x. name)) : x. binding. ty
352+ end
353+ function get_type (:: TypeInfo , x:: Expr )
354+ x. head === :boundscheck && return Bool
355+ return error (" Unrecognised expression $x found in argument slot." )
356+ end
357+
358+ function derive_copyable_task_ir (ir:: BBCode ):: Tuple{BBCode,Tuple,Vector{Any}}
322359
323360 # The location from which all state can be retrieved. Since we're using `OpaqueClosure`s
324361 # to implement `TapedTask`s, this appears via the first argument.
@@ -338,13 +375,17 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple}
338375 ssa_id_to_ref_index_map = Dict {ID,Int} ()
339376 ref_index_to_ssa_id_map = Dict {Int,ID} ()
340377 ref_index_to_type_map = Dict {Int,Type} ()
378+ id_to_type_map = Dict {ID,Type} ()
379+ is_used_dict = characterise_used_ids (collect_stmts (ir))
341380 n = 0
342381 for bb in ir. blocks
343382 for (id, stmt) in zip (bb. inst_ids, bb. insts)
383+ id_to_type_map[id] = CC. widenconst (stmt. type)
344384 stmt. stmt isa IDGotoNode && continue
345385 stmt. stmt isa IDGotoIfNot && continue
346386 stmt. stmt === nothing && continue
347387 stmt. stmt isa ReturnNode && continue
388+ is_used_dict[id] || continue
348389 n += 1
349390 ssa_id_to_ref_index_map[id] = n
350391 ref_index_to_ssa_id_map[n] = id
@@ -447,6 +488,9 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple}
447488 # A set of blocks from which we might wish to resume computation.
448489 resume_block_ids = Vector {ID} ()
449490
491+ # A list onto which we'll push the type of any statement which might produce.
492+ possible_produce_types = Any[]
493+
450494 # This where most of the action happens.
451495 #
452496 # For each split of each block, we must
@@ -582,10 +626,13 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple}
582626 push! (inst_pairs, (id, inst))
583627
584628 # If we know it is not possible for this statement to contain any calls
585- # to produce, then simply write out the result to its `Ref`.
586- out_ind = ssa_id_to_ref_index_map[id]
587- set_ref = Expr (:call , set_ref_at!, refs_id, out_ind, id)
588- push! (inst_pairs, (ID (), new_inst (set_ref)))
629+ # to produce, then simply write out the result to its `Ref`. If it is
630+ # never used, then there is no need to store it.
631+ if is_used_dict[id]
632+ out_ind = ssa_id_to_ref_index_map[id]
633+ set_ref = Expr (:call , set_ref_at!, refs_id, out_ind, id)
634+ push! (inst_pairs, (ID (), new_inst (set_ref)))
635+ end
589636 elseif Meta. isexpr (stmt, :boundscheck )
590637 push! (inst_pairs, (id, inst))
591638 elseif Meta. isexpr (stmt, :code_coverage_effect )
@@ -677,6 +724,10 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple}
677724 # computation is resumed from the statement _after_ this produce statement,
678725 # and to return whatever this produce statement returns.
679726
727+ # Log the result type of this statement.
728+ arg = stmt. args[Meta. isexpr (stmt, :invoke ) ? 3 : 2 ]
729+ push! (possible_produce_types, get_type ((ir. argtypes, id_to_type_map), arg))
730+
680731 # When this TapedTask is next called, we should resume from the first
681732 # statement of the next split.
682733 resume_id = splits_ids[n + 1 ]
@@ -733,6 +784,11 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple}
733784 # which involves calls that might produce, in order to get a sense of what
734785 # the resulting code looks like prior to digging into the code below.
735786
787+ # At present, we're not able to properly infer the values which might
788+ # potentially be produced by a call-which-might-produce. Consequently, we
789+ # have to assume they can produce anything.
790+ push! (possible_produce_types, Any)
791+
736792 # Create a new basic block from the existing statements, since all new
737793 # statement need to live in their own basic blocks.
738794 callable_block_id = ID ()
@@ -742,7 +798,8 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple}
742798 # Derive TapedTask for this statement.
743799 (callable, callable_args) = if Meta. isexpr (stmt, :invoke )
744800 sig = stmt. args[1 ]. specTypes
745- (LazyCallable {sig,callable_ret_type(sig)} (), stmt. args[2 : end ])
801+ v = Any[Any]
802+ (LazyCallable {sig,callable_ret_type(sig, v)} (), stmt. args[2 : end ])
746803 elseif Meta. isexpr (stmt, :call )
747804 (DynamicCallable (), stmt. args)
748805 else
@@ -815,8 +872,12 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple}
815872 # `ProducedValue`. In this case, we must first push the result to the `Ref`
816873 # associated to the call, and goto the next split.
817874 next_block_id = splits_ids[n + 1 ] # safe since the last split ends with a terminator
818- result_ref_ind = ssa_id_to_ref_index_map[id]
819- set_ref = Expr (:call , set_ref_at!, refs_id, result_ref_ind, result_id)
875+ if is_used_dict[id]
876+ result_ref_ind = ssa_id_to_ref_index_map[id]
877+ set_ref = Expr (:call , set_ref_at!, refs_id, result_ref_ind, result_id)
878+ else
879+ set_ref = nothing
880+ end
820881 not_produced_block_inst_pairs = Mooncake. IDInstPair[
821882 (ID (), new_inst (set_ref))
822883 (ID (), new_inst (IDGotoNode (next_block_id)))
@@ -851,13 +912,12 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple}
851912 new_argtypes = vcat (typeof (refs), copy (ir. argtypes))
852913
853914 # Return BBCode and the `Ref`s.
854- return BBCode (new_bblocks, new_argtypes, ir. sptypes, ir. linetable, ir. meta), refs
915+ new_ir = BBCode (new_bblocks, new_argtypes, ir. sptypes, ir. linetable, ir. meta)
916+ return new_ir, refs, possible_produce_types
855917end
856918
857919# Helper used in `derive_copyable_task_ir`.
858- @inline function get_ref_at (refs:: R , n:: Int ) where {R<: Tuple }
859- return refs[n][]
860- end
920+ @inline get_ref_at (refs:: R , n:: Int ) where {R<: Tuple } = refs[n][]
861921
862922# Helper used in `derive_copyable_task_ir`.
863923@inline function set_ref_at! (refs:: R , n:: Int , val) where {R<: Tuple }
0 commit comments