コンパイラ
気持ち入門
2017年12月11日
いらいざ@Eliza_0x
牧野研の民
です
牧野研はいいぞ
牧野研はいいぞ
自己紹介
ところで電車の乗り換えって
難しくないですか?
・大阪工業大学IC科一回生です
・コンパイラとかCPUとか作っています
・今日電車の乗り換えに三回失敗したつらい
Twitter: @Eliza_0x
私をフォローすると進捗が約束されます
チヤホヤされて〜
・がんばって髪をそめようとした
・今日がんばってワックスをつけてきた
チヤホヤされて〜
・がんばって髪をそめようとした
・剛毛で染まらなかった
・今日がんばってワックスをつけてきた
・風呂に三日入ってない人みたいな髪型になってしまった…
最近作ったもの
自作プログラミング言語 tmpla
main := fact 5;
fact :: Int -> Int
fact n := let
iszero n := n = 0;
in if iszero n
then 1
else n * fact (n - 1);
自作CPU: mins
隊長「ところで君から技術
とったらなにが残るん?」
全然気にしてません
フォローしてね
Twitter@Eliza_0x
本題
よくある会話
「プログラムバグったっす…」
「コンパイラの気持ちになって考えればいいよ」
コンパイラ
の気持ちに
なって
考えれば
いいよ
ほんまか
といっても、訓練されていない人間に
コンパイラの気持ちはわからない
問題点
・バリアフリーについて考えるとき
・車いす体験
・聾者体験
解決策
・バリアフリーについて考えるとき
・車いす体験
・聾者体験
解決策
・コンパイラを体験してみると良いのでは?
コンパイラ
を体験して
みると良い
のでは?
やってみよう
#include <stdio.h>
int main() {
int fact = 1;
int n = 1;
while (n <= 5) {
fact = fact * n;
n = n - 1;
}
}
・以下のコードを手動コンパイルすることを目標にします
・階乗です
コンパイル
・コンパイラの仕事
・ソースコードをコンパイルして機械語を出力
機械語
を出力
機械語?
・CPUに出すことができる命令
・プログラムはコンパイル後これに変換される
0000000 457f 464c 0102 0001 0000 0000 0000 0000
0000010 0002 003e 0001 0000 03e0 0040 0000 0000
0000020 0040 0000 0000 0000 19a8 0000 0000 0000
0000030 0000 0000 0040 0038 0009 0040 001f 001c
0000040 0006 0000 0005 0000 0040 0000 0000 0000
0000050 0040 0040 0000 0000 0040 0040 0000 0000
0000060 01f8 0000 0000 0000 01f8 0000 0000 0000
0000070 0008 0000 0000 0000 0003 0000 0004 0000
0000080 0238 0000 0000 0000 0238 0040 0000 0000
0000090 0238 0040 0000 0000 001c 0000 0000 0000
アセンブリ言語
・機械語は読めない
・そのまま人間が読めるように変換したもの
.L3:
movl -8(%rbp), %eax
imull -4(%rbp), %eax
movl %eax, -8(%rbp)
subl $1, -4(%rbp)
.L2:
cmpl $0, -4(%rbp)
jg .L3
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
アセンブリ言語
難しそう?
だいたいC言語の
ノリで読めます
レジスタ
・変数はプログラム中に無限に定義できる
・CPUの中には変数に対応するレジスタが複数個ある
・レジスタ0番は常に0であることを要求される
0
1
3
10
0
1
0
レジスタ0番
レジスタ1番
レジスタ
変数のようなもの
として理解してください
加算命令: ADD
ADD $1 $2 $3
0
3
3
10
5
1
0
0
13
3
5
1
0
10
加算命令: MUL
MUL $4 $5 $6
0
13
3
10
5
5
3
0
13
3
15
5
3
10
簡単ですね
演算命令
先の命令2つは、レジスタとレジスタを演算する命令
a = b + 5
c = 10
c = c - 10
のような演算をしたいときに困る
即値加算命令: ADDI
ADDI $1 $2 5
0
13
3
5
5
1
0
0
8
3
5
1
0
5
代入: $1 = 10
ADDI $1 $0 10
0
13
3
5
5
1
0
0
10
3
5
1
0
5
即値減算命令: SUBI
SUBI $3 $0 10
0
13
3
5
5
1
0
0
6
3
5
1
0
-5
簡単ですね
メモリは配列のようなもの
MEMORY
addi $1 $0 5
sub $1 $4 $1
addi $4 $5 5
jump 1
addi $1 $1 1
beq $3 $0 5
INSTRUCTION
プログラムカウンタ
・CPUはメモリにアクセスするための変数をもっている
・PC: プログラム・カウンタと呼ばれる
メモリは配列のようなもの
MEMORY
addi $1 $0 5
sub $1 $4 $1
addi $4 $5 5
jump 1
addi $1 $1 1
beq $3 $0 5
PC: 0
メモリは配列のようなもの
MEMORY
addi $1 $0 5
sub $1 $4 $1
addi $4 $5 5
jump 1
addi $1 $1 1
beq $3 $0 5
PC: 1
メモリは配列のようなもの
MEMORY
addi $1 $0 5
sub $1 $4 $1
addi $4 $5 5
jump 1
addi $1 $1 1
beq $3 $0 5
PC: 2
メモリは配列のようなもの
MEMORY
addi $1 $0 5
sub $1 $4 $1
addi $4 $5 5
jump 1
addi $1 $1 1
beq $3 $0 5
PC: 3
ジャンプ命令
・なんども同じことをかくのはめんどくさい
・ならばPCをいじってしまえば良い
・C言語のgotoと対応
jump
MEMORY
addi $1 $0 5
sub $1 $4 $1
addi $4 $5 5
jump 1
addi $1 $1 1
beq $3 $0 5
PC: 3→1
ジャンプ命令
・ただジャンプするだけでは芸がない
・beq命令は比較して、同じであればジャンプ
beq $1 $2 10
・上のコードだと、レジスタ1と2が一緒であれば
・PCを10に書き換え
・10番地にジャンプ
条件分岐
MEMORY
addi $1 $0 5
sub $1 $4 $1
addi $4 $5 5
beq $1 $2 5
addi $1 $1 1
beq $3 $0 5
if($1==$2){
PC = 5
}
0
5
5
7
ここにジャンプする
終了命令
・計算が終わったプログラムは停止させたい
halt
終了
ためしに今紹介した命令で1..10までの和を計算するプログラムを書いてみましょう
1..10の総和
int a = 1;
int sum = 0;
int border = 11;
while (a != border) {
sum = sum + a;
a++;
}
return 0;
・Cのプログラム例
1..10の総和
0: addi $1 $0 1
1: addi $2 $0 0
2: addi $3 $0 11
3: beq $1 $3 7
4: add $2 $2 $1
5: addi $1 $1 1
6: jump 3
7: halt
・アセンブリでの解答例
1..10の総和
0: addi $1 $0 1
1: addi $2 $0 0
2: addi $3 $0 11
3: beq $1 $3 7
4: add $2 $2 $1
5: addi $1 $1 1
6: jump 3
7: halt
int a = 1;
int sum = 0;
int border = 11;
while (a != border) {
sum = sum + a;
a++;
}
return 0;
・すなおにそのまま翻訳するだけ!
変数の初期化
0: addi $1 $0 1
1: addi $2 $0 0
2: addi $3 $0 11
int a = 1;
int sum = 0;
int border = 11;
・aを$1
・sumを$2,
・borderを$3に割り当てる
・addiで初期化するだけ
ループ
3: beq $1 $3 7
...
6: jump 3
while (a != border) {
...
}
・a != border であればループ継続なので、同じであればループ脱出
・PC: 6まで進むとループはじめまで復帰
ループ内部
4: add $2 $2 $1
5: addi $1 $1 1
sum = sum + a;
a++;
・これはやるだけ
もういちど
0: addi $1 $0 1
1: addi $2 $0 0
2: addi $3 $0 11
3: beq $1 $3 7
4: add $2 $2 $1
5: addi $1 $1 1
6: jump 3
7: halt
int a = 1;
int sum = 0;
int border = 11;
while (a != border) {
sum = sum + a;
a++;
}
return 0;
やるだけやん
実際のアセンブリをみてみる
gcc -O0 -S -o fact.s fact.c
コンパイルしてアセンブリを吐かせてみる
cmpl $0, -4(%rbp)
jg .L3
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
一部抜粋(fact.s)
よめるわ
おわりに
int a = 5;
int fact = 1;
while (a > 0) {
fact = fact * a;
a = a - 1;
}
return 0;
暇なときちょっと考えてみてください
今日からお前がコンパイラだ
・アセンブリは意外とシンプル
・コンパイラの気持ちわからんでもない
・一緒にコンパイラつくりませんか?
deck
By eliza0x
deck
- 634