Next: compiler lifting substitutions, Previous: compiler lifting optimisation, Up: compiler lifting [Index]
Let’s consider some examples of code creating a function and reason about the function being a combinator or a non–combinator; in these examples function integration has been disabled.
This core language expression:
(lambda () ((primitive read)))
is transformed into:
(codes ((lambda (label: asmlabel:anonymous:clambda) () (funcall (primref read)))) (closure-maker (code-loc asmlabel:anonymous:clambda) no-freevars))
the returned function looks like it is closed upon the binding
read
, but in truth read
is a primitive function whose
value is known at compile–time; so the returned function is a
combinator.
This core language expression:
(let ((f (lambda () '1)) (g (lambda () '2))) ((primitive list) (f) (g)))
defines two combinator functions; the closure-maker
struct is
introduced at the functions’ call site:
(codes ((lambda (label: asmlabel:g:clambda) () (constant 2)) (lambda (label: asmlabel:f:clambda) () (constant 1))) (funcall (primref list) (jmpcall asmlabel:f:clambda:case-0 (closure-maker (code-loc asmlabel:f:clambda) no-freevars)) (jmpcall asmlabel:g:clambda:case-0 (closure-maker (code-loc asmlabel:g:clambda) no-freevars))))
This core language form:
(let ((f (lambda () '1))) ((primitive list) (f) (f)))
defines a combinator function; multiple closure-maker
structs
are introduced at the call site for the same function:
(codes ((lambda (label: asmlabel:f:clambda) () (constant 1))) (funcall (primref list) (jmpcall asmlabel:f:clambda:case-0 (closure-maker (code-loc asmlabel:f:clambda) no-freevars)) (jmpcall asmlabel:f:clambda:case-0 (closure-maker (code-loc asmlabel:f:clambda) no-freevars))))
In the core language expression:
(let ((a ((primitive read)))) (lambda () a))
the returned function is a non–combinator because its return value
depends upon the value of the binding a
at the time the closure
object is created; a
is a free variable in the body of the
function and the function is closed upon it. Indeed such form is
transformed into:
(codes ((lambda (label: asmlabel:anonymous:clambda) () a_0)) (bind ((a_0 (funcall (primref read)))) (fix ((tmp_0 (closure-maker (code-loc asmlabel:anonymous:clambda) (freevars: a_0)))) tmp_0)))
In this example a single non–combinator is called twice:
(let ((a ((primitive read)))) (let ((f (lambda () a))) ((primitive list) (f) (f))))
the result of the transformation is:
(codes ((lambda (label: asmlabel:f:clambda) () a_0)) (bind ((a_0 (funcall (primref read)))) (fix ((f_0 (closure-maker (code-loc asmlabel:f:clambda) (freevars: a_0)))) (funcall (primref list) (jmpcall asmlabel:f:clambda:case-0 f_0) (jmpcall asmlabel:f:clambda:case-0 f_0)))))
we see that a single closure-maker
is introduced.
A self reference in a recursive function does not necessarily make it a non–combinator. For example:
(letrec ((f (lambda () (f)))) ?body)
is transformed into:
(codes ((lambda (label: asmlabel:f:clambda) () (jmpcall asmlabel:f:clambda:case-0 (closure-maker (code-loc asmlabel:f:clambda) no-freevars)))) (closure-maker (code-loc asmlabel:f:clambda) no-freevars))
there are no free variables other than the recursive reference, so the function is a combinator. Conversely, in the expression:
(let ((a ((primitive read)))) (letrec ((f (lambda (x) (f a)))) (f '1)))
in addition to the self reference there is a non–removable free variable, so the result of the transformation is:
(codes ((lambda (label: asmlabel:f:clambda) (x_0) (jmpcall asmlabel:f:clambda:case-1 f_0 a_0))) (bind ((a_0 (funcall (primref read)))) (fix ((f_0 (closure-maker (code-loc asmlabel:f:clambda) (freevars: a_0)))) (jmpcall asmlabel:f:clambda:case-1 f_0 (constant 1)))))
In this expression functions defined at the same lexical contour call each other in a cycle:
(letrec* ((a (lambda () (d))) (b (lambda () (a))) (c (lambda () (b))) (d (lambda () (c)))) ((primitive list) (a) (b) (c) (d)))
there are no true free variables, so all the functions are implemented as combinators:
(codes ((lambda (label: asmlabel:a:clambda) () (jmpcall asmlabel:d:clambda:case-0 (closure-maker (code-loc asmlabel:d:clambda) no-freevars))) (lambda (label: asmlabel:d:clambda) () (jmpcall asmlabel:c:clambda:case-0 (closure-maker (code-loc asmlabel:c:clambda) no-freevars))) (lambda (label: asmlabel:c:clambda) () (jmpcall asmlabel:b:clambda:case-0 (closure-maker (code-loc asmlabel:b:clambda) no-freevars))) (lambda (label: asmlabel:b:clambda) () (jmpcall asmlabel:a:clambda:case-0 (closure-maker (code-loc asmlabel:a:clambda) no-freevars)))) (funcall (primref list) (jmpcall asmlabel:a:clambda:case-0 (closure-maker (code-loc asmlabel:a:clambda) no-freevars)) (jmpcall asmlabel:b:clambda:case-0 (closure-maker (code-loc asmlabel:b:clambda) no-freevars)) (jmpcall asmlabel:c:clambda:case-0 (closure-maker (code-loc asmlabel:c:clambda) no-freevars)) (jmpcall asmlabel:d:clambda:case-0 (closure-maker (code-loc asmlabel:d:clambda) no-freevars))))
In this expression functions defined at the same lexical contour call each other in a cycle; one of them has a true free variable, causing all of them to be non–combinators:
(let ((v ((primitive read)))) (letrec* ((a (lambda (x) (d x))) (b (lambda (x) (a x))) (c (lambda (x) (b x))) (d (lambda (x) (c v)))) ((primitive list) (a '1) (b '2) (c '3) (d '4))))
the result of the transformation is:
(codes ((lambda (label: asmlabel:a:clambda) (x_0) (jmpcall asmlabel:d:clambda:case-1 d_0 x_0)) (lambda (label: asmlabel:d:clambda) (x_1) (jmpcall asmlabel:c:clambda:case-1 c_0 v_0)) (lambda (label: asmlabel:c:clambda) (x_2) (jmpcall asmlabel:b:clambda:case-1 b_0 x_2)) (lambda (label: asmlabel:b:clambda) (x_3) (jmpcall asmlabel:a:clambda:case-1 a_0 x_3))) (bind ((v_0 (funcall (primref read)))) (fix ((a_0 (closure-maker (code-loc asmlabel:a:clambda) (freevars: d_0))) (d_0 (closure-maker (code-loc asmlabel:d:clambda) (freevars: c_0 v_0))) (c_0 (closure-maker (code-loc asmlabel:c:clambda) (freevars: b_0))) (b_0 (closure-maker (code-loc asmlabel:b:clambda) (freevars: a_0)))) (funcall (primref list) (jmpcall asmlabel:a:clambda:case-1 a_0 (constant 1)) (jmpcall asmlabel:b:clambda:case-1 b_0 (constant 2)) (jmpcall asmlabel:c:clambda:case-1 c_0 (constant 3)) (jmpcall asmlabel:d:clambda:case-1 d_0 (constant 4))))))
Next: compiler lifting substitutions, Previous: compiler lifting optimisation, Up: compiler lifting [Index]