aboutsummaryrefslogtreecommitdiff
path: root/disas
diff options
context:
space:
mode:
authorChristoph Müllner <christoph.muellner@vrull.eu>2023-07-10 09:12:43 +0200
committerAlistair Francis <alistair.francis@wdc.com>2023-07-10 22:29:20 +1000
commita47842d16653b4f73b5d56ff0c252dd8a329481b (patch)
treef6d68188329b7d7119400614dc6ba2c32057f5ab /disas
parentb9f822215ee58b57863bc082c322bb88529bd958 (diff)
downloadqemu-a47842d16653b4f73b5d56ff0c252dd8a329481b.zip
qemu-a47842d16653b4f73b5d56ff0c252dd8a329481b.tar.gz
qemu-a47842d16653b4f73b5d56ff0c252dd8a329481b.tar.bz2
riscv: Add support for the Zfa extension
This patch introduces the RISC-V Zfa extension, which introduces additional floating-point instructions: * fli (load-immediate) with pre-defined immediates * fminm/fmaxm (like fmin/fmax but with different NaN behaviour) * fround/froundmx (round to integer) * fcvtmod.w.d (Modular Convert-to-Integer) * fmv* to access high bits of float register bigger than XLEN * Quiet comparison instructions (fleq/fltq) Zfa defines its instructions in combination with the following extensions: * single-precision floating-point (F) * double-precision floating-point (D) * quad-precision floating-point (Q) * half-precision floating-point (Zfh) Since QEMU does not support the RISC-V quad-precision floating-point ISA extension (Q), this patch does not include the instructions that depend on this extension. All other instructions are included in this patch. The Zfa specification can be found here: https://github.com/riscv/riscv-isa-manual/blob/master/src/zfa.tex The Zfa specifciation is frozen and is in public review since May 3, 2023: https://groups.google.com/a/groups.riscv.org/g/isa-dev/c/SED4ntBkabg The patch also includes a TCG test for the fcvtmod.w.d instruction. The test cases test for correct results and flag behaviour. Note, that the Zfa specification requires fcvtmod's flag behaviour to be identical to a fcvt with the same operands (which is also tested). Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Christoph Müllner <christoph.muellner@vrull.eu> Message-Id: <20230710071243.282464-1-christoph.muellner@vrull.eu> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Diffstat (limited to 'disas')
-rw-r--r--disas/riscv.c139
-rw-r--r--disas/riscv.h3
2 files changed, 142 insertions, 0 deletions
diff --git a/disas/riscv.c b/disas/riscv.c
index 9f0195b..cd7b6e8 100644
--- a/disas/riscv.c
+++ b/disas/riscv.c
@@ -829,6 +829,39 @@ typedef enum {
rv_op_fsh = 798,
rv_op_fmv_h_x = 799,
rv_op_fmv_x_h = 800,
+ rv_op_fli_s = 801,
+ rv_op_fli_d = 802,
+ rv_op_fli_q = 803,
+ rv_op_fli_h = 804,
+ rv_op_fminm_s = 805,
+ rv_op_fmaxm_s = 806,
+ rv_op_fminm_d = 807,
+ rv_op_fmaxm_d = 808,
+ rv_op_fminm_q = 809,
+ rv_op_fmaxm_q = 810,
+ rv_op_fminm_h = 811,
+ rv_op_fmaxm_h = 812,
+ rv_op_fround_s = 813,
+ rv_op_froundnx_s = 814,
+ rv_op_fround_d = 815,
+ rv_op_froundnx_d = 816,
+ rv_op_fround_q = 817,
+ rv_op_froundnx_q = 818,
+ rv_op_fround_h = 819,
+ rv_op_froundnx_h = 820,
+ rv_op_fcvtmod_w_d = 821,
+ rv_op_fmvh_x_d = 822,
+ rv_op_fmvp_d_x = 823,
+ rv_op_fmvh_x_q = 824,
+ rv_op_fmvp_q_x = 825,
+ rv_op_fleq_s = 826,
+ rv_op_fltq_s = 827,
+ rv_op_fleq_d = 828,
+ rv_op_fltq_d = 829,
+ rv_op_fleq_q = 830,
+ rv_op_fltq_q = 831,
+ rv_op_fleq_h = 832,
+ rv_op_fltq_h = 833,
} rv_op;
/* register names */
@@ -854,6 +887,23 @@ static const char rv_vreg_name_sym[32][4] = {
"v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"
};
+/* The FLI.[HSDQ] numeric constants (0.0 for symbolic constants).
+ * The constants use the hex floating-point literal representation
+ * that is printed when using the printf %a format specifier,
+ * which matches the output that is generated by the disassembler.
+ */
+static const char rv_fli_name_const[32][9] =
+{
+ "0x1p+0", "min", "0x1p-16", "0x1p-15",
+ "0x1p-8", "0x1p-7", "0x1p-4", "0x1p-3",
+ "0x1p-2", "0x1.4p-2", "0x1.8p-2", "0x1.cp-2",
+ "0x1p-1", "0x1.4p-1", "0x1.8p-1", "0x1.cp-1",
+ "0x1p+0", "0x1.4p+0", "0x1.8p+0", "0x1.cp+0",
+ "0x1p+1", "0x1.4p+1", "0x1.8p+1", "0x1p+2",
+ "0x1p+3", "0x1p+4", "0x1p+7", "0x1p+8",
+ "0x1p+15", "0x1p+16", "inf", "nan"
+};
+
/* pseudo-instruction constraints */
static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end };
@@ -1925,6 +1975,39 @@ const rv_opcode_data rvi_opcode_data[] = {
{ "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 },
{ "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
{ "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+ { "fli.s", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
+ { "fli.d", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
+ { "fli.q", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
+ { "fli.h", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
+ { "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+ { "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+ { "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+ { "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 },
+ { "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+ { "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 },
+ { "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+ { "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
};
/* CSR names */
@@ -2864,24 +2947,38 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
switch ((inst >> 12) & 0b111) {
case 0: op = rv_op_fmin_s; break;
case 1: op = rv_op_fmax_s; break;
+ case 2: op = rv_op_fminm_s; break;
+ case 3: op = rv_op_fmaxm_s; break;
}
break;
case 21:
switch ((inst >> 12) & 0b111) {
case 0: op = rv_op_fmin_d; break;
case 1: op = rv_op_fmax_d; break;
+ case 2: op = rv_op_fminm_d; break;
+ case 3: op = rv_op_fmaxm_d; break;
+ }
+ break;
+ case 22:
+ switch (((inst >> 12) & 0b111)) {
+ case 2: op = rv_op_fminm_h; break;
+ case 3: op = rv_op_fmaxm_h; break;
}
break;
case 23:
switch ((inst >> 12) & 0b111) {
case 0: op = rv_op_fmin_q; break;
case 1: op = rv_op_fmax_q; break;
+ case 2: op = rv_op_fminm_q; break;
+ case 3: op = rv_op_fmaxm_q; break;
}
break;
case 32:
switch ((inst >> 20) & 0b11111) {
case 1: op = rv_op_fcvt_s_d; break;
case 3: op = rv_op_fcvt_s_q; break;
+ case 4: op = rv_op_fround_s; break;
+ case 5: op = rv_op_froundnx_s; break;
case 6: op = rv_op_fcvt_s_bf16; break;
}
break;
@@ -2889,10 +2986,14 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
switch ((inst >> 20) & 0b11111) {
case 0: op = rv_op_fcvt_d_s; break;
case 3: op = rv_op_fcvt_d_q; break;
+ case 4: op = rv_op_fround_d; break;
+ case 5: op = rv_op_froundnx_d; break;
}
break;
case 34:
switch (((inst >> 20) & 0b11111)) {
+ case 4: op = rv_op_fround_h; break;
+ case 5: op = rv_op_froundnx_h; break;
case 8: op = rv_op_fcvt_bf16_s; break;
}
break;
@@ -2900,6 +3001,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
switch ((inst >> 20) & 0b11111) {
case 0: op = rv_op_fcvt_q_s; break;
case 1: op = rv_op_fcvt_q_d; break;
+ case 4: op = rv_op_fround_q; break;
+ case 5: op = rv_op_froundnx_q; break;
}
break;
case 44:
@@ -2922,6 +3025,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 0: op = rv_op_fle_s; break;
case 1: op = rv_op_flt_s; break;
case 2: op = rv_op_feq_s; break;
+ case 4: op = rv_op_fleq_s; break;
+ case 5: op = rv_op_fltq_s; break;
}
break;
case 81:
@@ -2929,6 +3034,14 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 0: op = rv_op_fle_d; break;
case 1: op = rv_op_flt_d; break;
case 2: op = rv_op_feq_d; break;
+ case 4: op = rv_op_fleq_d; break;
+ case 5: op = rv_op_fltq_d; break;
+ }
+ break;
+ case 82:
+ switch (((inst >> 12) & 0b111)) {
+ case 4: op = rv_op_fleq_h; break;
+ case 5: op = rv_op_fltq_h; break;
}
break;
case 83:
@@ -2936,6 +3049,18 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 0: op = rv_op_fle_q; break;
case 1: op = rv_op_flt_q; break;
case 2: op = rv_op_feq_q; break;
+ case 4: op = rv_op_fleq_q; break;
+ case 5: op = rv_op_fltq_q; break;
+ }
+ break;
+ case 89:
+ switch (((inst >> 12) & 0b111)) {
+ case 0: op = rv_op_fmvp_d_x; break;
+ }
+ break;
+ case 91:
+ switch (((inst >> 12) & 0b111)) {
+ case 0: op = rv_op_fmvp_q_x; break;
}
break;
case 96:
@@ -2952,6 +3077,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
case 1: op = rv_op_fcvt_wu_d; break;
case 2: op = rv_op_fcvt_l_d; break;
case 3: op = rv_op_fcvt_lu_d; break;
+ case 8: op = rv_op_fcvtmod_w_d; break;
}
break;
case 99:
@@ -2998,6 +3124,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_x_d; break;
case 1: op = rv_op_fclass_d; break;
+ case 8: op = rv_op_fmvh_x_d; break;
}
break;
case 114:
@@ -3011,30 +3138,35 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_x_q; break;
case 1: op = rv_op_fclass_q; break;
+ case 8: op = rv_op_fmvh_x_q; break;
}
break;
case 120:
switch (((inst >> 17) & 0b11111000) |
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_s_x; break;
+ case 8: op = rv_op_fli_s; break;
}
break;
case 121:
switch (((inst >> 17) & 0b11111000) |
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_d_x; break;
+ case 8: op = rv_op_fli_d; break;
}
break;
case 122:
switch (((inst >> 17) & 0b11111000) |
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_h_x; break;
+ case 8: op = rv_op_fli_h; break;
}
break;
case 123:
switch (((inst >> 17) & 0b11111000) |
((inst >> 12) & 0b00000111)) {
case 0: op = rv_op_fmv_q_x; break;
+ case 8: op = rv_op_fli_q; break;
}
break;
}
@@ -4298,6 +4430,10 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa)
break;
case rv_codec_zcmt_jt:
dec->imm = operand_tbl_index(inst);
+ break;
+ case rv_codec_fli:
+ dec->rd = operand_rd(inst);
+ dec->imm = operand_rs1(inst);
break;
case rv_codec_r2_imm5:
dec->rd = operand_rd(inst);
@@ -4708,6 +4844,9 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
append(buf, tmp, buflen);
break;
}
+ case 'h':
+ append(buf, rv_fli_name_const[dec->imm], buflen);
+ break;
default:
break;
}
diff --git a/disas/riscv.h b/disas/riscv.h
index 052a0c4..9cf901f 100644
--- a/disas/riscv.h
+++ b/disas/riscv.h
@@ -165,6 +165,7 @@ typedef enum {
rv_codec_r_imm2,
rv_codec_r2_immhl,
rv_codec_r2_imm2_imm5,
+ rv_codec_fli,
} rv_codec;
/* structures */
@@ -229,6 +230,7 @@ enum {
#define rv_fmt_rd_offset "O\t0,o"
#define rv_fmt_rd_rs1_rs2 "O\t0,1,2"
#define rv_fmt_frd_rs1 "O\t3,1"
+#define rv_fmt_frd_rs1_rs2 "O\t3,1,2"
#define rv_fmt_frd_frs1 "O\t3,4"
#define rv_fmt_rd_frs1 "O\t0,4"
#define rv_fmt_rd_frs1_frs2 "O\t0,4,5"
@@ -295,5 +297,6 @@ enum {
#define rv_fmt_rd_rs1_immh_imml "O\t0,1,i,j"
#define rv_fmt_rd_rs1_immh_imml_addr "O\t0,(1),i,j"
#define rv_fmt_rd2_imm "O\t0,2,(1),i"
+#define rv_fmt_fli "O\t3,h"
#endif /* DISAS_RISCV_H */