Date: Mon, 17 Aug 2009 17:19:30 -0400 From: Marc Feeley feeley@iro.umontreal.ca
I'm curious to know how this works. If you have a module which implements several procedures, say the srfi-1 list library, and you use only one of the self-contained procedures, say "xcons", will the Scheme48 compiler or linker eliminate the rest of the code of srfi-1? If so, how does it do that? What is the bytecode representation of the compiled library?
For each global variable, an object called a location is allocated. Locations have a little bit of structure, but primarily they have a field for the value of the global variable. The representation of procedures has two parts: an environment and a pointer to a /template/. The environment holds the values, or cells for the values, of local variables. The template is the static part of the procedure: it holds the byte code, the literals, the locations for global variables, and the templates for subprocedures, corresponding to the lambdas that require heap allocation. Templates are thus arranged into a tree like the tree of contours in the program, and parents can be garbage- collected if they are no longer used.
Compiling a package is similar to compiling a big lambda containing the forms in the package's source code, with the side effect of filling a table mapping names to the meanings defined in the source code. The procedure (and its template) thereby obtained can be garbage-collected once it has been used, since nothing else will have references to it.
If you write a program that uses XCONS, but nothing else defined in the SRFI 1 package, then your program will remember the location for with the global variable XCONS, which will remember the procedure that was stored in the location when SRFI 1 was loaded, which will remember all its own literals and locations and subtemplates, but nothing else of the SRFI 1 package.
Also in Scheme48, if you are using the command processor, you can type `,go <expression>', and most of the execution of Scheme48 will be tail-recursively replaced by that of <expression> -- so that the command processor can be garbage-collected. (There is a little bit of the run-time system left on the stack, but that's all.)
Naturally, Gambit can't do quite this, if you want a single C object or library per unit of source code (i.e., per file, in Gambit's case), because you can't usefully garbage-collect a portion of an object you have dlopened or statically linked; dlclose works at the granularity of entire objects, and what is statically linked can't be eliminated. But that's unrelated to whether one can just do (##GLOBAL-VAR-REF (##MAKE-GLOBAL-VAR ...)) in a `Lisp-like language'. (In fact, it's a little more closely related to whether one can do *((foo_t *) dlsym (RTLD_DEFAULT, "foo")) in C...)