diff --git a/src/decode.h b/src/decode.h index 08974fd79..dfbfdbcfd 100644 --- a/src/decode.h +++ b/src/decode.h @@ -827,6 +827,12 @@ enum op_field { _(vfwmsac_vf, 0, 4, 0, ENC(rs1, rs2, vd)) \ _(vfwnmsac_vv, 0, 4, 0, ENC(rs1, rs2, vd)) \ _(vfwnmsac_vf, 0, 4, 0, ENC(rs1, rs2, vd)) \ + _(vfcvt_xu_f_v, 0, 4, 0, ENC(rs1, rs2, vd)) \ + _(vfcvt_x_f_v, 0, 4, 0, ENC(rs1, rs2, vd)) \ + _(vfcvt_f_xu_v, 0, 4, 0, ENC(rs1, rs2, vd)) \ + _(vfcvt_f_x_v, 0, 4, 0, ENC(rs1, rs2, vd)) \ + _(vfcvt_rtz_xu_f_v, 0, 4, 0, ENC(rs1, rs2, vd)) \ + _(vfcvt_rtz_x_f_v, 0, 4, 0, ENC(rs1, rs2, vd)) \ ) /* clang-format on */ diff --git a/src/decode_v.c b/src/decode_v.c index 84ff5c49d..7ccf8dbba 100644 --- a/src/decode_v.c +++ b/src/decode_v.c @@ -980,8 +980,41 @@ static inline bool op_010010(rv_insn_t *ir, const uint32_t insn) ir->opcode = rv_insn_vsbc_vvm; break; case 1: - /* FIXME: Implement the decoding for VFUNARY0. */ - return false; + /* OPFVV / VFUNARY0 dispatch (V 1.0 §13.17): vs1 carries the + * sub-opcode. Only the single-width conversions are decoded here; + * the widening (vfwcvt, vs1 0x08-0x0f) and narrowing (vfncvt, + * vs1 0x10-0x17) forms are not implemented yet and fall through + * to the reject below rather than being misdecoded. + */ + switch (decode_rs1(insn)) { + case 0b00000: + decode_vvtype(ir, insn); + ir->opcode = rv_insn_vfcvt_xu_f_v; + break; + case 0b00001: + decode_vvtype(ir, insn); + ir->opcode = rv_insn_vfcvt_x_f_v; + break; + case 0b00010: + decode_vvtype(ir, insn); + ir->opcode = rv_insn_vfcvt_f_xu_v; + break; + case 0b00011: + decode_vvtype(ir, insn); + ir->opcode = rv_insn_vfcvt_f_x_v; + break; + case 0b00110: + decode_vvtype(ir, insn); + ir->opcode = rv_insn_vfcvt_rtz_xu_f_v; + break; + case 0b00111: + decode_vvtype(ir, insn); + ir->opcode = rv_insn_vfcvt_rtz_x_f_v; + break; + default: + return false; + } + break; case 2: /* OPMVV / VXUNARY0 (vzext.vf{2,4,8} and vsext.vf{2,4,8}). The * RVOPs for these are not implemented yet, so refuse the encoding diff --git a/src/rv32_v_constopt.c b/src/rv32_v_constopt.c index 17c56d471..ac78cb0df 100644 --- a/src/rv32_v_constopt.c +++ b/src/rv32_v_constopt.c @@ -538,6 +538,12 @@ CONSTOPT(vmsof_m, {}) CONSTOPT(vmsif_m, {}) CONSTOPT(viota_m, {}) CONSTOPT(vid_v, {}) +CONSTOPT(vfcvt_xu_f_v, {}) +CONSTOPT(vfcvt_x_f_v, {}) +CONSTOPT(vfcvt_f_xu_v, {}) +CONSTOPT(vfcvt_f_x_v, {}) +CONSTOPT(vfcvt_rtz_xu_f_v, {}) +CONSTOPT(vfcvt_rtz_x_f_v, {}) CONSTOPT(vfadd_vv, {}) CONSTOPT(vfadd_vf, {}) CONSTOPT(vfredusum_vs, {}) diff --git a/src/rv32_v_template.c b/src/rv32_v_template.c index e30263fc4..f8a89bbd4 100644 --- a/src/rv32_v_template.c +++ b/src/rv32_v_template.c @@ -6695,6 +6695,44 @@ static inline uint32_t rvv_fp_fnmsub32(uint32_t dest, .v; } +/* Single-width conversions for the vfcvt family (V 1.0 §13.17). The + * float-to-integer forms take an explicit rounding mode so the .rtz + * variants can pin round-towards-zero while the plain forms follow the + * dynamic mode already installed by rvv_fp_begin_round(). The trailing + * "exact" argument is false because these conversions signal inexact + * results through fflags rather than trapping. + */ +static inline uint32_t rvv_fp_cvt_xu_f32(uint32_t bits) +{ + return f32_to_ui32(rvv_fp32_from_raw(bits), softfloat_roundingMode, false); +} + +static inline uint32_t rvv_fp_cvt_x_f32(uint32_t bits) +{ + return (uint32_t) f32_to_i32(rvv_fp32_from_raw(bits), + softfloat_roundingMode, false); +} + +static inline uint32_t rvv_fp_cvt_rtz_xu_f32(uint32_t bits) +{ + return f32_to_ui32_r_minMag(rvv_fp32_from_raw(bits), false); +} + +static inline uint32_t rvv_fp_cvt_rtz_x_f32(uint32_t bits) +{ + return (uint32_t) f32_to_i32_r_minMag(rvv_fp32_from_raw(bits), false); +} + +static inline uint32_t rvv_fp_cvt_f_xu32(uint32_t bits) +{ + return ui32_to_f32(bits).v; +} + +static inline uint32_t rvv_fp_cvt_f_x32(uint32_t bits) +{ + return i32_to_f32((int32_t) bits).v; +} + static inline uint64_t rvv_fp_widen32(uint32_t bits) { return f32_to_f64(rvv_fp32_from_raw(bits)).v; @@ -6828,6 +6866,36 @@ static inline void rvv_exec_fp32_vf(riscv_t *rv, rv->csr_vstart = 0; } +/* Single-width unary element loop for the vfcvt family (V 1.0 §13.17): + * one source element in, one same-width destination element out. + */ +typedef uint32_t (*rvv_fp32_unop_fn)(uint32_t src); + +static inline void rvv_exec_fp32_unary(riscv_t *rv, + const rv_insn_t *ir, + uint32_t dest, + rvv_fp32_unop_fn op) +{ + uint32_t vlmax = rvv_vlmax(rv->csr_vtype); + uint8_t vma = (rv->csr_vtype >> 7) & 0x1; + uint8_t vta = (rv->csr_vtype >> 6) & 0x1; + + for (uint32_t elem = rv->csr_vstart; elem < rv->csr_vl; elem++) { + if (!rvv_mask_enabled_for_elem(rv, ir, elem)) { + if (vma) + rvv_set_elem(rv, dest, elem, 32, 0xFFFFFFFFU); + continue; + } + rvv_set_elem(rv, dest, elem, 32, + op(rvv_get_elem(rv, ir->vs2, elem, 32))); + } + if (vta) { + for (uint32_t elem = rv->csr_vl; elem < vlmax; elem++) + rvv_set_elem(rv, dest, elem, 32, 0xFFFFFFFFU); + } + rv->csr_vstart = 0; +} + static inline void rvv_exec_fp32_mask_vv(riscv_t *rv, const rv_insn_t *ir, uint32_t dest, @@ -7448,6 +7516,29 @@ static inline void rvv_exec_vfmv_v_f(riscv_t *rv, set_fflag(rv); \ }) +#define RVV_FP32_CVT_OP(name, opfn, dynamic_round) \ + RVOP(name, { \ + if (rvv_require_operable(rv)) \ + return false; \ + if ((rvv_sew_bits(rv->csr_vtype) != 32) || \ + !rvv_validate_data_reg(rv->csr_vtype, ir->vd) || \ + !rvv_validate_data_reg(rv->csr_vtype, ir->vs2)) \ + return rvv_trap_illegal_state(rv, 0); \ + if (dynamic_round) \ + rvv_fp_begin_round(rv); \ + else \ + rvv_fp_begin_flags(); \ + rvv_exec_fp32_unary(rv, ir, ir->vd, opfn); \ + set_fflag(rv); \ + }) + +RVV_FP32_CVT_OP(vfcvt_xu_f_v, rvv_fp_cvt_xu_f32, true); +RVV_FP32_CVT_OP(vfcvt_x_f_v, rvv_fp_cvt_x_f32, true); +RVV_FP32_CVT_OP(vfcvt_f_xu_v, rvv_fp_cvt_f_xu32, true); +RVV_FP32_CVT_OP(vfcvt_f_x_v, rvv_fp_cvt_f_x32, true); +RVV_FP32_CVT_OP(vfcvt_rtz_xu_f_v, rvv_fp_cvt_rtz_xu_f32, false); +RVV_FP32_CVT_OP(vfcvt_rtz_x_f_v, rvv_fp_cvt_rtz_x_f32, false); + RVV_FP32_VV_OP(vfadd_vv, rvv_fp_add32, true); RVV_FP32_VF_OP(vfadd_vf, rvv_fp_add32, true); RVV_FP32_RED_OP(vfredusum_vs, rvv_fp_add32, true); @@ -7619,6 +7710,12 @@ RVV_FP64_MAC_VF_OP(vfwmsac_vf, rvv_fp_wmsac64); RVV_FP64_MAC_VV_OP(vfwnmsac_vv, rvv_fp_wnmsac64); RVV_FP64_MAC_VF_OP(vfwnmsac_vf, rvv_fp_wnmsac64); #else +RVOP(vfcvt_xu_f_v, { V_NOP; }) +RVOP(vfcvt_x_f_v, { V_NOP; }) +RVOP(vfcvt_f_xu_v, { V_NOP; }) +RVOP(vfcvt_f_x_v, { V_NOP; }) +RVOP(vfcvt_rtz_xu_f_v, { V_NOP; }) +RVOP(vfcvt_rtz_x_f_v, { V_NOP; }) RVOP(vfadd_vv, { V_NOP; }) RVOP(vfadd_vf, { V_NOP; }) RVOP(vfredusum_vs, { V_NOP; }) diff --git a/tests/rvv-smoke.S b/tests/rvv-smoke.S index 70ce8c892..282076eec 100644 --- a/tests/rvv-smoke.S +++ b/tests/rvv-smoke.S @@ -98,6 +98,12 @@ mulfix_lhs: mulfix_rhs: .byte 0x40, 0x80, 0x40, 0x20 +cvt_f_src: + .word 0x3fc00000, 0x40200000, 0xbfc00000, 0x40e00000 + +cvt_i_src: + .word 3, -3, 5, 0 + narrow16_a: .short 0x8000, 0x0002 @@ -296,6 +302,8 @@ fixed_out: .space 16 wide_out: .space 32 +cvt_out: + .space 32 red_out: .space 16 wide_red_out: @@ -1813,6 +1821,73 @@ _start: csrr t1, vl assert_reg_imm t1, 4, fail_fractional + # Single-width FP conversions (V 1.0 13.17). frm is set to RNE so the + # dynamic-rounding forms can be told apart from the .rtz forms: 1.5 + # rounds to 2 under RNE but truncates to 1 under RTZ. + li t2, 0 + csrw frm, t2 + li a0, 4 + vsetvli t0, a0, e32, m1, tu, mu + la a1, cvt_f_src + vle32.v v20, (a1) + + # vfcvt.x.f.v: 1.5->2, 2.5->2, -1.5->-2, 7.0->7 + vfcvt.x.f.v v22, v20 + la a1, cvt_out + vse32.v v22, (a1) + lw t1, 0(a1) + assert_reg_imm t1, 2, fail_vfcvt_x_f + lw t1, 4(a1) + assert_reg_imm t1, 2, fail_vfcvt_x_f + lw t1, 8(a1) + assert_reg_imm t1, -2, fail_vfcvt_x_f + lw t1, 12(a1) + assert_reg_imm t1, 7, fail_vfcvt_x_f + + # vfcvt.rtz.x.f.v truncates instead: 1.5->1, -1.5->-1 + vfcvt.rtz.x.f.v v22, v20 + la a1, cvt_out + vse32.v v22, (a1) + lw t1, 0(a1) + assert_reg_imm t1, 1, fail_vfcvt_rtz_x_f + lw t1, 8(a1) + assert_reg_imm t1, -1, fail_vfcvt_rtz_x_f + lw t1, 12(a1) + assert_reg_imm t1, 7, fail_vfcvt_rtz_x_f + + # vfcvt.xu.f.v: negative input saturates to 0 in the unsigned form + vfcvt.xu.f.v v22, v20 + la a1, cvt_out + vse32.v v22, (a1) + lw t1, 0(a1) + assert_reg_imm t1, 2, fail_vfcvt_xu_f + lw t1, 12(a1) + assert_reg_imm t1, 7, fail_vfcvt_xu_f + + # Integer-to-float: -3 differs between the signed and unsigned forms + la a1, cvt_i_src + vle32.v v20, (a1) + + vfcvt.f.x.v v22, v20 + la a1, cvt_out + vse32.v v22, (a1) + lw t1, 0(a1) + li t6, 0x40400000 + bne t1, t6, fail_vfcvt_f_x + lw t1, 4(a1) + li t6, 0xc0400000 + bne t1, t6, fail_vfcvt_f_x + + vfcvt.f.xu.v v22, v20 + la a1, cvt_out + vse32.v v22, (a1) + lw t1, 0(a1) + li t6, 0x40400000 + bne t1, t6, fail_vfcvt_f_xu + lw t1, 4(a1) + li t6, 0x4f800000 + bne t1, t6, fail_vfcvt_f_xu + la a1, ok_msg li a2, ok_msg_len j write_and_exit_ok @@ -1911,6 +1986,21 @@ fail_vsmul4: fail_vwadd: li a3, 0xd5 j write_and_exit_fail +fail_vfcvt_x_f: + li a3, 0xf6 + j write_and_exit_fail +fail_vfcvt_rtz_x_f: + li a3, 0xf7 + j write_and_exit_fail +fail_vfcvt_xu_f: + li a3, 0xf8 + j write_and_exit_fail +fail_vfcvt_f_x: + li a3, 0xf9 + j write_and_exit_fail +fail_vfcvt_f_xu: + li a3, 0xfa + j write_and_exit_fail fail_vwmulu: li a3, 0xd6 j write_and_exit_fail