24
Scheme to x86 コンパイラ 多久島 信隆

Scheme to x86コンパイラ

Embed Size (px)

DESCRIPTION

Schemeコードをx86アセンブリにコンパイルする、トイコンパイラの紹介 https://github.com/nobutaka/nanopass

Citation preview

Page 1: Scheme to x86コンパイラ

Scheme to x86コンパイラ

多久島 信隆

Page 2: Scheme to x86コンパイラ

経緯

tracing GCを作ったことがない ⇒ せっかくな

ので処理系のGCを作りたい ⇒ Lisp

interpreter OR compiler ⇒ 黒魔術感のあるcompiler ⇒ ターゲットは資料の多いx86

Page 3: Scheme to x86コンパイラ

経緯SICP, PAIP, 3imp.pdf, Tiger book, EoPL, MinCaml, etc. を参考にはじめる。実装も沢山ある ⇒ An Incremental Approach to Compiler

Constructionに出会う ⇒ さくさく作れて楽し

この論文および関連資料を基礎にして進めてきた

Page 4: Scheme to x86コンパイラ

Schemeの紹介

極めてシンプル (ストイック) な言語仕様

予約語はありません

すべては式です

必要な構文は5つ (define quote if set! lambda)

Page 5: Scheme to x86コンパイラ

S式

z1 → ((a . (b . ())) . ())

S式は最小限の労力で、グラフをテキストにシリアライズする手段

LispのプログラムコードはS式

a = 1 + 1 は (set! a (+ 1 1)) となる

Page 6: Scheme to x86コンパイラ

Program as Data他の言語ならコンパイラが構文解析して内部に作られる構文木を、Lispでは直接プログラムとして書き下すわけだ。しかも、この構文木はプログラムからアクセスできるから、構文木自身を操作するプログラムを書くことができる。Lispではそのようなプログラムをマクロと呼ぶ。いわば、プログラムを生成するプログラムだ。

ポール・グレアム

⇒ 処理系が必要になれば、S式からS式のトランスレー

タを書けばよく、それは簡単、ということです。

Page 7: Scheme to x86コンパイラ

特徴的な言語要素レキシカルスコープ

無限のエクステント

1st class lambda, 1st class continuation

マクロ

GC

Page 8: Scheme to x86コンパイラ

nanopassコンパイラR5RSサブセット

x86=32bit register

machine

3bit tagged value

CPS変換によるcontinuation

伝統的マクロ

Exact copying GC

FFI callout, callback

Schemeで実装

parser, 最適化はなし

https://github.com/nobutaka/nanopass

Page 9: Scheme to x86コンパイラ

fixnum if

Page 10: Scheme to x86コンパイラ

flonum string vector

Page 11: Scheme to x86コンパイラ

let map list

Page 12: Scheme to x86コンパイラ

vararg FFI call/cc

Page 13: Scheme to x86コンパイラ

方針コンパイルフェーズを細かく分け、小さな変換を繰り返す ⇒ nanopass

micro schemeを実装してその上にfull scheme

を実装する

数値をコンパイルできるところからはじめ、ボトムアップに拡張する

Page 14: Scheme to x86コンパイラ

コンパイルフェーズ

約10フェーズ (parserはGaucheのreadで代用)

ライブラリ追加 ⇒ マクロ展開 ⇒ 内部define除

去 ⇒ begin構文単純化 ⇒ CPS変換 ⇒ 代入, free

変数, ヒープリテラル解析 ⇒ 代入boxing/un-

boxing ⇒ ヒープリテラルlifting ⇒ lambda

lifting ⇒ プロローグ追加, コード生成

Page 15: Scheme to x86コンパイラ

マクロ展開

詳しくはLisp in Small Pieces

コンパイル時に評価器が必要

マクロもコンパイルして実行 OR interpreterを実装しておく OR 外部プログラムを呼び出す

Gaucheのevalを呼んで実装

Page 16: Scheme to x86コンパイラ

継続の実装

スタックコピー OR CPS変換

スタックコピーの実装についてはfault on

continuation (Gauche-devel-jp)でぐぐる

コード変換だけで実装できることから、CPS変換を選択

Page 17: Scheme to x86コンパイラ

CPS変換

詳しくはThe 90 minute Scheme to C compiler

なんらかの簡約を行うか、素朴な変換を止めないとコードがとても大きくなります

継続がない場合はSSAと等価だそう

CPS変換するとすべて末尾呼び出しになります

Page 18: Scheme to x86コンパイラ

クロージャの実装

プログラミング言語の進化を追え: 第4回 大人のためのブラックボックス読解講座

interpreterならstatic linkをたどる実装が簡単

クロージャの自由変数を引数に追加して除去する、lambda liftingで実装

Page 19: Scheme to x86コンパイラ

Code Generation Architecture

http://www.cs.indiana.edu/eip/compile/back.html

frame, closure, allocation, accumulator, temporary*3 registers + 独自のスタック

Destination-driven Code Generation: 単純かつトップダウンのアプローチ

Page 20: Scheme to x86コンパイラ

GC

Cheney’s two-finger collectorをCで実装

current frame pointer, current closure pointer, accumulator, temporary registersをrootとする

当初の目的だったGCの実装はわずか300行……

Page 21: Scheme to x86コンパイラ

FFI calloutデータ変換とメモリ管理が要点

C friendlyな型 (null終端文字列や32bit word)

を保持できるようにして、Scheme側で自動変換。stable pointerではありません

(dlsym asciiz) (foreign-call fptr args size)

libffiなどは使えないのでOSXのABIに従いcall

Page 22: Scheme to x86コンパイラ

FFI callback

Scheme↔Cの相互呼び出しがあり得る

tall callのみでなくなる (フレームが伸びる)

コンテキストスイッチのため実行時にtrampolineコードを生成

いつGCが動いてもよいような配慮

Page 23: Scheme to x86コンパイラ

デモ

Page 24: Scheme to x86コンパイラ

振り返ると1 word 64bitにすべきだった

interpreterなら簡単なことが難しい

脳がとけるかと思ったGCのバグ

モデルと実装のギャップが埋まった

竹内先生の最終講義を聞きに行ったり

ちょっと草植えときますね型言語 Grassが分かるようになったり

ま、とにかく楽しかったです