[gambit-list] inlining eqv? etc.

Bradley Lucier lucier at math.purdue.edu
Thu Oct 12 13:08:25 EDT 2006


Marc is likely in a better position to comment, and I hope I don't  
misunderstand your questions, but I'll give it a go.

On Oct 12, 2006, at 9:48 AM, Christian wrote:

> I'm wondering: in which step of the compilation phase are inlinings
> of the kind that you are suggesting done? Is it in the code
> generation step? So will it inline the code *always*, even multiple
> times in the same compilation block, and not be subject to the
> inlining-limit declaration?

If (declare (runtime-bindings)) (which is the default), then each  
occurrence of the primitive will be inlined.

If, in addition, (declare (standard-bindings)) is in effect, then  
there will be no check that the current value of the global variable  
is indeed the primitive.

Finally, if (declare (not safe)) is in effect, then there will be no  
check that the arguments are suitable for the primitive.

And the behavior of some primitives changes if some numerical  
declarations are in effect; in particular, eqv? is changed to eq? if  
(declare (fixnum))

> If so, wouldn't it be better to change
> that to actually create a function, but in the same block, and let
> the function inliner decide how to handle that?

I don't know that there's an absolute answer to that question.

> Handling "optional inlining" is something I plan to do in chjmodule
> (I'm continuing to work on it currently), so that one can export
> functions from some module A as "inlinable", which means that they
> will be copied verbatim (but within the original namespace, by
> putting a ##namespace declaration around/into it) into caller
> modules, so e.g. module B importing module A will contain such a copy
> in it's toplevel. That doesn't necessarily mean that it is being
> inlined into every call site inside B: it's open to the normal Gambit
> inliner. At the very least the costs drop from ~200 to 30-40 cycles.

This seems similar to (declare (not core)), which makes a function  
available for inlining, but does not include a separate copy of the  
function in the file.  It's used a lot in the files in the gsc  
directory, in the macro include-adt, which is defined in fixnum.scm.

If you want some information of what beta-20 inlines by default, take  
the test-code.scm I sent in my previous e-mail and run it through

euler-35% gsc -c -expansion test-code.scm > ! crap.scm

This expands macros and shows some of the default inlining, so that

   (define (test-map-2 x0 x1) (map x0 x1))

gets expanded to

(define test-map-2
   (lambda (x0 x1)
     (if (and ('#<procedure #2 ##eq?> map '#<procedure #185 map>)
              ('#<procedure #184 ##procedure?> x0))
         (letrec ((loop2.248
                   (lambda (x0 lst2.249)
                     (if ('#<procedure #7 ##pair?> lst2.249)
                         (let ((x.250 (x0 ('#<procedure #11 ##car>  
lst2.249))))
                           ('#<procedure #9 ##cons>
                            x.250
                            (loop2.248 x0 ('#<procedure #13 ##cdr>  
lst2.249))))
                         '()))))
           (loop2.248 x0 x1))
         (map x0 x1))))

and

   (define (test-s8vector-set!-3 x0 x1 x2) (s8vector-set! x0 x1 x2))

gets expanded to

(define test-s8vector-set!-3
   (lambda (x0 x1 x2)
     (if (and ('#<procedure #2 ##eq?>
               s8vector-set!
               '#<procedure #287 s8vector-set!>)
              (and (and ('#<procedure #282 ##s8vector?> x0)
                        ('#<procedure #171 ##subtyped-mutable?> x0))
                   (and (and ('#<procedure #60 ##fixnum?> x1)
                             (and ('#<procedure #71 ##fx<=> 0 x1)
                                  ('#<procedure #65 ##fx<>
                                   x1
                                   ('#<procedure #284 ##s8vector- 
length> x0))))
                        (and ('#<procedure #60 ##fixnum?> x2)
                             (and ('#<procedure #71 ##fx<=> -128 x2)
                                  ('#<procedure #71 ##fx<=> x2 127))))))
         ('#<procedure #288 ##s8vector-set!> x0 x1 x2)
         (s8vector-set! x0 x1 x2))))

with (declare (standard-bindings) (extended-bindings)) (I needed to  
comment out test-##return-dynamic-env-bind?-1 and test-##procedure- 
info-1), these are expanded to

(define test-map-2
   (lambda (x0 x1)
     (if ('#<procedure #150 ##procedure?> x0)
         (letrec ((loop2.228
                   (lambda (x0 lst2.229)
                     (if ('#<procedure #7 ##pair?> lst2.229)
                         (let ((x.230 (x0 ('#<procedure #9 ##car>  
lst2.229))))
                           ('#<procedure #8 ##cons>
                            x.230
                            (loop2.228 x0 ('#<procedure #10 ##cdr>  
lst2.229))))
                         '()))))
           (loop2.228 x0 x1))
         (map x0 x1))))

and

(define test-s8vector-set!-3
   (lambda (x0 x1 x2)
     (if (and (and ('#<procedure #225 ##s8vector?> x0)
                   ('#<procedure #130 ##subtyped-mutable?> x0))
              (and (and ('#<procedure #35 ##fixnum?> x1)
                        (and ('#<procedure #43 ##fx<=> 0 x1)
                             ('#<procedure #39 ##fx<>
                              x1
                              ('#<procedure #228 ##s8vector-length>  
x0))))
                   (and ('#<procedure #35 ##fixnum?> x2)
                        (and ('#<procedure #43 ##fx<=> -128 x2)
                             ('#<procedure #43 ##fx<=> x2 127)))))
         ('#<procedure #230 ##s8vector-set!> x0 x1 x2)
         (s8vector-set! x0 x1 x2))))

and with (declare (standard-bindings)(extended-bindings)(not safe))

(define test-map-2
   (lambda (x0 x1)
     (letrec ((loop2.94
               (lambda (x0 lst2.95)
                 (if ('#<procedure #7 ##pair?> lst2.95)
                     (let ((x.96 (x0 ('#<procedure #9 ##car> lst2.95))))
                       ('#<procedure #8 ##cons>
                        x.96
                        (loop2.94 x0 ('#<procedure #10 ##cdr>  
lst2.95))))
                     '()))))
       (loop2.94 x0 x1))))

and

(define test-s8vector-set!-3
   (lambda (x0 x1 x2) ('#<procedure #260 ##s8vector-set!> x0 x1 x2)))

I think my suggested expansion of eqv? is at least comparable to  
these expansions.

> Using such module techniques, instead of hard coding functions like
> |equal?| into Gambit, they could be defined in a standard module like
> "R5RS" with an inlinable export flag and be imported from there.
>
> (BTW one could also split |equal?| like that:
>
> (define (equal? a b)
>     (or (eq? a b)
>         (real-equal? a b)))
> (define (real-equal? a b)
>     ..recursive definition))
>
> and so make it possible that the first part, which doesn't generate
> much code bloat, can be inlined whereas the rest still isn't.

This definition of equal? doesn't help much if most often two things  
are not equal?, because there is not a quick test that (equal? a b)  
is false.  Better might be

(define (equal? a b)
   (or (eq? a b)
       (and (subtyped? a)
            (subtyped? b)
            (real-equal? a b))))


Brad



More information about the Gambit-list mailing list