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
2 changes: 2 additions & 0 deletions src/decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,8 @@ 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)) \
_(vfmv_f_s, 0, 4, 0, ENC(rs1, rs2, vd)) \
_(vfmv_s_f, 0, 4, 0, ENC(rs1, rs2, vd)) \
)

/* clang-format on */
Expand Down
25 changes: 16 additions & 9 deletions src/decode_v.c
Original file line number Diff line number Diff line change
Expand Up @@ -873,12 +873,15 @@ static inline bool op_010000(rv_insn_t *ir, const uint32_t insn)
ir->opcode = rv_insn_vadc_vvm;
break;
case 1:
/* OPFVV / VWFUNARY0 (vfmv.f.s and friends). FP unary moves are
* not implemented yet; reject explicitly instead of falling
* through into the VWXUNARY0 path below, which would silently
* misdecode the encoding as vmv.x.s / vcpop.m / vfirst.m.
/* OPFVV / VWFUNARY0: vfmv.f.s reads element 0 into f[rd]. The
* masked encoding (vm=0) is reserved (V 1.0 §16.3), and vs1 must
* be zero - any other value is not a defined sub-opcode.
*/
return false;
if (!decode_vm(insn) || decode_rs1(insn))
return false;
decode_mtype(ir, insn);

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: In an RV32E+V build, valid upper floating-point registers are rejected for these new moves. The post-decode RV32E check treats op_010000 operands as integer registers, even though these fields are floating-point registers; extend that check to recognize these vector FP moves.

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

<comment>In an RV32E+V build, valid upper floating-point registers are rejected for these new moves. The post-decode RV32E check treats `op_010000` operands as integer registers, even though these fields are floating-point registers; extend that check to recognize these vector FP moves.</comment>

<file context>
@@ -873,12 +873,15 @@ static inline bool op_010000(rv_insn_t *ir, const uint32_t insn)
-        return false;
+        if (!decode_vm(insn) || decode_rs1(insn))
+            return false;
+        decode_mtype(ir, insn);
+        ir->opcode = rv_insn_vfmv_f_s;
+        break;
</file context>

ir->opcode = rv_insn_vfmv_f_s;
break;
case 2:
/* VWXUNARY0 dispatch: vmv.x.s requires vm=1 (vm=0 reserved per
* V 1.0 §16.1). vcpop.m / vfirst.m support both vm=0 (masked)
Expand Down Expand Up @@ -918,11 +921,15 @@ static inline bool op_010000(rv_insn_t *ir, const uint32_t insn)
ir->opcode = rv_insn_vadc_vxm;
break;
case 5:
/* OPFVF / VRFUNARY0 (vfmv.s.f). Not implemented yet; reject the
* encoding here so it does not fall through to the VRXUNARY0
* (vmv.s.x) path below and silently dispatch as the wrong op.
/* OPFVF / VRFUNARY0: vfmv.s.f writes f[rs1] into element 0. The
* masked encoding (vm=0) is reserved (V 1.0 §16.3), and vs2 must
* be zero - any other value is not a defined sub-opcode.
*/
return false;
if (!decode_vm(insn) || decode_rs2(insn))
return false;
decode_vxtype(ir, insn);
ir->opcode = rv_insn_vfmv_s_f;
break;
case 6:
/* VRXUNARY0 - vmv.s.x requires encoded vm=1; vm=0 is reserved. */
if (!decode_vm(insn))
Expand Down
2 changes: 2 additions & 0 deletions src/rv32_v_constopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,8 @@ CONSTOPT(vmsof_m, {})
CONSTOPT(vmsif_m, {})
CONSTOPT(viota_m, {})
CONSTOPT(vid_v, {})
CONSTOPT(vfmv_f_s, {})
CONSTOPT(vfmv_s_f, {})
CONSTOPT(vfadd_vv, {})
CONSTOPT(vfadd_vf, {})
CONSTOPT(vfredusum_vs, {})
Expand Down
47 changes: 47 additions & 0 deletions src/rv32_v_template.c
Original file line number Diff line number Diff line change
Expand Up @@ -6828,6 +6828,35 @@ static inline void rvv_exec_fp32_vf(riscv_t *rv,
rv->csr_vstart = 0;
}

/* Floating-point scalar moves (V 1.0 §16.3). Both ignore LMUL and touch
* only element 0.
*/
static inline void rvv_exec_vfmv_f_s(riscv_t *rv, const rv_insn_t *ir)
{
/* vfmv.f.s performs its operation even if vstart >= vl or vl == 0. */
rv->F[ir->rd].v = rvv_get_elem(rv, ir->vs2, 0, 32);
rv->csr_vstart = 0;
}

static inline void rvv_exec_vfmv_s_f(riscv_t *rv, const rv_insn_t *ir)
{
/* If vstart >= vl nothing is written; with vl == 0 that covers every
* vstart, so the destination is left alone. Remaining elements are
* tail elements governed by the usual vta policy.
*/
if (rv->csr_vstart < rv->csr_vl) {
uint32_t vlmax = rvv_vlmax(rv->csr_vtype);
uint8_t vta = (rv->csr_vtype >> 6) & 0x1;

rvv_set_elem(rv, ir->vd, 0, 32, rv->F[ir->rs1].v);
if (vta) {

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: When vta=1, vfmv.s.f overwrites all tail elements with 0xffffffff, although this scalar move must update only vd[0] and leave every other element unchanged regardless of vta. Remove the tail-fill block.

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 6852:

<comment>When `vta=1`, `vfmv.s.f` overwrites all tail elements with `0xffffffff`, although this scalar move must update only `vd[0]` and leave every other element unchanged regardless of `vta`. Remove the tail-fill block.</comment>

<file context>
@@ -6828,6 +6828,35 @@ static inline void rvv_exec_fp32_vf(riscv_t *rv,
+        uint8_t vta = (rv->csr_vtype >> 6) & 0x1;
+
+        rvv_set_elem(rv, ir->vd, 0, 32, rv->F[ir->rs1].v);
+        if (vta) {
+            for (uint32_t elem = rv->csr_vl; elem < vlmax; elem++)
+                rvv_set_elem(rv, ir->vd, elem, 32, 0xFFFFFFFFU);
</file context>

for (uint32_t elem = rv->csr_vl; elem < vlmax; elem++)
rvv_set_elem(rv, ir->vd, 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 +7477,22 @@ static inline void rvv_exec_vfmv_v_f(riscv_t *rv,
set_fflag(rv); \
})

RVOP(vfmv_f_s, {
if (rvv_require_operable(rv))
return false;
if (rvv_sew_bits(rv->csr_vtype) != 32)
return rvv_trap_illegal_state(rv, 0);
rvv_exec_vfmv_f_s(rv, ir);
})

RVOP(vfmv_s_f, {
if (rvv_require_operable(rv))
return false;
if (rvv_sew_bits(rv->csr_vtype) != 32)
return rvv_trap_illegal_state(rv, 0);
rvv_exec_vfmv_s_f(rv, ir);
})

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 +7664,8 @@ 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(vfmv_f_s, { V_NOP; })
RVOP(vfmv_s_f, { V_NOP; })
RVOP(vfadd_vv, { V_NOP; })
RVOP(vfadd_vf, { V_NOP; })
RVOP(vfredusum_vs, { V_NOP; })
Expand Down
73 changes: 73 additions & 0 deletions tests/rvv-smoke.S
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ mulfix_lhs:
mulfix_rhs:
.byte 0x40, 0x80, 0x40, 0x20

vfmv_src:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: vfmv_src is an unused test fixture: the smoke test stages the same values directly into vfmv_out and never references this label. Remove the dead data or load the vector from it.

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 101:

<comment>`vfmv_src` is an unused test fixture: the smoke test stages the same values directly into `vfmv_out` and never references this label. Remove the dead data or load the vector from it.</comment>

<file context>
@@ -98,6 +98,9 @@ mulfix_lhs:
 mulfix_rhs:
     .byte 0x40, 0x80, 0x40, 0x20
 
+vfmv_src:
+    .word 0x40490fdb, 0x3f800000, 0xc0000000, 0x00000000
+
</file context>

.word 0x40490fdb, 0x3f800000, 0xc0000000, 0x00000000

narrow16_a:
.short 0x8000, 0x0002

Expand Down Expand Up @@ -296,6 +299,8 @@ fixed_out:
.space 16
wide_out:
.space 32
vfmv_out:
.space 32
red_out:
.space 16
wide_red_out:
Expand Down Expand Up @@ -1813,6 +1818,65 @@ _start:
csrr t1, vl
assert_reg_imm t1, 4, fail_fractional

# Floating-point scalar moves (V 1.0 16.3). Both ignore LMUL and only
# touch element 0.
li a0, 4
vsetvli t0, a0, e32, m1, tu, mu
# Stage the source through the scratch buffer so every address in this
# block comes from the same base register.
la a2, vfmv_out
li t2, 0x40490fdb
sw t2, 0(a2)
li t2, 0x3f800000
sw t2, 4(a2)
vle32.v v20, (a2)

# vfmv.f.s: element 0 (0x40490fdb) lands in an f register. Spill it back
# to the scratch buffer to compare the bits.
vfmv.f.s ft0, v20
fmv.x.w t1, ft0
li t6, 0x40490fdb
bne t1, t6, fail_vfmv_f_s

# vfmv.s.f writes element 0 only; element 1 must keep its old value
# under the tail-undisturbed policy selected above.
la a1, vfmv_out
li t2, 0x11111111
sw t2, 0(a1)
sw t2, 4(a1)
sw t2, 8(a1)
sw t2, 12(a1)
vle32.v v22, (a1)
li t2, 0x3f800000
fmv.w.x ft1, t2
vfmv.s.f v22, ft1
vse32.v v22, (a1)
lw t1, 0(a1)
li t6, 0x3f800000
bne t1, t6, fail_vfmv_s_f
lw t1, 4(a1)
li t6, 0x11111111
bne t1, t6, fail_vfmv_s_f

# Per spec, vfmv.s.f performs no update when vstart >= vl; with vl = 0
# that holds for every vstart, so element 0 keeps its previous value.
la a1, vfmv_out
li t2, 0x22222222
sw t2, 0(a1)
vle32.v v22, (a1)
li a0, 0
vsetvli t0, a0, e32, m1, tu, mu
csrr t1, vl
assert_reg_imm t1, 0, fail_vfmv_vl0
vfmv.s.f v22, ft1
li a0, 4
vsetvli t0, a0, e32, m1, tu, mu
la a1, vfmv_out
vse32.v v22, (a1)
lw t1, 0(a1)
li t6, 0x22222222
bne t1, t6, fail_vfmv_vl0

la a1, ok_msg
li a2, ok_msg_len
j write_and_exit_ok
Expand Down Expand Up @@ -1911,6 +1975,15 @@ fail_vsmul4:
fail_vwadd:
li a3, 0xd5
j write_and_exit_fail
fail_vfmv_f_s:
li a3, 0x97
j write_and_exit_fail
fail_vfmv_s_f:
li a3, 0x98
j write_and_exit_fail
fail_vfmv_vl0:
li a3, 0x99
j write_and_exit_fail
fail_vwmulu:
li a3, 0xd6
j write_and_exit_fail
Expand Down
Loading