Next: compiler topics locbind, Up: compiler topics [Index]
Bindings defined by the core language form library-letrec*
are
named top level bindings; they are akin to the C language “global
variables”. The form library-letrec*
has the format:
(library-letrec* ((?lex ?loc ?init) ...) ?body)
in which: ?lex is a lexical gensym that uniquely identifies the
binding in the core language form; ?loc is a location gensym which
will be used at run–time to hold the current value of the binding in
its value
slot; ?init is the initialisation expression.
References to top level bindings are represented by standalone
?lex gensyms; assignments to top level 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 library-letrec*
form is recordised into a rec*bind
form:
(rec*bind ((?prel ?init) ...) ?body)
in which ?prel is a prelex
struct holding both the
?lex and ?loc gensyms. References to top level bindings are
represented by standalone prelex
structs; assignments to top
level bindings are represented by assign
structs:
(assign ?prel ?rhs)
Since the actual value of a top level binding is stored in the
value
field of a loc gensym:
(funcall (primref $symbol-value) (constant ?loc))
which extracts the value from slot value
of ?loc.
(funcall (primref $set-symbol-value!) (constant ?loc) ?rhs)
which stores a new value in the slot value
of ?loc.
(funcall (primref $init-symbol-value!) (constant ?loc) ?rhs)
which stores a new value in the slot value
of ?loc and,
only if the value is recognised at run–time as being closure object,
also stores value in the slot proc
.
rec*bind
formsA compiler pass takes care of performing “letrec
optimisation”:
structs of type rec*bind
are transformed into a nested
hierarchy of bind
, fix
and assign
forms.
Different cases must be handled in different ways.
(bind ((?prel ?init)) ?body)
the transformation of references is straightforward. As example, let’s consider:
(library-letrec* ((a.lex a.loc '1) (b.lex b.loc '2)) ((primitive display) a.lex b.lex))
which is recordised and transformed into:
(bind ((a.lex_0 (constant 1))) (bind ((b.lex_0 (constant 2))) (funcall (primref display) (constant 1) (constant 2))))
in which the references to bindings are integrated by the source optimiser.
(bind ((?prel ?init)) ?body)
the transformation of references and assignments is straightforward. As example, let’s consider:
(library-letrec* ((a.lex a.loc '1) (b.lex b.loc '2)) (begin (set! a.lex '11) (set! b.lex '22) ((primitive display) a.lex b.lex)))
which is recordised and transformed into:
(bind ((a.lex_0 (constant 1))) (bind ((b.lex_0 (constant 2))) (seq (funcall (primref $set-symbol-value!) (constant a.loc) (constant 11)) (funcall (primref $set-symbol-value!) (constant b.loc) (constant 22)) (funcall (primref display) (funcall (primref $symbol-value) (constant a.loc)) (funcall (primref $symbol-value) (constant b.loc))))))
(bind ((?prel (constant #<void>))) (assign ?prel ?init) ?body)
this might happen when the initialisation expressions in the original
rec*bind
need to access the machine words in which the
bindings’ values are stored; so, at run–time, first we need to allocate
the loc gensyms and then we can evaluate the initialisation expressions
and store the resulting value in the gensym itself.
In this special case, since the binding is unassigned in the original
code, the assign
struct is introduced by the compiler and it
is the only assignment for the binding. The compiler recognises this
case and transforms:
(assign ?prel ?init)
into:
(funcall (primref $init-symbol-value!) (constant ?loc) ?init)
in which ?loc is the loc gensym of the binding.
clambda
form: they end up being defined by
a fix
struct; the letrec
optimiser takes care of
recognising and handling such case. As example:
(library-letrec* ((a.lex a.loc (lambda () '1)) (b.lex a.loc (lambda () '2))) ((primitive display) (a.lex)))
is transformed into:
(fix ((a.lex_0 (lambda () (constant 1))) (b.lex_0 (lambda () (constant 2)))) (funcall (primref display) (constant 1)))
in which the call to a.lex
is integrated by the source optimiser.
Next: compiler topics locbind, Up: compiler topics [Index]