Next: machinery continuations escape, Previous: machinery continuations immut, Up: machinery continuations [Index]
We assume the validity of machinery simplifications to focus on some aspect of the runtime behaviour, Simplification assumptions. Let’s consider the following program that creates a continuation but does not call its escape function:
(import (rnrs)) (define (alpha A) (beta A)) (define (beta B) (call/cc (lambda (escape) (+ B 2)))) (alpha 1)
we can understand how the stack grows until right after the call to
call/cc
but before call/cc
actually does something,
machinery continuations non.
high memory | | |----------------------| | | <- pcb->frame_base |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . frame of toplevel |----------------------| -- | argument A == 1 | . |----------------------| . frame of alpha | return addr to alpha | . |----------------------| -- | argument B == 1 | . |----------------------| . frame of beta | return addr to beta | <- FPR . |----------------------| -- | closure reference | . locals of call/cc |----------------------| . | | low memory
For convenience we give a name to the closure (lambda (escape) (+
B 2))
, let’s call it receiver4. call/cc
creates a continuation object by freezing
the call frames currently on the stack between FPR and 2 words
below pcb->frame_base
, machinery
continuations non.
high memory | | |----------------------| | | <- pcb->frame_base |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . | argument A == 1 | . |----------------------| . freezed | return addr to alpha | . frames |----------------------| . | argument B == 1 | . |----------------------| . | return addr to beta | <- FPR . |----------------------| -- | receiver | . locals of |----------------------| . call/cc | | low memory
The continuation object references the array of freezed words on the
stack, keeping a reference to the value of FPR at the time of
creation. The continuation object is also a node in a simply linked
list of continuation objects, whose head is stored in a field of the
PCB: pcb->next_k
. In more detail call/cc
does the
following:
pcb->next_k
; let’s call this continuation object K_1;
machinery continuations non.
SL_continuation_code
, the
single slot for free variables references the continuation object.
pcb->frame_base
to exclude the
freezed frames from the stack segment;
machinery continuations non.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . | argument A == 1 | . |----------------------| . freezed | return addr to alpha | . frames |----------------------| . in K_1 | argument B == 1 | . |----------------------| . | return addr to beta | <- pcb->frame_base . |----------------------| -- | ik_underflow_handler | <- FPR . |----------------------| . locals of | escape closure | . receiver |----------------------| . closure | | low memory
In this example, the receiver closure executes (+ B 2)
and
returns; there is only a single return value: it is put in AAR and
the return from the closure is performed with a ret
Assembly
instruction. We skip the details of performing the addition and
consider the stack right before the ret
Assembly instruction is
executed by the CPU, machinery
continuations non.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . | argument A == 1 | . |----------------------| . freezed | return addr to alpha | . frames |----------------------| . in K_1 | argument B == 1 | . |----------------------| . | return addr to beta | <- pcb->frame_base . |----------------------| -- | ik_underflow_handler | <- FPR |----------------------| | | low memory
The FPR references the underflow handler, so the ret
instruction will cause the execution flow to jump there; the stack
underflow handler does the following:
beta
and a new
continuation object K_2 referencing all the other frames,
machinery continuations non.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . freezed | argument A == 1 | . frames |----------------------| . in K_2 | return addr to alpha | . |----------------------| -- | argument B == 1 | . freezed |----------------------| . frame | return addr to beta | <- pcb->frame_base . in K_1 |----------------------| -- | ik_underflow_handler | <- FPR |----------------------| | | low memory
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . freezed | argument A == 1 | . frames |----------------------| . in K_2 | return addr to alpha | . |----------------------| -- | argument B == 1 | . freezed |----------------------| . frame | return addr to beta | <- pcb->frame_base . in K_1 |----------------------| -- | ik_underflow_handler | |----------------------| | argument B == 1 | |----------------------| | return addr to beta | <- FPR |----------------------| | | low memory
ret
Assembly
instruction to go back to the body of beta
.
The code of beta
is reentered right after the call to
call/cc
and the FPR is adjusted to reference the address of
the stack underflow handler, compiler
machinery continuations non. beta
must return the same return
value, which is already in the AAR: it executes a ret
Assembly
instruction.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . freezed | argument A == 1 | . frames |----------------------| . in K_2 | return addr to alpha | . |----------------------| -- | argument B == 1 | . freezed |----------------------| . frame | return addr to beta | <- pcb->frame_base . in K_1 |----------------------| -- | ik_underflow_handler | <- FPR |----------------------| | | low memory
The stack underflow handler is reentered and does the following:
alpha
and a new
continuation object K_3 referencing all the other frames,
machinery continuations non.
high memory | | |----------------------| | ik_underflow_handler | freezed |----------------------| -- frame | return addr to top | . in K_3 |----------------------| -- | argument A == 1 | . freezed |----------------------| . frames | return addr to alpha | . in K_2 |----------------------| -- | argument B == 1 | . freezed |----------------------| . frame | return addr to beta | <- pcb->frame_base . in K_1 |----------------------| -- | ik_underflow_handler | <- FPR |----------------------| | | low memory
high memory | | |----------------------| | ik_underflow_handler | freezed |----------------------| -- frames | return addr to top | . in K_3 |----------------------| -- | argument A == 1 | . |----------------------| . frames | return addr to alpha | . in K_2 |----------------------| -- | argument B == 1 | . freezed |----------------------| . frame | return addr to beta | <- pcb->frame_base . in K_1 |----------------------| -- | ik_underflow_handler | |----------------------| | argument A == 1 | |----------------------| | return addr to alpha | <- FPR |----------------------| | | low memory
ret
Assembly
instruction to go back to the body of alpha
.
The code of alpha
is reentered right after the call to
beta
and the FPR is adjusted to reference the address of the
stack underflow handler, compiler
machinery continuations non. alpha
must return the same return
value, which is already in the AAR: it executes a ret
Assembly
instruction.
high memory | | |----------------------| | ik_underflow_handler | freezed |----------------------| -- frames | return addr to top | . in K_3 |----------------------| -- | argument A == 1 | . |----------------------| . frames | return addr to alpha | . in K_2 |----------------------| -- | argument B == 1 | . freezed |----------------------| . frame | return addr to beta | <- pcb->frame_base . in K_1 |----------------------| -- | ik_underflow_handler | <- FPR |----------------------| | | low memory
The stack underflow handler is reentered and does the following:
pcb->next_k
to the next field in
K_3, machinery continuations non.
high memory | | |----------------------| | ik_underflow_handler | freezed |----------------------| -- frames | return addr to top | . in K_3 |----------------------| -- | argument A == 1 | . |----------------------| . frames | return addr to alpha | . in K_2 |----------------------| -- | argument B == 1 | . freezed |----------------------| . frame | return addr to beta | <- pcb->frame_base . in K_1 |----------------------| -- | ik_underflow_handler | |----------------------| | return addr to top | <- FPR |----------------------| | | low memory
ret
Assembly
instruction to go back to the top level expression.
The code of the top level expression is reentered right after the call
to alpha
and the FPR is adjusted to reference the address of
the stack underflow handler, compiler
machinery continuations escape. The top level expression is the last
one in the program, so it returns the return value it receives, which is
already in the AAR: it executes a ret
Assembly instruction.
high memory | | |----------------------| | ik_underflow_handler | freezed |----------------------| -- frames | return addr to top | . in K_3 |----------------------| -- | argument A == 1 | . |----------------------| . frames | return addr to alpha | . in K_2 |----------------------| -- | argument B == 1 | . freezed |----------------------| . frame | return addr to beta | <- pcb->frame_base . in K_1 |----------------------| -- | ik_underflow_handler | <- FPR |----------------------| | | low memory
The underflow handler is reentered again with NULL
as “next PCB
continuation”: this means the program is terminated, so the underflow
handler does what is needed.
Springer and Friedman call it “receiver” in their “Scheme and the Art of Programming”, The MIT Press, 1989.
Next: machinery continuations escape, Previous: machinery continuations immut, Up: machinery continuations [Index]