Skip to content

Commit 15453a8

Browse files
committed
Implement RVV integer extension instructions
Add vzext.vf{2,4,8} and vsext.vf{2,4,8} (V 1.0 section 11.3), which widen a narrow source element into a full SEW-wide destination element. The overlap rule here is not the one implemented by rvv_cross_eew_overlap_illegal(): for a destination whose EEW exceeds the source EEW, section 5.2 requires the source EMUL to be at least 1 and the overlap to sit in the highest-numbered part of the destination group. The check is therefore spelled out in the execution helper. Source EMUL is compared against 1 through rvv_lmul_ratio() rather than the register span, because rvv_eew_reg_span() clamps a fractional EMUL to a span of one register.
1 parent 5e4686a commit 15453a8

5 files changed

Lines changed: 228 additions & 4 deletions

File tree

src/decode.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,12 @@ enum op_field {
752752
_(vmsif_m, 0, 4, 0, ENC(rs1, rs2, vd)) \
753753
_(viota_m, 0, 4, 0, ENC(rs1, rs2, vd)) \
754754
_(vid_v, 0, 4, 0, ENC(rs1, rs2, vd)) \
755+
_(vzext_vf2, 0, 4, 0, ENC(rs1, rs2, vd)) \
756+
_(vsext_vf2, 0, 4, 0, ENC(rs1, rs2, vd)) \
757+
_(vzext_vf4, 0, 4, 0, ENC(rs1, rs2, vd)) \
758+
_(vsext_vf4, 0, 4, 0, ENC(rs1, rs2, vd)) \
759+
_(vzext_vf8, 0, 4, 0, ENC(rs1, rs2, vd)) \
760+
_(vsext_vf8, 0, 4, 0, ENC(rs1, rs2, vd)) \
755761
/* OPF */ \
756762
_(vfadd_vv, 0, 4, 0, ENC(rs1, rs2, vd)) \
757763
_(vfadd_vf, 0, 4, 0, ENC(rs1, rs2, vd)) \

src/decode_v.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -983,11 +983,39 @@ static inline bool op_010010(rv_insn_t *ir, const uint32_t insn)
983983
/* FIXME: Implement the decoding for VFUNARY0. */
984984
return false;
985985
case 2:
986-
/* OPMVV / VXUNARY0 (vzext.vf{2,4,8} and vsext.vf{2,4,8}). The
987-
* RVOPs for these are not implemented yet, so refuse the encoding
988-
* rather than silently misdecoding it as vsbc.vxm.
986+
/* OPMVV / VXUNARY0 dispatch (V 1.0 §11.3): vs1 carries the
987+
* sub-opcode selecting the extension factor and signedness.
988+
* Both masked (vm=0) and unmasked (vm=1) forms are permitted.
989989
*/
990-
return false;
990+
switch (decode_rs1(insn)) {
991+
case 0b00010:
992+
decode_vvtype(ir, insn);
993+
ir->opcode = rv_insn_vzext_vf8;
994+
break;
995+
case 0b00011:
996+
decode_vvtype(ir, insn);
997+
ir->opcode = rv_insn_vsext_vf8;
998+
break;
999+
case 0b00100:
1000+
decode_vvtype(ir, insn);
1001+
ir->opcode = rv_insn_vzext_vf4;
1002+
break;
1003+
case 0b00101:
1004+
decode_vvtype(ir, insn);
1005+
ir->opcode = rv_insn_vsext_vf4;
1006+
break;
1007+
case 0b00110:
1008+
decode_vvtype(ir, insn);
1009+
ir->opcode = rv_insn_vzext_vf2;
1010+
break;
1011+
case 0b00111:
1012+
decode_vvtype(ir, insn);
1013+
ir->opcode = rv_insn_vsext_vf2;
1014+
break;
1015+
default:
1016+
return false;
1017+
}
1018+
break;
9911019
case 3:
9921020
/* OPIVI: vsbc has no immediate form per V 1.0 §11.4 (only vvm/vxm
9931021
* exist). Reject explicitly instead of falling through. */

src/rv32_v_constopt.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,12 @@ CONSTOPT(vmsof_m, {})
538538
CONSTOPT(vmsif_m, {})
539539
CONSTOPT(viota_m, {})
540540
CONSTOPT(vid_v, {})
541+
CONSTOPT(vzext_vf2, {})
542+
CONSTOPT(vsext_vf2, {})
543+
CONSTOPT(vzext_vf4, {})
544+
CONSTOPT(vsext_vf4, {})
545+
CONSTOPT(vzext_vf8, {})
546+
CONSTOPT(vsext_vf8, {})
541547
CONSTOPT(vfadd_vv, {})
542548
CONSTOPT(vfadd_vf, {})
543549
CONSTOPT(vfredusum_vs, {})

src/rv32_v_template.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6499,6 +6499,93 @@ RVOP(vid_v, {
64996499
rvv_exec_vid(rv, ir, ir->vd);
65006500
})
65016501

6502+
/* Integer extension (V 1.0 §11.3): widen each SEW/factor-wide source
6503+
* element into a full SEW-wide destination element, either zero- or
6504+
* sign-extended. The source EEW is SEW/factor with EMUL = LMUL/factor,
6505+
* so the source group span is derived from that narrower width while the
6506+
* destination keeps the SEW-based span.
6507+
*/
6508+
static inline bool rvv_exec_int_extend(riscv_t *rv,
6509+
const rv_insn_t *ir,
6510+
uint32_t factor,
6511+
bool is_signed)
6512+
{
6513+
uint32_t sew_bits = rvv_sew_bits(rv->csr_vtype);
6514+
uint32_t src_bits = sew_bits / factor;
6515+
uint32_t vlmax = rvv_vlmax(rv->csr_vtype);
6516+
uint64_t fill = rvv_elem_mask64(sew_bits);
6517+
uint8_t vma = (rv->csr_vtype >> 7) & 0x1;
6518+
uint8_t vta = (rv->csr_vtype >> 6) & 0x1;
6519+
uint32_t src_span, dest_span = rvv_group_regs(rv->csr_vtype);
6520+
uint32_t lmul_num, lmul_den;
6521+
bool src_emul_below_one;
6522+
6523+
/* EMUL of the source is (EEW/SEW)*LMUL (V 1.0 §11.3); compare it to 1
6524+
* without losing the fractional case, which rvv_eew_reg_span() clamps
6525+
* to a span of one register.
6526+
*/
6527+
rvv_lmul_ratio(rv->csr_vtype, &lmul_num, &lmul_den);
6528+
src_emul_below_one =
6529+
((uint64_t) lmul_num * src_bits) < ((uint64_t) lmul_den * sew_bits);
6530+
6531+
/* A source narrower than 8 bits has no architectural encoding; this
6532+
* rejects e.g. vzext.vf8 at SEW=32 (V 1.0 §11.3).
6533+
*/
6534+
if (src_bits < 8)
6535+
return rvv_trap_illegal_state(rv, 0);
6536+
if (!rvv_eew_reg_span(rv, src_bits, &src_span) ||
6537+
!rvv_validate_data_reg(rv->csr_vtype, ir->vd) ||
6538+
!rvv_validate_eew_reg(rv, src_bits, ir->vs2))
6539+
return rvv_trap_illegal_state(rv, 0);
6540+
/* Per V 1.0 §5.2, a destination whose EEW exceeds the source EEW may
6541+
* overlap the source only when the source EMUL is at least 1 and the
6542+
* overlap sits in the HIGHEST-numbered part of the destination group
6543+
* (at LMUL=8, `vzext.vf4 v0, v6` is legal but `v0, v0` is not). This
6544+
* differs from the widening-arithmetic rule in
6545+
* rvv_cross_eew_overlap_illegal(), which pins both groups to a shared
6546+
* base register, so the check is spelled out here.
6547+
*/
6548+
if (rvv_reg_spans_overlap(ir->vd, dest_span, ir->vs2, src_span) &&
6549+
(src_emul_below_one || ((ir->vs2 + src_span) != (ir->vd + dest_span))))
6550+
return rvv_trap_illegal_state(rv, 0);
6551+
6552+
for (uint32_t elem = rv->csr_vstart; elem < rv->csr_vl; elem++) {
6553+
uint64_t value;
6554+
if (!rvv_mask_enabled_for_elem(rv, ir, elem)) {
6555+
if (vma)
6556+
rvv_set_elem_ext(rv, ir->vd, elem, sew_bits, fill);
6557+
continue;
6558+
}
6559+
value = rvv_get_elem_ext(rv, ir->vs2, elem, src_bits);
6560+
if (is_signed)
6561+
value = (uint64_t) rvv_sign_extend64(value, src_bits);
6562+
rvv_set_elem_ext(rv, ir->vd, elem, sew_bits,
6563+
value & rvv_elem_mask64(sew_bits));
6564+
}
6565+
if (vta) {
6566+
for (uint32_t elem = rv->csr_vl; elem < vlmax; elem++)
6567+
rvv_set_elem_ext(rv, ir->vd, elem, sew_bits, fill);
6568+
}
6569+
6570+
rv->csr_vstart = 0;
6571+
return true;
6572+
}
6573+
6574+
#define RVV_INT_EXTEND_OP(name, factor, is_signed) \
6575+
RVOP(name, { \
6576+
if (rvv_require_operable(rv)) \
6577+
return false; \
6578+
if (!rvv_exec_int_extend(rv, ir, factor, is_signed)) \
6579+
return false; \
6580+
})
6581+
6582+
RVV_INT_EXTEND_OP(vzext_vf2, 2, false)
6583+
RVV_INT_EXTEND_OP(vsext_vf2, 2, true)
6584+
RVV_INT_EXTEND_OP(vzext_vf4, 4, false)
6585+
RVV_INT_EXTEND_OP(vsext_vf4, 4, true)
6586+
RVV_INT_EXTEND_OP(vzext_vf8, 8, false)
6587+
RVV_INT_EXTEND_OP(vsext_vf8, 8, true)
6588+
65026589
#if RV32_HAS(EXT_F)
65036590
typedef uint32_t (*rvv_fp32_binop_fn)(uint32_t lhs, uint32_t rhs);
65046591
typedef uint32_t (*rvv_fp32_triop_fn)(uint32_t dest,

tests/rvv-smoke.S

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

101+
ext_src:
102+
.byte 0x7f, 0x80, 0x01, 0xff
103+
.balign 2
104+
101105
narrow16_a:
102106
.short 0x8000, 0x0002
103107

@@ -296,6 +300,8 @@ fixed_out:
296300
.space 16
297301
wide_out:
298302
.space 32
303+
ext_out:
304+
.space 32
299305
red_out:
300306
.space 16
301307
wide_red_out:
@@ -1813,6 +1819,82 @@ _start:
18131819
csrr t1, vl
18141820
assert_reg_imm t1, 4, fail_fractional
18151821

1822+
# Integer extension (V 1.0 11.3): vzext/vsext widen a narrow source
1823+
# element into a full SEW-wide destination.
1824+
li a0, 4
1825+
vsetvli t0, a0, e16, m1, tu, mu
1826+
la a1, ext_src
1827+
vle8.v v20, (a1)
1828+
1829+
# vzext.vf2: 0x7f,0x80,0x01,0xff -> zero-extended to 16-bit
1830+
vzext.vf2 v22, v20
1831+
la a1, ext_out
1832+
vse16.v v22, (a1)
1833+
lhu t1, 0(a1)
1834+
assert_reg_imm t1, 0x007f, fail_vzext_vf2
1835+
lhu t1, 2(a1)
1836+
assert_reg_imm t1, 0x0080, fail_vzext_vf2
1837+
lhu t1, 6(a1)
1838+
assert_reg_imm t1, 0x00ff, fail_vzext_vf2
1839+
1840+
# vsext.vf2: 0x80 and 0xff are negative when sign-extended
1841+
vsext.vf2 v22, v20
1842+
la a1, ext_out
1843+
vse16.v v22, (a1)
1844+
lhu t1, 0(a1)
1845+
assert_reg_imm t1, 0x007f, fail_vsext_vf2
1846+
lhu t1, 2(a1)
1847+
assert_reg_imm t1, 0xff80, fail_vsext_vf2
1848+
lhu t1, 6(a1)
1849+
assert_reg_imm t1, 0xffff, fail_vsext_vf2
1850+
1851+
# vf4: 8-bit source widened to 32-bit destination
1852+
li a0, 4
1853+
vsetvli t0, a0, e32, m1, tu, mu
1854+
la a1, ext_src
1855+
vle8.v v20, (a1)
1856+
1857+
vzext.vf4 v22, v20
1858+
la a1, ext_out
1859+
vse32.v v22, (a1)
1860+
lw t1, 0(a1)
1861+
assert_reg_imm t1, 0x0000007f, fail_vzext_vf4
1862+
lw t1, 4(a1)
1863+
assert_reg_imm t1, 0x00000080, fail_vzext_vf4
1864+
1865+
vsext.vf4 v22, v20
1866+
la a1, ext_out
1867+
vse32.v v22, (a1)
1868+
lw t1, 4(a1)
1869+
assert_reg_imm t1, -128, fail_vsext_vf4
1870+
lw t1, 12(a1)
1871+
assert_reg_imm t1, -1, fail_vsext_vf4
1872+
1873+
# Masked form leaves inactive elements undisturbed (vma=0). v0 bit
1874+
# pattern 0x5 activates elements 0 and 2 only.
1875+
li a0, 4
1876+
vsetvli t0, a0, e32, m1, tu, mu
1877+
la a1, ext_out
1878+
li t2, 0
1879+
sw t2, 0(a1)
1880+
sw t2, 4(a1)
1881+
sw t2, 8(a1)
1882+
sw t2, 12(a1)
1883+
vle32.v v22, (a1)
1884+
la a1, mask_bits
1885+
vle8.v v0, (a1)
1886+
la a1, ext_src
1887+
vle8.v v20, (a1)
1888+
vzext.vf4 v22, v20, v0.t
1889+
la a1, ext_out
1890+
vse32.v v22, (a1)
1891+
lw t1, 0(a1)
1892+
assert_reg_imm t1, 0x0000007f, fail_vzext_mask
1893+
lw t1, 4(a1)
1894+
assert_reg_imm t1, 0, fail_vzext_mask
1895+
lw t1, 8(a1)
1896+
assert_reg_imm t1, 0x00000001, fail_vzext_mask
1897+
18161898
la a1, ok_msg
18171899
li a2, ok_msg_len
18181900
j write_and_exit_ok
@@ -1911,6 +1993,21 @@ fail_vsmul4:
19111993
fail_vwadd:
19121994
li a3, 0xd5
19131995
j write_and_exit_fail
1996+
fail_vzext_vf2:
1997+
li a3, 0xf1
1998+
j write_and_exit_fail
1999+
fail_vsext_vf2:
2000+
li a3, 0xf2
2001+
j write_and_exit_fail
2002+
fail_vzext_vf4:
2003+
li a3, 0xf3
2004+
j write_and_exit_fail
2005+
fail_vsext_vf4:
2006+
li a3, 0xf4
2007+
j write_and_exit_fail
2008+
fail_vzext_mask:
2009+
li a3, 0xf5
2010+
j write_and_exit_fail
19142011
fail_vwmulu:
19152012
li a3, 0xd6
19162013
j write_and_exit_fail

0 commit comments

Comments
 (0)