Consider this code:
(... (const code-pointer (code ...)) (prim closure cons code-pointer bound-vars) (jump p closure))
bound-vars is partially static. Then
closure would be lifted to
D before the jump. But if
closure will only be called, then we can do better:
replace it with a closure where
code-pointer has been specialized
to the static portion of
Rather than automatically recognizing this opportunity or using a
typed language, nitrous relies on a hint in the code:
closure-cons marks a particular cons cell as a closure.
D| (cons bt bt cp is-clo) | (const v) cp ::= instruction is-clo ::= boolean v ::= any-value
Unlike other higher-order partial-evaluators, specialization of the
code pointer to its bound variables does not happen at the point in
the source where the lambda (
closure-cons) appears. Instead, the
full structure is propagated as a normal value until its binding time
is lifted to
Cogen will create an extension for code structure
partially static binding time
p when it encounters the
(cons (const cd) p _ #t) -->The compiler calls this extension to preserve the static information in
prather than lose it to the lift.
closure-cons guarantees to cogen that the cell will only be used
in a particular way (jump to the car, passing the cell itself as the
first argument), thus it defines a higher-order calling convention.
If the programmer used a different representation, cogen would not be
able to follow the control flow. This is one way the system is
This is somewhat different from how lambda expressions are handled in [Consel90].