--- nono/exp/optestm88k/optestm88k_gen.cpp 2026/04/29 17:04:57 1.1 +++ nono/exp/optestm88k/optestm88k_gen.cpp 2026/04/29 17:05:00 1.1.1.2 @@ -32,6 +32,7 @@ // XXX mul/div/divu 命令の FPU disabled のケースは未検査 #include "header.h" +#include #include #include #include @@ -39,6 +40,60 @@ #include #include +struct fpi +{ + union { + double d {}; + uint64 q; + // float は h の位置に置く +#if _BYTE_ORDER == _LITTLE_ENDIAN + struct { uint32 l, h; }; + struct { float dummy_f, f; }; + struct { uint32 dummy_i, i; }; +#else + struct { uint32 h, l; }; + struct { float f, dummy_f; }; + struct { uint32 i, dummy_i; }; +#endif + }; + + enum { + NONE, + FLOAT, + DOUBLE, + } type {}; + bool IsFloat() const { return (type == FLOAT); } + bool IsDouble() const { return (type == DOUBLE); } + + // どっちの値を保持しているか覚えておく + void Set(float f_) { + f = f_; + type = FLOAT; + } + void Set(double d_) { + d = d_; + type = DOUBLE; + } + void Set(uint32 i_) { + i = i_; + type = FLOAT; + } + void Set(uint64 q_) { + q = q_; + type = DOUBLE; + } + + // コンストラクタ + fpi() { + } + fpi(float f_) { + Set(f_); + } + fpi(double d_) { + Set(d_); + } +}; + // テスト情報 (グローバル) static const char *testname; static int testnum; @@ -47,11 +102,18 @@ static FILE *fp; #define IMM (0xff) -// 理解しやすさのため 1, 2, 3 で表現しているが実際は r11, r12, r13 を使う +// 理解しやすさのため 1, 2, 3 で表現しているが実際は r8, r10, r12 を使う +static constexpr uint32 ntor(uint32 n) { + assert(n < 4); + if (n == 0) return 0; + if (n == 1) return 8; + if (n == 2) return 10; + if (n == 3) return 12; +} static constexpr uint32 R3(uint32 d, uint32 s1, uint32 s2) { - d = (d > 0) ? (d + 10) : 0; - s1 = (s1 > 0) ? (s1 + 10) : 0; - s2 = (s2 > 0) ? (s2 + 10) : 0; + d = ntor(d); + s1 = ntor(s1); + s2 = ntor(s2); return (d << 16) | (s1 << 8) | s2; } // 3オペランドの場合のレジスタの組み合わせ @@ -88,6 +150,7 @@ static std::vector reglist2 = { static std::vector v0 = { 0, }; + // 数値演算用の入力値リスト static std::vector vlist_arith = { 0, @@ -144,38 +207,40 @@ static std::vector vlist_ff = { }; template -using Tuple3 = std::tuple; +using Tuple2_1 = std::tuple; template -using Tuple4 = std::tuple; +using Tuple2_2 = std::tuple; template -using Tuple5 = std::tuple; +using Tuple3_2 = std::tuple; -static std::vector> test_arith_reg; -static std::vector> test_arith_imm; -static std::vector> test_bitfield_reg; -static std::vector> test_bitfield_imm; -static std::vector> test_ff01; +static std::vector> test_arith_reg; +static std::vector> test_arith_imm; +static std::vector> test_bitfield_reg; +static std::vector> test_bitfield_imm; +static std::vector> test_ff01; +static std::vector> test_fp_add; +static std::vector> test_flt; // 2オペランドテストの要素を変数に取り出す部分 (副作用のあるマクロ) #define VAR_DIADIC(t, rd, rs2, vs2) \ uint32 rd = std::get<0>(t); \ uint32 rs2 = std::get<1>(t); \ - uint32 vs2 = std::get<2>(t) + auto vs2 = std::get<2>(t) // 3オペランドテストの要素を変数に取り出す部分 (副作用のあるマクロ) #define VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2) \ uint32 rd = std::get<0>(t); \ uint32 rs1 = std::get<1>(t); \ uint32 rs2 = std::get<2>(t); \ - uint32 vs1 = std::get<3>(t); \ - uint32 vs2 = std::get<4>(t) + auto vs1 = std::get<3>(t); \ + auto vs2 = std::get<4>(t) // 3オペランド(IMM)テストの要素を変数に取り出す部分 (副作用のあるマクロ) #define VAR_TRI_IMM(t, rd, rs1, vs1, imm) \ uint32 rd = std::get<0>(t); \ uint32 rs1 = std::get<1>(t); \ - uint32 vs1 = std::get<2>(t); \ - uint32 imm = std::get<3>(t) + auto vs1 = std::get<2>(t); \ + auto imm = std::get<3>(t) // fp に出力 @@ -218,11 +283,18 @@ MOV_imm(int regno, uint32 imm) // r2[1] = &exception_expected // r3: 引数2: 今回のテストのパラメータの先頭。内容は code ごとに異なる。 // -// r9: r1 のバックアップ -// r10: 期待 rD -// r11: rD (テストで使う)、結果の rD をここに入れて返す -// r12: rS1 (テストで使う) -// r13: rS2 (テストで使う) +// r5: r1 のバックアップ +// r6(,r7): 期待 rD +// r8(,r9): rD (テストで使う)、結果の rD をここに入れて返す +// r10(,r11): rS1 (テストで使う) +// r12(,r13): rS2 (テストで使う) +// +// 規約では r10-13 がサブルーチンが破壊してよいレジスタで、 +// r2-r9 は呼び出し側が引数を置くレジスタだがここもサブルーチンが破壊してよい +// ので使ってしまう。 + +#define Rexp (6) // 期待値を格納するレジスタ +#define Rres (8) // 結果を格納するレジスタ // add でのオーバーフロー条件 static inline bool @@ -269,8 +341,8 @@ gen_generic_test(const char *mnemonic, out(" %s %%r%d, %%r%d, %%r%d | test\n", mnemonic, rd, rs1, rs2); } - if (rd != 11) { - out(" or %%r11, %%r0, %%r%d\n", rd); + if (rd != Rres) { + out(" or %%r%d, %%r0, %%r%d\n", Rres, rd); } out(" jmp %%r1\n"); out("\n"); @@ -297,8 +369,8 @@ gen_bitfield_test(const char *mnemonic, out(" %s %%r%d, %%r%d, %d<%d> | test\n", mnemonic, rd, rs1, w, o); - if (rd != 11) { - out(" or %%r11, %%r0, %%r%d\n", rd); + if (rd != Rres) { + out(" or %%r%d, %%r0, %%r%d\n", Rres, rd); } out(" jmp %%r1\n"); out("\n"); @@ -322,8 +394,8 @@ gen_ff01_test(const char *mnemonic, out(" %s %%r%d, %%r%d | test\n", mnemonic, rd, rs2); - if (rd != 11) { - out(" or %%r11, %%r0, %%r%d\n", rd); + if (rd != Rres) { + out(" or %%r%d, %%r0, %%r%d\n", Rres, rd); } out(" jmp %%r1\n"); out("\n"); @@ -366,9 +438,9 @@ gen_addsub_test(const char *mnemonic, } if (exception) { // 例外期待なら、rD (と Cy)の期待値は命令実行前の値 (p.6-19, 6.6) - out(" or %%r10, %%r0, %%r%d\n", rd); + out(" or %%r%d, %%r0, %%r%d\n", Rexp, rd); } else { - MOV_imm(10, exp_rd); + MOV_imm(Rexp, exp_rd); } if (exception) { @@ -382,8 +454,8 @@ gen_addsub_test(const char *mnemonic, out(" %s %%r%d, %%r%d, %%r%d | test\n", mnemonic, rd, rs1, rs2); } - if (rd != 11) { - out(" or %%r11, %%r0, %%r%d\n", rd); + if (rd != Rres) { + out(" or %%r%d, %%r0, %%r%d\n", Rres, rd); } out(" jmp %%r1\n"); out("\n"); @@ -420,9 +492,9 @@ gen_div_test(const char *mnemonic, } if (exception) { // 例外期待なら、rD の期待値は命令実行前の値 (p.6-40, 6.8.3) - out(" or %%r10, %%r0, %%r%d\n", rd); + out(" or %%r%d, %%r0, %%r%d\n", Rexp, rd); } else { - MOV_imm(10, exp_rd); + MOV_imm(Rexp, exp_rd); } if (exception) { @@ -434,8 +506,46 @@ gen_div_test(const char *mnemonic, out(" %s %%r%d, %%r%d, %%r%d | test\n", mnemonic, rd, rs1, rs2); } - if (rd != 11) { - out(" or %%r11, %%r0, %%r%d\n", rd); + if (rd != Rres) { + out(" or %%r%d, %%r0, %%r%d\n", Rres, rd); + } + out(" jmp %%r1\n"); + out("\n"); + + testnum++; +} + +// fp 命令の共通部分 +static void +gen_flt_test(const char *fsz0, + uint32 rd, uint32 rs2, uint32 vs2, fpi exp, bool exception) +{ + out("test_%s_%d:\n", testname, testnum); + out(" /* flt.%ss r%d, r%d (S2=$%08x) */\n", fsz0, rd, rs2, vs2); + + out(" .word 0x%08x | exp_ex\n", exception ? 0x1000 : 0); + // ここからテストコード + MOV_imm(rs2, vs2); + + if (exception) { + // 例外期待なら rD の期待値は命令実行前の値 + out(" or %%r%d, %%r0, %%r%d\n", Rexp, rd); + out(" or %%r%d, %%r0, %%r%d\n", Rexp + 1, rd + 1); + } else { + // 結果の比較を簡単にするため + if (exp.IsFloat()) { + MOV_imm(rd + 1, 0); + } + // 例外が起きないことを期待する + MOV_imm(Rexp, exp.h); + MOV_imm(Rexp + 1, exp.l); + } + + out(" flt.%ss %%r%d, %%r%d | test\n", fsz0, rd, rs2); + + if (rd != Rres) { + out(" or %%r%d, %%r0, %%r%d\n", Rres, rd); + out(" or %%r%d, %%r0, %%r%d\n", Rres + 1, rd + 1); } out(" jmp %%r1\n"); out("\n"); @@ -1027,6 +1137,31 @@ gen_ff1() } } +template +static void +gen_flt(const char *fsz0) +{ + for (const auto& t : test_flt) { + VAR_DIADIC(t, rd, rs2, vs2); + DD exp = (DD)(int32)vs2; + fpi exp_fpi(exp); + + gen_flt_test(fsz0, rd, rs2, vs2, exp, (rd == 0)); + } +} + +static void +gen_flt_ds() +{ + gen_flt("d"); +} + +static void +gen_flt_ss() +{ + gen_flt("s"); +} + // lda.* rD, rS1, #imm は addu rD, rS1, #imm と同じ static void gen_lda_imm() @@ -1620,6 +1755,7 @@ enum { G_BITFIELD, G_LDA, G_MULDIV, + G_FLT, }; // テスト一覧 (編集しやすいように実体は一番下に置いてある) @@ -1696,13 +1832,13 @@ main(int ac, char *av[]) if (rs1 == rs2) { for (const auto& vs1 : *v_arith_s1) { - Tuple5 val(rd, rs1, rs2, vs1, vs1); + Tuple3_2 val(rd, rs1, rs2, vs1, vs1); append(test_arith_reg, val); } } else { for (const auto& vs1 : *v_arith_s1) { for (const auto& vs2 : *v_arith_s2) { - Tuple5 val(rd, rs1, rs2, vs1, vs2); + Tuple3_2 val(rd, rs1, rs2, vs1, vs2); append(test_arith_reg, val); } } @@ -1717,7 +1853,7 @@ main(int ac, char *av[]) for (const auto& vs1 : *v_arith_s1) { for (const auto& imm : vlist_arith) { if (imm <= 0xffff) { - Tuple4 val(rd, rs1, vs1, imm); + Tuple2_2 val(rd, rs1, vs1, imm); append(test_arith_imm, val); } } @@ -1733,17 +1869,17 @@ main(int ac, char *av[]) if (rs1 == rs2) { for (const auto& vs1 : *v_bf) { - Tuple5 val(rd, rs1, rs2, vs1, vs1); + Tuple3_2 val(rd, rs1, rs2, vs1, vs1); append(test_bitfield_reg, val); } for (const auto& vs2 : *v_wo) { - Tuple5 val(rd, rs1, rs2, vs2, vs2); + Tuple3_2 val(rd, rs1, rs2, vs2, vs2); append(test_bitfield_reg, val); } } else { for (const auto& vs1 : *v_bf) { for (const auto& vs2 : *v_wo) { - Tuple5 val(rd, rs1, rs2, vs1, vs2); + Tuple3_2 val(rd, rs1, rs2, vs1, vs2); append(test_bitfield_reg, val); } } @@ -1757,7 +1893,7 @@ main(int ac, char *av[]) for (const auto& vs1 : *v_bf) { for (const auto& wo : vlist_wo) { - Tuple4 val(rd, rs1, vs1, wo); + Tuple2_2 val(rd, rs1, vs1, wo); append(test_bitfield_imm, val); } } @@ -1778,10 +1914,21 @@ main(int ac, char *av[]) } for (const auto& vs2 : v_ff) { - Tuple3 val(rd, rs2, vs2); + Tuple2_1 val(rd, rs2, vs2); append(test_ff01, val); } } + for (const auto& rlist : reglist2) { + auto rd = (rlist >> 16) & 0xff; + auto rs2 = (rlist >> 8) & 0xff; + + std::vector *v_arith_s2 = (rs2 == 0) ? &v0 : &vlist_arith; + + for (const auto& vs2 : *v_arith_s2) { + Tuple2_1 val(rd, rs2, vs2); + append(test_flt, val); + } + } // // add.s を出力 @@ -1850,6 +1997,17 @@ main(int ac, char *av[]) FCLOSE(); // + // flt.s を出力 + // + FOPEN("optestm88k_flt.s"); + for (const auto& t : test_table) { + if (t.group == G_FLT) { + print_test(t); + } + } + FCLOSE(); + + // // table.h を出力 // FOPEN("optestm88k_table.h"); @@ -2018,4 +2176,7 @@ std::vector test_table = { T(G_MULDIV, "div", div_imm), T(G_MULDIV, "div", divu), T(G_MULDIV, "div", divu_imm), + + T(G_FLT, "flt", flt_ss), + T(G_FLT, "flt", flt_ds), };