Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
37 changes: 35 additions & 2 deletions src/decode_v.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/rv32_v_constopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, {})
Expand Down
97 changes: 97 additions & 0 deletions src/rv32_v_template.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Float-to-integer conversions pass exact=false, which suppresses SoftFloat’s inexact exception. Set exact=true for all four float-to-integer helpers so rounded results update fflags.NX as required.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/rv32_v_template.c, line 6707:

<comment>Float-to-integer conversions pass `exact=false`, which suppresses SoftFloat’s inexact exception. Set `exact=true` for all four float-to-integer helpers so rounded results update `fflags.NX` as required.</comment>

<file context>
@@ -6695,6 +6695,44 @@ static inline uint32_t rvv_fp_fnmsub32(uint32_t dest,
+ */
+static inline uint32_t rvv_fp_cvt_xu_f32(uint32_t bits)
+{
+    return f32_to_ui32(rvv_fp32_from_raw(bits), softfloat_roundingMode, false);
+}
+
</file context>

}

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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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; })
Expand Down
90 changes: 90 additions & 0 deletions tests/rvv-smoke.S
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -296,6 +302,8 @@ fixed_out:
.space 16
wide_out:
.space 32
cvt_out:
.space 32
red_out:
.space 16
wide_red_out:
Expand Down Expand Up @@ -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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The vfcvt.xu.f.v smoke test does not verify its signedness difference: it skips the negative -1.5 element at offset 8, so a signed vfcvt.x.f.v implementation would still pass this block. Assert that offset 8 is zero to test the behavior described by the comment.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/rvv-smoke.S, line 1844:

<comment>The `vfcvt.xu.f.v` smoke test does not verify its signedness difference: it skips the negative `-1.5` element at offset 8, so a signed `vfcvt.x.f.v` implementation would still pass this block. Assert that offset 8 is zero to test the behavior described by the comment.</comment>

<file context>
@@ -1813,6 +1821,73 @@ _start:
+    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
+
</file context>
Suggested change
lw t1, 12(a1)
lw t1, 8(a1)
assert_reg_imm t1, 0, fail_vfcvt_xu_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
Expand Down Expand Up @@ -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
Expand Down
Loading