Guile’s CPS language is composed of terms, expressions, and continuations.
A term can either evaluate an expression and pass the resulting values to some continuation, or it can declare local continuations and contain a sub-term in the scope of those continuations.
Evaluate the expression exp and pass the resulting values (if any)
to the continuation labelled k. The source information associated
with the expression may be found in src, which is either an alist
source-properties or is
#f if there is no associated
Bind conts, a list of continuations (
$cont instances), in
the scope of the sub-term body. The continuations are mutually
Additionally, the early stages of CPS allow for a set of mutually
recursive functions to be declared as a term. This
is like Tree-IL’s
<fix>. The contification pass will attempt to
transform the functions declared in a
$letrec into local
continuations. Any remaining functions are later lowered to
Declare the mutually recursive set of functions denoted by names,
syms, and funs within the sub-term body. names
and syms are lists of symbols, and funs is a list of
$fun values. syms are globally unique.
Here is an inventory of the kinds of expressions in Guile’s CPS
language. Recall that all expressions are wrapped in a
term which specifies their continuation.
Continue with the unspecified value.
Continue with the constant value val.
Continue with the procedure that implements the primitive operation named by name.
Continue with a procedure. src identifies the source information
for the procedure declaration, and meta is the metadata alist as
described above in Tree-IL’s
<lambda>. free is a list of
free variables accessed by the procedure. Early CPS uses an empty list
for free; only after closure conversion is it correctly populated.
Finally, body is the
$cont of the procedure
Call proc with the arguments args, and pass all values to
the continuation. proc and the elements of the args list
should all be variable names. The continuation identified by the term’s
k should be a
$kreceive or a
$callk is for the case where the call target is known to be in
the same compilation unit. label should be some continuation
label, though it need not be in scope. In this case the proc is
simply an additional argument, since it is not used to determine the
call target at run-time.
Perform the primitive operation identified by
name, a well-known
symbol, passing it the arguments args, and pass all resulting
values to the continuation. The set of available primitives includes
all primitives known to Tree-IL and then some more; see the source code
Pass the values named by the list args to the continuation.
Push a prompt on the stack identified by the variable name tag,
which may be escape-only if escape? is true, and continue with
zero values. If the body aborts to this prompt, control will proceed at
the continuation labelled handler, which should be a
$kreceive continuation. Prompts are later popped by
The remaining element of the CPS language in Guile is the continuation.
In CPS, all continuations have unique labels. Since this aspect is
common to all continuation types, all continuations are contained in a
Declare a continuation labelled k. All references to the continuation will use this label.
The most common kind of continuation binds some number of values, and
then evaluates a sub-term.
$kargs is this kind of simple
Bind the incoming values to the variables syms, with original names names, and then evaluate the sub-term body.
Variable names (the names in the syms of a
be globally unique, and also disjoint from continuation labels. To bind
a value to a variable and then evaluate some term, you would continue
with the value to a
$kargs that declares one variable. The bound
value would then be available for use within the body of the
Receive one value. If it is true for the purposes of Scheme, branch to the continuation labelled kt, passing no values; otherwise, branch to kf.
For internal reasons, only certain terms may continue to a
$kif avoids allocating space for the test variable, so
it needs to be preceded by expressions that can test-and-branch without
temporary values. In practice this condition is true for
=, and similar primitives that
br-if-foo VM operations; see the source
code for full details. When in doubt, bind the test expression to a
variable, and continue to the
$kif with a
expression. The optimizer should elide the
$values if it is not
Calls out to other functions need to be wrapped in a
continuation in order to adapt the returned values to their uses in the
calling function, if any.
Receive values on the stack. Parse them according to arity, and
then proceed with the parsed values to the
labelled k. As a limitation specific to
arity may only contain required and rest arguments.
$arity is a helper data structure used by
$kclause, described below.
A data type declaring an arity. req and opt are lists of
source names of required and optional arguments, respectively.
rest is either the source name of the rest variable, or
if this arity does not accept additional values. kw is a list of
((keyword name var) ...), describing
the keyword arguments. allow-other-keys? is true if other keyword
arguments are allowed and false otherwise.
Note that all of these names with the exception of the vars in the kw list are source names, not unique variable names.
Additionally, there are three specific kinds of continuations that can only be declared at function entries.
Declare a function entry. self is a variable bound to the
procedure being called, and which may be used for self-references.
tail declares the
$cont wrapping the
$ktail for this
function, corresponding to the function’s tail continuation.
clauses is a list of
A tail continuation.
A clause of a function with a given arity. Applications of a function
with a compatible set of actual arguments will continue to cont, a
$cont instance representing the clause body.