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)
fn is known at the compile time, and
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
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
to invoke the mutator.