Next: compiler topics clambda, Previous: compiler topics topbind, Up: compiler topics [Index]
Lexical local bindings are defined in the core language by the forms:
let, letrec, letrec*, lambda,
case-lambda, annotated-case-lambda. These forms have
syntax:
(let ((?lex ?rhs) ...) ?body) (letrec ((?lex ?rhs) ...) ?body) (letrec* ((?lex ?rhs) ...) ?body) (case-lambda (?formals ?body) ...) (annotated-case-lambda ?annotation (?formals ?body) ...) (lambda ?formals ?body)
in which: ?lex is a lexical gensym that uniquely identifies the binding in the core language form; ?rhs is the initialisation expression; ?formals is a proper or improper list of lex gensyms uniquely identifying arguments to function.
References to local bindings are represented by standalone ?lex
gensyms; assignments to local bindings are represented by set!
forms:
(set! ?lex ?rhs)
where the right–hand side expression ?rhs will, at run–time, evaluate to the new binding’s value.
The let, letrec and letrec* forms are recordised
into nested hierarchies of bind, recbind and
rec*bind structs:
(bind ((?prel ?rhs) ...) ?body) (recbind ((?prel ?rhs) ...) ?body) (rec*bind ((?prel ?rhs) ...) ?body)
in which ?prel is a prelex struct holding the ?lex
gensym.
The lambda, case-lambda and annotated-case-lambda
forms are recordised as clambda structs:
(clambda (?prel-formals ?body) ...)
in which ?prel-formals is a a proper or improper list of
prel structs representing arguments to each clambda
clause.
References to local bindings are represented by standalone
prelex structs; assignments to local bindings are represented
by assign structs:
(assign ?prel ?rhs)
We distinguish between bindings that are only referenced (unassigned,
read–only) and bindings that are also assigned (assigned, read–write).
Example of code in which the binding X is only referenced:
(let ((X 123)) (display X))
example of code in which the binding X is assigned and
referenced:
(let ((X 123)) (set! X 456) (display X))
The implementation technique for continuations used by Vicare mandates that: once an immediate Scheme object or reference to non–immediate Scheme object is stored in a machine word on the Scheme stack, such machine word must not be mutated; this allows the run–time system to copy stack frames at will. As a consequence the following implementation techniques for local bindings are adopted:
References to unassigned bindings must be implemented as Scheme stack operations; references and assignments to assigned bindings must be substituted with appropriate vector operations.
After the letrec optimiser has processed recbind and
rec*bind forms, unassigned local bindings always end up
defined as:
(bind ((?prel ?rhs)) ?body)
and references are represented by standalone prelex structs.
Whenever a ?rhs expression is recognised to be of type
clambda, the corresponding bind is transformed into:
(fix ((?prel ?rhs)) ?body)
No further transformations are needed for recordised code.
After the letrec optimiser has processed recbind and
rec*bind forms, assigned local bindings always end up defined
as:
(bind ((?prel ?rhs)) ?body)
references are represented by standalone prelex structs and
assignments are represented by assign structs.
NOTE No matter if a ?rhs expression is of type
clambda: its associatedbindstruct is not transformed into afixstruct; only unassignedclambdabindings can be defined by afixstruct.
Definitions, references and assignments are transformed by introducing the appropriate vector operations; such operation are equivalent to the following:
(let ((x 123)) (set! x 456) x)
is transformed into:
(let ((t 123))
(let ((x (vector t)))
($vector-set! x 0 456)
($vector-ref x 0)))
where the temporary binding t is generated by the compiler; the
code always only access the first and single slot of the vector. Using
recordised code:
(bind ((x_0 123))
(seq
(assign x_0 456)
x))
is transformed into:
(bind ((x_0 123))
(bind ((x_1 (funcall (primref vector) x_0)))
(seq
(funcall (primref $vector-set!)
x_1
(constant 0)
(constant 456))
(funcall (primref $vector-ref)
x_1
(constant 0)))))
Formal bindings in clambda structs are also processed this
way; for example:
(lambda (a) (display a) (set! a 1) a)
is transformed into:
(lambda (a_0)
(bind ((a_1 (funcall (primref vector) a_0)))
(seq
(funcall (primref display)
(funcall (primref $vector-ref)
a_1 (constant 0)))
(funcall (primref $vector-set!)
a_1 (constant 0) (constant 1))
(funcall (primref $vector-ref)
a_1 (constant 0)))))
NOTE Assigned local bindings whose RHS expression is a
clambdastruct are also transformed by introducing a vector. After this transformation: there are no morebindstruct whose RHS is aclambdastruct.
Next: compiler topics clambda, Previous: compiler topics topbind, Up: compiler topics [Index]