LoginSignup
16
11

More than 5 years have passed since last update.

RV32I インストラクション・セット

Posted at

はじめに

RISC-V の最も基本となるインストラクション・セット・アーキテクチャ(ISA)モジュールである RV32I: RISC-V 基本整数ISA について紹介します。

アセンブリコードの種類

GNU のアセンブラである gas の場合,アセンブリコードは大きく次の3つに分かれます。

種類 説明
インストラクション li a5, i プログラムコードを変換したCPUに対する命令です。だいたいはアセンブリコードと1対1に対応した機械語命令に変換されますが,中には同じ意味を表す別の機械語列に変換することもあります。ISAが異なると,異なるアセンブリコードで表されます。
ラベル func: アドレスに名前をつけたシンボルを定義します。制御命令やロードアドレス命令などと組み合わせます。英数字で表された名前の末尾にコロン(:)をつけます。
ディレクティブ .file "sample.c" アセンブラに対する特殊な指示を表します。ピリオド(.)の後に英単語が続きます。

RISC-V のインストラクションの読み方

例えば次のように書かれていたとします。

{\rm \underline{s}et} \, {\rm \underline{l}ess} \, {\rm \underline{t}han} \left\{
\begin{array}{l}
\_ \\
{\rm \underline{i}mmediate}
\end{array}
\right\}
\left\{
\begin{array}{l}
\_ \\
{\rm \underline{u}nsigned}
\end{array}
\right\}

この図式で表されるRV32Iの命令は,slt, slti, sltu, sltiu の4つです。

読み方としては,次の通りです。

  • アンダーラインの引かれている文字をつなぎ合わせて命令を作ります。
  • 「{ }」で囲まれている1組の項目は,その中の各行のどれかを選んだ文字となります。
  • アンダースコア( _ )は空文字,すなわち文字がないことを意味します。

RV32I のインストラクション・セット (一部)

RV32I のインストラクション・セットは比較的単純ですが,それでも結構たくさんの命令が存在します。初学者の学習のしやすさを考慮し,厳選してみました。慣れてきたら,段階的に増やしていくと良いでしょう。

算術演算命令

加算命令

{\rm \underline{add}} \left\{
\begin{array}{l}
\_ \\
{\rm \underline{i}mmediate}
\end{array}
\right\}

add rd, rs1, rs2 は,レジスタrs1とレジスタrs2の値を加えて,結果をレジスタrdに書き込みます。算術オーバーフロー,すなわち加算の結果,レジスタの桁があふれた場合は無視します。

immediate は即値,すなわちレジスタではなく数値を与えることを意味します。

addi rd, rs1, immediate は,レジスタrs1の値と,即値immediateを符号付きだと解釈して加えて(このようなことを「レジスタrs1の値にimmediate符号拡張して加える」と言います),結果をレジスタ rd に書き込みます。算術オーバーフローは無視します。

減算命令

{\rm \underline{sub}tract}

sub rd, rs1, rs2 は,レジスタrs1の値からレジスタrs2の値を引いて,結果をレジスタrdに書き込みます。算術オーバーフローは無視します。

subi rd, rs1, immediate存在しません。代わりに,addi命令を使ってimmediateの符号を反転させます。つまり,addi x10, x5, -5 とすると,レジスタx5から5を引いた結果をレジスタx10に書き込みます。

転送命令

ロード命令

{\rm \underline{l}oad}
\left\{
\begin{array}{l}
{\rm \underline{b}yte} \\
{\rm \underline{h}alfword}
\end{array}
\right\}
\left\{
\begin{array}{l}
\_ \\
{\rm \underline{u}nsigned }
\end{array}
\right\}\\
{\rm \underline{l}oad}\,{\rm \underline{w}ord}

ロード命令は,指定したメモリから値を読み取って結果にレジスタrdに書き込む命令です。

メモリのアドレスの指定の方法(アドレッシング・モード(addressing mode)と呼びます)は,レジスタrs1にオフセットoffsetの値を符号付きだと解釈して(符号拡張)加えた値をアドレスとします。このような指定のしかたをベース・オフセットもしくはレジスタ・オフセットと呼びます。

lb rd, offset(rs1) は,レジスタrs1で表される値と符号拡張されたoffsetの値を加えた値をメモリアドレスとして解釈して1バイトロード(読み込み)し,結果を符号拡張してレジスタrdに書き込みます。

たとえば,レジスタrs1の値が 0x0100,offsetの値が 0x40 だったときには 0x140 番地のメモリを1バイト読み,その値が0xffだったとすると,レジスタrdには -1 すなわち 0xffffffff を書き込みます。

lbu rd, offset(rs1) は,レジスタrs1で表される値と符号拡張されたoffsetの値を加えた値をメモリアドレスとして解釈して1バイトロードし,結果をゼロ拡張してレジスタrdに書き込みます。

たとえば,レジスタrs1の値が 0x0100,offsetの値が -1 すなわち 0xffffffff だったときには 0xff 番地のメモリを1バイト読み,その値が0xffだったとすると,レジスタrdには 255 すなわち 0x000000ff を書き込みます。

lblbuの違いは,'0x80から0xffの間の値(すなわち最上位ビットが1の場合)をレジスタrd`に書き込む場合に,値が符号付きになるか符号無しになるかです。符号付きの場合は符号拡張(上位ビットを最上位ビットの値にする)を行い,符号無しの場合はゼロ拡張(上位ビットを0にする)を行います。

lh rd, offset(rs1) は,レジスタrs1で表される値と符号拡張されたoffsetの値を加えた値をメモリアドレスとして解釈して2バイトロードし,結果を符号拡張してレジスタrdに書き込みます。

たとえば,レジスタrs1の値が 0x0100,offsetの値が 0x40 だったときには 0x140 番地のメモリを1バイト読み,その値が0xffffだったとすると,レジスタrdには -1 すなわち 0xffffffff を書き込みます。

lhu rd, offset(rs1) は,レジスタrs1で表される値と符号拡張されたoffsetの値を加えた値をメモリアドレスとして解釈して2バイトロードし,結果をゼロ拡張してレジスタrdに書き込みます。

たとえば,レジスタrs1の値が 0x0100,offsetの値が -1 すなわち 0xffffffff だったときには 0xffff 番地のメモリを1バイト読み,その値が0xffだったとすると,レジスタrdには 65535 すなわち 0x0000ffff を書き込みます。

lhlhuの違いは,'0x8000から0xffffの間の値(すなわち最上位ビットが1の場合)をレジスタrd`に書き込む場合に,値が符号付きになるか符号無しになるかです。符号付きの場合は符号拡張(上位ビットを最上位ビットの値にする)を行い,符号無しの場合はゼロ拡張(上位ビットを0にする)を行います。

lw rd, offset(rs1) は,レジスタrs1で表される値と符号拡張されたoffsetの値を加えた値をメモリアドレスとして解釈して4バイトロードし,結果をレジスタrdに書き込みます。

たとえば,レジスタrs1の値が 0x0100,offsetの値が 0x40 だったときには 0x140 番地のメモリを1バイト読み,その値が0xffffffffだったとすると,レジスタrdには -1 すなわち 0xffffffff を書き込みます。

ストア命令

{\rm \underline{s}tore}
\left\{
\begin{array}{l}
{\rm \underline{b}yte} \\
{\rm \underline{h}alfword} \\
{\rm \underline{w}ord}
\end{array}
\right\}

ストア命令は,レジスタrdの値を指定したメモリに書き込みます。メモリのアドレッシング・モードは,ロード命令と同様,レジスタrsを用いたベース・オフセットです。

sb rs2, offset(rs1) は,レジスタrs2の値の下位8ビットを,レジスタrs1で表される値と符号拡張されたoffsetの値を加えた値をメモリアドレスとして解釈してメモリにストア(書き込み)します。

たとえば,レジスタrs2の値が 0xffffffff だった場合には,下位8ビットの値である 0xffを,レジスタrs1の値が 0x0100,offsetの値が 0x40 だったときには 0x140 番地のメモリに書き込みます。

sh rs2, offset(rs1) は,レジスタrs2の値の下位16ビットを,レジスタrs1で表される値と符号拡張されたoffsetの値を加えた値をメモリアドレスとして解釈してメモリにストアします。

たとえば,レジスタrs2の値が 0xffffffff だった場合には,下位16ビットの値である 0xffffを,レジスタrs1の値が 0x0100,offsetの値が 0x40 だったときには 0x140 番地のメモリに書き込みます。

sw rs2, offset(rs1) は,レジスタrs2の値を,レジスタrs1で表される値と符号拡張されたoffsetの値を加えた値をメモリアドレスとして解釈してメモリにストアします。

たとえば,レジスタrs2の値 0xffffffffを ,レジスタrs1の値が 0x0100,offsetの値が 0x40 だったときには 0x140 番地のメモリに書き込みます。

ストア命令では,sbu, shu 命令は存在しません。

移動命令 (擬似命令)

{\rm \underline{m}o\underline{v}e}

mv rd, rs1 は,レジスタrs1の値をレジスタrdにコピーします。

実際には,addi rd, rs1, 0 に展開されます。

即値ロード命令 (擬似命令)

{\rm \underline{l}oad \, \underline{i}mmediate}

li rd, immediate は,即値immediateをレジスタrdに書き込みます。

実際の機械語命令への展開のしかたは複雑で,即値の具体的な値によって,どのような命令に展開するかが変わってきます。

ロードアドレス命令 (擬似命令)

{\rm \underline{l}oad \, \underline{a}ddress}

la rd, symbol は,シンボルsymbolで表されるアドレス値をレジスタrdに書き込みます。

実際の機械語命令への展開のしかたは複雑で,ディレクティブの設定によって変わってきます。

制御命令

ジャンプ命令 (擬似命令)

{\rm \underline{j}ump}

j offset は,現在のプログラムカウンタ pc に,符号拡張された offset を加えて,pc に設定します。

offset がシンボル symbol で表される場合は,pc にシンボル値を設定します。

実際には jal x0, offset に展開されます。

ジャンプ・アンド・リンク命令

{\rm \underline{j}ump}\,{\rm \underline{a}nd}\,{\rm \underline{l}ink}
\left\{
\begin{array}{l}
\_ \\
{\rm \underline{r}esister}
\end{array}
\right\}

jal rd, offset は,次の命令のアドレス (pc + 4) をレジスタ rd に書き込み,それから現在の pc に符号拡張された offset を加えて,pc に設定します。

jalr rd, offset(rs1) は,まず,次の命令のアドレスpc+4を一時保存します。レジスタrs1に符号拡張されたoffsetを加えて,算出されたアドレスの最下位ビットをマスクする,つまり最下位ビットは強制的に0として pc に設定します。最後に,一時保存したpc+4の値をレジスタrdに書き込みます。

どちらも,rdを省略した場合には,x1を想定します。

分岐命令(ブランチ命令) (一部,擬似命令)

{\rm \underline{b}ranch}
\left\{
\begin{array}{l}
{\rm \underline{eq}ual} \\
{\rm \underline{n}ot \, \underline{e}qual}
\end{array}
\right\}
\left\{
\begin{array}{l}
\_ \\
{\rm \underline{z}ero}
\end{array}
\right\}
\\
{\rm \underline{b}ranch}
\left\{
\begin{array}{l}
{\rm \underline{g}reater \, than \, or \, \underline{e}qual} \\
{\rm \underline{g}reater \, \underline{t}han} \\
{\rm \underline{l}ess \, than \, or \, \underline{e}qual} \\
{\rm \underline{l}ess \, \underline{t}han}
\end{array}
\right\}
\left\{
\begin{array}{l}
\_ \\
{\rm \underline{u}nsigned} \\
{\rm \underline{z}ero}
\end{array}
\right\}

beq rs1, rs2, offset は,レジスタrs1がレジスタrs2に等しければ,現在のpcに符号拡張された offset を加えて,pc に設定します。

beqz rs1, offset は,レジスタrs1が0に等しければ,現在のpcに符号拡張された offset を加えて,pcに設定します。擬似命令で,実際には beq rs1, x0, offset に展開されます。

bne rs1, rs2, offset は,レジスタrs1がレジスタrs2に等しくなければ,現在のpcに符号拡張された offset を加えて,pc に設定します。

bnez rs1, offset は,レジスタrs1が0に等しくなければ,現在のpcに符号拡張された offset を加えて,pcに設定します。擬似命令で,実際には beq rs1, x0, offset に展開されます。

bge rs1, rs2, offset は,値を符号付きだと解釈して,レジスタrs1の値がレジスタrs2の値以上であれば,現在のpcに符号拡張された offsetを加えて,pcに設定します。

bgeu rs1, rs2, offset は,値を符号なしだと解釈して,レジスタrs1の値がレジスタrs2の値以上であれば,現在のpcに符号拡張された offsetを加えて,pcに設定します。

bgez rs1, offset は,値を符号付きだと解釈して,レジスタrs1の値が0以上であれば,現在のpcに符号拡張された offsetを加えて,pcに設定します。擬似命令で,実際には bge rs1, x0, offset に展開されます。

bgeuz 命令はありません。値を符号なしだと解釈した時には,レジスタの値は必ず0以上になるので,値を比較する意味がありません。

bgt rs1, rs2, offset は,値を符号付きだと解釈して,レジスタrs1の値がレジスタrs2の値より大きければ,現在のpcに符号拡張された offsetを加えて,pcに設定します。擬似命令で,実際には blt rs2, rs1, offset に展開されます。

bgtu rs1, rs2, offset は,値を符号なしだと解釈して,レジスタrs1の値がレジスタrs2の値より大きければ,現在のpcに符号拡張された offsetを加えて,pcに設定します。擬似命令で,実際には bltu rs2, rs1, offset に展開されます。

bgtz rs2, offset は,値を符号付きだと解釈して,レジスタrs2の値が0より大きければ,現在のpcに符号拡張された offsetを加えて,pcに設定します。擬似命令で,実際には blt x0, rs2, offset に展開されます。

bgtuz 命令はありません。レジスタが0より大きいかどうかで分岐するので,値を符号なしだと解釈した時には,0と等しいかどうかの条件分岐と同じ意味になるからです。

ble rs1, rs2, offset は,値を符号付きだと解釈して,レジスタrs1の値がレジスタrs2の値以下であれば,現在のpcに符号拡張された offsetを加えて,pcに設定します。擬似命令で,実際には bge rs2, rs1, offset に展開されます。

bleu rs1, rs2, offset は,値を符号なしだと解釈して,レジスタrs1の値がレジスタrs2の値以下であれば,現在のpcに符号拡張された offsetを加えて,pcに設定します。擬似命令で,実際には bgeu rs2, rs1, offset に展開されます。

blez rs2, offset は,値を符号付きだと解釈して,レジスタrs2の値が0以下であれば,現在のpcに符号拡張された offsetを加えて,pcに設定します。擬似命令で,実際には bge x0, rs2, offset に展開されます。

bleuz 命令はありません。レジスタが0以下かどうかで分岐するので,値を符号なしだと解釈した時には,0と等しいかどうかの条件分岐と同じ意味になるからです。

blt rs1, rs2, offset は,値を符号付きだと解釈して,レジスタrs1の値がレジスタrs2の値未満であれば,現在のpcに符号拡張された offsetを加えて,pcに設定します。

bltu rs1, rs2, offset は,値を符号なしだと解釈して,レジスタrs1の値がレジスタrs2の値未満であれば,現在のpcに符号拡張された offsetを加えて,pcに設定します。

bltz rs1, offset は,値を符号付きだと解釈して,レジスタrs1の値が0未満であれば,現在のpcに符号拡張された offsetを加えて,pcに設定します。擬似命令で,実際には bge rs1, x0, offset に展開されます。

bltuz 命令はありません。値を符号なしだと解釈した時には,レジスタの値は必ず0以上になるので,値を比較する意味がありません。

ディレクティブについて

さしあたり,無視して構いません。

詳しく知りたくなったら,gas のマニュアルなどを参照してください。

16
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
11