Next: , Previous: Macro-Expansion and Analysis, Up: Principles of Compilation

5.2 Building Closures

Here Hobbit produces code for creating real closures for all the lambda-terms which are not marked as being liftable by the previous liftability analysis.

Global variables (eg x-glob) are translated as pointers (locations) to SCM objects and used via a fetch: *x_glob (or a fetch macro GLOBAL(x-glob) which translates to *x_glob).

While producing closures hobbit tries to minimize the indirection levels necessary. Generally a local variable x may have to be translated to an element of a vector of local variables built in the procedure creating x. If x occurs in a non-liftable closure, the whole vector of local variables is passed to a closure.

Such a translation using a local vector will only take place if either x is set! inside a non-liftable lambda-term or x is a name of a recursively defined non-liftable function, and the definition of x is irregular. The definition of x is irregular if x is given the non-liftable recursive value t by extra computation, eg as

```     (set! x (let ((u 1)) (lambda (y) (display u) (x (+ u 1)))))
```

and not as a simple lambdaterm:

```     (set! x (lambda (y) (display x) (x (+ y 1))))
```

In all the other cases a local scheme variable x is translated directly to a local C variable x having the type SCM (a 32-bit integer). If such an x occurs in a non-liftable closure, then only its value is passed to a closure via the closure-vector. In case the directly-translated variable x is passed to a liftable lambda-term where it is set!, then x is passed indirectly by using its address &x. In the lifted lambda-term it is then accessed via *.

If all the variables x1, ..., xn created in a procedure can be translated directly as C variables, then the procedure does not create a special vector for (a subset of) local variables.

An application (foo ...) is generally translated to C by an internal apply of the SCM interpreter: apply(GLOBAL(foo), ...). Using an internal apply is much slower than using direct a C function call, since:

• there is an extra fetch by GLOBAL(foo),
• internal apply performs some computations,
• the arguments of foo are passed as a list constructed during application: that is, there is a lot of expensive consing every time foo is applied via an internal apply.

However, in case foo is either a name of a non-redefined primitive or a name of a non-redefined liftable procedure, the application is translated to C directly without the extra layer of calling apply: foo(...).

Sometimes lambda-lifting generates the case that some variable x is accessed not directly, but by *x. See the next section.

Undefined procedures are assumed to be defined via interpreter and are called using an internal apply.