FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RISC-Vのデコードエンジンrv8がC拡張を誤ってデコードしてしまう問題の解析

rv8というのはRISC-VのデコーダやJITエンジンなどをまとめたパッケージで、Sniperサイクル精度シミュレータはrv8をデコードエンジンとして使用している。 Sniperでのサイクル計算の中で、どうも16ビットのC拡張をうまくデコードすることができていないように見えたのでその解析をした。

  • sniper/decoder_lib/riscv_decoder.cc
void RISCVDecoder::decode(DecodedInst * inst)
/* ... 途中省略 ... */
  riscv::decode_inst_rv64(dec, r_inst);
  decode_inst_type(dec, r_inst);
  decode_pseudo_inst(dec);

decode_inst_rv64で、RV64モードでの命令のデコードを行う。decompress_inst_rv64で、C拡張の16ビット命令を32ビットの命令に変換する。

 template <typename T>
    inline void decode_inst_rv64(T &dec, inst_t inst)
    {
        decode_inst<T,false,true,false>(dec, inst);
        decompress_inst_rv64<T>(dec);
    }

decode_inst_type() によって、命令のデコードを行う。

  • rv8/src/asm/switch.h
template <typename T>
inline void decode_inst_type(T &dec, riscv::inst_t inst)
{
    dec.codec = rv_inst_codec[dec.op];
    switch (dec.codec) {
        case rv_codec_none:             riscv::decode_none(dec, inst);                     break;
        case rv_codec_u:                riscv::decode_u(dec, inst);                        break;
        case rv_codec_uj:               riscv::decode_uj(dec, inst);                       break;
        case rv_codec_i:                riscv::decode_i(dec, inst);                        break;
        case rv_codec_i_sh5:            riscv::decode_i_sh5(dec, inst);                    break;
        case rv_codec_i_sh6:            riscv::decode_i_sh6(dec, inst);                    break;
        case rv_codec_i_sh7:            riscv::decode_i_sh7(dec, inst);                    break;
        case rv_codec_i_csr:            riscv::decode_i_csr(dec, inst);                    break;
        case rv_codec_s:                riscv::decode_s(dec, inst);                        break;
        case rv_codec_sb:               riscv::decode_sb(dec, inst);                       break;
        case rv_codec_r:                riscv::decode_r(dec, inst);                        break;
        case rv_codec_r_m:              riscv::decode_r_m(dec, inst);                      break;
  • rv8/src/asm/decode.h
/* Decode I */
template <typename T> inline void decode_i(T &dec, inst_t inst)
{
    dec.rd = operand_rd::decode(inst);
    dec.rs1 = operand_rs1::decode(inst);
    dec.rs2 = rv_ireg_zero;
    dec.imm = operand_imm12::decode(inst);
}

問題はこの時、C拡張の機械語のままこのデコードを適用してしまうので、例えばdec.immの値はそのまま再利用することができない。 これによって、さらに最適化の機能が働いてしまい、命令が変換されてしまう。例えばc.addiでオペランドが誤って0に認識されてしまい、mv命令に変換されてしまう。

  • rv8/src/asm/meta.cc
const rv_comp_data rvcd_rv64_addi[] = {
    { rv_op_c_addi4spn, rvcc_c_addi4spn },
    { rv_op_c_nop, rvcc_c_nop },
    { rv_op_c_addi, rvcc_c_addi },
    { rv_op_c_li, rvcc_c_li },
    { rv_op_c_addi16sp, rvcc_c_addi16sp },
    { rv_op_c_mv, rvcc_c_mv },
    { rv_op_illegal, nullptr }
};

したがって、とりあえずC拡張がうまくデコードされない問題を解決したい場合は、デコード以下の最適化を省略すればいい。

  riscv::decode_inst_rv64(dec, r_inst);
  // decode_inst_type(dec, r_inst);
  // decode_pseudo_inst(dec);