[gambit-list] equiv of __FILE__ and __LINE__

Marc Feeley feeley at iro.umontreal.ca
Thu May 7 15:23:03 EDT 2009


On 7-May-09, at 2:40 PM, lowly coder wrote:

> Your response to 2 was really enlightening.
>
> More on 3:
>
> ~:$ cat test.scm
> (define source? 10)
> (define ##source? 10)
> (pp source?)
> (pp ##source?)
> ~:$ gsi test.scm
> 10
> 10
>
>
> This surprised me a bit ... initially, intuitively, I had a notion  
> that ##blah was a blah that had it's value hard coded internally in  
> gambit (so that even if the user did weird things to his/her  
> environment, I can still get the 'system functions').
>
> In gambit, are ##-vars treated just like regular ##-vars ?

Yes, ##car and car are two global variables.

But that is not the whole story because the compiler can be told,  
using a declaration, that it can assume that global variables are  
bound to their predefined value.  So in the code

(let ()
   (declare (standard-bindings))
   (car (f x)))

the programmer is telling the compiler that it can assume that car is  
bound to the predefined car procedure.  This allows the compiler to  
inline the car procedure.  The code (once transformed by the compiler)  
becomes:

(let ((temp.0 (f x)))
   (if ('#<procedure #2 ##pair?> temp.0)
       ('#<procedure #3 ##car> temp.0)
       (car temp.0)))

So the logic of the car procedure, including the type test, is inlined  
at the location of the original call to car.  Note that if the type  
test fails, the car procedure in the runtime library is called, and it  
will raise the appropriate exception (moreover the continuation of  
that exception will be the same continuation that was passed to the  
original call to car).

If the programmer wants the compiler to assume that the type check  
will succeed (i.e. the result of (f x) is always a pair) then the (not  
safe) declaration can be added:

(let ()
   (declare (standard-bindings) (not safe))
   (car (f x)))

After the compiler's transformations the code becomes:

('#<procedure #2 ##car> (f x))

Here only a call to the ##car primitive remains.  This primitive will  
be inlined by the back-end of the compiler, which will generate a call  
to the ___CAR(obj) C macro defined in include/gambit.h .  The ___CAR  
macro is defined like this:

#define ___CAR(obj)___PAIR_CAR(obj)
#define ___PAIR_CAR(obj)(*(___BODY_AS(obj,___tPAIR)+___PAIR_CAR_OFS))
#define ___BODY_AS(obj,tag)(___UNTAG_AS(obj,tag)+___BODY_OFS)
#define ___UNTAG_AS(obj,tag)___CAST(___WORD*,(obj)-(tag))
#define ___CAST(type,val)((type)(val))
#define ___tPAIR 3
#define ___PAIR_CAR_OFS 1
#define ___BODY_OFS 1
#define ___WORD int /* usually */

so in the end, the call to car has become the C code:

    *((int*)(obj-3)+2)

which on many processor architectures, is a single machine instruction  
(because the C compiler combines the substraction and addition of the  
constants into a single offset).

Marc




More information about the Gambit-list mailing list