2025年07月07日

河田くんのバグ修正パッチがQEMU本家に取り込まれました

実機では正しく動くプログラムが QEMU(ARM Cortex-R52 ターゲット)では動かないという例が見つかり、なかなか真の原因に辿りつけず(そればかりやっていたわけではありませんが)2 週間ぐらい悩んでいた所、とても優秀な後輩の河田くんがサクッとバグ修正パッチを本家に送ってくれて無事取り込まれました。感謝!

https://gitlab.com/qemu-project/qemu/-/issues/3002 (バグ報告)

https://github.com/qemu/qemu/commit/0d0fc3f4658937fb81fcc16a89738e83bd8d4795 (コミット)

以下は調査の過程やこのパッチの内容となります。


まず最初に、これはおそらく JIT の最適化のバグだろう(-accel tcg,one-insn-per-tb=on オプションを付けて 1 命令ずつ実行すると正常動作するので)という目星を付けました。

そこで QEMU の TCG の最適化(tcg/optimize.c)あたりを調査したのですが、どうも最適化後の中間表現の段階までは正しそう。そして一部の最適化コードをコメントアウトして、中間変数をあえて経由させると正常に動きました。(JIT を 1 命令単位にすると動いたのも同じ理由でした。)
0x400002fa:  eea0 0b90  vdup.32  q8, r0

 ---- 00000000000002fa 0000000000000000 0000000000000000
 st_vec v128,e8,v128$0x0,env,$0x1500      dead: 0

↓

---- 00000000000002fa 0000000000000000 0000000000000000
 mov_i32 tmp33,r0                         dead: 1  pref=0xffff0000
 dup_vec v128,e32,tmp5,tmp33              dead: 1  pref=0xffff0000
 st_vec v128,e8,tmp5,env,$0x1500          dead: 0
QEMU の -d op_opt ログ出力上は全く同じ意味なのに結果が変わるとなると、やっぱり中間表現のデータ構造やコード生成のバグという、難しいデバッグになりそうです。(実は最初の調査段階で vdup.32 の結果がおかしいことには気づいていたのですが、コード生成のデバッグはできればやりたくなかったので、最適化のバグであってくれ…と願っていたのですが…。)

予想通り調査はなかなか進まず、ああでもない、こうでもないといろいろ悩みながら、ちょこちょこ社内の Mattermost に進捗状況を書いていた所、河田くんが興味をもってくれました。

そして上記の vdup.32 命令は r0 レジスタ(32-bit)を q8 レジスタ(128-bit)に 4 つ複製するのですが、64-bit 値を 2 つ複製するコードが生成されていることがわかりました。これは、最適化により st_vec オペコードのオペランドが定数になると、tcg_out_dupi_vec() という特別なコード生成関数が呼ばれるようになるからでした。

この関数を修正してオペランドの型が 32-bit の時は MOVDDUP ではなく VBROADCASTSS 命令を生成するようにした所、正しく動くようになり、めでたしめでたし…と思いきや、今度は AARCH64 が動かなくなってしまいました。汎用レジスタが 64-bit の場合、オペランドの型が 32-bit でも、64-bit に符号拡張してから DUP しないといけないのです。難しい。

最終的に、コード生成関数に定数値をそのまま渡していることが悪いことがわかり、河田くんがバグの再現例や、定数値を dup_const 関数経由で渡す修正パッチを作って本家に投稿してくれて、無事に取り込まれました。
/* Duplicate C as per VECE.  */
uint64_t (dup_const)(unsigned vece, uint64_t c)
{
    switch (vece) {
    case MO_8:
        return 0x0101010101010101ull * (uint8_t)c;
    case MO_16:
        return 0x0001000100010001ull * (uint16_t)c;
    case MO_32:
        return 0x0000000100000001ull * (uint32_t)c;
    case MO_64:
        return c;
    default:
        g_assert_not_reached();
    }
}


kmckk at 18:00コメント(0)qemu | 若槻 

コメントする

名前
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード