マクロ展開の結果とコンパイルの結果を見る方法

マクロ展開

マクロ展開の様子を見るにはdebug-expandという手続きを使います。この手続きは0.9.6-update2(2008/09/03)より添付されているライブラリ(debug)からexportされています。debug-expandはコンパイラへの入力を直前で取り出し、これを見やすい形式に変換して出力しています。これによりマクロ展開が思ったように行われているのかどうかや、展開後のボリュームを確認することができます。ちなみに変換前の出力はmacro-expandで見ることができます。

Ypsilon 0.9.6-update2 Copyright (c) 2008 Y.Fujita, LittleWing Company Limited.
> (import (debug))
> (debug-expand '(let ((x (foo n)) (y (bar n))) (- x y (/ x y))))
(let ((x.1 (foo n)) (y.2 (bar n))) (- x.1 y.2 (/ x.1 y.2)))

この時点で使用される特殊フォームはbegin, quote, define, set!, lambda, let, letrec*, if, or, andのみとなります。またSchemeレベルでの最適化も終了しています。

最適化のため結果の確認が困難な場合にはパラメータのcoreform-optimizeに#fにセットすることでそれを抑制することができます。例としてcircular-list?(from SRFI-1 list-processing library reference implementation copyright (c) 1998, 1999 by Olin Shivers)の展開と最適化の様子を確認してみます。

最適化あり(まだまだ甘いですけどね:p)

Ypsilon 0.9.6-update2 Copyright (c) 2008 Y.Fujita, LittleWing Company Limited.
> (import (debug))
> (debug-expand
   '(define (circular-list? x)
      (let lp ((x x) (lag x))
        (and (pair? x)
             (let ((x (cdr x)))
               (and (pair? x)
                    (let ((x (cdr x))
                          (lag (cdr lag)))
                      (or (eq? x lag) (lp x lag)))))))))
(begin
  (define lp.1
    (lambda (x.2 lag.3)
      (and (pair? x.2)
           (pair? (cdr x.2))
           (let ((x.4 (cdr (cdr x.2)))) 
             (or (eq? x.4 (cdr lag.3)) (lp.1 x.4 (cdr lag.3)))))))
  (define circular-list? (lambda (x.5) (lp.1 x.5 x.5))))

最適化なし

Ypsilon 0.9.6-update2 Copyright (c) 2008 Y.Fujita, LittleWing Company Limited.
> (import (debug))
> (parameterize ((coreform-optimize #f))
    (debug-expand
     '(define (circular-list? x)
        (let lp ((x x) (lag x))
          (and (pair? x)
               (let ((x (cdr x)))
                 (and (pair? x)
                      (let ((x (cdr x))
                            (lag (cdr lag)))
                        (or (eq? x lag) (lp x lag))))))))))
(begin
  (define circular-list?
    (lambda (x.1)
      (let ((x.2 x.1) (lag.3 x.1))
        (letrec* ((lp.4
                    (lambda (x.5 lag.6)
                      (and (pair? x.5)
                           (let ((x.7 (cdr x.5)))
                             (and (pair? x.7)
                                  (let ((x.8 (cdr x.7)) (lag.9 (cdr lag.6)))
                                    (or (eq? x.8 lag.9) (lp.4 x.8 lag.9)))))))))
          (lp.4 x.2 lag.3))))))

コンパイル

コンパイル結果を見るにはdebug-compileという手続きを使います。この手続きも0.9.6-update2(2008/09/03)より添付されているライブラリ(debug)からexportされています。circular-list?で試してみます。debug-compileの出力は実際のコンパイラの出力からバックトレース用の情報などを取り除いて見やすくしたものです。ちなみに変換前の出力はcompileで見ることができます。

Ypsilon 0.9.6-update2 Copyright (c) 2008 Y.Fujita, LittleWing Company Limited.
> (import (debug))
> (debug-compile
   '(define (circular-list? x)
      (let lp ((x x) (lag x))
        (and (pair? x)
             (let ((x (cdr x)))
               (and (pair? x)
                    (let ((x (cdr x))
                          (lag (cdr lag)))
                      (or (eq? x lag) (lp x lag)))))))))
((close
   (2 0 . lp)
   (iloc.0 . 0)
   (if.not.pair?.ret.const . #f)
   (cdr.iloc (0 . 0))
   (if.not.pair?.ret.const . #f)
   (push.cdr.iloc (0 . 0))
   (push.subr.gloc.of cdr 1)
   (extend . 1)
   (push.iloc.0 . 0)
   (cdr.iloc (1 . 1))
   (if.eq?.ret.const . #t)
   (push.iloc.0 . 0)
   (push.cdr.iloc (1 . 1))
   (apply.gloc.of lp.1))
 (set.gloc.of lp.1)
 (close (1 0 . circular-list?) (push.iloc.0 . 0) (push.iloc.0 . 0) (apply.gloc.of lp.1))
 (set.gloc.of circular-list?)
 (ret.const.unspec))