RISC-Vには64bitの乗算など、通常の計算の範囲では追いつけないような演算も含まれている。 例えば、MUL命令やMULHSU命令などは、64bitのデータ同士の演算であり、MULHは演算結果の128bitのうち上位の64bitを返してきたりする。
これらの演算をサポートするためには、GNUの多倍長演算ライブラリを利用すればよい。自作ISSでは、GMPを使っている。
GMPを用いることで64bit同士の乗算でも、問題なく実行できるようになっているのだが、いくつかのパタンでうまく実行できない状態が続いていた。
よい機会なので、解析していこう。
GMPにおける符号付整数の演算の考え方
例えば、MUL命令は64bitデータ同士の乗算を行い、結果の下位64bitをレジスタに格納する。
reg[dst] = lower64bit(sign(reg[op1]) * sign(reg[op2]))
この場合、最初の実装としては、GMPで普通に符号付で乗算し、その結果をget_si()で取得していた。
rs2_mpz.set_str(rs2_str.str(), 10);
mpz_class res_mpz = (rs1_mpz * rs2_mpz);
DWord_t res = res_mpz.get_si ();
この場合、MUL命令の仕様としては、乗算の結果得られた128bit数の符号は特に関係なく、下位の64bitを切り取る。なので、get_si()をしてから64bitに縮小する(符号を保持したまま)とはちょっと意味が違う。
- get_siを使って符号付64bit数にシュリンクする場合
たとえば演算結果が 0xffff0000_00000000_00000000_00000001 だった場合、符号を残したいので、64bitに変換するとき(値はどうなるかに関わらず) 0x80000000_00000001 に近いものになる(符号は残る)。
- get_uiを使って符号なし64bit数にシュリンクする場合
ところがMUL命令としては、純粋に64bitをシュリンクして残したいので、演算結果が負の数だった場合、とりあえず符号なし整数として取得し、ビットを反転させる。
bool is_result_minus = ((rs1_val < 0) && (rs2_val > 0)) || ((rs1_val > 0) && (rs2_val < 0)); DWord_t res = res_mpz.get_ui (); if (is_result_minus) { res = ~res + 1; } m_env->GRegWrite (rd_addr, res);
これはMULHSU命令でも基本的には変わらない。MULHSU命令は符号付64bitデータと符号なし64bitデータを乗算し、上位の64bitの結果をレジスタに格納する。
DWord_t res = res_mpz2.get_ui ();
if (is_result_minus) {
res = ~res;
}
m_env->GRegWrite (rd_addr, res);
という訳で、無事に64bit乗算の演算命令もパスするようになった。
96% tests passed, 19 tests failed out of 445 Total Test time (real) = 68.28 sec The following tests FAILED: 8 - rv32mi-p-timer (Failed) 9 - rv32si-p-csr (Failed) 47 - rv32ui-pm-lrsc (Failed) 126 - rv64mi-p-dirty (Failed) 130 - rv64mi-p-mcsr (Failed) 134 - rv64mi-p-timer (Failed) 136 - rv64si-p-csr (Failed) 154 - rv64uf-p-recoding (Failed) 167 - rv64uf-pt-recoding (Failed) 180 - rv64uf-v-recoding (Failed) 191 - rv64ui-p-amomaxu_d (Failed) 200 - rv64ui-p-amoswap_d (Failed) 230 - rv64ui-pm-lrsc (Failed) 274 - rv64ui-pt-amomaxu_d (Failed) 283 - rv64ui-pt-amoswap_d (Failed) 360 - rv64ui-v-amomaxu_d (Failed) 369 - rv64ui-v-amoswap_d (Failed) 436 - median (Failed) 443 - spmv (Failed) Errors while running CTest