Skip to main content

ab_riscv_interpreter/
zvbb.rs

1//! Zvbb extension
2
3#[cfg(test)]
4mod tests;
5pub mod zvbb_helpers;
6pub mod zvkb;
7
8use crate::v::vector_registers::VectorRegistersExt;
9use crate::v::zvexx::arith::zvexx_arith_helpers;
10use crate::v::zvexx::carry::zvexx_carry_helpers;
11use crate::v::zvexx::config::zvexx_config_helpers;
12use crate::v::zvexx::fixed_point::zvexx_fixed_point_helpers;
13use crate::v::zvexx::load::zvexx_load_helpers;
14use crate::v::zvexx::mask::zvexx_mask_helpers;
15use crate::v::zvexx::muldiv::zvexx_muldiv_helpers;
16use crate::v::zvexx::perm::zvexx_perm_helpers;
17use crate::v::zvexx::reduction::zvexx_reduction_helpers;
18use crate::v::zvexx::store::zvexx_store_helpers;
19use crate::v::zvexx::widen_narrow::zvexx_widen_narrow_helpers;
20use crate::v::zvexx::zvexx_helpers;
21use crate::zicsr::zicsr_helpers;
22use crate::zvbb::zvkb::zvkb_helpers;
23use crate::{
24    CsrError, Csrs, ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands,
25    ExecutionError, ProgramCounter, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands,
26    VirtualMemory,
27};
28use ab_riscv_macros::instruction_execution;
29use ab_riscv_primitives::prelude::*;
30use core::fmt;
31use core::ops::ControlFlow;
32
33#[instruction_execution]
34impl<Reg> ExecutableInstructionOperands for ZvbbInstruction<Reg> where Reg: Register {}
35
36#[instruction_execution]
37impl<Reg, ExtState, CustomError> ExecutableInstructionCsr<ExtState, CustomError>
38    for ZvbbInstruction<Reg>
39where
40    Reg: Register,
41{
42}
43
44#[instruction_execution]
45impl<Reg, Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
46    ExecutableInstruction<Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
47    for ZvbbInstruction<Reg>
48where
49    Reg: Register,
50    Regs: RegisterFile<Reg>,
51    ExtState: VectorRegistersExt<Reg, CustomError>,
52    [(); SUPPORTED_ELEN_VLEN::<{ ExtState::ELEN }, { ExtState::VLEN }>]:,
53    Memory: VirtualMemory,
54    PC: ProgramCounter<Reg::Type, Memory, CustomError>,
55    CustomError: fmt::Debug,
56{
57    #[inline(always)]
58    fn execute(
59        self,
60        Rs1Rs2OperandValues {
61            rs1_value,
62            rs2_value,
63        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
64        _regs: &mut Regs,
65        ext_state: &mut ExtState,
66        memory: &mut Memory,
67        program_counter: &mut PC,
68        _system_instruction_handler: &mut InstructionHandler,
69    ) -> Result<
70        ControlFlow<(), (Self::Reg, <Self::Reg as Register>::Type)>,
71        ExecutionError<Reg::Type, CustomError>,
72    > {
73        match self {
74            // vbrev: reverse all bits within each SEW-wide element
75            Self::VbrevV { vd, vs2, vm } => {
76                if !ext_state.vector_instructions_allowed() {
77                    ::core::hint::cold_path();
78                    return Err(ExecutionError::IllegalInstruction {
79                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
80                    });
81                }
82                if !vm && vd == VReg::V0 {
83                    ::core::hint::cold_path();
84                    return Err(ExecutionError::IllegalInstruction {
85                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
86                    });
87                }
88                let Some(vtype) = ext_state.vtype() else {
89                    ::core::hint::cold_path();
90                    return Err(ExecutionError::IllegalInstruction {
91                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
92                    });
93                };
94                let group_regs = vtype.vlmul().register_count();
95                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
96                    program_counter,
97                    vd,
98                    group_regs,
99                )?;
100                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
101                    program_counter,
102                    vs2,
103                    group_regs,
104                )?;
105                let sew = vtype.vsew();
106                // SAFETY: alignments checked above
107                unsafe {
108                    zvbb_helpers::execute_vbrev::<Reg, _, _>(ext_state, vd, vs2, sew, vm);
109                }
110            }
111            // vclz: count leading zeros within each SEW-wide element; result in [0, SEW]
112            Self::VclzV { vd, vs2, vm } => {
113                if !ext_state.vector_instructions_allowed() {
114                    ::core::hint::cold_path();
115                    return Err(ExecutionError::IllegalInstruction {
116                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
117                    });
118                }
119                if !vm && vd == VReg::V0 {
120                    ::core::hint::cold_path();
121                    return Err(ExecutionError::IllegalInstruction {
122                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
123                    });
124                }
125                let Some(vtype) = ext_state.vtype() else {
126                    ::core::hint::cold_path();
127                    return Err(ExecutionError::IllegalInstruction {
128                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
129                    });
130                };
131                let group_regs = vtype.vlmul().register_count();
132                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
133                    program_counter,
134                    vd,
135                    group_regs,
136                )?;
137                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
138                    program_counter,
139                    vs2,
140                    group_regs,
141                )?;
142                let sew = vtype.vsew();
143                // SAFETY: alignments checked above
144                unsafe {
145                    zvbb_helpers::execute_vclz::<Reg, _, _>(ext_state, vd, vs2, sew, vm);
146                }
147            }
148            // vctz: count trailing zeros within each SEW-wide element; result in [0, SEW]
149            Self::VctzV { vd, vs2, vm } => {
150                if !ext_state.vector_instructions_allowed() {
151                    ::core::hint::cold_path();
152                    return Err(ExecutionError::IllegalInstruction {
153                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
154                    });
155                }
156                if !vm && vd == VReg::V0 {
157                    ::core::hint::cold_path();
158                    return Err(ExecutionError::IllegalInstruction {
159                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
160                    });
161                }
162                let Some(vtype) = ext_state.vtype() else {
163                    ::core::hint::cold_path();
164                    return Err(ExecutionError::IllegalInstruction {
165                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
166                    });
167                };
168                let group_regs = vtype.vlmul().register_count();
169                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
170                    program_counter,
171                    vd,
172                    group_regs,
173                )?;
174                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
175                    program_counter,
176                    vs2,
177                    group_regs,
178                )?;
179                let sew = vtype.vsew();
180                // SAFETY: alignments checked above
181                unsafe {
182                    zvbb_helpers::execute_vctz::<Reg, _, _>(ext_state, vd, vs2, sew, vm);
183                }
184            }
185            // vcpop: population count (number of set bits) within each SEW-wide element
186            Self::VcpopV { vd, vs2, vm } => {
187                if !ext_state.vector_instructions_allowed() {
188                    ::core::hint::cold_path();
189                    return Err(ExecutionError::IllegalInstruction {
190                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
191                    });
192                }
193                if !vm && vd == VReg::V0 {
194                    ::core::hint::cold_path();
195                    return Err(ExecutionError::IllegalInstruction {
196                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
197                    });
198                }
199                let Some(vtype) = ext_state.vtype() else {
200                    ::core::hint::cold_path();
201                    return Err(ExecutionError::IllegalInstruction {
202                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
203                    });
204                };
205                let group_regs = vtype.vlmul().register_count();
206                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
207                    program_counter,
208                    vd,
209                    group_regs,
210                )?;
211                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
212                    program_counter,
213                    vs2,
214                    group_regs,
215                )?;
216                let sew = vtype.vsew();
217                // SAFETY: alignments checked above
218                unsafe {
219                    zvbb_helpers::execute_vcpop::<Reg, _, _>(ext_state, vd, vs2, sew, vm);
220                }
221            }
222            // vwsll: widening shift-left-logical; vd is 2*SEW wide, vs2/src are SEW wide.
223            // SEW=E64 is illegal (cannot double); LMUL=M8 is illegal (EMUL(vd)=16 out of range).
224            Self::VwsllVv { vd, vs2, vs1, vm } => {
225                if !ext_state.vector_instructions_allowed() {
226                    ::core::hint::cold_path();
227                    return Err(ExecutionError::IllegalInstruction {
228                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
229                    });
230                }
231                if !vm && vd == VReg::V0 {
232                    ::core::hint::cold_path();
233                    return Err(ExecutionError::IllegalInstruction {
234                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
235                    });
236                }
237                let Some(vtype) = ext_state.vtype() else {
238                    ::core::hint::cold_path();
239                    return Err(ExecutionError::IllegalInstruction {
240                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
241                    });
242                };
243                let sew = vtype.vsew();
244                let Some(double_sew) = sew.double_width() else {
245                    ::core::hint::cold_path();
246                    return Err(ExecutionError::IllegalInstruction {
247                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
248                    });
249                };
250                let group_regs = vtype.vlmul().register_count();
251                let Some(dest_group_regs) =
252                    vtype.vlmul().data_register_count(double_sew.as_eew(), sew)
253                else {
254                    ::core::hint::cold_path();
255                    return Err(ExecutionError::IllegalInstruction {
256                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
257                    });
258                };
259                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
260                    program_counter,
261                    vd,
262                    dest_group_regs,
263                )?;
264                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
265                    program_counter,
266                    vs2,
267                    group_regs,
268                )?;
269                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
270                    program_counter,
271                    vs1,
272                    group_regs,
273                )?;
274                // SAFETY: alignments checked above
275                unsafe {
276                    zvbb_helpers::execute_vwsll::<Reg, _, _>(
277                        ext_state,
278                        vd,
279                        vs2,
280                        zvbb_helpers::OpSrc::Vreg(vs1),
281                        sew,
282                        double_sew,
283                        vm,
284                    );
285                }
286            }
287            Self::VwsllVx {
288                vm,
289                vd,
290                vs2,
291                rs1: _,
292            } => {
293                if !ext_state.vector_instructions_allowed() {
294                    ::core::hint::cold_path();
295                    return Err(ExecutionError::IllegalInstruction {
296                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
297                    });
298                }
299                if !vm && vd == VReg::V0 {
300                    ::core::hint::cold_path();
301                    return Err(ExecutionError::IllegalInstruction {
302                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
303                    });
304                }
305                let Some(vtype) = ext_state.vtype() else {
306                    ::core::hint::cold_path();
307                    return Err(ExecutionError::IllegalInstruction {
308                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
309                    });
310                };
311                let sew = vtype.vsew();
312                let Some(double_sew) = sew.double_width() else {
313                    ::core::hint::cold_path();
314                    return Err(ExecutionError::IllegalInstruction {
315                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
316                    });
317                };
318                let group_regs = vtype.vlmul().register_count();
319                let Some(dest_group_regs) =
320                    vtype.vlmul().data_register_count(double_sew.as_eew(), sew)
321                else {
322                    ::core::hint::cold_path();
323                    return Err(ExecutionError::IllegalInstruction {
324                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
325                    });
326                };
327                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
328                    program_counter,
329                    vd,
330                    dest_group_regs,
331                )?;
332                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
333                    program_counter,
334                    vs2,
335                    group_regs,
336                )?;
337                let scalar = rs1_value.as_i64().cast_unsigned();
338                // SAFETY: alignments checked above
339                unsafe {
340                    zvbb_helpers::execute_vwsll::<Reg, _, _>(
341                        ext_state,
342                        vd,
343                        vs2,
344                        zvbb_helpers::OpSrc::Scalar(scalar),
345                        sew,
346                        double_sew,
347                        vm,
348                    );
349                }
350            }
351            // vwsll.vi: standard 5-bit immediate; vm is the normal mask-control bit
352            Self::VwsllVi { vd, vs2, uimm, vm } => {
353                if !ext_state.vector_instructions_allowed() {
354                    ::core::hint::cold_path();
355                    return Err(ExecutionError::IllegalInstruction {
356                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
357                    });
358                }
359                if !vm && vd == VReg::V0 {
360                    ::core::hint::cold_path();
361                    return Err(ExecutionError::IllegalInstruction {
362                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
363                    });
364                }
365                let Some(vtype) = ext_state.vtype() else {
366                    ::core::hint::cold_path();
367                    return Err(ExecutionError::IllegalInstruction {
368                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
369                    });
370                };
371                let sew = vtype.vsew();
372                let Some(double_sew) = sew.double_width() else {
373                    ::core::hint::cold_path();
374                    return Err(ExecutionError::IllegalInstruction {
375                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
376                    });
377                };
378                let group_regs = vtype.vlmul().register_count();
379                let Some(dest_group_regs) =
380                    vtype.vlmul().data_register_count(double_sew.as_eew(), sew)
381                else {
382                    ::core::hint::cold_path();
383                    return Err(ExecutionError::IllegalInstruction {
384                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
385                    });
386                };
387                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
388                    program_counter,
389                    vd,
390                    dest_group_regs,
391                )?;
392                zvbb_helpers::check_vreg_group_alignment::<Reg, _, _, _>(
393                    program_counter,
394                    vs2,
395                    group_regs,
396                )?;
397                // SAFETY: alignments checked above
398                unsafe {
399                    zvbb_helpers::execute_vwsll::<Reg, _, _>(
400                        ext_state,
401                        vd,
402                        vs2,
403                        zvbb_helpers::OpSrc::Scalar(u64::from(uimm)),
404                        sew,
405                        double_sew,
406                        vm,
407                    );
408                }
409            }
410        }
411        Ok(ControlFlow::Continue(Default::default()))
412    }
413}