[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