Download - コンパイラ演習 第 1 回 (2011/10/06)
コンパイラ演習第 1 回
(2011/10/06)中村 晃一 野瀬 貴史 前田 俊行
秋山 茂樹 池尻 拓朗鈴木 友博 渡邊 裕貴
潮田 資秀小酒井 隆広
山下 諒蔵 佐藤 春旗大山 恵弘 佐藤 秀明
住井 英二郎
プロセッサ・コンパイラ実験全体の大目標
•計算機システムを作る– 計算機システムが ( ハードもソフトも )
“black box” でないことを身をもって学ぶ
プロセッサ・コンパイラ実験全体の進め方
• 班 (4 〜 5 人程度 ?) に分かれて進める– うち 1 人をコンパイラ係とする
• 具体的には以下を作成する– 自作 CPU の設計・実装– 自作 CPU 向けのコンパイラ ( クロスコンパイ
ラ )– etc.
• レイトレーシングプログラムを正しく動作させることが条件
コンパイラ係の目標
•自作 CPU 向けのコンパイラを実装する
演習の進め方• 毎回講義を 15 〜 45 分ほど行う– 対象言語 : OCaml もどき– 記述言語 : OCaml
• あとは各自で演習してもらう– コンパイラを実装する、課題を解く等
• 講義資料等は以下に置いておきます– http://www.yl.is.s.u-tokyo.ac.jp
/wiki/lectures/CompilerEnshu2011.ja
お手本とするコンパイラ• 「美しい日本の ML コンパイラ」 ( 通称
MinCaml)• 作者 : 住井 英二郎
– 対象言語 : OCaml もどき– 記述言語 : OCaml– 対象 CPU: SPARC 、 PowerPC
– URL:http://min-caml.sourceforge.net/
講義内容 ( 変更の可能性あり )
• コンパイラの概要 ( 今回 )• K 正規化、 α 変換、最適化• クロージャ変換• 仮想マシンコード生成• レジスタ割り当て• アセンブリ生成、末尾呼び出し最適化• ??????????????• ??????????????• ??????????????• ??????????????• ??????????????• ??????????????
基礎編
応用編
課題について• 二種類の課題がある
– 共通課題• 全員が解くことを想定している課題
– コンパイラ係向け課題• コンパイラ係が解くことを想定している課題
コンパイラ係以外の人の単位取得条件
• 共通課題を 6 つ以上提出することかつ
• CPU 担当教員の出す条件をクリアすること
コンパイラ係の人の単位取得条件
• 自作 CPU ( またはシミュレータ ) 上でレイトレーシングプログラムが完動すること– MinCaml を改造する形でもよい
かつ• 共通課題を 6 つ以上提出すること
かつ• コンパイラ係向け課題を 1 つ以上提出するこ
とかつ
• CPU 担当教員の出す条件をクリアすること
コンパイラの概要
コンパイラとは• ある言語のプログラムを
他の言語に翻訳するシステム– gcc
• C アセンブリ– javac
• Java JVM bytecode
– ocamlc• OCaml OCaml VM bytecode
– ocamlopt• OCaml アセンブリ
– etc.
コンパイルの例
let rec gcd m n = if m = 0 then n else if m <= n then gcd m (n - m) else gcd n (m - n)
.section ".text"gcd.7: cmp %i2, 0 bne be_else.17 nop mov %i3, %i2 retl nopbe_else.17: cmp %i2, %i3 bg ble_else.18 nop sub %i3, %i2, %i3 b gcd.7 nopble_else.18: sub %i2, %i3, %o5 mov %i3, %i2 mov %o5, %i3 b gcd.7 nop
ソースコード
アセンブリコード
コンパイラの難しいところ言語間のギャップが大きい
• 変数• 関数・クロージャ• 式・文
• レジスタ・メモリ• ジャンプ・条件分岐• CPU 命令
ソースコード アセンブリコード
解決法 : ちょっとずつ変換する
中間言語 1
• 変数• ジャンプ・条件分岐• 式・文
アセンブリ• レジスタ・メモリ• ジャンプ・条件分岐• CPU 命令
ソースコード• 変数• 関数・クロージャ• 式・文
中間言語 2
• 変数• ジャンプ・条件分岐• CPU 命令
クロージャ変換
仮想マシンコード生成
レジスタ割当て
概念図
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
字句解析
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
構文解析
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
型検査
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
K 正規化
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
α 変換
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
最適化
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
クロージャ変換
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
仮想マシンコード生成
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
レジスタ割当て
MinCaml ではこんな感じ(* main.ml *)let lexbuf outchan l = Id.counter := 0; Typing.extenv := M.empty; Emit.f outchan (RegAlloc.f (Simm13.f (Virtual.f (Closure.f (iter !limit (Alpha.f (KNormal.f (Typing.f (Parser.exp Lexer.token l)))))))))
アセンブリ生成
字句解析・構文解析• 字句解析– 文字列をトークン ( 単語 ) の列に変換する
• 構文解析– トークン列を抽象構文木に変換する
• MinCaml では ocamllex 、 ocamlyacc を用いる– ML 演習第 4 回参照
MinCaml の型検査• 基本的には ML の型推論– ML 演習第 6 回参照
• ただし、以下の点に注意–推論の実装に reference を用いている–未定義の変数があった場合に
「外部変数」とみなす–型多相はサポートしていない
K 正規化• 計算途中の結果にも全て変数をつける– アセンブリに少し近づける–評価順序が少し明確になる
1 + 2 - 3 + 4 - 5
let a = 1 + 2 inlet b = a - 3 inlet c = b + 4 inlet d = c – 5 ind
α 変換• 変数の名前をつけかえる
• 全ての変数が互いに異なる名前をもつようにする– 最適化やその後の処理が楽になる
let x = 1 inlet x = x + 1 inx
let x0 = 1 inlet x1 = x0 + 1 inx1
最適化• MinCaml では
以下の 3 つの処理を繰り返し行っている– インライン展開– 定数たたみこみ–不要定義削除
• 他にも最適化の手法はたくさんあるので自分で色々試してみるとよい
クロージャ変換• ネストした関数定義を平らにする
let f a = let g b = a + b in g 3
let g a b = a + blet f a = g a 3
超大雑把な図式
実際には MinCaml では
a はクロージャを介して
渡される
仮想マシンコード生成• よりアセンブリに近い形式に変換する
• 実際のアセンブリとの違い– レジスタは無限個ある– if 式や関数呼出しがある
– タプルやクロージャ、配列の操作をメモリロード・ストア命令の組み合わせに変換する
レジスタ割当て• 仮想マシンコードのレジスタに
実際のレジスタを割り当てる
– MinCaml ではそれ程複雑なことはしていない• そこそこの速さ & そこそこの易しさ
– MinCaml で性能を出すためにはここの改造は必須の予感
アセンブリ生成• 実際のアセンブリを出力する– ここでようやく関数呼出しや if 式を
実際の命令に変換している• 加えて末尾呼出しの最適化をしている
– ループ最適化を頑張りたい場合にはここではちょっと遅いかも ?
課題
共通課題
• 今回は全 3問のうちどれかを解けばよい
– もちろん全部解いてもよい
共通課題 その 1
• MinCaml を改造し Syntax.t や KNormal.t などの値を出力できるようにせよ
• 出力の形式や方法は問わないが真面目にやればデバッグのとき役に立つかも
• 例えば以下のような感じ
(* test/fib.ml *)let rec fib n = if n <= 1 then 1 else fib (n-1) + fib (n-2) inprint_int(fib 30)
…If LE Var(n) Int(1) Int(1) Add…
入力
出力
共通課題 その 2
• MinCaml を改造しパースエラー時および型エラー時にどの箇所 ( 例 : 行番号や列番号等 )に問題があったかを表示するようにせよ
共通課題 その 3
• MinCaml を改造し出力されたアセンブリのどの命令が入力されたソースのどの式に対応するかを判別できるようにせよ– 例えば、対応を表すファイルを
コンパイル時に生成するなど
課題の提出先と締切• 提出先 : [email protected]
– 質問等は [email protected] まで
• 締切 : 2 週間後 (10/20) の午後 1 時( JST)– コンパイラ係向け課題の締切は 2012年 2月 28 日
• Subject は Report < 課題番号 > < 学籍番号 :5桁 > とすること– 例 : Report 1 11099
• 間にスペースを 1 つずついれること
• メールの本文にも氏名と学籍番号を明記すること– 正常に受理されれば
通知メールが届くはずなので確認してください
課題提出についての注意• プログラムだけでなく説明・実行例・考察なども書くこと
• 基本的にはメール本文に解答を記述すること
– ただし、多くのソースコードを送る必要がある場合にはtar などで固めて提出してよい
謝辞• 本講義では、昨年度までの講義を担当さ
れた以下の方々によって書かれた資料の多くを再利用させていただいております。ここに感謝いたします。
住井英二郎先生、大山恵弘先生、佐藤秀明氏、山下諒蔵氏、佐藤春旗氏、
小酒井隆広氏、潮田資秀氏、鈴木友博氏、渡邊裕貴氏、秋山 茂樹氏、池尻 拓朗氏