Upload
nobutaka-takushima
View
2.347
Download
5
Embed Size (px)
DESCRIPTION
Schemeコードをx86アセンブリにコンパイルする、トイコンパイラの紹介 https://github.com/nobutaka/nanopass
Citation preview
経緯
tracing GCを作ったことがない ⇒ せっかくな
ので処理系のGCを作りたい ⇒ Lisp
interpreter OR compiler ⇒ 黒魔術感のあるcompiler ⇒ ターゲットは資料の多いx86
経緯SICP, PAIP, 3imp.pdf, Tiger book, EoPL, MinCaml, etc. を参考にはじめる。実装も沢山ある ⇒ An Incremental Approach to Compiler
Constructionに出会う ⇒ さくさく作れて楽し
い
この論文および関連資料を基礎にして進めてきた
Schemeの紹介
極めてシンプル (ストイック) な言語仕様
予約語はありません
すべては式です
必要な構文は5つ (define quote if set! lambda)
S式
z1 → ((a . (b . ())) . ())
S式は最小限の労力で、グラフをテキストにシリアライズする手段
LispのプログラムコードはS式
a = 1 + 1 は (set! a (+ 1 1)) となる
Program as Data他の言語ならコンパイラが構文解析して内部に作られる構文木を、Lispでは直接プログラムとして書き下すわけだ。しかも、この構文木はプログラムからアクセスできるから、構文木自身を操作するプログラムを書くことができる。Lispではそのようなプログラムをマクロと呼ぶ。いわば、プログラムを生成するプログラムだ。
ポール・グレアム
⇒ 処理系が必要になれば、S式からS式のトランスレー
タを書けばよく、それは簡単、ということです。
特徴的な言語要素レキシカルスコープ
無限のエクステント
1st class lambda, 1st class continuation
マクロ
GC
nanopassコンパイラR5RSサブセット
x86=32bit register
machine
3bit tagged value
CPS変換によるcontinuation
伝統的マクロ
Exact copying GC
FFI callout, callback
Schemeで実装
parser, 最適化はなし
https://github.com/nobutaka/nanopass
fixnum if
flonum string vector
let map list
vararg FFI call/cc
方針コンパイルフェーズを細かく分け、小さな変換を繰り返す ⇒ nanopass
micro schemeを実装してその上にfull scheme
を実装する
数値をコンパイルできるところからはじめ、ボトムアップに拡張する
コンパイルフェーズ
約10フェーズ (parserはGaucheのreadで代用)
ライブラリ追加 ⇒ マクロ展開 ⇒ 内部define除
去 ⇒ begin構文単純化 ⇒ CPS変換 ⇒ 代入, free
変数, ヒープリテラル解析 ⇒ 代入boxing/un-
boxing ⇒ ヒープリテラルlifting ⇒ lambda
lifting ⇒ プロローグ追加, コード生成
マクロ展開
詳しくはLisp in Small Pieces
コンパイル時に評価器が必要
マクロもコンパイルして実行 OR interpreterを実装しておく OR 外部プログラムを呼び出す
Gaucheのevalを呼んで実装
継続の実装
スタックコピー OR CPS変換
スタックコピーの実装についてはfault on
continuation (Gauche-devel-jp)でぐぐる
コード変換だけで実装できることから、CPS変換を選択
CPS変換
詳しくはThe 90 minute Scheme to C compiler
なんらかの簡約を行うか、素朴な変換を止めないとコードがとても大きくなります
継続がない場合はSSAと等価だそう
CPS変換するとすべて末尾呼び出しになります
クロージャの実装
プログラミング言語の進化を追え: 第4回 大人のためのブラックボックス読解講座
interpreterならstatic linkをたどる実装が簡単
クロージャの自由変数を引数に追加して除去する、lambda liftingで実装
Code Generation Architecture
http://www.cs.indiana.edu/eip/compile/back.html
frame, closure, allocation, accumulator, temporary*3 registers + 独自のスタック
Destination-driven Code Generation: 単純かつトップダウンのアプローチ
GC
Cheney’s two-finger collectorをCで実装
current frame pointer, current closure pointer, accumulator, temporary registersをrootとする
当初の目的だったGCの実装はわずか300行……
FFI calloutデータ変換とメモリ管理が要点
C friendlyな型 (null終端文字列や32bit word)
を保持できるようにして、Scheme側で自動変換。stable pointerではありません
(dlsym asciiz) (foreign-call fptr args size)
libffiなどは使えないのでOSXのABIに従いcall
FFI callback
Scheme↔Cの相互呼び出しがあり得る
tall callのみでなくなる (フレームが伸びる)
コンテキストスイッチのため実行時にtrampolineコードを生成
いつGCが動いてもよいような配慮
デモ
振り返ると1 word 64bitにすべきだった
interpreterなら簡単なことが難しい
脳がとけるかと思ったGCのバグ
モデルと実装のギャップが埋まった
竹内先生の最終講義を聞きに行ったり
ちょっと草植えときますね型言語 Grassが分かるようになったり
ま、とにかく楽しかったです