Scheme Compiler の勉強(25) - リファクタリング

小休止して、汚く冗長なコンパイラを少し整理。今までは何でも文字列で処理していたので、見た目も悪いしメンテしにくかった。instruction を定義して(まだ乱暴だけど)、以前だと (emit "~a = load i32 %ret" %tmp1) などと書いていた処理を、(assign %tmp1 (inst-load 'i32 "%ret")) と書けるようになった。まだぜんぜんエレガントでないのは仕方ないけど、とりあえず。SSA ってひたすら assign ばっかりしているのだなぁ、と今更気付く。以下のような感じになっている。

(define-primitive ($fxadd1 env expr)
  (with-uvar (%tmp %tmp2)
    (emit-expr env expr)
    (assign %tmp (inst-load 'i32* "%ret"))
    (assign %tmp2 (inst-add 'i32 (immediate-rep 1) %tmp))
    (inst-store 'i32 %tmp2 'i32* "%ret")))
...
(define-primitive (fixnum? env expr)
  (with-uvar (%tmp %tmp2 %tmp3 %tmp4)
    (emit-expr env expr)
    (assign %tmp (inst-load 'i32* "%ret"))
    (assign %tmp2 (inst-and 'i32 %tmp fxmask))
    (assign %tmp3 (inst-icmp 'eq 'i32 %tmp2 0))
    (assign %tmp4 (inst-select %tmp3 'i32 bool_t 'i32 bool_f))
    (inst-store 'i32 %tmp4 'i32* "%ret")))
...
(define (emit-lambda env)
  (lambda (expr label args)
    (define-function label args
      (let ((fmls (lambda-formals expr))
	    (body (lambda-body expr)))
	(let f ((fmls fmls) (env env) (idx 1))
	  (cond
	   ((null? fmls) (emit-expr env body))
	   (else
	    (with-svar (%arg_addr)
	      (assign %arg_addr (inst-alloca 'i32))
	      (inst-store 'i32 (format #f "%arg~a" idx) 'i32* %arg_addr)
	      (bind-value env (car fmls) %arg_addr)
	      (f (cdr fmls)
		 (extend-env env) (+ idx 1))))))))))

マクロを上手く作るにはまだまだ修行が必要。