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    type Reg = Reg;
87
88    #[inline(always)]
89    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
90    fn try_decode(instruction: u32) -> Option<Self> {
91        let opcode = (instruction & 0b111_1111) as u8;
92
93        if opcode != 0b101_0111 {
94            None?;
95        }
96
97        let vd_bits = ((instruction >> 7) & 0x1f) as u8;
98        let funct3 = ((instruction >> 12) & 0b111) as u8;
99        let vs1_bits = ((instruction >> 15) & 0x1f) as u8;
100        let vs2_bits = ((instruction >> 20) & 0x1f) as u8;
101        // vm=1 means unmasked, vm=0 means masked by v0
102        let vm = ((instruction >> 25) & 1) as u8 == 1;
103        let funct6 = ((instruction >> 26) & 0b11_1111) as u8;
104
105        let vd = VReg::from_bits(vd_bits)?;
106        let vs2 = VReg::from_bits(vs2_bits)?;
107
108        match funct3 {
109            // OPIVV: vwsll.vv
110            0b000 => {
111                if funct6 != 0b11_0101 {
112                    None?;
113                }
114                let vs1 = VReg::from_bits(vs1_bits)?;
115                Some(Self::VwsllVv { vd, vs2, vs1, vm })
116            }
117            // OPIVX: vwsll.vx
118            0b100 => {
119                if funct6 != 0b11_0101 {
120                    None?;
121                }
122                let rs1 = Reg::from_bits(vs1_bits)?;
123                Some(Self::VwsllVx { vd, vs2, rs1, vm })
124            }
125            // OPIVI: vwsll.vi - standard 5-bit unsigned immediate in bits[19:15]; vm is bit[25]
126            0b011 => {
127                if funct6 != 0b11_0101 {
128                    None?;
129                }
130                let uimm = vs1_bits;
131                Some(Self::VwsllVi { vd, vs2, uimm, vm })
132            }
133            // OPMVV: vbrev.v, vclz.v, vctz.v, vcpop.v
134            // funct6=0b010010 (VXUNARY0 sub-space); Zvkb claims vs1 0b01000/0b01001;
135            // Zvbb claims 0b01010, 0b01100, 0b01101, 0b01110; vs1 0b01011 is an undefined gap
136            0b010 => {
137                if funct6 != 0b01_0010 {
138                    None?;
139                }
140                match vs1_bits {
141                    0b01010 => Some(Self::VbrevV { vd, vs2, vm }),
142                    0b01100 => Some(Self::VclzV { vd, vs2, vm }),
143                    0b01101 => Some(Self::VctzV { vd, vs2, vm }),
144                    0b01110 => Some(Self::VcpopV { vd, vs2, vm }),
145                    _ => None,
146                }
147            }
148            _ => None,
149        }
150    }
151
152    #[inline(always)]
153    fn alignment() -> u8 {
154        align_of::<u32>() as u8
155    }
156
157    #[inline(always)]
158    fn size(&self) -> u8 {
159        size_of::<u32>() as u8
160    }
161}
162
163#[instruction]
164impl<Reg> fmt::Display for ZvbbInstruction<Reg>
165where
166    Reg: fmt::Display + Copy,
167{
168    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169        #[rustfmt::skip]
170        match self {
171            Self::VbrevV  { vd, vs2, vm }        => write!(f, "vbrev.v {vd}, {vs2}{}", mask_suffix(vm)),
172            Self::VclzV   { vd, vs2, vm }        => write!(f, "vclz.v {vd}, {vs2}{}", mask_suffix(vm)),
173            Self::VctzV   { vd, vs2, vm }        => write!(f, "vctz.v {vd}, {vs2}{}", mask_suffix(vm)),
174            Self::VcpopV  { vd, vs2, vm }        => write!(f, "vcpop.v {vd}, {vs2}{}", mask_suffix(vm)),
175            Self::VwsllVv { vd, vs2, vs1, vm }   => write!(f, "vwsll.vv {vd}, {vs2}, {vs1}{}", mask_suffix(vm)),
176            Self::VwsllVx { vd, vs2, rs1, vm }   => write!(f, "vwsll.vx {vd}, {vs2}, {rs1}{}", mask_suffix(vm)),
177            Self::VwsllVi { vd, vs2, uimm, vm }  => write!(f, "vwsll.vi {vd}, {vs2}, {uimm}{}", mask_suffix(vm)),
178        }
179    }
180}
181
182/// Format mask suffix for display
183#[inline(always)]
184fn mask_suffix(vm: &bool) -> &'static str {
185    if *vm { "" } else { ", v0.t" }
186}