Skip to main content

ab_riscv_primitives/instructions/
zvbb.rs

1//! Zvbb extension
2
3#[cfg(test)]
4mod tests;
5pub mod zvkb;
6
7use crate::instructions::Instruction;
8use crate::instructions::v::zvexx::ZveXxInstruction;
9use crate::instructions::v::zvexx::arith::ZveXxArithInstruction;
10use crate::instructions::v::zvexx::carry::ZveXxCarryInstruction;
11use crate::instructions::v::zvexx::config::ZveXxConfigInstruction;
12use crate::instructions::v::zvexx::fixed_point::ZveXxFixedPointInstruction;
13use crate::instructions::v::zvexx::load::{LoadStoreNreg, Nf, SegVmNf, ZveXxLoadInstruction};
14use crate::instructions::v::zvexx::mask::ZveXxMaskInstruction;
15use crate::instructions::v::zvexx::muldiv::ZveXxMulDivInstruction;
16use crate::instructions::v::zvexx::perm::ZveXxPermInstruction;
17use crate::instructions::v::zvexx::reduction::ZveXxReductionInstruction;
18use crate::instructions::v::zvexx::store::ZveXxStoreInstruction;
19use crate::instructions::v::zvexx::widen_narrow::ZveXxWidenNarrowInstruction;
20use crate::instructions::v::{Eew, V};
21use crate::instructions::zicsr::ZicsrInstruction;
22use crate::instructions::zvbb::zvkb::ZvkbInstruction;
23use crate::registers::general_purpose::Register;
24use crate::registers::vector::VReg;
25use ab_riscv_macros::instruction;
26use core::fmt;
27
28/// RISC-V Zvbb vector bit-manipulation instruction.
29///
30/// Zvbb is a strict superset of Zvkb; this type encodes only the instructions unique to Zvbb.
31/// The Zvkb subset (vandn, vbrev8, vrev8, vrol, vror) is inherited from [`ZvkbInstruction`].
32///
33/// All use the OP-V major opcode (0b101_0111). Encoding spaces:
34///
35/// - `vbrev.v`:       funct6=0b010010, OPMVV, vs1=0b01010; `vm` controls masking
36/// - `vclz.v`:        funct6=0b010010, OPMVV, vs1=0b01100; `vm` controls masking
37/// - `vctz.v`:        funct6=0b010010, OPMVV, vs1=0b01101; `vm` controls masking
38/// - `vcpop.v`:       funct6=0b010010, OPMVV, vs1=0b01110; `vm` controls masking
39/// - `vwsll.[vv,vx]`: funct6=0b110101, OPIVV/OPIVX; `vm` controls masking
40/// - `vwsll.vi`:      funct6=0b110101, OPIVI; 5-bit unsigned immediate in bits\[19:15] (0-31);
41///   bit\[25] is the standard `vm` field, orthogonal to the immediate
42///
43/// The four unary operations share VXUNARY0 (funct6=0b010010, OPMVV); Zvkb claims vs1
44/// sub-opcodes 0b01000 and 0b01001; Zvbb claims 0b01010, 0b01100, 0b01101, and 0b01110.
45/// Sub-opcode 0b01011 is an undefined gap between vbrev and vclz.
46///
47/// Note: `vwsll` funct6 (0b110101) coincides with `vwadd.wv` from the base V extension;
48/// these instructions are mutually exclusive within a combined decoder.
49///
50/// For instructions with a `vm` field: `vm=true` means unmasked (process all body elements);
51/// `vm=false` means masked by v0 (skip elements where v0\[i]=0, leaving them undisturbed).
52#[instruction(
53    inherit = [ZvkbInstruction],
54)]
55#[derive(Debug, Clone, Copy)]
56#[derive_const(PartialEq, Eq)]
57#[rustfmt::skip]
58pub enum ZvbbInstruction<Reg> {
59    // vbrev: bit-reverse within each SEW-wide element (element granularity, unlike vbrev8's byte granularity)
60    /// `vbrev.v vd, vs2, vm`
61    VbrevV  { vd: VReg, vs2: VReg, vm: bool },
62    // vclz: count leading zeros within each SEW-wide element; result in [0, SEW]
63    /// `vclz.v vd, vs2, vm`
64    VclzV   { vd: VReg, vs2: VReg, vm: bool },
65    // vctz: count trailing zeros within each SEW-wide element; result in [0, SEW]
66    /// `vctz.v vd, vs2, vm`
67    VctzV   { vd: VReg, vs2: VReg, vm: bool },
68    // vcpop: population count (number of set bits) within each SEW-wide element
69    /// `vcpop.v vd, vs2, vm`
70    VcpopV  { vd: VReg, vs2: VReg, vm: bool },
71    // vwsll: widening shift left logical; result is 2*SEW wide, both sources are SEW wide
72    /// `vwsll.vv vd, vs2, vs1, vm`
73    VwsllVv { vd: VReg, vs2: VReg, vs1: VReg, vm: bool },
74    /// `vwsll.vx vd, vs2, rs1, vm`
75    VwsllVx { vd: VReg, vs2: VReg, rs1: Reg,  vm: bool },
76    /// `vwsll.vi vd, vs2, uimm, vm` - `uimm` is 5-bit unsigned (0..=31); `vm` is the
77    /// standard mask-control bit at bit\[25], orthogonal to the immediate.
78    VwsllVi { vd: VReg, vs2: VReg, uimm: u8,  vm: bool },
79}
80
81#[instruction]
82const impl<Reg> Instruction for ZvbbInstruction<Reg>
83where
84    Reg: [const] Register,
85{
86    const ALIGNMENT: u8 = align_of::<u32>() as u8;
87
88    type Reg = Reg;
89
90    #[inline(always)]
91    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
92    fn try_decode(instruction: u32) -> Option<Self> {
93        let opcode = (instruction & 0b111_1111) as u8;
94
95        if opcode != 0b101_0111 {
96            None?;
97        }
98
99        let vd_bits = ((instruction >> 7) & 0x1f) as u8;
100        let funct3 = ((instruction >> 12) & 0b111) as u8;
101        let vs1_bits = ((instruction >> 15) & 0x1f) as u8;
102        let vs2_bits = ((instruction >> 20) & 0x1f) as u8;
103        // vm=1 means unmasked, vm=0 means masked by v0
104        let vm = ((instruction >> 25) & 1) as u8 == 1;
105        let funct6 = ((instruction >> 26) & 0b11_1111) as u8;
106
107        let vd = VReg::from_bits(vd_bits)?;
108        let vs2 = VReg::from_bits(vs2_bits)?;
109
110        match funct3 {
111            // OPIVV: vwsll.vv
112            0b000 => {
113                if funct6 != 0b11_0101 {
114                    None?;
115                }
116                let vs1 = VReg::from_bits(vs1_bits)?;
117                Some(Self::VwsllVv { vd, vs2, vs1, vm })
118            }
119            // OPIVX: vwsll.vx
120            0b100 => {
121                if funct6 != 0b11_0101 {
122                    None?;
123                }
124                let rs1 = Reg::from_bits(vs1_bits)?;
125                Some(Self::VwsllVx { vd, vs2, rs1, vm })
126            }
127            // OPIVI: vwsll.vi - standard 5-bit unsigned immediate in bits[19:15]; vm is bit[25]
128            0b011 => {
129                if funct6 != 0b11_0101 {
130                    None?;
131                }
132                let uimm = vs1_bits;
133                Some(Self::VwsllVi { vd, vs2, uimm, vm })
134            }
135            // OPMVV: vbrev.v, vclz.v, vctz.v, vcpop.v
136            // funct6=0b010010 (VXUNARY0 sub-space); Zvkb claims vs1 0b01000/0b01001;
137            // Zvbb claims 0b01010, 0b01100, 0b01101, 0b01110; vs1 0b01011 is an undefined gap
138            0b010 => {
139                if funct6 != 0b01_0010 {
140                    None?;
141                }
142                match vs1_bits {
143                    0b01010 => Some(Self::VbrevV { vd, vs2, vm }),
144                    0b01100 => Some(Self::VclzV { vd, vs2, vm }),
145                    0b01101 => Some(Self::VctzV { vd, vs2, vm }),
146                    0b01110 => Some(Self::VcpopV { vd, vs2, vm }),
147                    _ => None,
148                }
149            }
150            _ => None,
151        }
152    }
153
154    #[inline(always)]
155    fn size(&self) -> u8 {
156        size_of::<u32>() as u8
157    }
158}
159
160#[instruction]
161impl<Reg> fmt::Display for ZvbbInstruction<Reg>
162where
163    Reg: fmt::Display + Copy,
164{
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        #[rustfmt::skip]
167        match self {
168            Self::VbrevV  { vd, vs2, vm }        => write!(f, "vbrev.v {vd}, {vs2}{}", mask_suffix(vm)),
169            Self::VclzV   { vd, vs2, vm }        => write!(f, "vclz.v {vd}, {vs2}{}", mask_suffix(vm)),
170            Self::VctzV   { vd, vs2, vm }        => write!(f, "vctz.v {vd}, {vs2}{}", mask_suffix(vm)),
171            Self::VcpopV  { vd, vs2, vm }        => write!(f, "vcpop.v {vd}, {vs2}{}", mask_suffix(vm)),
172            Self::VwsllVv { vd, vs2, vs1, vm }   => write!(f, "vwsll.vv {vd}, {vs2}, {vs1}{}", mask_suffix(vm)),
173            Self::VwsllVx { vd, vs2, rs1, vm }   => write!(f, "vwsll.vx {vd}, {vs2}, {rs1}{}", mask_suffix(vm)),
174            Self::VwsllVi { vd, vs2, uimm, vm }  => write!(f, "vwsll.vi {vd}, {vs2}, {uimm}{}", mask_suffix(vm)),
175        }
176    }
177}
178
179/// Format mask suffix for display
180#[inline(always)]
181fn mask_suffix(vm: &bool) -> &'static str {
182    if *vm { "" } else { ", v0.t" }
183}