Skip to content

Commit 3180c15

Browse files
committed
Expand on refs and misty closure usage
1 parent 4472ede commit 3180c15

File tree

1 file changed

+38
-7
lines changed

1 file changed

+38
-7
lines changed

src/copyable_task.jl

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,44 @@ julia> consume(t)
222222
223223
# Implementation Notes
224224
225-
Under the hood, a `TapedTask` is implemented in terms of a `MistyClosure.` Each SSA value in
226-
the original IR is replaced with a `Base.RefValue` of the same type. Each statement writes
227-
to / reads from these SSAs. These refs are stored in the data that the `MistyClosure` closes
228-
over, meaning that they persist between calls to `consume`. These refs are copied when a
229-
copy of a `TapedTask` is made -- since they define the entire state of a task, making a
230-
completely independent copy of them ensures that a completely independent copy of the task
231-
is made.
225+
Under the hood, we implement a `TapedTask` by obtaining the `IRCode` associated to the
226+
original function, transforming it so that it implements the semantics required by the
227+
`produce` / `consume` interface, and placing it inside a `MistyClosure` to make it possible
228+
to execute.
229+
230+
There are two main considerations when transforming the `IRCode`. The first is to ensure
231+
that the "state" of a `TapedTask` can be copied, so that a `TapedTask` can be copied, and
232+
resumed later. The complete state of a `TapedTask` is given by its arguments, and the value
233+
associated to each ssa (these are initially undefined).
234+
To make it possible to copy the state of the ssa values, we place a `Base.RefValue{T}`s into
235+
the captures of the `MistyClosure` which implements the `TapedTask`, one for each ssa in the
236+
IR (`T` is the type inferred for that ssa). A call is replaced by reading in values of ssas
237+
from these refs, applying the original operation, and writing the result to the ref
238+
associated to the instruction. For example, if the original snippet of `IRCode` is something
239+
like
240+
```julia
241+
%5 = f(%3, _1)
242+
```
243+
the transformed IR would be something like
244+
```julia
245+
%5 = ref_for_%3[]
246+
%6 = f(%5, _1)
247+
ref_for_%5[] = %6
248+
```
249+
Setting things up in this manner ensures that an independent copy is made by simply copying
250+
all of the refs. A `deepcopy` is required for correctness as, while the refs to not alias
251+
one another (by construction), their contents might. For example, two refs may contain the
252+
same `Array`, and in general the behaviour of a function depends on this relationship.
253+
254+
The second component of the transformation is implementing the `produce` mechanism, and the
255+
ability to resume computation from where we produced. Roughly speaking, the `IRCode` must be
256+
modified to ensure that whenever a `produce` call in encountered, the `MistyClosure` returns
257+
the argument to `produce`, and that subsequent calls resume computation immediately after
258+
the `produce` statement. Observe that this is also facilitated by the ref mechanism
259+
discussed above, as it ensures that the state persists between calls to a `MistyClosure`.
260+
261+
The above gives the broad outline of how `TapedTask`s are implemented. We refer interested
262+
readers to the code, which is extensively commented to explain implementation details.
232263
"""
233264
function TapedTask(taped_globals::Any, fargs...; kwargs...)
234265
all_args = isempty(kwargs) ? fargs : (Core.kwcall, getfield(kwargs, :data), fargs...)

0 commit comments

Comments
 (0)