前回は、まさかのトラブル勃発で、最低説明しておくべき項目を落としてしまいました。64bit かつ 単精度/倍精度の浮動小数点演算命令を備えたRISC-Vのレジスタセットと、レジスタ間転送についてです。今回はその補遺であります。ぶちゃけレジスタの図が1枚。
※「ぐだぐだ低レベルプログラミング」投稿順indexはこちら
前回の単精度浮動小数点加算の結果についての補遺
以下のような「簡単な」加算をやっていました。
1.000001 + 0.999999 = 2.000000 HEX: 40000000
1.000001に0.999999を足したら2.000000だろ~当たり前、と思われたかもしれませぬが、RISC-Vが採用している浮動小数点数の表現は歴史と伝統のIEEE754の2進数表現です。そうそう10進表現とピタリと一致する筈はありません。
上でちょっと引っ掛かるのは単精度浮動小数点加算 fadd.s の結果、10進表現 2.000000が、16進数にすると
0x40000000
というところです。
私のようなものがIEEE754の説明をするのは気が引けるのですが、冒頭のアイキャッチ画像に、単精度浮動小数点フォーマットを掲げました。
-
- S、符号ビット1ビット
- EXP、指数部8ビット
- FRACTION、仮数部23ビット
であります。上の例の0x40000000を2進数で上記に当てはめると
-
- S=0
- EXP=10000000
- FRACTION=オール0
となります。こんなんで 2.0 の意味になるのか?なります。
まず符号Sの0はプラスです(1ならマイナス。)
次に指数部(2のべき乗)は十進表現で128です。ところがここは127の下駄ばき表現というやつです。127のとき指数部は2の0乗つまり1となります。今回は128なので、指数部は2の1乗で2です。
そして仮数部ですが、23ビットのオール0の頭に「隠された 1.」があるのです。浮動小数点数なので、指数部を調整すれば仮数の頭を 1. で始まるようにすることは可能。これにより1ビット稼いでいるわけです。よってオール0は、すなわち 1.0 ということ。これに指数部の2を乗ずれば 2.0。ホントか?
64ビットRISC-Vの整数レジスタと単精度、倍精度浮動小数点レジスタ
これまた、以下の図などを描いてしまうのが恐れ多いのですが、描かないと後でアセンブラ関数を書いたときに説明しずらいので書きました。
※上の図で fmv.s fmv.d と2つ並べて書くべきところ、fmv.sだけになってます。すみません。
まず、レジスタのビット幅と本数です。
-
- 整数レジスタは64ビット幅、これが32本
- 浮動小数点数レジスタも64ビット幅、これが32本
シンプルで分かり易い?です。上の図のハコがレジスタを表しています。各ハコの外にx0とかf0とか書かれているのが、番号順のレジスタ名です。ハコの中に書かれている a0 とfa0 などが、ABIで決められている「用途」を反映したレジスタ名です。アセンブラ書くときはこちらを使用です。
さてレジスタに数値を入れる場合
-
- 整数レジスタは、LSB側をそろえて、バイト(8ビット)、ハーフワード(16ビット)、ワード(32ビット)、ダブルワード(64ビット)の数値を置く。レジスタ幅より値のビット幅が狭ければ符号拡張。
- 浮動小数点数レジスタに単精度浮動小数点数を置く場合、下の32ビットに置く。倍精度浮動小数の場合は64ビット全てを使う。
というルールです。
整数、浮動小数点数レジスタ内での転送には、以下の「疑似」命令をつかいます。
-
- 整数レジスタは mv
- 浮動小数点数レジスタは fmv.s または fmv.d
RISC-Vらしいところですが、mvもfmvも実はそんな命令は存在せず、他の命令をうまく使ってmv(MOVE)相当の仕事をやらせています。それで疑似命令。
前回は説明もせずに使ってしまいましたが、整数と浮動小数点数レジスタ間の転送には、ちゃんと命令が用意されています。オペコードにケチケチしているRISC-Vにしたら大盤ぶるまいの4命令も。
-
- fmv.w.x 整数レジスタから単精度浮動小数点数レジスタへ
- fmv.d.x 整数レジスタから倍精度浮動小数点数レジスタヘ
- fmv.x.w 単精度浮動小数点数レジスタから整数レジスタへ
- fmv.x.d 倍精度浮動小数点数レジスタから整数レジスタヘ
上記は、何も変換を加えない単純転送ですが、整数表現と浮動小数点表現の間の変換命令もあります。また当然、メモリとの間のロード、ストアもありです。それらはおいおいにやってみたいと思います。
前回記事を補足説明するだけで疲れました。次回はちゃんとアセンブラ命令を動かしますです。