Previous: machinery continuations non, 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 and invokes its escape function:
(import (rnrs)) (define (alpha A) (call/cc (lambda (escape) (beta escape A)))) (define (beta escape B) (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 escape.
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 | . |----------------------| -- | receiver | . locals of call/cc |----------------------| . | | low memory
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 escape.
high memory | | |----------------------| | | <- pcb->frame_base |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . freezed | argument A == 1 | . frames |----------------------| . | return addr to alpha | <- 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 creation time. 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 escape.
SL_continuation_code
, the
single slot for free variables references the continuation object.
pcb->frame_base
is set to
exclude the freezed frames from the stack segment;
machinery continuations escape.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . freezed | argument A == 1 | . frames |----------------------| . in K_1 | return addr to alpha | <- pcb->frame_base . |----------------------| -- | ik_underflow_handler | <- FPR |----------------------| -- locals of | escape closure | . receiver |----------------------| . closure | | low memory
In this example, the receiver closure calls beta
with 2
arguments, machinery continuations
escape.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . freezed | argument A == 1 | . frames |----------------------| . in K_1 | return addr to alpha | <- pcb->frame_base . |----------------------| -- | ik_underflow_handler | |----------------------| -- frame of | return addr to receiv| <- FPR . receiver |----------------------| -- | escape closure | . locals |----------------------| . in beta | argument B == 1 | . |----------------------| . | | low memory
beta
applies the escape closure to the result of (+ B 2)
,
let’s skip the details of performing the addition. To apply the escape
closure to the single argument representing the addition: the reference
to escape function is copied in the CPR, the single argument is
placed after an empty machine word, the FPR is moved right above the
empty word, finally a call
Assembly instruction is executed,
machinery continuations escape.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . freezed | argument A == 1 | . frames |----------------------| . in K_1 | return addr to alpha | <- pcb->frame_base . |----------------------| -- | ik_underflow_handler | |----------------------| -- frame of | return addr to receiv| <- FPR . receiver |----------------------| -- | escape closure | . |----------------------| . frame | argument B == 1 | . of beta |----------------------| . | return addr to beta | <- FPR . |----------------------| -- | argument fixnum 3 | . locals of |----------------------| . escape | | low memory
A continuation’s escape function is a special subroutine that throws
away all the stack frames up to, and excluding, the undeflow handler
address below the pcb->frame_base
,
machinery continuations escape.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- | return addr to top | . |----------------------| . freezed | argument A == 1 | . frames |----------------------| . in K_1 | return addr to alpha | <- pcb->frame_base . |----------------------| -- | ik_underflow_handler | <- FPR |----------------------| | | low memory
The argument of the escape function becomes its return value; in this
example there is a single return value, so it is moved in the AAR.
The continuation object K_1 in the escape closure is stored in
pcb->next_k
, becoming the “next PCB continuation”; the old
value of pcb->next_k
is simply overwritten: a new list of
continuations is installed in the PCB. Finally a ret
Assembly
instruction is executed.
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:
alpha
and a new
continuation object K_2 referencing all the other frames,
machinery continuations escape.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- freezed | return addr to top | . frame K_2 |----------------------| -- | argument A == 1 | . freezed |----------------------| . frame | return addr to alpha | <- pcb->frame_base . in K_1 |----------------------| -- | ik_underflow_handler | <- FPR |----------------------| | | low memory
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- freezed | return addr to top | . frame K_2 |----------------------| -- | argument A == 1 | . freezed |----------------------| . frame | return addr to alpha | <- 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
call/cc
and the FPR is adjusted to reference the address of
the stack underflow handler, compiler
machinery continuations escape. 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 | return addr to top | . frame K_2 |----------------------| -- | argument A == 1 | . freezed |----------------------| . frame | return addr to alpha | <- 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
, machinery
continuations escape.
high memory | | |----------------------| | ik_underflow_handler | |----------------------| -- freezed | return addr to top | . frame K_2 |----------------------| -- | argument A == 1 | . freezed |----------------------| . frame | return addr to alpha | <- 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 | return addr to top | . frame K_2 |----------------------| -- | argument A == 1 | . freezed |----------------------| . frame | return addr to alpha | <- 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.
We can understand that this mechanism of duplicating the freezed frames can be repeated any number of times, allowing the execution flow to go back to a saved continuation any number of times.
Previous: machinery continuations non, Up: machinery continuations [Index]