コンパイラとプログラミング言語 · 2014-06-24 · java vm...

35
コンパイラとプログラミング言語 12関数呼び出しのコード生成 2014年6月25日 金岡 晃

Upload: others

Post on 20-May-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

コンパイラとプログラミング言語第12週 関数呼び出しのコード生成

2014年6月25日

金岡 晃

授業計画

1

第1週

(4/9)

コンパイラの概要

第2週

(4/16)

コンパイラの構成

第3週

(4/23)

プログラミング言語の形式的な記述

第4週

(4/30)

プログラミング言語の形式的な記述

第5週

(5/7)

字句解析の概要と非決定性有限オートマトン、決定性有限オートマトン・字句解析プログラム

第6週

(5/14)

中間試験

第7週

(5/21)

構文解析の概要/上向き構文解析

第8週

(5/28)

下向き構文解析/構文解析プログラム

第9週

(6/4)

中間表現と意味解析

第10週

(6/11)

Java仮想マシンとその機械語

第11週

(6/18)

条件分岐文と繰り返し文のコード生成

第12週

(6/25)

関数呼び出しのコード生成

第13週

(7/2)

休講

第14週

(7/9)

休講

(7/23-8/5)

期末試験

2014/6/25 コンパイラとプログラミング言語

【復習】第11週条件分岐文と繰り返し文のコード生成

コンパイラとプログラミング言語

2 2014/6/25 コンパイラとプログラミング言語

Java VMバイトコード:データ操作(1)

2014/6/25 コンパイラとプログラミング言語3

ローカル変数を保持数変数エリア領域から、値を計算に使うために、オペランドスタックに積む命令

iload 形式:iload index

indexは符号なし1バイト整数。ローカル変数へアクセスできるインデックス。indexにあるローカル変数の値をオペランドスタックへプッシュする。

iload_<n> 形式:iload_0, iload_1,…

インデックス<n>のローカル変数の値をオペランドスタックへプッシュする

計算結果があるオペランドスタックの先頭の値を、ローカル変数を保持する領域に格納する命令

istore 形式:istore index

indexは符号なしの1バイトの整数で、ローカル変数へアクセスできるインデックスである。オペランドスタックの先頭の値をポップして、ローカル変数のindexが示すエリアにその値を格納する

istore_<n> 形式:istore_0, istore_1, …

オペランドスタックの先頭の値をポップして、インデックス<n>のローカル変数のエリアへ格納する

ローカル変数の操作

Java VMバイトコード:データ操作(2)

2014/6/25 コンパイラとプログラミング言語4

定数を計算に用いる場合、直接値をオペランドスタックに積む命令

iconst_m1 オペランドスタックに-1を積む

iconst_<i> 形式:iconst_1, iconst_2, …

<i>は1,2,3,4,5のいずれかである。整定数iをスタックに積む。

bipush<n> 整定数nをオペランドスタックに積む。<n>は符号付整数で-128≦<n>≦127の範囲である

sipush<n> 整定数nをオペランドスタックに積む。<n>は符号付整数で-32768≦<n>≦32767の範囲である

定数値の処理

pop オペランドスタックの先頭の1ワードをポップする。データを1つオペランドスタックから捨てるための命令である。

スタック操作

Java VMバイトコード:算術演算

2014/6/25 コンパイラとプログラミング言語5

iadd オペランドスタックから2つの整数値をポップし、加算した結果をオペランドスタックにプッシュする

isub オペランドスタックから2つの整数値をポップし、先頭の値を減数、1つ前の値を被減数として減算した結果をオペランドスタックにプッシュする

imul オペランドスタックから2つの整数値をポップし、乗算した結果をオペランドスタックにプッシュする

idiv オペランドスタックから2つの整数値をポップし、先頭の値を除数、1つ前の値を被除数として除算した結果をオペランドスタックにプッシュする

ineg オペランドスタックの先頭の値の符号を反転させる。

Java VMバイトコード:フロー制御(1)条件分岐

2014/6/25 コンパイラとプログラミング言語6

ifeq <address> オペランドスタックの先頭の値をポップし、これが0ならば、指定した<address>を分岐オフセットとして、このifeq命令からオフセットだけ離れた命令に制御が移る。そうでなければ次の命令に制御が移る。<address>は分岐オフセットを表す16ビットの符号付き整数である。

ifge <address> オペランドスタックの先頭の値をポップし、これが0以上であれば、指定した<address>を分岐オフセットとして、このifge命令からオフセットだけ離れた命令に制御が移る。そうでなければ次の命令に制御が移る。<address>は分岐オフセットを表す16ビットの符号付き整数である。

ifgt <address> オペランドスタックの先頭の値をポップし、これが0より大きければ、指定した<address>を分岐オフセットとして、このifgt命令からオフセットだけ離れた命令に制御が移る。そうでなければ次の命令に制御が移る。<address>は分岐オフセットを表す16ビットの符号付き整数である。

ifle <address> オペランドスタックの先頭の値をポップし、これが0以下ならば、指定した<address>を分岐オフセットとして、このifle命令からオフセットだけ離れた命令に制御が移る。そうでなければ次の命令に制御が移る。<address>は分岐オフセットを表す16ビットの符号付き整数である。

iflt <address> オペランドスタックの先頭の値をポップし、これが0未満ならば、指定した<address>を分岐オフセットとして、このiflt命令からオフセットだけ離れた命令に制御が移る。そうでなければ次の命令に制御が移る。<address>は分岐オフセットを表す16ビットの符号付き整数である。

Java VMバイトコード:フロー制御(2)

2014/6/25 コンパイラとプログラミング言語7

goto <address> 指定した<address>を分岐オフセットとして、このgoto命令からオフセットだけ離れた命令に制御が移る。<address>は、分岐オフセットを表す16ビットの符号付き整数である。

goto_w <address> 指定した<address>を分岐オフセットとして、このgoto_w命令からオフセットだけ離れた命令に制御が移る。基本的にはgoto命令と同じだが、分岐先が16ビットの符号付き整数で表現できないほど離れている場合に用いる。<address>は、分岐オフセットを表す32ビットの符号付き整数である。

無条件分岐

invokestatic<method>

スタティックメソッドを呼び出し、引数の値を順にスタックに積んでクラスメソッドを呼び出す。返戻値がある場合は、それがスタックの先頭に積まれた状態で戻る。<method>はメソッドのエントリ番号を示す指定するコードである。

ireturn オペランドスタックの先頭の整数値をポップして、返戻値とし(呼び出し元のメソッドのオペランドスタックにプッシュして)、実行中のメソッドを終了して呼び出し元に戻る。

メソッド呼び出しと復帰

Java VMバイトコード:条件分岐その2

2014/6/25 コンパイラとプログラミング言語8

if_icmpeq <address> オペランドスタックから2つの整数値をポップし、両方の値が等しければ、指定した<address>に制御が移る。そうでなければ次の命令に制御が移る。

if_icmpne <address> オペランドスタックから2つの整数値をポップし、両方の値が異なる場合、指定した<address>に制御が移る。そうでなければ次の命令に制御が移る。

if_icmplt <address> オペランドスタックから2つの整数値をポップし、先頭の値>1つ前の値の場合、指定した<address>に制御が移る。そうでなければ次の命令に制御が移る。

if_icmpgt <address> オペランドスタックから2つの整数値をポップし、先頭の値<1つ前の値の場合、指定した<address>に制御が移る。そうでなければ次の命令に制御が移る。

if_icmple <address> オペランドスタックから2つの整数値をポップし、先頭の値≧1つ前の値の場合、指定した<address>に制御が移る。そうでなければ次の命令に制御が移る。

if_icmpge <address> オペランドスタックから2つの整数値をポップし、先頭の値≦1つ前の値の場合、指定した<address>に制御が移る。そうでなければ次の命令に制御が移る。

Java VMバイトコード:算術演算その2

2014/6/25 コンパイラとプログラミング言語9

iinc 形式:iinc index, value

indexにあるローカル変数の値をvalueだけ増加させ、indexが示すエリアにその値を格納する

0: iconst_31: istore_12: iconst_43: istore_24: iload_25: iconst_36: if_icmplt 119: iconst_210: istore_111: return

分岐をつかったコード例(3)

2014/6/25 コンパイラとプログラミング言語10

class Test03{

public static void main(String[] arg){

int i;

int j;

i=3;

j=4;

if(j>=3){

i=2;

}

}

}

ソースコード バイトコード

繰り返しをつかったコード例(3)

2014/6/25 コンパイラとプログラミング言語11

class Test05{

public static void main(String[] arg){

int i;

int j;

i=3;

j=4;

for(i=0;i<5;i++){

j--;

}

}

}

ソースコード バイトコード

0: iconst_3

1: istore_1

2: iconst_4

3: istore_2

4: iconst_0

5: istore_1

6: iload_1

7: iconst_5

8: if_icmpge 20

11: iinc 2, -1

14: iinc 1, 1

17: goto 6

20: return

第12週関数呼び出しのコード生成

コンパイラとプログラミング言語

12 2014/6/25 コンパイラとプログラミング言語

本日の到達目標と概要

• 到達目標

– Java VMバイトコードにおける関数呼び出しの理解

• 概要

– 関数呼び出しに関するJava VMバイトコード

– バイトコードにおける関数呼び出しの実現

13 2014/6/25 コンパイラとプログラミング言語

Java VMバイトコード:フロー制御(2)

2014/6/25 コンパイラとプログラミング言語14

goto <address> 指定した<address>を分岐オフセットとして、このgoto命令からオフセットだけ離れた命令に制御が移る。<address>は、分岐オフセットを表す16ビットの符号付き整数である。

goto_w <address> 指定した<address>を分岐オフセットとして、このgoto_w命令からオフセットだけ離れた命令に制御が移る。基本的にはgoto命令と同じだが、分岐先が16ビットの符号付き整数で表現できないほど離れている場合に用いる。<address>は、分岐オフセットを表す32ビットの符号付き整数である。

無条件分岐

invokestatic<method>

スタティックメソッドを呼び出し、引数の値を順にスタックに積んでクラスメソッドを呼び出す。返戻値がある場合は、それがスタックの先頭に積まれた状態で戻る。<method>はメソッドのエントリ番号を示す指定するコードである。

ireturn オペランドスタックの先頭の整数値をポップして、返戻値とし(呼び出し元のメソッドのオペランドスタックにプッシュして)、実行中のメソッドを終了して呼び出し元に戻る。

メソッド呼び出しと復帰

エントリ番号

分岐をつかったコード例、の前に

2014/6/25 コンパイラとプログラミング言語15

invokestatic <method>

<method>はメソッドのエントリ番号を示す指定するコードである。

プログラム中に登場するクラス、メソッドはそれぞれ別のバイトコードが構成され、それぞれのコードは1つのエントリとしてまとまる。エントリはコンパイル時に分けられ、エントリ番号が振られる。バイトコード上では、エントリ番号で指定されて呼び出しなどを受ける。

スタックマシンはエントリごとに構成される(スタック、変数エリアもエントリごと)

分岐をつかったコード例(3)を見る

2014/6/25 コンパイラとプログラミング言語16

class Test03{

public static void main(String[] arg){

int i;

int j;

i=3;

j=4;

if(j>=3){

i=2;

}

}

}

ソースコード クラスにエントリ番号0(#0)

0: aload_01: invokespecial #1 4: return

0: iconst_31: istore_12: iconst_43: istore_24: iload_25: iconst_36: if_icmplt 119: iconst_210: istore_111: return

mainメソッドにエントリ番号1(#1)

呼んでる

関数呼び出しをつかったコード例(1)

2014/6/25 コンパイラとプログラミング言語17

public class Test06{

public static void main(String[] args){

int i=3;

int j=4;

int k=0;

k = Test06.testAdd(i,j);

}

public static int testAdd(int i, int j){

return (i+j);

}

}

ソースコード

0: iconst_31: istore_12: iconst_43: istore_24: iconst_05: istore_36: iload_17: iload_28: invokestatic #2 11: istore_312: return

mainメソッドにエントリ番号1(#1)

0: iload_01: iload_12: iadd3: ireturn

testAddメソッドにエントリ番号2(#2)

呼んでる(いくつ引数があるか知っている)

関数呼び出しをつかったコード例(2)

2014/6/25 コンパイラとプログラミング言語18

public class Test07{

public static void main(String[] args){

int i=3;

int j=4;

int k=0;

k = Test07.testMulp1(i,j);

}

public static int testMulp1(int i, int j){

return (i*j+1);

}

}

ソースコード

mainメソッドにエントリ番号1(#1)

testMulp1メソッドにエントリ番号2(#2)

呼んでる(いくつ引数があるか知っている)

まとめ

コンパイラとプログラミング言語

19 2014/6/25 コンパイラとプログラミング言語

授業用Webサイト

• http://www.klab.is.sci.toho-u.ac.jp/classes/

• 金岡が受け持っている講義の資料(この講義以外も)をアップロードしています

• 「コンパイラとプログラミング言語」のページも作成しました

• これまでの講義資料をPDF化してすべて載せてあります。

2014/6/25 コンパイラとプログラミング言語20

言語処理系

• コンパイラ、リンケージエディタ、エディタ、デバッガ、実行時ライブラリなど、プログラムの開発・翻訳(コンパイル)・実行に関係するプログラム群の総称

• 処理系とも呼ぶ

2014/6/25 コンパイラとプログラミング言語21

エディタ

ソースプログラム

ソースプログラム

・・・

コンパイラ

コンパイラ

目的プログラム

目的プログラム

リンケージエディタ

実行時ライブラリ

実行プログラム

実行デバッガ

コンパイラの論理的な構成

2014/6/25 コンパイラとプログラミング言語22

ソースプログラム

目的プログラム

コンパイラ

字句解析

構文解析

意味解析

最適化コード生成

中間情報(中間語、名前表)

コンパイラの開発における重要なポイント

2014/6/25 コンパイラとプログラミング言語23

ソースプログラム

目的プログラム

コンパイラ

字句解析

構文解析

意味解析

最適化コード生成

中間情報(中間語、名前表)

プログラム言語の文法の出来の良し悪しが最重要事項

→ 言語の仕様範囲と文法を厳密に定める必要

文法の形式的な記述方式

• バッカス記法• 構文図式

字句解析の概要

• ソースプログラムからトークンを取り出して構文解析に渡す

2014/6/25 コンパイラとプログラミング言語24

ソースプログラム

目的プログラム

コンパイラ

字句解析

構文解析

意味解析

最適化コード生成

トークン

中間情報(中間語、名前表)

字句解析の流れ

2014/6/25 コンパイラとプログラミング言語25

ソースプログラム

字句解析

構文解析

文字(列)読み取り

字句読み取り

トークン

• 人間から見るとプログラム• コンピュータから見ると文字列

ただの文字列扱い

意味を持つ要素に分割

分割されたトークンの集まりが文法として間違っていないかチェック(ここでBNFを使って定義された

文法と照らし合わせる)

• 正規表現• 有限オートマトン

構文解析

2014/6/25 コンパイラとプログラミング言語26

ソースプログラム

目的プログラム

コンパイラ

字句解析

構文解析

意味解析

最適化コード生成

トークン

中間情報(中間語、名前表)

構文解析

• 字句解析が出力したトークンを読み込みながら、そのトークンの列がプログラム言語の文法で許されているパターンと合うかを解析する

2014/6/25 コンパイラとプログラミング言語27

ソースプログラム

字句解析 構文解析トークン

構文木

名前表

• 許されているパターンであれば、トークンの列は構文規則に従って構成され、字句を葉とする構文木が生成される。

• 構文解析の結果、ソースプログラムの構造は構文木として出力され、名前や数字などの情報は名前表に出力される。

構文解析法

2014/6/25 コンパイラとプログラミング言語28

上向き構文解析法(bottom-up parser)

下向き構文解析法(top-down parser)

入力した記号列が構文規則の右辺と一致した場合に左辺の記号に置き換えていく

記号列として次に何が来るのかを仮定しながら構文解析を進めていく a * ( )b + c

名前 名前

因子

名前

因子

因子

因子

式上向き構文解析法

下向き構文解析法

LL構文解析

2014/6/25 コンパイラとプログラミング言語29

入力された記号列の先頭(最も左)の非終端記号に適用すべき生成規則を、それ以降の記号列を調べることによって一意に決定することが可能となるようにする

LL:Left to right scanLeft most derivation

LL(k)構文解析

先読み記号を最大k個まで許すことを意味する

k=1 のとき、バックトラックのない下向き構文解析が可能。そのとき、LL(1)構文解析を可能にする文法をLL(1)文法という

構文木の表現方法:二分木と四つ組

• 二分木

– 二項演算子から成る式や代入文で利用される

– 演算子というノード(節点)が2つの子を持つ。

– 2つの子がオペランド

– 変数や定数は子を持たない(葉ノード)

• 四つ組

– 演算子、2つのオペランド、演算結果の保管先アドレス、という4つのデータを1つの組として保管する方法

– 二分木に比べ、メモリ消費が少ない

2014/6/25 コンパイラとプログラミング言語30

=

a +

b c

+ b c tmp#1

= tmp#1 a

名前表

• ソースプログラム中のデータ宣言部より、ユーザが名前を付けた変数や配列、関数についての情報がまとめられた表

• 書かれる情報例

– 形式:変数、配列、関数、ユーザ定義型など

– 型:整数、実数、文字列、ポインタなど

– 語長:1バイト、4バイト、8バイト

– 種類:グローバル、仮引数

– メモリ上の番地

– …

2014/6/25 コンパイラとプログラミング言語31

エントリ番号 名前 データ型 番地 領域長

1 a int 12 4

2 b int 16 4

3 c int 20 4

4 d int 24 4

5 $wk1 int 28 4

6 $wk2 int 32 4

コンパイラの論理的な構成

2014/6/25 コンパイラとプログラミング言語32

ソースプログラム

目的プログラム

コンパイラ

字句解析

構文解析

意味解析

最適化コード生成

中間情報(中間語、名前表)

• 名前表を利用したエラーチェック

– ある識別子がプログラム中に現れたときに、構文的には正しくても、意味が不明になるケースが現れたらエラーを返す

コンパイラの論理的な構成

2014/6/25 コンパイラとプログラミング言語33

ソースプログラム

目的プログラム

コンパイラ

字句解析

構文解析

意味解析

最適化コード生成

中間情報(中間語、名前表)

目的プログラムを出力する、コンパイラの最終フェーズ

コンピュータが読むため、目的プログラムの形式はコンピュータの種類などの環境に強く依存

本講義ではJavaをターゲットにする

Javaの仕組み

• コンパイラの種類とバイトコードの実行

– Javaコンパイラ→ インタプリタ

– 動的コンパイラ(Just-In-Timeコンパイラ)→実行

– ネイティブコンパイラ→ 実行

2014/6/25 コンパイラとプログラミング言語34

ソースコードバイトコード(クラスファイル)

コンパイラ

字句解析

構文解析

意味解析

コード生成

中間情報(中間語、名前表)