Island Life

< Greedy goose | マイコン少年 >

2013/11/07

コンパイラマクロ

Saggitariusがコンパイラマクロ導入か。

Gaucheにも実は非公開だけどdefine-compiler-macroは備わっている。

コンパイラマクロは、Common Lispで性能が必要なアプリを書いてる時は 多用するので必要性はわかるのだけれど、個人的には言語機能としていまいちだなと感じている。

コンパイラマクロはマクロ展開時という比較的速いステージで呼ばれるため、 使える情報が限られてしまう。マクロと同様に、ソースコードの字面の情報しかわからない。

そこからできることは主にふたつ。

  1. 与えられている引数が即値リテラルの場合に部分評価 (コンパイル時計算できる部分を計算してしまう) ができる。
  2. 関数本体をインライン展開できる。

なんだけど、1については、関数引数に直接リテラルが与えられた場合しか対応できない。

例えばfooにコンパイラマクロがついてるとして、こんなコードがあった場合:

(define-constant *some-constant* ...)

...
(let ([x (constant-expression)])  ; xはset!されてない
  (define (bar y)
    (foo x y))
  ...
  (bar *some-constant*)
  ...)

コンパイラの定数伝搬パス以降であれば、fooの呼び出しが (foo <定数> <定数>) になっている可能性が高いのだけれど、 コンパイラマクロ展開時に見えるのはxyといった変数だけなので、 コンパイラマクロ内で部分評価をすることはできないのだ。

fooをインライン展開すれば、インライン展開パスで一旦コードが展開された後、 定数伝搬パスやlambda liftingパスの後の定数畳み込みによって ある程度コンパイル時計算が進められることが多い。でもインライン展開するだけなら 既にdefine-inlineがある。

define-inlineに比べ、インライン展開のためのコンパイラマクロは、 ほとんど同じようなコードを (手続き本体とコンパイラマクロ展開器の) 2箇所で 書く必要があり、これは大きなデメリットだ。一方だけを直してはまった 苦い思い出の数々。

なので、本当に欲しいのは、ある程度コンパイラのパスが進んで 引数の属性が分かった後に突っ込めるマクロなんだけれど、 そういうことをするにはコンパイラが使ってる内部データ構造を見せないとならないんで そのへんをまだ考えている。うかつに内部データ構造をいじれるようにしちゃうと 後でコンパイラを改良する時に足を引っ張られるから。

「この引数が定数とわかったらこういうパターンで変形せよ」みたいな ルールベースのマクロ (内部データ構造はopaqueにして触らせない) あたりが 落としどころかなあとは思っている。

ただまあ、そっちの方向で複雑にしすぎるとC++みたいなことになりかねないんで、 バランスが難しい。 (最近のC++の機能の多くは、コンパイラにどうやって安全なフックポイントを つけて自分が思うようなコードを吐かせるかって話のように見える。)

Tags: Programming, Gauche

Post a comment

Name: