1秒に30億回の同じ手順
会社で配られるノートパソコンのプロセッサは、1秒に30億回ほどの手順を進めます。その1回1回は、どれも同じ小さな手順のくり返しです。
次の命令を読み、何をするのかを調べ、それを行い、次へ進む。1つの命令でこの手順を追えるようになれば、どんなプログラムでも追えます。
このレッスンのゴール
メモ
1つの命令について、フェッチ、デコード、実行の流れを追うことができる。
キーワード
| ことば | いみ | れい |
|---|---|---|
| プログラムカウンタ | 次の命令の番地を持つレジスタ | pc |
| 命令レジスタ | 取り出したばかりの命令を置いておく場所 | |
| フェッチ | 次の命令をメモリから読むこと | |
| デコード | 命令をフィールドに分けて、意味を調べること | |
| 実行 | 命令が求める仕事を行うこと | |
| オペコード | 命令の種類を表すフィールド |
学生が入力するセルは、このプレビューでは空白です。
同じ手順をくり返す
プログラムカウンタ pc は、次の命令の番地を持っています。フェッチでは、その番地の語を命令レジスタへ読み、pc に4を足します。
デコードでは、語をフィールドに分けます。命令の種類はどれか、どのレジスタを使うか、を調べます。実行では仕事を行い、答えをレジスタへ書き戻します。そして、またフェッチに戻ります。
アーキテクチャのフォルダへ移る
cd ~/archここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
ラボの add.s をアセンブルする
riscv64-linux-gnu-as add.s -o add.oここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
リンクする
riscv64-linux-gnu-ld add.o -o addここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
エミュレータのフェッチとデコードを見る
qemu-riscv64 -d in_asm ./addここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
エミュレータが取り出したもの
qemu はメモリから5つの語を取り出し、1つずつ解読しました。各行には、番地、16進数の語、解読した結果が並んでいます。
番地は 0x000100b0 から 0x000100c0 まで、4ずつ増えています。ここの命令はどれも4バイトだからです。li が消えていることにも注目しましょう。プロセッサが見るのは addi a0,zero,2 だけです。
デコードは語をフィールドに分ける
addi a0,zero,2 の語は 00200513 です。32ビットを5つのフィールドに分けます。下の7ビットがオペコードで、19 は「レジスタと即値の計算」を表します。
その上に、答えを入れる rd が5ビット、細かい種類が3ビット、入力の rs1 が5ビットあります。上の12ビットが即値です。a0 は x10、zero は x0 なので、この語は「x10 = x0 + 2」という意味です。
例: 00200513 のフィールド
| ビット | フィールド | 値 | 意味 |
|---|---|---|---|
| 31 から 20 | imm | 000000000010(2) | 即値の2 |
| 19 から 15 | rs1 | 00000(0) | 入力は x0、zero |
| 14 から 12 | funct3 | 000 | 足し算 |
| 11 から 7 | rd | 01010(10) | 答えは x10、a0 へ |
| 6 から 0 | opcode | 0010011(19) | レジスタと即値の計算 |
学生が入力するセルは、このプレビューでは空白です。
命令を32ビットで見る
python3 -c "print(format(0x00200513, '032b'))"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
opcode、rd、rs1、即値を取り出す
python3 -c "print(0x00200513 & 127, (0x00200513 >> 7) & 31, (0x00200513 >> 15) & 31, 0x00200513 >> 20)"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
実行のあと、pc は先へ進む
実行では、足し算、ロード、比較などの仕事を行います。ほとんどの命令は、pc をフェッチが進めた4つ先のままにします。条件が合った分岐だけは、pc に新しい番地を書きこみます。飛ぶとは、それだけのことです。
-d exec を付けると、qemu は実行した命令ごとに1行を出します。角かっこの中の2つ目の数が、RISC-V の pc です。
実行した命令ごとに1行
qemu-riscv64 -one-insn-per-tb -d exec,nochain ./addここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
「プログラムカウンタは、実行した命令の数を数えている」と考える人が多くいます。何も数えていません。持っているのは番地、つまり次の命令がある場所です。ループの中では、同じ番地に何度も戻ります。
このマシン自身のプロセッサを1歩ずつ
gdb は、ふつうのプログラムを止めて、1命令ずつ進められます。手で打つと、gdb は入力を待ち続けます。-batch とコマンドの並びを渡すと、それを実行して終わるので、Run で使えます。
次の Run は main で止まり、その命令を表示し、stepi で1命令進めて、次の命令を表示します。Arm のプロセッサなら番地は4進みます。x86 では命令の長さが1から15バイトなので、進み方が変わります。
ラボの loop プログラムを作る
gcc -O2 -o loop loop.cここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
main で止めて、1歩進める
gdb -batch -iex 'set debuginfod enabled off' -ex 'break main' -ex run -ex 'x/i $pc' -ex stepi -ex 'x/i $pc' ./loopここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
さらに挑戦
命令のレッスンで作った times が、何個の命令を実行するかを、実行する前に計算してください。ループの前に3個、1周で3個を5周、ループのあとに2個です。
それから qemu-riscv64 -one-insn-per-tb -d exec,nochain ./times 2>&1 | wc -l を自分で打って比べます。5回ずつ出てくる番地は、どれでしょうか。