shirok / Gauche

Scheme Scripting Engine
https://practical-scheme.net/gauche
Other
813 stars 81 forks source link

Inlining with `getter-with-setter` #1076

Open shirok opened 1 week ago

shirok commented 1 week ago

If we use getter-with-setter as follows:

(define-inline (foo x) (car x))
(define-inline (foo-set! x v) (set-car! x v))
(define-inline Foo (getter-with-setter foo foo-set!))

Foo is fully inlined:

gosh> (disasm (lambda (x) (Foo x)))
CLOSURE #<closure (#f x)>
=== main_code (name=#f, cc=0x77d9bede3780, codevec=0x77d9bef2e9f0, size=2, const=0 stack=0):
signatureInfo: ((#f x))
     0 LREF0-CAR                ; (car x)
     1 RET 

But if we do this:

(define-inline Foo (getter-with-setter
                    (lambda (x) (car x))
                    (lambda (x v) (set-car! x v))))

It doesn't:

gosh> (disasm (lambda (x) (Foo x)))
CLOSURE #<closure (#f x)>
=== main_code (name=#f, cc=0x77d9bede30c0, codevec=0x77d9be7bb2a0, size=4, const=1 stack=4):
signatureInfo: ((#f x))
     0 LREF0-PUSH               ; x
     1 GREF-TAIL-CALL(1) #<identifier user#Foo.be622100>; (Foo x)
     3 RET 

It's because defining inlinable procedure involves two distinct operations; (1) inset a global binding with 'inlinable' flag, and (2) attach packed IForm to the procedure object, so that the later reference can replace procedure call to its body. With the second example, the compiler failed to do (2).

shirok commented 5 days ago

Inlining globally bound one is addressed in https://github.com/shirok/Gauche/commit/ebcbd534b6ae9e28b4ae2cb2fc052eddfd32a6d1 .

However, locally bound one isn't optimized yet.

(define (foo x)
   (define bar (getter-with-setter (^y (car y)) (^[y v] (set-car y v))))
  ...)

This is uncommon case, but a macro may produce this kind of local definition and it is better to be optimized.

shirok commented 5 days ago

Another, completely different approach that can solve both global and local binding cases.