Skip to content

Commit 815b9ba

Browse files
alanhcclaude
andcommitted
Implement RVV floating-point scalar moves
Add vfmv.f.s and vfmv.s.f (V 1.0 section 16.3), which transfer element 0 between a vector register and an f register, ignoring LMUL. Two details from the specification are easy to miss: vfmv.f.s performs its operation even when vl is 0, while vfmv.s.f writes nothing when vstart >= vl. The masked encodings of both are reserved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 5e4686a commit 815b9ba

5 files changed

Lines changed: 140 additions & 9 deletions

File tree

src/decode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,8 @@ enum op_field {
827827
_(vfwmsac_vf, 0, 4, 0, ENC(rs1, rs2, vd)) \
828828
_(vfwnmsac_vv, 0, 4, 0, ENC(rs1, rs2, vd)) \
829829
_(vfwnmsac_vf, 0, 4, 0, ENC(rs1, rs2, vd)) \
830+
_(vfmv_f_s, 0, 4, 0, ENC(rs1, rs2, vd)) \
831+
_(vfmv_s_f, 0, 4, 0, ENC(rs1, rs2, vd)) \
830832
)
831833

832834
/* clang-format on */

src/decode_v.c

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -873,12 +873,15 @@ static inline bool op_010000(rv_insn_t *ir, const uint32_t insn)
873873
ir->opcode = rv_insn_vadc_vvm;
874874
break;
875875
case 1:
876-
/* OPFVV / VWFUNARY0 (vfmv.f.s and friends). FP unary moves are
877-
* not implemented yet; reject explicitly instead of falling
878-
* through into the VWXUNARY0 path below, which would silently
879-
* misdecode the encoding as vmv.x.s / vcpop.m / vfirst.m.
876+
/* OPFVV / VWFUNARY0: vfmv.f.s reads element 0 into f[rd]. The
877+
* masked encoding (vm=0) is reserved (V 1.0 §16.3), and vs1 must
878+
* be zero - any other value is not a defined sub-opcode.
880879
*/
881-
return false;
880+
if (!decode_vm(insn) || decode_rs1(insn))
881+
return false;
882+
decode_mtype(ir, insn);
883+
ir->opcode = rv_insn_vfmv_f_s;
884+
break;
882885
case 2:
883886
/* VWXUNARY0 dispatch: vmv.x.s requires vm=1 (vm=0 reserved per
884887
* V 1.0 §16.1). vcpop.m / vfirst.m support both vm=0 (masked)
@@ -918,11 +921,15 @@ static inline bool op_010000(rv_insn_t *ir, const uint32_t insn)
918921
ir->opcode = rv_insn_vadc_vxm;
919922
break;
920923
case 5:
921-
/* OPFVF / VRFUNARY0 (vfmv.s.f). Not implemented yet; reject the
922-
* encoding here so it does not fall through to the VRXUNARY0
923-
* (vmv.s.x) path below and silently dispatch as the wrong op.
924+
/* OPFVF / VRFUNARY0: vfmv.s.f writes f[rs1] into element 0. The
925+
* masked encoding (vm=0) is reserved (V 1.0 §16.3), and vs2 must
926+
* be zero - any other value is not a defined sub-opcode.
924927
*/
925-
return false;
928+
if (!decode_vm(insn) || decode_rs2(insn))
929+
return false;
930+
decode_vxtype(ir, insn);
931+
ir->opcode = rv_insn_vfmv_s_f;
932+
break;
926933
case 6:
927934
/* VRXUNARY0 - vmv.s.x requires encoded vm=1; vm=0 is reserved. */
928935
if (!decode_vm(insn))

src/rv32_v_constopt.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,8 @@ CONSTOPT(vmsof_m, {})
538538
CONSTOPT(vmsif_m, {})
539539
CONSTOPT(viota_m, {})
540540
CONSTOPT(vid_v, {})
541+
CONSTOPT(vfmv_f_s, {})
542+
CONSTOPT(vfmv_s_f, {})
541543
CONSTOPT(vfadd_vv, {})
542544
CONSTOPT(vfadd_vf, {})
543545
CONSTOPT(vfredusum_vs, {})

src/rv32_v_template.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6828,6 +6828,35 @@ static inline void rvv_exec_fp32_vf(riscv_t *rv,
68286828
rv->csr_vstart = 0;
68296829
}
68306830

6831+
/* Floating-point scalar moves (V 1.0 §16.3). Both ignore LMUL and touch
6832+
* only element 0.
6833+
*/
6834+
static inline void rvv_exec_vfmv_f_s(riscv_t *rv, const rv_insn_t *ir)
6835+
{
6836+
/* vfmv.f.s performs its operation even if vstart >= vl or vl == 0. */
6837+
rv->F[ir->rd].v = rvv_get_elem(rv, ir->vs2, 0, 32);
6838+
rv->csr_vstart = 0;
6839+
}
6840+
6841+
static inline void rvv_exec_vfmv_s_f(riscv_t *rv, const rv_insn_t *ir)
6842+
{
6843+
/* If vstart >= vl nothing is written; with vl == 0 that covers every
6844+
* vstart, so the destination is left alone. Remaining elements are
6845+
* tail elements governed by the usual vta policy.
6846+
*/
6847+
if (rv->csr_vstart < rv->csr_vl) {
6848+
uint32_t vlmax = rvv_vlmax(rv->csr_vtype);
6849+
uint8_t vta = (rv->csr_vtype >> 6) & 0x1;
6850+
6851+
rvv_set_elem(rv, ir->vd, 0, 32, rv->F[ir->rs1].v);
6852+
if (vta) {
6853+
for (uint32_t elem = rv->csr_vl; elem < vlmax; elem++)
6854+
rvv_set_elem(rv, ir->vd, elem, 32, 0xFFFFFFFFU);
6855+
}
6856+
}
6857+
rv->csr_vstart = 0;
6858+
}
6859+
68316860
static inline void rvv_exec_fp32_mask_vv(riscv_t *rv,
68326861
const rv_insn_t *ir,
68336862
uint32_t dest,
@@ -7448,6 +7477,22 @@ static inline void rvv_exec_vfmv_v_f(riscv_t *rv,
74487477
set_fflag(rv); \
74497478
})
74507479

7480+
RVOP(vfmv_f_s, {
7481+
if (rvv_require_operable(rv))
7482+
return false;
7483+
if (rvv_sew_bits(rv->csr_vtype) != 32)
7484+
return rvv_trap_illegal_state(rv, 0);
7485+
rvv_exec_vfmv_f_s(rv, ir);
7486+
})
7487+
7488+
RVOP(vfmv_s_f, {
7489+
if (rvv_require_operable(rv))
7490+
return false;
7491+
if (rvv_sew_bits(rv->csr_vtype) != 32)
7492+
return rvv_trap_illegal_state(rv, 0);
7493+
rvv_exec_vfmv_s_f(rv, ir);
7494+
})
7495+
74517496
RVV_FP32_VV_OP(vfadd_vv, rvv_fp_add32, true);
74527497
RVV_FP32_VF_OP(vfadd_vf, rvv_fp_add32, true);
74537498
RVV_FP32_RED_OP(vfredusum_vs, rvv_fp_add32, true);
@@ -7619,6 +7664,8 @@ RVV_FP64_MAC_VF_OP(vfwmsac_vf, rvv_fp_wmsac64);
76197664
RVV_FP64_MAC_VV_OP(vfwnmsac_vv, rvv_fp_wnmsac64);
76207665
RVV_FP64_MAC_VF_OP(vfwnmsac_vf, rvv_fp_wnmsac64);
76217666
#else
7667+
RVOP(vfmv_f_s, { V_NOP; })
7668+
RVOP(vfmv_s_f, { V_NOP; })
76227669
RVOP(vfadd_vv, { V_NOP; })
76237670
RVOP(vfadd_vf, { V_NOP; })
76247671
RVOP(vfredusum_vs, { V_NOP; })

tests/rvv-smoke.S

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ mulfix_lhs:
9898
mulfix_rhs:
9999
.byte 0x40, 0x80, 0x40, 0x20
100100

101+
vfmv_src:
102+
.word 0x40490fdb, 0x3f800000, 0xc0000000, 0x00000000
103+
101104
narrow16_a:
102105
.short 0x8000, 0x0002
103106

@@ -296,6 +299,8 @@ fixed_out:
296299
.space 16
297300
wide_out:
298301
.space 32
302+
vfmv_out:
303+
.space 32
299304
red_out:
300305
.space 16
301306
wide_red_out:
@@ -1813,6 +1818,65 @@ _start:
18131818
csrr t1, vl
18141819
assert_reg_imm t1, 4, fail_fractional
18151820

1821+
# Floating-point scalar moves (V 1.0 16.3). Both ignore LMUL and only
1822+
# touch element 0.
1823+
li a0, 4
1824+
vsetvli t0, a0, e32, m1, tu, mu
1825+
# Stage the source through the scratch buffer so every address in this
1826+
# block comes from the same base register.
1827+
la a2, vfmv_out
1828+
li t2, 0x40490fdb
1829+
sw t2, 0(a2)
1830+
li t2, 0x3f800000
1831+
sw t2, 4(a2)
1832+
vle32.v v20, (a2)
1833+
1834+
# vfmv.f.s: element 0 (0x40490fdb) lands in an f register. Spill it back
1835+
# to the scratch buffer to compare the bits.
1836+
vfmv.f.s ft0, v20
1837+
fmv.x.w t1, ft0
1838+
li t6, 0x40490fdb
1839+
bne t1, t6, fail_vfmv_f_s
1840+
1841+
# vfmv.s.f writes element 0 only; element 1 must keep its old value
1842+
# under the tail-undisturbed policy selected above.
1843+
la a1, vfmv_out
1844+
li t2, 0x11111111
1845+
sw t2, 0(a1)
1846+
sw t2, 4(a1)
1847+
sw t2, 8(a1)
1848+
sw t2, 12(a1)
1849+
vle32.v v22, (a1)
1850+
li t2, 0x3f800000
1851+
fmv.w.x ft1, t2
1852+
vfmv.s.f v22, ft1
1853+
vse32.v v22, (a1)
1854+
lw t1, 0(a1)
1855+
li t6, 0x3f800000
1856+
bne t1, t6, fail_vfmv_s_f
1857+
lw t1, 4(a1)
1858+
li t6, 0x11111111
1859+
bne t1, t6, fail_vfmv_s_f
1860+
1861+
# Per spec, vfmv.s.f performs no update when vstart >= vl; with vl = 0
1862+
# that holds for every vstart, so element 0 keeps its previous value.
1863+
la a1, vfmv_out
1864+
li t2, 0x22222222
1865+
sw t2, 0(a1)
1866+
vle32.v v22, (a1)
1867+
li a0, 0
1868+
vsetvli t0, a0, e32, m1, tu, mu
1869+
csrr t1, vl
1870+
assert_reg_imm t1, 0, fail_vfmv_vl0
1871+
vfmv.s.f v22, ft1
1872+
li a0, 4
1873+
vsetvli t0, a0, e32, m1, tu, mu
1874+
la a1, vfmv_out
1875+
vse32.v v22, (a1)
1876+
lw t1, 0(a1)
1877+
li t6, 0x22222222
1878+
bne t1, t6, fail_vfmv_vl0
1879+
18161880
la a1, ok_msg
18171881
li a2, ok_msg_len
18181882
j write_and_exit_ok
@@ -1911,6 +1975,15 @@ fail_vsmul4:
19111975
fail_vwadd:
19121976
li a3, 0xd5
19131977
j write_and_exit_fail
1978+
fail_vfmv_f_s:
1979+
li a3, 0x97
1980+
j write_and_exit_fail
1981+
fail_vfmv_s_f:
1982+
li a3, 0x98
1983+
j write_and_exit_fail
1984+
fail_vfmv_vl0:
1985+
li a3, 0x99
1986+
j write_and_exit_fail
19141987
fail_vwmulu:
19151988
li a3, 0xd6
19161989
j write_and_exit_fail

0 commit comments

Comments
 (0)