[gambit-list] Gambit's Linking Model

Marc Feeley feeley at iro.umontreal.ca
Thu Jun 14 07:34:25 EDT 2007

Hash: SHA1

On 13-Jun-07, at 11:02 PM, Guillaume Cartier wrote:

>> Yes... if no declarations are used.  If your code uses the  
>> declaration
>> (declare (block)) then inter-procedural optimizations are possible
>> (such as inlining) within that file.  Of course if you want to be  
>> able
>> to redefine functions (as seems to be the case for Jazz) then you
>> cannot use this declaration.
> In fact it's the opposite. A method in Jazz cannot be redefined, so I
> was planning on generating for methods something like
> (begin
>   (declare (block))
>   (define (method-x) ...))

Let me explain what happens if you do this, so that you don't  
misunderstand the consequences.

In Gambit each global variable is implemented with two cells: the  
"value" cell and the "primitive" cell.  The value cell is what is  
accessed when you mutate a global variable and reference it with no  
special declarations in effect.  Both the value cell and the  
primitive cell are initialized when a toplevel procedure definition  
is evaluated *and* the (block) declaration is in effect.  All of the  
predefined procedures (cons, append, make-thread, ...) are defined in  
the runtime library while the (block) declaration is in effect.  The  
Gambit compiler uses this fact when compiling Scheme code while the  
(standard-bindings) and/or (extended-bindings) declarations are in  
effect.  In this case a call to a predefined procedure will reference  
the primitive cell.  This is faster because the compiler knows this  
is a procedure, and can avoid the procedure? check.  Note that a call  
like (append X Y) in the context of a (standard-bindings) declaration  
will call the append procedure in the runtime library even if the  
user has mutated the append variable with a set! .

The (block) declaration also allows the compiler to perform  
optimizations within the file because the value attached to a  
variable is known not to change (unless there is a set! within the  
file).  This allows copy propagation, function inlining, etc at the  
file level.  So a file containing this code:

(declare (block))
(define n 10)
(define (f x) (* x x))
(define (g) (f (+ n 1)))

will be compiled as though it was:

(define n 10)
(define (f x) (* x x))
(define (g) (let ((x (+ 10 1))) (* x x)))

So you have to be aware that the (block) declaration causes linking  
optimizations at the file level.  So it is OK to use the (block)  
declaration if the granularity of "redefinition" is the file, but it  
is not OK if you want to have the ability to redefine individual  
procedures.  After loading the above (compiled) file, a (define (f x)  
(* x 2)) at the REPL or in another file that you load will not affect  
the behavior of a call to g.  The only way to "update" g is to  
redefine it.

Note also that if another file does a (define h g), and then you  
reload the above file with new definitions for f and g, you will  
access the original definition of g when you call h.  In other words,  
reloading a file compiled with the (block) declaration changes the  
content of the value and primitive cells, it does not "overwrite",  
purge or invalidate the previous machine code.


Version: GnuPG v1.4.3 (Darwin)


More information about the Gambit-list mailing list