Skip to main content

ab_riscv_interpreter/v/zvexx/
mask.rs

1//! ZveXx mask instructions
2
3#[cfg(test)]
4mod tests;
5pub mod zvexx_mask_helpers;
6
7use crate::v::vector_registers::VectorRegistersExt;
8use crate::v::zvexx::zvexx_helpers;
9use crate::{
10    ExecutableInstruction, ExecutableInstructionCsr, ExecutableInstructionOperands, ExecutionError,
11    ProgramCounter, RegisterFile, Rs1Rs2OperandValues, Rs1Rs2Operands, VirtualMemory,
12};
13use ab_riscv_macros::instruction_execution;
14use ab_riscv_primitives::prelude::*;
15use core::fmt;
16use core::ops::ControlFlow;
17
18#[instruction_execution]
19impl<Reg> ExecutableInstructionOperands for ZveXxMaskInstruction<Reg> where Reg: Register {}
20
21#[instruction_execution]
22impl<Reg, ExtState, CustomError> ExecutableInstructionCsr<ExtState, CustomError>
23    for ZveXxMaskInstruction<Reg>
24where
25    Reg: Register,
26{
27}
28
29#[instruction_execution]
30impl<Reg, Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
31    ExecutableInstruction<Regs, ExtState, Memory, PC, InstructionHandler, CustomError>
32    for ZveXxMaskInstruction<Reg>
33where
34    Reg: Register,
35    Regs: RegisterFile<Reg>,
36    ExtState: VectorRegistersExt<Reg, CustomError>,
37    [(); ExtState::ELEN as usize]:,
38    [(); ExtState::VLEN as usize]:,
39    [(); ExtState::VLENB as usize]:,
40    Memory: VirtualMemory,
41    PC: ProgramCounter<Reg::Type, Memory, CustomError>,
42    CustomError: fmt::Debug,
43{
44    #[inline(always)]
45    fn execute(
46        self,
47        Rs1Rs2OperandValues {
48            rs1_value: _,
49            rs2_value: _,
50        }: Rs1Rs2OperandValues<<Self::Reg as Register>::Type>,
51        regs: &mut Regs,
52        ext_state: &mut ExtState,
53        _memory: &mut Memory,
54        program_counter: &mut PC,
55        _system_instruction_handler: &mut InstructionHandler,
56    ) -> Result<
57        ControlFlow<(), (Self::Reg, <Self::Reg as Register>::Type)>,
58        ExecutionError<Reg::Type, CustomError>,
59    > {
60        match self {
61            // Mask-register logical instructions (§16.1).
62            // These compute the body elements [vstart, vl); prestart bits [0, vstart) are
63            // undisturbed and the tail (past vl) is tail-agnostic (realised here as
64            // undisturbed). They still require vtype to be valid (vill=0); any vector
65            // instruction must be rejected when vill is set, regardless of whether it uses
66            // SEW or vl.
67            Self::Vmandn { vd, vs2, vs1 } => {
68                if !ext_state.vector_instructions_allowed() {
69                    return Err(ExecutionError::IllegalInstruction {
70                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
71                    });
72                }
73                ext_state
74                    .vtype()
75                    .ok_or(ExecutionError::IllegalInstruction {
76                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
77                    })?;
78                // SAFETY: all VReg values are valid indices < 32; `vl <= VLEN` and
79                // `vstart <= vl` are architectural invariants; snapshot-before-write inside
80                // the helper means vd may overlap vs2 or vs1 safely.
81                unsafe {
82                    zvexx_mask_helpers::execute_mask_logical_op(ext_state, vd, vs2, vs1, |a, b| {
83                        a && !b
84                    });
85                }
86            }
87            Self::Vmand { vd, vs2, vs1 } => {
88                if !ext_state.vector_instructions_allowed() {
89                    return Err(ExecutionError::IllegalInstruction {
90                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
91                    });
92                }
93                ext_state
94                    .vtype()
95                    .ok_or(ExecutionError::IllegalInstruction {
96                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
97                    })?;
98                // SAFETY: see `Vmandn`
99                unsafe {
100                    zvexx_mask_helpers::execute_mask_logical_op(ext_state, vd, vs2, vs1, |a, b| {
101                        a & b
102                    });
103                }
104            }
105            Self::Vmor { vd, vs2, vs1 } => {
106                if !ext_state.vector_instructions_allowed() {
107                    return Err(ExecutionError::IllegalInstruction {
108                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
109                    });
110                }
111                ext_state
112                    .vtype()
113                    .ok_or(ExecutionError::IllegalInstruction {
114                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
115                    })?;
116                // SAFETY: see `Vmandn`
117                unsafe {
118                    zvexx_mask_helpers::execute_mask_logical_op(ext_state, vd, vs2, vs1, |a, b| {
119                        a | b
120                    });
121                }
122            }
123            Self::Vmxor { vd, vs2, vs1 } => {
124                if !ext_state.vector_instructions_allowed() {
125                    return Err(ExecutionError::IllegalInstruction {
126                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
127                    });
128                }
129                ext_state
130                    .vtype()
131                    .ok_or(ExecutionError::IllegalInstruction {
132                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
133                    })?;
134                // SAFETY: see `Vmandn`
135                unsafe {
136                    zvexx_mask_helpers::execute_mask_logical_op(ext_state, vd, vs2, vs1, |a, b| {
137                        a ^ b
138                    });
139                }
140            }
141            Self::Vmorn { vd, vs2, vs1 } => {
142                if !ext_state.vector_instructions_allowed() {
143                    return Err(ExecutionError::IllegalInstruction {
144                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
145                    });
146                }
147                ext_state
148                    .vtype()
149                    .ok_or(ExecutionError::IllegalInstruction {
150                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
151                    })?;
152                // SAFETY: see `Vmandn`
153                unsafe {
154                    zvexx_mask_helpers::execute_mask_logical_op(ext_state, vd, vs2, vs1, |a, b| {
155                        a || !b
156                    });
157                }
158            }
159            Self::Vmnand { vd, vs2, vs1 } => {
160                if !ext_state.vector_instructions_allowed() {
161                    return Err(ExecutionError::IllegalInstruction {
162                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
163                    });
164                }
165                ext_state
166                    .vtype()
167                    .ok_or(ExecutionError::IllegalInstruction {
168                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
169                    })?;
170                // SAFETY: see `Vmandn`
171                unsafe {
172                    zvexx_mask_helpers::execute_mask_logical_op(ext_state, vd, vs2, vs1, |a, b| {
173                        !(a & b)
174                    });
175                }
176            }
177            Self::Vmnor { vd, vs2, vs1 } => {
178                if !ext_state.vector_instructions_allowed() {
179                    return Err(ExecutionError::IllegalInstruction {
180                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
181                    });
182                }
183                ext_state
184                    .vtype()
185                    .ok_or(ExecutionError::IllegalInstruction {
186                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
187                    })?;
188                // SAFETY: see `Vmandn`
189                unsafe {
190                    zvexx_mask_helpers::execute_mask_logical_op(ext_state, vd, vs2, vs1, |a, b| {
191                        !(a | b)
192                    });
193                }
194            }
195            Self::Vmxnor { vd, vs2, vs1 } => {
196                if !ext_state.vector_instructions_allowed() {
197                    return Err(ExecutionError::IllegalInstruction {
198                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
199                    });
200                }
201                ext_state
202                    .vtype()
203                    .ok_or(ExecutionError::IllegalInstruction {
204                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
205                    })?;
206                // SAFETY: see `Vmandn`
207                unsafe {
208                    zvexx_mask_helpers::execute_mask_logical_op(ext_state, vd, vs2, vs1, |a, b| {
209                        !(a ^ b)
210                    });
211                }
212            }
213            // vcpop.m (§16.2): count set bits in vs2 over active elements, write to GPR rd.
214            Self::Vcpop { rd, vs2, vm } => {
215                if !ext_state.vector_instructions_allowed() {
216                    return Err(ExecutionError::IllegalInstruction {
217                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
218                    });
219                }
220                // vcpop/vfirst require a valid vtype to know vl, but do not use SEW.
221                ext_state
222                    .vtype()
223                    .ok_or(ExecutionError::IllegalInstruction {
224                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
225                    })?;
226                // SAFETY: `vl <= VLMAX <= VLEN`, so `vl.div_ceil(8) <= VLENB`; `vstart <= vl`
227                // by spec invariant.
228                unsafe {
229                    zvexx_mask_helpers::execute_vcpop(regs, ext_state, rd, vs2, vm);
230                }
231            }
232            // vfirst.m (§16.3): find lowest-numbered active set bit in vs2, write index to rd.
233            Self::Vfirst { rd, vs2, vm } => {
234                if !ext_state.vector_instructions_allowed() {
235                    return Err(ExecutionError::IllegalInstruction {
236                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
237                    });
238                }
239                ext_state
240                    .vtype()
241                    .ok_or(ExecutionError::IllegalInstruction {
242                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
243                    })?;
244                // SAFETY: same as `Vcpop`
245                unsafe {
246                    zvexx_mask_helpers::execute_vfirst(regs, ext_state, rd, vs2, vm);
247                }
248            }
249            // vmsbf.m (§16.4): set-before-first mask bit.
250            // Constraints: vd != vs2 (overlap illegal), vm=false implies vd != v0.
251            Self::Vmsbf { vd, vs2, vm } => {
252                if !ext_state.vector_instructions_allowed() {
253                    return Err(ExecutionError::IllegalInstruction {
254                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
255                    });
256                }
257                ext_state
258                    .vtype()
259                    .ok_or(ExecutionError::IllegalInstruction {
260                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
261                    })?;
262                // Spec §16.4: vmsbf/vmsif/vmsof with vstart != 0 raise an illegal instruction
263                // exception.
264                if ext_state.vstart() != 0 {
265                    return Err(ExecutionError::IllegalInstruction {
266                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
267                    });
268                }
269                // Per spec §16.4: vd must not overlap vs2
270                if vd == vs2 {
271                    return Err(ExecutionError::IllegalInstruction {
272                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
273                    });
274                }
275                if !vm && vd == VReg::V0 {
276                    return Err(ExecutionError::IllegalInstruction {
277                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
278                    });
279                }
280                let vl = ext_state.vl();
281                // SAFETY: `vd != vs2` checked above; `vd != v0` when masked checked above;
282                // `vstart == 0` checked above; `vl <= VLEN` so `vl.div_ceil(8) <= VLENB`.
283                unsafe {
284                    zvexx_mask_helpers::execute_vmsbf(ext_state, vd, vs2, vm, vl);
285                }
286            }
287            // vmsof.m (§16.5): set-only-first mask bit.
288            // Same overlap constraints as vmsbf.
289            Self::Vmsof { vd, vs2, vm } => {
290                if !ext_state.vector_instructions_allowed() {
291                    return Err(ExecutionError::IllegalInstruction {
292                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
293                    });
294                }
295                ext_state
296                    .vtype()
297                    .ok_or(ExecutionError::IllegalInstruction {
298                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
299                    })?;
300                // Spec §16.4: vmsbf/vmsif/vmsof with vstart != 0 raise an illegal instruction
301                // exception.
302                if ext_state.vstart() != 0 {
303                    return Err(ExecutionError::IllegalInstruction {
304                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
305                    });
306                }
307                if vd == vs2 {
308                    return Err(ExecutionError::IllegalInstruction {
309                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
310                    });
311                }
312                if !vm && vd == VReg::V0 {
313                    return Err(ExecutionError::IllegalInstruction {
314                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
315                    });
316                }
317                let vl = ext_state.vl();
318                // SAFETY: see `Vmsbf`
319                unsafe {
320                    zvexx_mask_helpers::execute_vmsof(ext_state, vd, vs2, vm, vl);
321                }
322            }
323            // vmsif.m (§16.6): set-including-first mask bit.
324            // Same overlap constraints as vmsbf.
325            Self::Vmsif { vd, vs2, vm } => {
326                if !ext_state.vector_instructions_allowed() {
327                    return Err(ExecutionError::IllegalInstruction {
328                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
329                    });
330                }
331                ext_state
332                    .vtype()
333                    .ok_or(ExecutionError::IllegalInstruction {
334                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
335                    })?;
336                // Spec §16.4: vmsbf/vmsif/vmsof with vstart != 0 raise an illegal instruction
337                // exception.
338                if ext_state.vstart() != 0 {
339                    return Err(ExecutionError::IllegalInstruction {
340                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
341                    });
342                }
343                if vd == vs2 {
344                    return Err(ExecutionError::IllegalInstruction {
345                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
346                    });
347                }
348                if !vm && vd == VReg::V0 {
349                    return Err(ExecutionError::IllegalInstruction {
350                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
351                    });
352                }
353                let vl = ext_state.vl();
354                // SAFETY: see `Vmsbf`
355                unsafe {
356                    zvexx_mask_helpers::execute_vmsif(ext_state, vd, vs2, vm, vl);
357                }
358            }
359            // viota.m (§16.8): write prefix popcount of vs2 bits as SEW-wide elements into vd.
360            // Constraints: vd must not overlap vs2 or v0 (when masked); vd alignment per LMUL;
361            // vstart must be zero (mandatory trap per spec §16.8). There is no SEW-width
362            // constraint: if SEW is too narrow to hold the prefix count the result simply wraps
363            // (truncates to SEW), matching the spec's "integer operations wrap around on overflow"
364            // rule rather than raising an exception.
365            Self::Viota { vd, vs2, vm } => {
366                if !ext_state.vector_instructions_allowed() {
367                    return Err(ExecutionError::IllegalInstruction {
368                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
369                    });
370                }
371                let vtype = ext_state
372                    .vtype()
373                    .ok_or(ExecutionError::IllegalInstruction {
374                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
375                    })?;
376                // Spec §16.8: viota.m with vstart != 0 raises an illegal instruction exception.
377                if ext_state.vstart() != 0 {
378                    return Err(ExecutionError::IllegalInstruction {
379                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
380                    });
381                }
382                let group_regs = vtype.vlmul().register_count();
383                let vd_idx = vd.to_bits();
384                if !vd_idx.is_multiple_of(group_regs) || vd_idx + group_regs > 32 {
385                    return Err(ExecutionError::IllegalInstruction {
386                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
387                    });
388                }
389                // vd must not overlap vs2; vs2 is always a single mask register (group size 1).
390                let vd_start = u32::from(vd.to_bits());
391                let vs2_start = u32::from(vs2.to_bits());
392                if vd_start < vs2_start + 1 && vs2_start < vd_start + u32::from(group_regs) {
393                    return Err(ExecutionError::IllegalInstruction {
394                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
395                    });
396                }
397                if !vm && vd == VReg::V0 {
398                    return Err(ExecutionError::IllegalInstruction {
399                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
400                    });
401                }
402                let sew = vtype.vsew();
403                let vl = ext_state.vl();
404                // SAFETY: vd alignment checked above; vd group does not overlap vs2 checked above;
405                // `vm=false` implies `vd != v0` checked above; vstart == 0 checked above;
406                // `vl <= VLMAX = group_regs * VLENB / sew_bytes`, all element indices valid.
407                unsafe {
408                    zvexx_mask_helpers::execute_viota(ext_state, vd, vs2, vm, vl, sew);
409                }
410            }
411            // vid.v (§16.9): write element index i as SEW-wide integer into vd[i].
412            // Constraints: vm=false implies vd != v0; vd alignment per LMUL.
413            Self::Vid { vd, vm } => {
414                if !ext_state.vector_instructions_allowed() {
415                    return Err(ExecutionError::IllegalInstruction {
416                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
417                    });
418                }
419                let vtype = ext_state
420                    .vtype()
421                    .ok_or(ExecutionError::IllegalInstruction {
422                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
423                    })?;
424                let group_regs = vtype.vlmul().register_count();
425                let vd_idx = vd.to_bits();
426                if !vd_idx.is_multiple_of(group_regs) || vd_idx + group_regs > 32 {
427                    return Err(ExecutionError::IllegalInstruction {
428                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
429                    });
430                }
431                if !vm && vd == VReg::V0 {
432                    return Err(ExecutionError::IllegalInstruction {
433                        address: program_counter.old_pc(zvexx_helpers::INSTRUCTION_SIZE),
434                    });
435                }
436                let sew = vtype.vsew();
437                // SAFETY: vd alignment checked above; `vm=false` implies `vd != v0` checked above;
438                // `vl <= VLMAX = group_regs * VLENB / sew_bytes`, all element indices valid.
439                unsafe {
440                    zvexx_mask_helpers::execute_vid(ext_state, vd, vm, sew);
441                }
442            }
443        }
444
445        Ok(ControlFlow::Continue(Default::default()))
446    }
447}