Gauche Devlog

< Procedure inliner improvement | Static linking and standalone executables >

2018/03/05

Generalized setter inlining

(This is a continuation of Procedure inliner improvement.)

Another improvement of procedure inlining in 0.9.6 is about generalized setters.

Generalized setter srfi:17 allows set! to behave as if it takes lvalue of the first argument.

gosh> (define p (list (vector 1)))
p
gosh> p
(#(1))
gosh> (set! (vector-ref (car p) 0) 2)
#<undef>
gosh> p
(#(2))

It isn't really calculating lvalue of (vector-ref (car p) 0). The trick is a very simple conversion:

(set! (fn arg ...) value)
  ↓
((setter fn) arg ... value)

If fn is known at the compile time, and (setter fn) is a constant binding, the compiler can inline the value of (setter fn). In the following example, you see the compiler knows (setter vector-ref) is vector-set!, and emit the VM instruction instead of making a procedure call:

gosh> (disasm (^[] (set! (vector-ref (car p) 0) 2)))
CLOSURE #<closure (#f)>
=== main_code (name=#f, code=0x2939600, size=6, const=1 stack=1):
signatureInfo: ((#f))
     0 GREF #<identifier user#p.2629740>; p
     2 CAR-PUSH                 ; (car p)
     3 CONSTI(2) 
     4 VEC-SETI(0)              ; ((setter vector-ref) (car p) 0 2)
     5 RET 

This is exactly the same code as (vector-set! (car p) 0 2).

Actually, srfi-17 is written with this optimization in mind. The ref:getter-with-setter procedure is specified to bind an accessor and a mutator in immutable way, so that the compiler can safely replace (setter fn) form with fn's mutator. It's just that we haven't taken advantage of it until now.

Since we have this optimization now, it is less important to have separate mutator procedure than accessor---you can attach the mutator as the setter of the accessor, and without penalty you can use set! to invoke the mutator.

Tags: 0.9.6, Compiler, srfi-17

Post a comment

Name: