自作ISSのバグでしばらくハマっていた。32ビットモードでxv6やCoremarkなどのベンチマークを動かすと、ちゃんと動作してくれず、トレースモードが正常出力されない。
しばらく追いかけていたのだが、32ビットモードと64ビットモードの混在環境での詰めの甘さに引っかかっていた。
現在のRISC-V ISSでは、実装を簡略化するため、32ビットモードと64bitモードの命令実装を1つの関数で済ませてしまっている。これはとても問題で、32ビットモードの時も、64bitの大きさでレジスタ読み書きと演算を行い、その結果を32ビットにカットしてトレースを表示している。
void InstEnv::RISCV_INST_LW (Word_t inst_hex) { RegAddr_t rd_addr = ExtractRDField (inst_hex); RegAddr_t rs1_addr = ExtractR1Field (inst_hex); UDWord_t rs1_val = m_env->GRegRead<UDWord_t> (rs1_addr); UDWord_t imm = ExtractIField (inst_hex); Addr_t mem_addr = rs1_val + imm; Word_t res_32; MemResult except = m_env->LoadFromBus (mem_addr, Size_Word, &res_32); DWord_t res = res_32; // just extending sign if (except == MemResult::MemMisAlign) { m_env->GenerateException (ExceptCode::Except_LoadAddrMisalign); return; } m_env->GRegWrite (rd_addr, res); }
上記の実装では、32ビットだろうが64ビットだろうが、レジスタを最大幅で読み込んでいる。
この時に何が問題化というと、32ビットモードの時に、上位の32ビットのどのように扱うかということを取り決めておかなければならない。 本ISSでは、上位の32ビットは下位の32ビットの符号拡張ということにした。
このときに問題になったのが、バイナリファイルからシンボルをロードするときにbfdの関数を使うのだが、これを32ビットモードの時にきちんと符号を埋めてやらなければならない。
Addr_t addr; if (FLAGS_bit_mode == 32) { addr = static_cast<Addr_t>(static_cast<int32_t>(bfd_asymbol_value(symbol_table[i]))); } else { addr = bfd_asymbol_value(symbol_table[i]); }
とりあえずこれでバグは直った。やっとOSのブートシミュレーションに戻れるかな。