コンパイラ資料 中間コード 160601a.

Slides:



Advertisements
Similar presentations
1 B10 CPU を作る 1 日目 解説 TA 高田正法
Advertisements

2.5 プログラムの構成要素 (1)文字セット ① ASCII ( American Standard Code for Interchange ) JIS コードと同じ ② EBCDIC ( Extended Binary Coded Decimal for Information Code ) 1.
2006/10/26 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井 英二郎
プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
プログラミング言語としてのR 情報知能学科 白井 英俊.
情報工学基礎(改訂版) 岡崎裕之.
コンパイラ 第9回 コード生成 ― スタックマシン ―
OSとコマンド OS:コンピュータを使うための基本プログラム コマンド:OS上で使用できる命令 OS本体であるカーネルの内部コマンド
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
2012年度 計算機システム演習 第4回 白幡 晃一.
ソフトウェアを美味しく 解析する方法 Security Ark
App. A アセンブラ、リンカ、 SPIMシミュレータ
ソフトウェアとのインターフェース.
2006/10/19 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井英二郎
言語処理系(8) 金子敬一.
アルゴリズムとデータ構造1 2009年6月25日
  【事例演習6】  数式インタプリタ      解 説     “インタプリタの基本的な仕組み”.
の まとめ 2007/04/02 (Mon) / d;id:hzkr
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
コンパイラの解析 (2) GCJのデータ構造 - 1.
プログラミング言語入門 手続き型言語としてのJava
関数とポインタ 値呼び出しと参照呼び出し swapのいろいろ 関数引数 数値積分
言語プロセッサ2016 Language Processors 2016
言語プロセッサ2007 平成19年9月26日(水) (Ver.2 平成19年10月3日変更)
セキュリティ(3) 05A2013 大川内 斉.
プログラミング論 II 2008年10月30日 文字列
コンピュータ系実験Ⅲ 「ワンチップマイコンの応用」 第1週目 アセンブリ言語講座
プログラミング言語入門.
アルゴリズムとデータ構造 2010年6月28日
アルゴリズムとデータ構造1 2005年6月28日
TA 高田正法 B10 CPUを作る 3日目 SPIMの改造 TA 高田正法
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
アルゴリズムとデータ構造 補足資料5-1 「メモリとポインタ」
A Provably Sound TAL for Back-end Optimization について
東京工科大学 コンピュータサイエンス学部 亀田弘之
コンパイラ資料 4章 記号表 改訂1版( ).
コンパイラ資料 実行時環境.
計算機構成 第3回 データパス:計算をするところ テキスト14‐19、29‐35
C言語を用いたマシン非依存な JITコンパイラ作成フレームワーク
情報とコンピュータ 静岡大学工学部 安藤和敏
先週の復習: CPU が働く仕組み コンピュータの構造 pp 制御装置+演算装置+レジスタ 制御装置がなければ電卓と同様
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
C言語 はじめに 2016年 吉田研究室.
統計ソフトウエアRの基礎.
コンピュータアーキテクチャ 第 2 回.
コンピュータアーキテクチャ 第 4 回.
計算機プログラミングI 木曜日 1時限・5時限 担当: 増原英彦 第1回 2002年10月10日(木)
コンピュータアーキテクチャ 第 2 回.
コンピュータアーキテクチャ 第 5 回.
コンピュータアーキテクチャ 第 4 回.
コンピュータアーキテクチャ 第 5 回.
言語プロセッサ 第12日目 平成20年1月9日.
ドキュメントジェネレータ 詳細仕様 長谷川啓
ca-9. 数の扱い (コンピュータアーキテクチャとプロセッサ)
オブジェクト指向言語論 第二回 知能情報学部 新田直也.
コンパイラ 2012年10月11日
Inline 展開のアルゴリズム 長谷川啓
コンパイラ 第12回 実行時環境 ― 変数と関数 ― 38号館4階N-411 内線5459
演算子のオーバーロード.
プログラミング言語論 第九回 理工学部 情報システム工学科 新田直也.
プログラミング演習I 2003年6月11日(第9回) 木村巌.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
プログラミング言語論 第九回 理工学部 情報システム工学科 新田直也.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
情報処理Ⅱ 2005年11月25日(金).
プログラミング入門2 第5回 配列 変数宣言、初期化について
情報処理Ⅱ 小テスト 2005年2月1日(火).
C#プログラミング実習 第1回.
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
6.5 最終コード生成 (1)コードの形式 ①絶対2進コード(AB : absolute binary) 命令後のオペランドが絶対番地指定。
Presentation transcript:

コンパイラ資料 中間コード 160601a

ここまでの流れ 意味解析 型検査 トークン 字句解析 構文解析 AST 属性 中間コード レジスタ 割り付け

Intermediate representation (IR) ASTを抽象機械のアセンブリ言語に翻訳する block Mov r4 Loc(0) Mov r5 r4 Add r5 3 Mov r4 r5 := decl { int x; x:=x+3; } (x : int) + x x 3 AST IR

IRの特徴 CPUを模した抽象機械の命令 データ構造のまま(まだ機械的な編集操作が可能) 汎用レジスタ無限個 一般的な命令 (詳細はあとで) 一般的な命令 (詳細はあとで) 一般的なデータアクセス(メモリ、レジスタ、即値) 特定のフレームレイアウト(7章)に依存しない データ構造のまま(まだ機械的な編集操作が可能) 汎用レジスタ無限個

IRの役割 CPUにとってのAST レジスタの割り当てのためのデータ構造 最適化のためのデータ構造(オペランドも) マシン非依存(マシン依存コードへの中継地点) レジスタの割り当てのためのデータ構造 最適化のためのデータ構造(オペランドも) mov r5 r5 同一性検出⇒除去可能

本IR独自の設計方針 x86アセンブリに近い(マシン独立でない) kittyの機能に必要なもののみに限定 命令選択(instruction selection)の処理をスキップできる kittyの機能に必要なもののみに限定 アドレス計算(配列,ポインタの脱参照に必要)はしない

IR (とはいってもほぼx86/アセンブリ(INTEL記法)) 命令: Mov a1 a2 Add a1 a2 Sub a1 a2 Mul a1 Div a1 Not a Call fl Label sl Jmp sl Cmp a1 a2 Je sl Jne sl オペランド: ()内はパラメータ Imm(n) --- 即値n Reg(n) --- n番目の汎用レジスタ Formal(n) --- n番目の仮引数の場所 Arg(n) --- n番目の実引数の場所 Local(n)--- n番目の局所変数の場所 Save(n) --- レジスタ退避場所n番目 Formal, Arg,Localはメモリ(フレーム、後述)上の場所 ラベル SimpleLabel(n)--- ジャンプ用ラベル FunLabel(文字列) --- 関数名ラベル a1, a2,aはオペランド flはFunLabel, slはSimpleLabel 即値へのmove、memoryからmemoryへのmoveは禁止

命令の意味 命令 # 意味 Mov a1 a2 # a2(の内容)をa1にコピー Add a1 a2 # a1←a1+a2 命令 # 意味 Mov a1 a2  # a2(の内容)をa1にコピー Add a1 a2     # a1←a1+a2 Sub a1 a2 # a1←a1-a2 Mul a1 # eax ←eax ×a1 (edxを壊す) Div a1        # eax←eax ÷a1 (edxを壊す) Not a #否定(bit反転) Call funlabel # ラベル funlabelをもつ関数を呼ぶ Label simplelabel   # (ラベル) Jmp simplelabel #labelに飛ぶ Cmp a1 a2   # a1とa2を比較(次と組で使う)→a1 Je simplelabel #直前のCmpがa1=a2ならsimplelabelに飛ぶ Jne simplelabel    #直前のCmpがa1!=a2ならsimplelabelに飛ぶ Jg simplelabel #直前のCmpがa1>a2ならsimplelabelに飛ぶ {Jge, Jl, Jle}  # {>=,<,<=} (符号つき) ....

命令とオペランドのデータ構造 Label SimpleLabel 2 Move Reg 4 Local 0 Reg 4 Move Reg 5 //instr.h class Instr{ set<Reg*> use; set<Reg*> def; }; class Move_ : Instr{ Access* dst; Access* src; ..... class Access{/* 当面は空 */}; class Imm : Access{ int value; class Reg : Access{ int sn; int RA_temp; class Local : Access{ int pos; class SimpleLabel { L2: Mov r4 Loc(0) Mov r5 r4 Add r5 3 Mov r4 r5 Label SimpleLabel 2 Move Reg 4 Local 0 Reg 4 Move Reg 5 Add Reg 5 Imm 3 Move Reg 4 Reg 5

翻訳に用いる変数・関数 命令列(コードリスト)記録用グローバル変数 list<Instr*>* code; 命令用コンストラクタ兼出力記録関数(icode)   void Move(Access* d,Access* s);   Move_(d,s)を作成してコードリスト末尾に追加   (ほかの命令も同様) ※ 大文字関数名はこの用途に用いることにする。 Access、ラベルのコンストラクタ関数(小文字名)  Reg* reg(void), Imm* imm(int n), SimpleLabel* new_label(void), FunLabel* fun_label(std::string) etc.

出力アセンブリコード仕様 アセンブリ言語:MASM (32bit) ターゲットマシン:x86 実行環境,Windows ※ IRではまだコード出力はしない

MASMの構文:レジスタ 汎用レジスタ: eax, ebx, ecx, edx, esi, edi スタックポインタ: esp ベースポインタ: ebp ptrの指す番地のdsip個上は DWORD PTR [ptr+disp]で参照 例  スタックに1ワード(32bit)積むとespは WRD=32bit/1バイト=4だけ減る。 積む前のスタックの最上位要素を参照するには [esp+4] とする 

MASMの構文:ニーモニック Op Arg1, Arg2 例 mov ebx, eax 意味:レジスタeaxの値をレジスタebxにコピー 例 mov DWORD PTR [ebx+n], eax 意味:レジスタeaxの値をebxの値をアドレスのn番地上のメモリにコピー 例 mov eax, 5 意味:即値5をレジスタeaxにコピー gas(AT&T記法)とMASM(microsoft asm, Intel記法)は 移動の向きとオペランドの順が逆なので注意

演算子ニーモニック 算術/2 add, sub 整数 和、差 構文 add t, s // t ← t + s 構文 sub t, s   // t ← t - s 算術/1 imul, idiv  整数 積、商   構文 imul t   // edx(上位),eax ← t×eax 論理/2 and,or ビットごとの論理積、和 比較/2 cmp 制御/1 jmp, je, jne スタックからポップ pop t スタックにプッシュ push s

アセンブリソースファイルの構成 .686P ; コメント: Pentium Pro以降 .XMM ; SIMD命令 .model flat ; メモリモデル(flat = 32bit/Windows) EXTERN printf : PROC ; 外部記号宣言 _TEXT SEGHENT hoge DB '%d', 0aH, 00H ;定数定義(文字列) _TEXT ENDS PUBLIC _fact ;公開記号宣言 _TEXT SEGMENT _fact PROC push ebp push ebp, esp ….. ret _fact ENDP END

Visual Studioでアセンブリを出力する Projectのプロパティー → 構成プロパティ → C/C++ → 出力ファイル → アセンブリの出力 → (「なし」以外を選ぶ)

実験1 C/C++で次のコードをコンパイルして出力コードを観察せよ。 int main(){ int x=10; x=x*x+7; printf(“%d\n”x); }

MASM Reference MASM Reference Directive Reference http://msdn.microsoft.com/en-us/library/afzk3475 Directive Reference http://msdn.microsoft.com/en-us/library/8t163bt0%28v=vs.100%29.aspx

コマンドラインからMASMを使う Visual Studio 2010 → Visual Studio Tools Visual Studio コマンドプロンプトを起動 >ml [options] filelist [/link] linkoptions 例: ml factorial.asm msvcrt.lib (factorial.asmをアセンブル後、オブジェクトを生成してCランタイムライブラリmsvcrt.libとリンク) コマンドラインスイッチのヘルプ: >ml /? または ml /help

Visual Studio IDEからMASMを使う プロジェクトのプロパティー → カスタムビルドツール → コマンドライン [Release構成] ml /c /Fo"$(Configuration)\$(ProjectName).obj" "$(ProjectName).asm" [Debug構成] ml /c /Zi /Fo"$(Configuration)\$(ProjectName).obj" "$(ProjectName).asm" プロジェクトのプロパティー → カスタムビルドツール →出力ファイル [すべての構成] $(OutDir)$(ProjectName).obj プロジェクトのプロパティー → リンカー → 入力→追加の依存ファイル msvcrt.lib 【参考】 Visual Studioの設定で使用できる変数一覧: http://msdn.microsoft.com/ja-jp/library/c02as0cs.aspx

実験2 実験1で生成したアセンブリファイルをアセンブル・リンクして実行結果を確かめよ

x86/32bit/MASMアセンブリ練習 10の階乗のプログラムをMASMで書け while文 再帰

IRへの翻訳の概略 ASTノードごとに、生成すべき命令列をicodeで記述する。 icodeはC++コードでIRを記述する、いわば言語内言語 icodeの命令の引数(Access*)はC++側で準備する icode命令はアセンブリをその場で書く要領 値をもつASTノードは生成命令列が値を置く(仮想)レジスタを返す。

レジスタは無限個 各ノードで計算の中間結果(値)は新しいレジスタに格納する。 reg() は呼ばれるたびに新しいシリアルナンバーをもつレジスタを作って返す。 (new_label()も同様に新しいラベルを作って返す。) シリアルナンバー0~5(=REG_SIZE-1)の仮想レジスタはあらかじめ実レジスタに割り当てておく 特定のレジスタを参照するために用いる(precolored仮想レジスタ)

precolored の作成 翻訳のはじめにまとめて作成しておく Reg* Eax=reg(); //r0 Reg* Ebx=reg(); //r1 Reg* Ecx=reg(); //r2 Reg* Edx=reg(); //r3 Reg* Esi=reg(); //r4 Reg* Edi=reg(); //r5

中間コード生成 IRVisitor : public Visitor{   ConditionIRVisitor* cv;//visitor切り替え用   void* visit(XXNode* n, void* obj);  // nを根とする木をIRに翻訳(条件式は除く) // 式の木の場合は値を格納する場所(Access*)を返す。 } ConditionIRVisitor : public IRVisitor{ IRVisitor* ev; //visitor切り替え用   void* visit(XXNode* n, SimpleLabel* label); //条件式専用visitor: //値が偽のときlabelにジャンプするIRに翻訳

void* IRVisitor::visit(OpNode* n, void* obj) { Access *t1,*t2; Reg* rand; t1=static_cast<Access*>(n->children[0]->accept(this,obj));   //第1被演算数を計算するコードを生成し、結果がおかれる場所をt1に代入する   t2=static_cast<Access*>(n->children[1]->accept(this,obj));  //第2被演算数を計算するコードを生成し、結果がおかれる場所をt2に代入する switch(n->op_kind){ case PLUS_OP: rand=reg();    //新しいレジスタをつくりrandに代入 Move(rand, t1);   //Move命令 rand ← t1 を生成 Add(rand,t2);    //Add命令 rand ← rand + t2 を生成 break; ..... } return rand;

void* IRVisitor::visit(IfNode* n, void* obj) { SimpleLabel* L1=new_label(); //ラベルL1を作る(まだ置かない) SimpleLabel* L2=new_label();  //別のラベルL2を作る n->children[0]->accept(this->cv,L1);//visitor切り替え // 「条件式の値がfalseならL1にjumpするコード」を生成   n->children[1]->accept(this,obj); //if文のTrue部のコードを生成 Jmp(L2);      //「 ラベルL2にjumpする命令」を生成 Label(L1);     //「ラベルL1」をここ(現時点のコード末尾)に貼る n->children[2]->accept(this,obj); //if文のFalse部のコードを生成 Label(L2); //「ラベルL2」をここに貼る return nullptr; }

void* IRVisitor::visit(RelNode* n, void* ob) { Acccess *t1,*t2; t1=static_cast<Access*> (n->children[0]->accept(this,ob)); t2=static_cast<Access*> (n->children[1]->accept(this,ob)); Reg* rand=reg(); Move(rand,t1); Rel(n->rel_kind, rand , t2); return rand } Access* Rel(int kind, Access* left, Access* right){ SimpleLabel *L1,*L2; Cmp(left,right); L1=new_label(); L2=new_label(); switch(kind){ case EQ: Je(L1); Move(left, imm(0)); Jmp(L2); Label(L1); Move(left, imm(~0)); Label(L2); break; ... return left; void* ConditionIRVisitor::visit(RelNode*n, void* Lf) {    Acccess *t1,*t2; t1=static_cast<Access*> (n->children[0]->accept(this,nullptr)); t2=static_cast<Access*> (n->children[1]->accept(this,nullptr));   Reg* rand=reg(); Move(rand, t1); cRel(n->rel_kind, rand, t2, static_cast<SimpleLabel*>(Lf)); return nullptr; } void cRel(int kind, Access* left, Access* right, Access* Lf){ Cmp(left,right); switch(kind){ case EQ: Jne(Lf); break; ...

課題 IR_visitor 印字例 関数呼び出しとリターン文を除くstatementをIRに翻訳するプログラム 入力例 { int x,s; Mov r6, 100 Mov Loc(1), r6 Mov r7, 0 Mov Loc(0), r7 L_1 : Mov r8, Loc(1) Cmp r8, 0 Je L_2 Mov r9, Loc(0) Add r9, Loc(1) Mov r10, r9 Mov Loc(0), r10 Mov r11, Loc(1) Sub r11, 1 Mov r12, r11 Mov Loc(1), r12 Jmp L_1 L_2 : 関数呼び出しとリターン文を除くstatementをIRに翻訳するプログラム 入力例 { int x,s; x := 100; s := 0; while (x <> 0) { s := s+x; x := x-1; } 印字プログラムを(Vsitorパターンで)作成して確かめよ。

参考(GNU assembler, gas)

コンパイルから実行ファイルまで gccの場合 cc (gcc): Cを gasに翻訳(コンパイル) foo.c  foo.s as (Gnu Assembler, gas) :gas を再配置可能オブジェクトに変換(アセンブル)    foo.s  foo.o ld : リンカ(または、リンクエディタ)   複数のオブジェクトを結合して、実行可能形式に。   foo.o  foo.exe

調査課題 cc -S filename.c はアセンブリコードのみを生成する. いろいろなC言語ソースファイルで試し, 出力コードを調べよ. cc -v filename.c はgccがどのような外部プログラムを呼び出しているかを表示する. 試せ. cc -v filename.c >& hoge.sh でhoge.shに格納して編集し, アセンブリコードfilename.sをもとに 実行可能ファイルを作成するシェルスクリプトをつくれ. 引数, スイッチを削除してみてどれが必須かしらべよ. シェルスクリプト: 1行めに #!/usr/bin/sh と書いておけば以下の行がshから実行される. シェルスクリプトの引数は$1で参照できる. (ファイル名など)

出力アセンブリコード仕様 アセンブリ言語:gas (32bit) ターゲットマシン:x86 実行環境,OS: Linux, Cygwin※など(POSIX) ※CygwinはPOSIXのみエミュレートするのでシステムコールはない

gasの構文:レジスタ 汎用レジスタ: %eax, %ebx, %ecx, %edx, %esi, %edi スタックポインタ: %esp ベースポインタ: %ebp ptrの指す番地のdsip個上はdisp(ptr)で参照 例  スタックに1ワード(32bit)積むと%espは WRD=32bit/1バイト=4だけ減る。 積む前のスタックの最上位要素を参照するには 4(%esp) とする 

gasの構文:ニーモニック Op Arg1, Arg2 例 movl %eax, %ebx 例 movl %eax, n(%ebx) 意味:レジスタ%eaxの値を%ebxの値をアドレスのn番地上のメモリにコピー 例 movl $5, %eax 意味:即値5をレジスタ%eaxにコピー gas(AT&T記法)とmasm(microsoft asm, Intel記法)は 移動の向きとオペランドの順が逆なので注意

演算子ニーモニック 算術/2 addl, subl 整数 和、差 構文 addl s, t // t + s → t 構文 subl s, t   // t - s  → t 算術/1 imul, idiv  整数 積、商   構文 imul t   // t×%eax →%edx(上位),%eax 論理/2 and,or ビットごとの論理積、和 比較/2 cmp 制御/1 jmp, je, jne スタックからポップ popl t スタックにプッシュ pushl s

x86/32bit/gasアセンブリ練習 10の階乗のプログラムをgasで書け while文 再帰 Cで次のコードをコンパイル(cc -S)して出力コードを観察せよ。 int main(){ int x=10; x=x*x+7; printf(“%d\n”,x); } 10の階乗のプログラムをgasで書け while文 再帰