--- nono/exp/optestm88k/optestm88k_gen.cpp 2026/04/29 17:04:57 1.1.1.1 +++ nono/exp/optestm88k/optestm88k_gen.cpp 2026/04/29 17:05:04 1.1.1.3 @@ -16,6 +16,8 @@ // v // +--> optestm88k_add.s ---+ // +--> optestm88k_bitfield.s ---+ +// +--> optestm88k_fcmp.s ---+ +// +--> optestm88k_flt.s ---+ // +--> optestm88k_lda.s ---+ // +--> optestm88k_logical.s ---+ // +--> optestm88k_muldiv.s ---+ @@ -32,13 +34,102 @@ // XXX mul/div/divu 命令の FPU disabled のケースは未検査 #include "header.h" +#include "mystring.h" +#include #include #include #include +#include +#include #include #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, + INT, + } type {}; + bool IsFloat() const { return (type == FLOAT); } + bool IsDouble() const { return (type == DOUBLE); } + bool IsInt() const { return (type == INT); } + + // コンストラクタ + fpi() { + } + fpi(float f_) { + Set(f_); + } + fpi(double d_) { + Set(d_); + } + fpi(uint32 i_) { + SetInt(i_); + } + + // どの値を保持しているか覚えておく + 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; + } + void SetInt(uint32 i_) { + i = i_; + type = INT; + } + + // どっちであっても double 値を返す (printf 用) + double AsDouble() const { + if (IsFloat()) { + return (double)f; + } else { + return d; + } + } + + std::string ToString() const { + switch (type) { + case FLOAT: + return string_format("%9.8g", f); + case DOUBLE: + return string_format("%19.18g", d); + case INT: + return string_format("$%08x", i); + default: + return "NONE"; + } + } +}; + // テスト情報 (グローバル) static const char *testname; static int testnum; @@ -47,11 +138,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 +186,7 @@ static std::vector reglist2 = { static std::vector v0 = { 0, }; + // 数値演算用の入力値リスト static std::vector vlist_arith = { 0, @@ -142,42 +241,132 @@ static std::vector vlist_ff = { 0x06000000, 0xf0000000, }; +// r0 用 +static std::vector vf0 = { + 0.0, +}; +// 浮動小数点数の入力値リスト +static std::vector vlist_double = { + 0.0, + 1.0, + 2.0, + 16777215.0, // float → int で正確に表現できる最大値? + 16777215.5, + 16777217.0, // 16777216 は 1.0*2^24 で表現できるため省略 + 1000000000.0, // 2^29 以上 2^30 以下 + 2147483646.0, // INT32_MAX と区別可能な最大値 + 2147483647.0, // INT32_MAX + 2147483647.5, + 2147483649.0, + 4294967295.0, // double → uint32 で正確に表現できる最大値? + 4294967295.5, + 4294967297.0, + + -0.0, + -1.0, + -2.0, + -16777215.0, + -16777215.5, + -16777217.0, + -1000000000.0, + -2000000000.0, + -2147483648.0, + -2147483648.5, + -2147483649.0, + + std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + std::nan(""), +}; + +#define RM_NEAR (0) +#define RM_ZERO (1) +#define RM_MINUS (2) +#define RM_PLUS (3) + +static const char *rmstr[] = { + "RN", + "RZ", + "RM", + "RP", +}; 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> param_arith_reg; +static std::vector> param_arith_imm; +static std::vector> param_bitfield_reg; +static std::vector> param_bitfield_imm; +static std::vector> param_ff01; +static std::vector> param_flt; +static std::vector> param_trnc; +static std::vector> param_fcmp; // 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) +// 自前の Round Mode 定数でシステムの丸めモードを設定する。 +static void +setround(uint32 rm) +{ + int mode; + switch (rm) { + case RM_NEAR: + mode = FE_TONEAREST; + break; + case RM_ZERO: + mode = FE_TOWARDZERO; + break; + case RM_MINUS: + mode = FE_DOWNWARD; + break; + case RM_PLUS: + mode = FE_UPWARD; + break; + default: + __unreachable(); + } + std::fesetround(mode); +} + +// d が float でも同じ値として表現可能なら true を返す +static bool +can_float(double d) +{ + float f = (float)d; + + // NAN 同士の == 比較は false になるため + if (std::isnan(d) && std::isnan(f)) + return true; + if (d == f) + return true; + return false; +} + // fp に出力 static void out(const char *fmt, ...) { @@ -204,7 +393,10 @@ MOV_imm(int regno, uint32 imm) } else { out(" or.u %%r%d, %%r0, 0x%04x | MOV %%r%d, 0x%08x\n", regno, (imm >> 16), regno, imm); - out(" or %%r%d, %%r%d, 0x%04x\n", regno, regno, imm & 0xffff); + if ((imm & 0xffff) != 0) { + out(" or %%r%d, %%r%d, 0x%04x\n", + regno, regno, imm & 0xffff); + } } } @@ -218,11 +410,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 @@ -240,7 +439,7 @@ isovf_sub(uint32 s1, uint32 s2, uint32 r // 汎用演算命令共通のテストパターン出力部分 static void -gen_generic_test(const char *mnemonic, +outtest_generic(const char *mnemonic, uint32 rd, uint32 rs1, uint32 rs2, uint32 vs1, uint32 vs2, uint32 exp_rd) { // rs2 が IMM なら #imm @@ -269,8 +468,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"); @@ -279,9 +478,9 @@ gen_generic_test(const char *mnemonic, } // 2項ビットフィールド命令共通のテストパターン出力部分 -// (gen_generic_test と似てるけど微妙に違う) +// (outtest_generic と似てるけど微妙に違う) static void -gen_bitfield_test(const char *mnemonic, +outtest_bitfield(const char *mnemonic, uint32 rd, uint32 rs1, uint32 vs1, uint32 wo, uint32 exp_rd) { uint32 w = (wo >> 5) & 0x1f; // XXX 表記上は 0 なのか 32 なのか @@ -297,8 +496,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"); @@ -307,9 +506,9 @@ gen_bitfield_test(const char *mnemonic, } // ff0/ff1 命令共通のテストパターン出力部分 -// (gen_generic_test と似てるけど微妙に違う) +// (outtest_generic と似てるけど微妙に違う) static void -gen_ff01_test(const char *mnemonic, +outtest_ff01(const char *mnemonic, uint32 rd, uint32 rs2, uint32 vs2, uint32 exp_rd) { out("test_%s_%d:\n", testname, testnum); @@ -322,8 +521,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"); @@ -335,7 +534,7 @@ gen_ff01_test(const char *mnemonic, // rs2 == IMM なら即値指定。 // scale == true なら lda rD, rS1[rS2] 形式。 static void -gen_addsub_test(const char *mnemonic, +outtest_addsub(const char *mnemonic, uint32 rd, uint32 rs1, uint32 rs2, uint32 vs1, uint32 vs2, int cin, uint32 exp_rd, uint32 exp_cy, bool exception = false, bool is_scale = false) { @@ -366,9 +565,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 +581,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"); @@ -393,7 +592,7 @@ gen_addsub_test(const char *mnemonic, // div 命令共通のテストパターン出力部分 static void -gen_div_test(const char *mnemonic, +outtest_div(const char *mnemonic, uint32 rd, uint32 rs1, uint32 rs2, uint32 vs1, uint32 vs2, uint32 exp_rd, bool exception = false) { @@ -410,7 +609,6 @@ gen_div_test(const char *mnemonic, mnemonic, rd, rs1, rs2, vs1, vs2); } - out(" .word 0x%08x | exp_rd\n", exp_rd); out(" .word 0x%08x | exp_ex\n", exception ? 2 : 0); // ここからテストコード // ソースレジスタをセット、実行、結果を保存 @@ -420,9 +618,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 +632,90 @@ 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 命令共通のテストパターン出力部分。 +// ソースレジスタが1つの場合 (rs1 を使わない場合) は fsz1 = '\0'、rs1 = -1 に +// すること(vs1 は不定でよい)。 +static void +outtest_fop(const char *mnemonic, char fsz0, char fsz1, char fsz2, + uint32 rd, uint32 rs1, uint32 rs2, fpi vs1, fpi vs2, + uint32 rm, fpi expected, uint32 exception) +{ + bool r4_backup = false; + + out("test_%s_%d:\n", testname, testnum); + if (fsz1 == '\0') { + out(" /* %s.%c%c r%d, r%d (S2=%s; %s) */\n", + mnemonic, fsz0, fsz2, rd, rs2, + vs2.ToString().c_str(), rmstr[rm]); + } else { + out(" /* %s.%c%c%c r%d, r%d, r%d (S1=%s, S2=%s; %s) */\n", + mnemonic, fsz0, fsz1, fsz2, rd, rs1, rs2, + vs1.ToString().c_str(), vs2.ToString().c_str(), rmstr[rm]); + } + + out(" .word 0x%08x | RndMode | exp_ex\n", + exception | (rm << (16 + 14))); + // ここからテストコード + if (fsz1 != '\0') { + MOV_imm(rs1, vs1.h); + if (fsz1 == 'd') { + if (rs1 == 0) { + // ソースが r0,r1 になるので r1 を(さらに)退避 + out(" or %%r4, %%r0, %%r1\n"); + r4_backup = true; + } + MOV_imm(rs1 + 1, vs1.l); + } + } + MOV_imm(rs2, vs2.h); + if (fsz2 == 'd') { + if (r4_backup == false && rs2 == 0) { + // ソースが r0,r1 になるので r1 を(さらに)退避 + out(" or %%r4, %%r0, %%r1\n"); + r4_backup = true; + } + MOV_imm(rs2 + 1, vs2.l); + } + + if (exception != 0) { + // 例外期待なら rD の期待値は命令実行前の値 + out(" or %%r%d, %%r0, %%r%d\n", Rexp, rd); + if (fsz0 == 'd') { + out(" or %%r%d, %%r0, %%r%d\n", Rexp + 1, rd + 1); + } + } else { + // 例外が起きないことを期待する + MOV_imm(Rexp, expected.h); + if (fsz0 == 'd') { + MOV_imm(Rexp + 1, expected.l); + } + } + + if (fsz1 == '\0') { + out(" %s.%c%c %%r%d, %%r%d | test\n", + mnemonic, fsz0, fsz2, rd, rs2); + } else { + out(" %s.%c%c%c %%r%d, %%r%d, %%r%d | test\n", + mnemonic, fsz0, fsz1, fsz2, rd, rs1, rs2); + } + + if (r4_backup) { + out(" or %%r1, %%r0, %%r4\n"); + } + if (rd != Rres) { + out(" or %%r%d, %%r0, %%r%d\n", Rres, rd); + if (fsz0 == 'd') { + out(" or %%r%d, %%r0, %%r%d\n", Rres + 1, rd + 1); + } } out(" jmp %%r1\n"); out("\n"); @@ -443,10 +723,41 @@ gen_div_test(const char *mnemonic, testnum++; } +// FP 命令共通 (ソースレジスタが1つの形式) +static void +outtest_fop(const char *mnemonic, char fsz0, char fsz2, + uint32 rd, uint32 rs2, fpi vs2, + uint32 rm, fpi expected, uint32 exception) +{ + fpi dummy_vs1; + + outtest_fop(mnemonic, fsz0, '\0', fsz2, + rd, (uint32)-1, rs2, dummy_vs1, vs2, + rm, expected, exception); +} + +// int, nint, trnc 命令共通のテストパターン出力部分。 +// rm はテスト環境内の FPCR に設定する丸めモード (入力パラメータ)。 +static void +outtest_toint(const char *mnemonic, uint32 rd, uint32 rs2, fpi vs2, + uint32 rm, uint64 exp) +{ + uint32 exception = exp >> 32; + uint32 expected = exp; + + outtest_fop(mnemonic, 's', (vs2.IsFloat() ? 's' : 'd'), + rd, rs2, vs2, rm, expected, exception); +} + + +// +// ここからテストの出力 +// + static void gen_add() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -462,7 +773,7 @@ gen_add() exp_cy = cin; } - gen_addsub_test("add", + outtest_addsub("add", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex); } } @@ -471,7 +782,7 @@ gen_add() static void gen_add_ci() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -487,7 +798,7 @@ gen_add_ci() exp_cy = cin; } - gen_addsub_test("add.ci", + outtest_addsub("add.ci", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex); } } @@ -496,7 +807,7 @@ gen_add_ci() static void gen_add_co() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -512,7 +823,7 @@ gen_add_co() exp_cy = cin; } - gen_addsub_test("add.co", + outtest_addsub("add.co", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex); } } @@ -521,7 +832,7 @@ gen_add_co() static void gen_add_cio() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -537,7 +848,7 @@ gen_add_cio() exp_cy = cin; } - gen_addsub_test("add.cio", + outtest_addsub("add.cio", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex); } } @@ -546,7 +857,7 @@ gen_add_cio() static void gen_add_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); for (int cin = 0; cin <= 1; cin++) { @@ -562,7 +873,7 @@ gen_add_imm() exp_cy = cin; } - gen_addsub_test("add", + outtest_addsub("add", rd, rs1, IMM, vs1, imm, cin, exp_rd, exp_cy, exp_ex); } } @@ -572,7 +883,7 @@ gen_add_imm() static void gen_addu_lda(const char *mnemonic) { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -584,7 +895,7 @@ gen_addu_lda(const char *mnemonic) exp_rd = 0; } - gen_addsub_test(mnemonic, + outtest_addsub(mnemonic, rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy); } } @@ -599,7 +910,7 @@ gen_addu() static void gen_addu_ci() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -611,7 +922,7 @@ gen_addu_ci() exp_rd = 0; } - gen_addsub_test("addu.ci", + outtest_addsub("addu.ci", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy); } } @@ -620,7 +931,7 @@ gen_addu_ci() static void gen_addu_co() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -632,7 +943,7 @@ gen_addu_co() exp_rd = 0; } - gen_addsub_test("addu.co", + outtest_addsub("addu.co", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy); } } @@ -641,7 +952,7 @@ gen_addu_co() static void gen_addu_cio() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -653,7 +964,7 @@ gen_addu_cio() exp_rd = 0; } - gen_addsub_test("addu.cio", + outtest_addsub("addu.cio", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy); } } @@ -663,7 +974,7 @@ gen_addu_cio() static void gen_addu_lda_imm(const char *mnemonic) { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); for (int cin = 0; cin <= 1; cin++) { @@ -675,7 +986,7 @@ gen_addu_lda_imm(const char *mnemonic) exp_rd = 0; } - gen_addsub_test(mnemonic, + outtest_addsub(mnemonic, rd, rs1, IMM, vs1, imm, cin, exp_rd, exp_cy); } } @@ -690,52 +1001,52 @@ gen_addu_imm() static void gen_and() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = vs1 & vs2; if (rd == 0) { exp_rd = 0; } - gen_generic_test("and", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("and", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_and_c() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = vs1 & ~vs2; if (rd == 0) { exp_rd = 0; } - gen_generic_test("and.c", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("and.c", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_and_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = vs1 & (imm | 0xffff0000); if (rd == 0) { exp_rd = 0; } - gen_generic_test("and", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("and", rd, rs1, IMM, vs1, imm, exp_rd); } } static void gen_and_u() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = vs1 & ((imm << 16) | 0xffff); if (rd == 0) { exp_rd = 0; } - gen_generic_test("and.u", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("and.u", rd, rs1, IMM, vs1, imm, exp_rd); } } @@ -751,26 +1062,26 @@ calc_clr(uint32 src, uint32 wo) static void gen_clr_imm() { - for (const auto& t : test_bitfield_imm) { + for (const auto& t : param_bitfield_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = calc_clr(vs1, imm); if (rd == 0) { exp_rd = 0; } - gen_bitfield_test("clr", rd, rs1, vs1, imm, exp_rd); + outtest_bitfield("clr", rd, rs1, vs1, imm, exp_rd); } } static void gen_clr_reg() { - for (const auto& t : test_bitfield_reg) { + for (const auto& t : param_bitfield_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = calc_clr(vs1, vs2); if (rd == 0) { exp_rd = 0; } - gen_generic_test("clr", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("clr", rd, rs1, rs2, vs1, vs2, exp_rd); } } @@ -797,26 +1108,26 @@ calc_cmp(uint32 vs1, uint32 vs2) static void gen_cmp() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = calc_cmp(vs1, vs2); if (rd == 0) { exp_rd = 0; } - gen_generic_test("cmp", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("cmp", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_cmp_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = calc_cmp(vs1, imm); if (rd == 0) { exp_rd = 0; } - gen_generic_test("cmp", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("cmp", rd, rs1, IMM, vs1, imm, exp_rd); } } @@ -839,7 +1150,7 @@ calc_div(uint32 *exp_rd, bool *exp_ex, i static void gen_div() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd; bool exp_ex; @@ -847,14 +1158,14 @@ gen_div() if (rd == 0) { exp_rd = 0; } - gen_div_test("div", rd, rs1, rs2, vs1, vs2, exp_rd, exp_ex); + outtest_div("div", rd, rs1, rs2, vs1, vs2, exp_rd, exp_ex); } } static void gen_div_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd; bool exp_ex; @@ -862,7 +1173,7 @@ gen_div_imm() if (rd == 0) { exp_rd = 0; } - gen_div_test("div", rd, rs1, IMM, vs1, imm, exp_rd, exp_ex); + outtest_div("div", rd, rs1, IMM, vs1, imm, exp_rd, exp_ex); } } @@ -884,7 +1195,7 @@ calc_divu(uint32 *exp_rd, bool *exp_ex, static void gen_divu() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd; bool exp_ex; @@ -892,14 +1203,14 @@ gen_divu() if (rd == 0) { exp_rd = 0; } - gen_div_test("divu", rd, rs1, rs2, vs1, vs2, exp_rd, exp_ex); + outtest_div("divu", rd, rs1, rs2, vs1, vs2, exp_rd, exp_ex); } } static void gen_divu_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd; bool exp_ex; @@ -907,7 +1218,7 @@ gen_divu_imm() if (rd == 0) { exp_rd = 0; } - gen_div_test("divu", rd, rs1, IMM, vs1, imm, exp_rd, exp_ex); + outtest_div("divu", rd, rs1, IMM, vs1, imm, exp_rd, exp_ex); } } @@ -932,26 +1243,26 @@ calc_ext(uint32 src, uint32 wo) static void gen_ext_imm() { - for (const auto& t : test_bitfield_imm) { + for (const auto& t : param_bitfield_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = calc_ext(vs1, imm); if (rd == 0) { exp_rd = 0; } - gen_bitfield_test("ext", rd, rs1, vs1, imm, exp_rd); + outtest_bitfield("ext", rd, rs1, vs1, imm, exp_rd); } } static void gen_ext_reg() { - for (const auto& t : test_bitfield_reg) { + for (const auto& t : param_bitfield_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = calc_ext(vs1, vs2); if (rd == 0) { exp_rd = 0; } - gen_generic_test("ext", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("ext", rd, rs1, rs2, vs1, vs2, exp_rd); } } @@ -968,33 +1279,128 @@ calc_extu(uint32 src, uint32 wo) static void gen_extu_imm() { - for (const auto& t : test_bitfield_imm) { + for (const auto& t : param_bitfield_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = calc_extu(vs1, imm); if (rd == 0) { exp_rd = 0; } - gen_bitfield_test("extu", rd, rs1, vs1, imm, exp_rd); + outtest_bitfield("extu", rd, rs1, vs1, imm, exp_rd); } } static void gen_extu_reg() { - for (const auto& t : test_bitfield_reg) { + for (const auto& t : param_bitfield_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = calc_extu(vs1, vs2); if (rd == 0) { exp_rd = 0; } - gen_generic_test("extu", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("extu", rd, rs1, rs2, vs1, vs2, exp_rd); + } +} + +// fcmp.sXX の計算部分のみ +static uint32 +calc_fcmp(double vs1, double vs2) +{ + uint32 res = 0; + + if (vs2 >= 0) { + res |= (vs1 <= 0 || vs1 >= vs2) ? 0x0800 : 0; // ob + res |= (0 < vs1 && vs1 < vs2) ? 0x0400 : 0; // in + res |= (0 <= vs1 && vs1 <= vs2) ? 0x0200 : 0; // ib + res |= (vs1 < 0 || vs1 > vs2) ? 0x0100 : 0; // ou + } + res |= (vs1 >= vs2) ? 0x0080 : 0; // ge + res |= (vs1 < vs2) ? 0x0040 : 0; // lt + res |= (vs1 <= vs2) ? 0x0020 : 0; // le + res |= (vs1 > vs2) ? 0x0010 : 0; // gt + res |= (vs1 != vs2) ? 0x0008 : 0; // ne + res |= (vs1 == vs2) ? 0x0004 : 0; // eq + // cp; if and only if the two operands are comparable + if (!std::isnan(vs1) && !std::isnan(vs2)) { + res |= 0x0002; + } + // nc; if, and only if, the two operands are not comparable + if (std::isnan(vs1) || std::isnan(vs2)) { + res |= 0x0001; + } + + return res; +} + +// fcmp.sXX の共通部分 +static void +gen_fcmp(char fsz1, const char fsz2) +{ + for (uint32 rm = 0; rm < 4; rm++) { + for (const auto& t : param_fcmp) { + VAR_TRI_REG(t, rd, rs1, rs2, v1, v2); + uint32 exception = 0; + uint32 exp_rd = 0; + fpi fpi1; + fpi fpi2; + + // rS1, rS2 が同じで幅が違うのは(ゼロ以外は)意味がないので飛ばす + if (rs1 == rs2 && fsz1 != fsz2 && (v1 != 0 && v2 != 0)) { + continue; + } + // double で r0 を指すのは実質意味がないので飛ばす + // (ゼロの場合は飛ばさなくてもいいけど面倒なので飛ばす) + if (fsz1 == 'd' && rs1 == 0) + continue; + if (fsz2 == 'd' && rs2 == 0) + continue; + + // ソースサイズが s なら float で表現できない値は飛ばす + if (fsz1 == 's' && can_float(v1) == false) + continue; + if (fsz2 == 's' && can_float(v2) == false) + continue; + + // r0 なら値も 0 としておかないといけない + if (rs1 == 0) { + v1 = 0; + } + if (rs2 == 0) { + v2 = 0; + } + + // rd ==0 の場合もソースに出力するコメントのために必要 + if (fsz1 == 's') { + fpi1.Set((float)v1); + } else { + fpi1.Set(v1); + } + if (fsz2 == 's') { + fpi2.Set((float)v2); + } else { + fpi2.Set(v2); + } + + if (rd == 0) { + exception = 0x1000; + } else { + exp_rd = calc_fcmp(fpi1.AsDouble(), fpi2.AsDouble()); + } + + outtest_fop("fcmp", 's', fsz1, fsz2, + rd, rs1, rs2, fpi1, fpi2, rm, exp_rd, exception); + } } } +static void gen_fcmp_sss() { gen_fcmp('s', 's'); } +static void gen_fcmp_ssd() { gen_fcmp('s', 'd'); } +static void gen_fcmp_sds() { gen_fcmp('d', 's'); } +static void gen_fcmp_sdd() { gen_fcmp('d', 'd'); } static void gen_ff0() { - for (const auto& t : test_ff01) { + for (const auto& t : param_ff01) { VAR_DIADIC(t, rd, rs2, vs2); // 最も MSB 側にある %0 のビット位置(LSBを0とする)を返す // %0 がなければ 32 を返す @@ -1005,14 +1411,14 @@ gen_ff0() if (rd == 0) { exp_rd = 0; } - gen_ff01_test("ff0", rd, rs2, vs2, exp_rd); + outtest_ff01("ff0", rd, rs2, vs2, exp_rd); } } static void gen_ff1() { - for (const auto& t : test_ff01) { + for (const auto& t : param_ff01) { VAR_DIADIC(t, rd, rs2, vs2); // 最も MSB 側にある %1 のビット位置(LSBを0とする)を返す // %0 がなければ 32 を返す @@ -1023,7 +1429,105 @@ gen_ff1() if (rd == 0) { exp_rd = 0; } - gen_ff01_test("ff1", rd, rs2, vs2, exp_rd); + outtest_ff01("ff1", rd, rs2, vs2, exp_rd); + } +} + +static void +gen_flt_ss() +{ + for (uint32 rm = 0; rm < 4; rm++) { + setround(rm); + for (const auto& t : param_flt) { + VAR_DIADIC(t, rd, rs2, vs2); + fpi fpi((float)(int32)vs2); + + outtest_fop("flt", 's', 's', + rd, rs2, vs2, rm, fpi, (rd == 0 ? 0x1000 : 0)); + } + } +} + +static void +gen_flt_ds() +{ + for (uint32 rm = 0; rm < 4; rm++) { + setround(rm); + for (const auto& t : param_flt) { + VAR_DIADIC(t, rd, rs2, vs2); + fpi fpi((double)(int32)vs2); + + outtest_fop("flt", 'd', 's', + rd, rs2, vs2, rm, fpi, (rd == 0 ? 0x1000 : 0)); + } + } +} + +// int, nint, trnc 命令共通の期待値計算。 +// 期待値を計算するための丸めモードはあらかじめ setround() で設定しておくこと。 +// 戻り値は上位 32bit が例外、下位 32bit が期待値。 +// 例外が出ることを期待する時は、上位が例外コード(非0)、下位は無視。 +// 例外が出ないことを期待する場合は、上位が 0、下位が期待値。 +static uint64 +calc_toint(uint32 rd, fpi vs2) +{ + uint32 expected = 0; + uint32 exception = 0; + + if (rd == 0) { + exception = 0x1000; + } else { + double src = vs2.AsDouble(); + double result = std::nearbyint(src); + + int exponent = 0; + std::frexp(result, &exponent); + + // 実際には trnc 命令は 30ビット以上で例外を出すが、 + // そこから先は (OpenBSD の) ソフトウェアハンドラが処理して + // 実際に収まらなかった時は最大値を返す。 + if (std::isinf(result) || std::isnan(result) || exponent >= 32) { + if (std::signbit(result)) { + expected = 0x80000000; + } else { + expected = 0x7fffffff; + } + } else { + expected = (int32)result; + } + } + + return ((uint64)exception << 32) | expected; +} + +static void +gen_int_ss() +{ + for (uint32 rm = 0; rm < 4; rm++) { + setround(rm); + for (const auto& t : param_trnc) { + VAR_DIADIC(t, rd, rs2, vs2); + // float で表現できない値は飛ばす + if (can_float(vs2)) + continue; + fpi fpi((float)vs2); + uint64 exp = calc_toint(rd, fpi); + outtest_toint("int", rd, rs2, fpi, rm, exp); + } + } +} + +static void +gen_int_sd() +{ + for (uint32 rm = 0; rm < 4; rm++) { + setround(rm); + for (const auto& t : param_trnc) { + VAR_DIADIC(t, rd, rs2, vs2); + fpi fpi(vs2); + uint64 exp = calc_toint(rd, fpi); + outtest_toint("int", rd, rs2, fpi, rm, exp); + } } } @@ -1074,7 +1578,7 @@ gen_lda_d_reg() static void gen_lda_scale(const char *mnemonic, uint32 scale) { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -1088,7 +1592,7 @@ gen_lda_scale(const char *mnemonic, uint bool exception = false; bool is_scale = true; - gen_addsub_test(mnemonic, + outtest_addsub(mnemonic, rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exception, is_scale); } @@ -1128,130 +1632,165 @@ calc_mak(uint32 src, uint32 wo) static void gen_mak_imm() { - for (const auto& t : test_bitfield_imm) { + for (const auto& t : param_bitfield_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = calc_mak(vs1, imm); if (rd == 0) { exp_rd = 0; } - gen_bitfield_test("mak", rd, rs1, vs1, imm, exp_rd); + outtest_bitfield("mak", rd, rs1, vs1, imm, exp_rd); } } static void gen_mak_reg() { - for (const auto& t : test_bitfield_reg) { + for (const auto& t : param_bitfield_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = calc_mak(vs1, vs2); if (rd == 0) { exp_rd = 0; } - gen_generic_test("mak", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("mak", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_mask_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = vs1 & imm; if (rd == 0) { exp_rd = 0; } - gen_generic_test("mask", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("mask", rd, rs1, IMM, vs1, imm, exp_rd); } } static void gen_mask_u() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = vs1 & (imm << 16); if (rd == 0) { exp_rd = 0; } - gen_generic_test("mask.u", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("mask.u", rd, rs1, IMM, vs1, imm, exp_rd); } } static void gen_mul() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = (uint32)((uint64)vs1 * vs2); if (rd == 0) { exp_rd = 0; } - gen_generic_test("mul", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("mul", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_mul_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = (uint32)((uint64)vs1 * imm); if (rd == 0) { exp_rd = 0; } - gen_generic_test("mul", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("mul", rd, rs1, IMM, vs1, imm, exp_rd); + } +} + +static void +gen_nint_ss() +{ + // 期待値は NEAR で計算 + setround(RM_NEAR); + + for (uint32 rm = 0; rm < 4; rm++) { + for (const auto& t : param_trnc) { + VAR_DIADIC(t, rd, rs2, vs2); + // float で表現できない値は飛ばす + if (can_float(vs2)) + continue; + fpi fpi((float)vs2); + uint64 exp = calc_toint(rd, fpi); + outtest_toint("nint", rd, rs2, fpi, rm, exp); + } + } +} + +static void +gen_nint_sd() +{ + // 期待値は NEAR で計算 + setround(RM_NEAR); + + for (uint32 rm = 0; rm < 4; rm++) { + for (const auto& t : param_trnc) { + VAR_DIADIC(t, rd, rs2, vs2); + fpi fpi(vs2); + uint64 exp = calc_toint(rd, fpi); + outtest_toint("nint", rd, rs2, fpi, rm, exp); + } } } static void gen_or() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = vs1 | vs2; if (rd == 0) { exp_rd = 0; } - gen_generic_test("or", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("or", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_or_c() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = vs1 | ~vs2; if (rd == 0) { exp_rd = 0; } - gen_generic_test("or.c", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("or.c", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_or_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = vs1 | imm; if (rd == 0) { exp_rd = 0; } - gen_generic_test("or", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("or", rd, rs1, IMM, vs1, imm, exp_rd); } } static void gen_or_u() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = vs1 | (imm << 16); if (rd == 0) { exp_rd = 0; } - gen_generic_test("or.u", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("or.u", rd, rs1, IMM, vs1, imm, exp_rd); } } @@ -1266,13 +1805,13 @@ static void gen_rot_imm() { // 手抜きで W は無視するのでパターンが重複する - for (const auto& t : test_bitfield_imm) { + for (const auto& t : param_bitfield_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = calc_rot(vs1, imm); if (rd == 0) { exp_rd = 0; } - gen_bitfield_test("rot", rd, rs1, vs1, imm, exp_rd); + outtest_bitfield("rot", rd, rs1, vs1, imm, exp_rd); } } @@ -1280,13 +1819,13 @@ static void gen_rot_reg() { // 手抜きで W は無視するのでパターンが重複する - for (const auto& t : test_bitfield_reg) { + for (const auto& t : param_bitfield_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = calc_rot(vs1, vs2); if (rd == 0) { exp_rd = 0; } - gen_generic_test("rot", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("rot", rd, rs1, rs2, vs1, vs2, exp_rd); } } @@ -1302,33 +1841,33 @@ calc_set(uint32 src, uint32 wo) static void gen_set_imm() { - for (const auto& t : test_bitfield_imm) { + for (const auto& t : param_bitfield_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = calc_set(vs1, imm); if (rd == 0) { exp_rd = 0; } - gen_bitfield_test("set", rd, rs1, vs1, imm, exp_rd); + outtest_bitfield("set", rd, rs1, vs1, imm, exp_rd); } } static void gen_set_reg() { - for (const auto& t : test_bitfield_reg) { + for (const auto& t : param_bitfield_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = calc_set(vs1, vs2); if (rd == 0) { exp_rd = 0; } - gen_generic_test("set", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("set", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_sub() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -1344,7 +1883,7 @@ gen_sub() exp_cy = cin; } - gen_addsub_test("sub", + outtest_addsub("sub", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex); } } @@ -1353,7 +1892,7 @@ gen_sub() static void gen_sub_ci() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -1369,7 +1908,7 @@ gen_sub_ci() exp_cy = cin; } - gen_addsub_test("sub.ci", + outtest_addsub("sub.ci", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex); } } @@ -1378,7 +1917,7 @@ gen_sub_ci() static void gen_sub_co() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -1394,7 +1933,7 @@ gen_sub_co() exp_cy = cin; } - gen_addsub_test("sub.co", + outtest_addsub("sub.co", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex); } } @@ -1403,7 +1942,7 @@ gen_sub_co() static void gen_sub_cio() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -1419,7 +1958,7 @@ gen_sub_cio() exp_cy = cin; } - gen_addsub_test("sub.cio", + outtest_addsub("sub.cio", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy, exp_ex); } } @@ -1428,7 +1967,7 @@ gen_sub_cio() static void gen_sub_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); for (int cin = 0; cin <= 1; cin++) { @@ -1444,7 +1983,7 @@ gen_sub_imm() exp_cy = cin; } - gen_addsub_test("sub", + outtest_addsub("sub", rd, rs1, IMM, vs1, imm, cin, exp_rd, exp_cy, exp_ex); } } @@ -1453,7 +1992,7 @@ gen_sub_imm() static void gen_subu() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -1465,7 +2004,7 @@ gen_subu() exp_rd = 0; } - gen_addsub_test("subu", + outtest_addsub("subu", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy); } } @@ -1474,7 +2013,7 @@ gen_subu() static void gen_subu_ci() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -1486,7 +2025,7 @@ gen_subu_ci() exp_rd = 0; } - gen_addsub_test("subu.ci", + outtest_addsub("subu.ci", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy); } } @@ -1495,7 +2034,7 @@ gen_subu_ci() static void gen_subu_co() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -1507,7 +2046,7 @@ gen_subu_co() exp_rd = 0; } - gen_addsub_test("subu.co", + outtest_addsub("subu.co", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy); } } @@ -1516,7 +2055,7 @@ gen_subu_co() static void gen_subu_cio() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); for (int cin = 0; cin <= 1; cin++) { @@ -1528,7 +2067,7 @@ gen_subu_cio() exp_rd = 0; } - gen_addsub_test("subu.cio", + outtest_addsub("subu.cio", rd, rs1, rs2, vs1, vs2, cin, exp_rd, exp_cy); } } @@ -1537,7 +2076,7 @@ gen_subu_cio() static void gen_subu_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); for (int cin = 0; cin <= 1; cin++) { @@ -1549,61 +2088,96 @@ gen_subu_imm() exp_rd = 0; } - gen_addsub_test("subu", + outtest_addsub("subu", rd, rs1, IMM, vs1, imm, cin, exp_rd, exp_cy); } } } static void +gen_trnc_ss() +{ + // 期待値は ZERO で計算 + setround(RM_ZERO); + + for (uint32 rm = 0; rm < 4; rm++) { + for (const auto& t : param_trnc) { + VAR_DIADIC(t, rd, rs2, vs2); + // float で表現できない値は飛ばす + if (can_float(vs2)) + continue; + fpi fpi((float)vs2); + uint64 exp = calc_toint(rd, fpi); + outtest_toint("trnc", rd, rs2, fpi, rm, exp); + } + } +} + +static void +gen_trnc_sd() +{ + // 期待値は ZERO で計算 + setround(RM_ZERO); + + for (uint32 rm = 0; rm < 4; rm++) { + for (const auto& t : param_trnc) { + VAR_DIADIC(t, rd, rs2, vs2); + fpi fpi(vs2); + uint64 exp = calc_toint(rd, fpi); + outtest_toint("trnc", rd, rs2, fpi, rm, exp); + } + } +} + +static void gen_xor() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = vs1 ^ vs2; if (rd == 0) { exp_rd = 0; } - gen_generic_test("xor", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("xor", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_xor_c() { - for (const auto& t : test_arith_reg) { + for (const auto& t : param_arith_reg) { VAR_TRI_REG(t, rd, rs1, rs2, vs1, vs2); uint32 exp_rd = vs1 ^ ~vs2; if (rd == 0) { exp_rd = 0; } - gen_generic_test("xor.c", rd, rs1, rs2, vs1, vs2, exp_rd); + outtest_generic("xor.c", rd, rs1, rs2, vs1, vs2, exp_rd); } } static void gen_xor_imm() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = vs1 ^ imm; if (rd == 0) { exp_rd = 0; } - gen_generic_test("xor", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("xor", rd, rs1, IMM, vs1, imm, exp_rd); } } static void gen_xor_u() { - for (const auto& t : test_arith_imm) { + for (const auto& t : param_arith_imm) { VAR_TRI_IMM(t, rd, rs1, vs1, imm); uint32 exp_rd = vs1 ^ (imm << 16); if (rd == 0) { exp_rd = 0; } - gen_generic_test("xor.u", rd, rs1, IMM, vs1, imm, exp_rd); + outtest_generic("xor.u", rd, rs1, IMM, vs1, imm, exp_rd); } } @@ -1620,6 +2194,8 @@ enum { G_BITFIELD, G_LDA, G_MULDIV, + G_FLT, + G_FCMP, }; // テスト一覧 (編集しやすいように実体は一番下に置いてある) @@ -1627,6 +2203,7 @@ struct testentry { int group; const char *code; void (*genfunc)(); + int quad; // 結果が 64bit なら 1 const char *name; }; extern std::vector test_table; @@ -1656,6 +2233,54 @@ static void append(std::vector& list, compare_and_move(tmpname, filename); \ } while (0) +// src, dst のファイルが異なっていれば src を dst に rename する +static void +compare_and_move(const std::string& src, const std::string& dst) +{ + struct stat st; + int r; + bool update; + + update = false; + r = stat(dst.c_str(), &st); + if (r < 0) { + // dst not found + update = true; + } else { + // dst exists + + std::string cmd = "cmp -s " + src + " " + dst; + r = system(cmd.c_str()); + if (r < 0) { + err(1, "system"); + } + r = WEXITSTATUS(r); + if (r > 1) { + errx(1, "cmp failed?"); + } + + if (r == 0) { + // src and dst are the same + } else { + update = true; + } + } + + if (update) { + // 違っていれば、src を dst に移動 + r = rename(src.c_str(), dst.c_str()); + if (r < 0) { + err(1, "rename: %s %s", src.c_str(), dst.c_str()); + } + printf("Updated: %s\n", dst.c_str()); + } else { + // 同じなら、今作った src のほうを消す + r = remove(src.c_str()); + if (r < 0) { + err(1, "remove: %s", src.c_str()); + } + } +} // テストアセンブリソースの出力共通部分 static void print_test(const testentry& t) @@ -1677,6 +2302,22 @@ print_test(const testentry& t) out("\n"); } +// ソースファイルを作成 +static void +create_file(const std::string& filename_, int group) +{ + std::string filename; + std::string tmpname; + + FOPEN(filename_); + for (const auto& t : test_table) { + if (t.group == group) { + print_test(t); + } + } + FCLOSE(); +} + static void compare_and_move(const std::string&, const std::string&); int @@ -1696,14 +2337,14 @@ main(int ac, char *av[]) if (rs1 == rs2) { for (const auto& vs1 : *v_arith_s1) { - Tuple5 val(rd, rs1, rs2, vs1, vs1); - append(test_arith_reg, val); + Tuple3_2 val(rd, rs1, rs2, vs1, vs1); + append(param_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); - append(test_arith_reg, val); + Tuple3_2 val(rd, rs1, rs2, vs1, vs2); + append(param_arith_reg, val); } } } @@ -1717,8 +2358,8 @@ 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); - append(test_arith_imm, val); + Tuple2_2 val(rd, rs1, vs1, imm); + append(param_arith_imm, val); } } } @@ -1733,18 +2374,18 @@ main(int ac, char *av[]) if (rs1 == rs2) { for (const auto& vs1 : *v_bf) { - Tuple5 val(rd, rs1, rs2, vs1, vs1); - append(test_bitfield_reg, val); + Tuple3_2 val(rd, rs1, rs2, vs1, vs1); + append(param_bitfield_reg, val); } for (const auto& vs2 : *v_wo) { - Tuple5 val(rd, rs1, rs2, vs2, vs2); - append(test_bitfield_reg, val); + Tuple3_2 val(rd, rs1, rs2, vs2, vs2); + append(param_bitfield_reg, val); } } else { for (const auto& vs1 : *v_bf) { for (const auto& vs2 : *v_wo) { - Tuple5 val(rd, rs1, rs2, vs1, vs2); - append(test_bitfield_reg, val); + Tuple3_2 val(rd, rs1, rs2, vs1, vs2); + append(param_bitfield_reg, val); } } } @@ -1757,8 +2398,8 @@ main(int ac, char *av[]) for (const auto& vs1 : *v_bf) { for (const auto& wo : vlist_wo) { - Tuple4 val(rd, rs1, vs1, wo); - append(test_bitfield_imm, val); + Tuple2_2 val(rd, rs1, vs1, wo); + append(param_bitfield_imm, val); } } } @@ -1778,76 +2419,68 @@ main(int ac, char *av[]) } for (const auto& vs2 : v_ff) { - Tuple3 val(rd, rs2, vs2); - append(test_ff01, val); + Tuple2_1 val(rd, rs2, vs2); + append(param_ff01, val); } } + for (const auto& rlist : reglist2) { + auto rd = (rlist >> 16) & 0xff; + auto rs2 = (rlist >> 8) & 0xff; - // - // add.s を出力 - // - FOPEN("optestm88k_add.s"); - for (const auto& t : test_table) { - if (t.group == G_ADD) { - print_test(t); - } - } - FCLOSE(); + std::vector *v_arith_s2 = (rs2 == 0) ? &v0 : &vlist_arith; - // - // sub.s を出力 - // - FOPEN("optestm88k_sub.s"); - for (const auto& t : test_table) { - if (t.group == G_SUB) { - print_test(t); + for (const auto& vs2 : *v_arith_s2) { + Tuple2_1 val(rd, rs2, vs2); + append(param_flt, val); } } - FCLOSE(); + for (const auto& rlist : reglist2) { + auto rd = (rlist >> 16) & 0xff; + auto rs2 = (rlist >> 8) & 0xff; - // - // logical.s を出力 - // - FOPEN("optestm88k_logical.s"); - for (const auto& t : test_table) { - if (t.group == G_LOGICAL) { - print_test(t); + if (rs2 == 0) { + Tuple2_1 val(rd, rs2, 0.0); + append(param_trnc, val); + } else { + for (const auto& vs2 : vlist_double) { + Tuple2_1 val(rd, rs2, vs2); + append(param_trnc, val); + } } } - FCLOSE(); - // - // bitfield.s を出力 - // - FOPEN("optestm88k_bitfield.s"); - for (const auto& t : test_table) { - if (t.group == G_BITFIELD) { - print_test(t); - } - } - FCLOSE(); + for (const auto& rlist : reglist3) { + auto rd = (rlist >> 16) & 0xff; + auto rs1 = (rlist >> 8) & 0xff; + auto rs2 = rlist & 0xff; - // - // lda.s を出力 - // - FOPEN("optestm88k_lda.s"); - for (const auto& t : test_table) { - if (t.group == G_LDA) { - print_test(t); - } - } - FCLOSE(); + std::vector *vlist1 = (rs1 == 0) ? &vf0 : &vlist_double; + std::vector *vlist2 = (rs2 == 0) ? &vf0 : &vlist_double; - // - // muldiv.s を出力 - // - FOPEN("optestm88k_muldiv.s"); - for (const auto& t : test_table) { - if (t.group == G_MULDIV) { - print_test(t); + if (rs1 == rs2) { + for (const auto& vs1 : *vlist1) { + Tuple3_2 val(rd, rs1, rs2, vs1, vs1); + append(param_fcmp, val); + } + } else { + for (const auto& vs1 : *vlist1) { + for (const auto& vs2 : *vlist2) { + Tuple3_2 val(rd, rs1, rs2, vs1, vs2); + append(param_fcmp, val); + } + } } } - FCLOSE(); + + // ソースファイルを出力 + create_file("optestm88k_add.s", G_ADD); + create_file("optestm88k_sub.s", G_SUB); + create_file("optestm88k_logical.s", G_LOGICAL); + create_file("optestm88k_bitfield.s",G_BITFIELD); + create_file("optestm88k_lda.s", G_LDA); + create_file("optestm88k_muldiv.s", G_MULDIV); + create_file("optestm88k_flt.s", G_FLT); + create_file("optestm88k_fcmp.s", G_FCMP); // // table.h を出力 @@ -1882,140 +2515,106 @@ main(int ac, char *av[]) out("\n"); out("struct testentry test_table[] = {\n"); for (const auto& t : test_table) { - out(" { test_%s,\tlist_%s,\t\"%s\" },\n", t.code, t.name, t.name); + out(" { test_%s,\tlist_%s,\t%d, \"%s\" },\n", + t.code, t.name, t.quad, t.name); } - out(" { NULL, NULL, NULL },\n"); + out(" { NULL },\n"); out("};\n"); FCLOSE(); return 0; } -// src, dst のファイルが異なっていれば src を dst に rename する -static void -compare_and_move(const std::string& src, const std::string& dst) -{ - struct stat st; - int r; - bool update; - - update = false; - r = stat(dst.c_str(), &st); - if (r < 0) { - // dst not found - update = true; - } else { - // dst exists - - std::string cmd = "cmp -s " + src + " " + dst; - r = system(cmd.c_str()); - if (r < 0) { - err(1, "system"); - } - r = WEXITSTATUS(r); - if (r > 1) { - errx(1, "cmp failed?"); - } - - if (r == 0) { - // src and dst are the same - } else { - update = true; - } - } - - if (update) { - // 違っていれば、src を dst に移動 - r = rename(src.c_str(), dst.c_str()); - if (r < 0) { - err(1, "rename: %s %s", src.c_str(), dst.c_str()); - } - printf("Updated: %s\n", dst.c_str()); - } else { - // 同じなら、今作った src のほうを消す - r = remove(src.c_str()); - if (r < 0) { - err(1, "remove: %s", src.c_str()); - } - } -} - // // テスト一覧 // -#define T(group, code, name) { group, code, gen_##name, #name } +#define T(group, code, quad, name) { group, code, gen_##name, quad, #name } std::vector test_table = { - T(G_ADD, "addsub", add), - T(G_ADD, "addsub", add_ci), - T(G_ADD, "addsub", add_co), - T(G_ADD, "addsub", add_cio), - T(G_ADD, "addsub", add_imm), - T(G_ADD, "addsub", addu), - T(G_ADD, "addsub", addu_ci), - T(G_ADD, "addsub", addu_co), - T(G_ADD, "addsub", addu_cio), - T(G_ADD, "addsub", addu_imm), - - T(G_SUB, "addsub", sub), - T(G_SUB, "addsub", sub_ci), - T(G_SUB, "addsub", sub_co), - T(G_SUB, "addsub", sub_cio), - T(G_SUB, "addsub", sub_imm), - T(G_SUB, "addsub", subu), - T(G_SUB, "addsub", subu_ci), - T(G_SUB, "addsub", subu_co), - T(G_SUB, "addsub", subu_cio), - T(G_SUB, "addsub", subu_imm), - T(G_SUB, "generic", cmp), - T(G_SUB, "generic", cmp_imm), - - T(G_LOGICAL, "generic", and), - T(G_LOGICAL, "generic", and_c), - T(G_LOGICAL, "generic", and_imm), - T(G_LOGICAL, "generic", and_u), - T(G_LOGICAL, "generic", mask_imm), - T(G_LOGICAL, "generic", mask_u), - T(G_LOGICAL, "generic", or), - T(G_LOGICAL, "generic", or_c), - T(G_LOGICAL, "generic", or_imm), - T(G_LOGICAL, "generic", or_u), - T(G_LOGICAL, "generic", xor), - T(G_LOGICAL, "generic", xor_c), - T(G_LOGICAL, "generic", xor_imm), - T(G_LOGICAL, "generic", xor_u), - - T(G_BITFIELD, "generic", clr_imm), - T(G_BITFIELD, "generic", clr_reg), - T(G_BITFIELD, "generic", ext_imm), - T(G_BITFIELD, "generic", ext_reg), - T(G_BITFIELD, "generic", extu_imm), - T(G_BITFIELD, "generic", extu_reg), - T(G_BITFIELD, "generic", ff0), - T(G_BITFIELD, "generic", ff1), - T(G_BITFIELD, "generic", mak_imm), - T(G_BITFIELD, "generic", mak_reg), - T(G_BITFIELD, "generic", rot_imm), - T(G_BITFIELD, "generic", rot_reg), - T(G_BITFIELD, "generic", set_imm), - T(G_BITFIELD, "generic", set_reg), - - T(G_LDA, "addsub", lda_imm), - T(G_LDA, "addsub", lda_b_imm), - T(G_LDA, "addsub", lda_h_imm), - T(G_LDA, "addsub", lda_d_imm), - T(G_LDA, "addsub", lda_reg), - T(G_LDA, "addsub", lda_b_reg), - T(G_LDA, "addsub", lda_h_reg), - T(G_LDA, "addsub", lda_d_reg), - T(G_LDA, "addsub", lda_scale), - T(G_LDA, "addsub", lda_b_scale), - T(G_LDA, "addsub", lda_h_scale), - T(G_LDA, "addsub", lda_d_scale), - - T(G_MULDIV, "generic", mul), - T(G_MULDIV, "generic", mul_imm), - T(G_MULDIV, "div", div), - T(G_MULDIV, "div", div_imm), - T(G_MULDIV, "div", divu), - T(G_MULDIV, "div", divu_imm), + T(G_ADD, "addsub", 0, add), + T(G_ADD, "addsub", 0, add_ci), + T(G_ADD, "addsub", 0, add_co), + T(G_ADD, "addsub", 0, add_cio), + T(G_ADD, "addsub", 0, add_imm), + T(G_ADD, "addsub", 0, addu), + T(G_ADD, "addsub", 0, addu_ci), + T(G_ADD, "addsub", 0, addu_co), + T(G_ADD, "addsub", 0, addu_cio), + T(G_ADD, "addsub", 0, addu_imm), + + T(G_SUB, "addsub", 0, sub), + T(G_SUB, "addsub", 0, sub_ci), + T(G_SUB, "addsub", 0, sub_co), + T(G_SUB, "addsub", 0, sub_cio), + T(G_SUB, "addsub", 0, sub_imm), + T(G_SUB, "addsub", 0, subu), + T(G_SUB, "addsub", 0, subu_ci), + T(G_SUB, "addsub", 0, subu_co), + T(G_SUB, "addsub", 0, subu_cio), + T(G_SUB, "addsub", 0, subu_imm), + T(G_SUB, "generic", 0, cmp), + T(G_SUB, "generic", 0, cmp_imm), + + T(G_LOGICAL, "generic", 0, and), + T(G_LOGICAL, "generic", 0, and_c), + T(G_LOGICAL, "generic", 0, and_imm), + T(G_LOGICAL, "generic", 0, and_u), + T(G_LOGICAL, "generic", 0, mask_imm), + T(G_LOGICAL, "generic", 0, mask_u), + T(G_LOGICAL, "generic", 0, or), + T(G_LOGICAL, "generic", 0, or_c), + T(G_LOGICAL, "generic", 0, or_imm), + T(G_LOGICAL, "generic", 0, or_u), + T(G_LOGICAL, "generic", 0, xor), + T(G_LOGICAL, "generic", 0, xor_c), + T(G_LOGICAL, "generic", 0, xor_imm), + T(G_LOGICAL, "generic", 0, xor_u), + + T(G_BITFIELD, "generic", 0, clr_imm), + T(G_BITFIELD, "generic", 0, clr_reg), + T(G_BITFIELD, "generic", 0, ext_imm), + T(G_BITFIELD, "generic", 0, ext_reg), + T(G_BITFIELD, "generic", 0, extu_imm), + T(G_BITFIELD, "generic", 0, extu_reg), + T(G_BITFIELD, "generic", 0, ff0), + T(G_BITFIELD, "generic", 0, ff1), + T(G_BITFIELD, "generic", 0, mak_imm), + T(G_BITFIELD, "generic", 0, mak_reg), + T(G_BITFIELD, "generic", 0, rot_imm), + T(G_BITFIELD, "generic", 0, rot_reg), + T(G_BITFIELD, "generic", 0, set_imm), + T(G_BITFIELD, "generic", 0, set_reg), + + T(G_LDA, "addsub", 0, lda_imm), + T(G_LDA, "addsub", 0, lda_b_imm), + T(G_LDA, "addsub", 0, lda_h_imm), + T(G_LDA, "addsub", 0, lda_d_imm), + T(G_LDA, "addsub", 0, lda_reg), + T(G_LDA, "addsub", 0, lda_b_reg), + T(G_LDA, "addsub", 0, lda_h_reg), + T(G_LDA, "addsub", 0, lda_d_reg), + T(G_LDA, "addsub", 0, lda_scale), + T(G_LDA, "addsub", 0, lda_b_scale), + T(G_LDA, "addsub", 0, lda_h_scale), + T(G_LDA, "addsub", 0, lda_d_scale), + + T(G_MULDIV, "generic", 0, mul), + T(G_MULDIV, "generic", 0, mul_imm), + T(G_MULDIV, "div", 0, div), + T(G_MULDIV, "div", 0, div_imm), + T(G_MULDIV, "div", 0, divu), + T(G_MULDIV, "div", 0, divu_imm), + + T(G_FLT, "fop_s", 0, flt_ss), + T(G_FLT, "fop_d", 1, flt_ds), + T(G_FLT, "fop_s", 0, trnc_ss), + T(G_FLT, "fop_s", 0, trnc_sd), + T(G_FLT, "fop_s", 0, nint_ss), + T(G_FLT, "fop_s", 0, nint_sd), + T(G_FLT, "fop_s", 0, int_ss), + T(G_FLT, "fop_s", 0, int_sd), + + T(G_FCMP, "fop_s", 0, fcmp_sss), + T(G_FCMP, "fop_s", 0, fcmp_ssd), + T(G_FCMP, "fop_s", 0, fcmp_sds), + T(G_FCMP, "fop_s", 0, fcmp_sdd), };