Skip to main content

ab_riscv_interpreter/v/zve64x/
mask.rs

1//! Zve64x mask instructions
2
3#[cfg(test)]
4mod tests;
5pub mod zve64x_mask_helpers;
6
7use crate::v::vector_registers::VectorRegistersExt;
8use crate::v::zve64x::zve64x_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 Zve64xMaskInstruction<Reg> where Reg: Register {}
20
21#[instruction_execution]
22impl<Reg, ExtState, CustomError> ExecutableInstructionCsr<ExtState, CustomError>
23    for Zve64xMaskInstruction<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 Zve64xMaskInstruction<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 operate on the full VLENB bytes regardless of vtype/vl, but still require
63            // vtype to be valid (vill=0). Any vector instruction must be rejected when vill is
64            // set, regardless of whether it uses SEW or vl.
65            Self::Vmandn { vd, vs2, vs1 } => {
66                if !ext_state.vector_instructions_allowed() {
67                    Err(ExecutionError::IllegalInstruction {
68                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
69                    })?;
70                }
71                ext_state
72                    .vtype()
73                    .ok_or(ExecutionError::IllegalInstruction {
74                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
75                    })?;
76                // SAFETY: all VReg values are valid indices < 32; snapshot-before-write
77                // inside the helper means vd may overlap vs2 or vs1 safely.
78                unsafe {
79                    zve64x_mask_helpers::execute_mask_logical_op(
80                        ext_state,
81                        vd,
82                        vs2,
83                        vs1,
84                        |a, b| a & !b,
85                    );
86                }
87            }
88            Self::Vmand { vd, vs2, vs1 } => {
89                if !ext_state.vector_instructions_allowed() {
90                    Err(ExecutionError::IllegalInstruction {
91                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
92                    })?;
93                }
94                ext_state
95                    .vtype()
96                    .ok_or(ExecutionError::IllegalInstruction {
97                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
98                    })?;
99                // SAFETY: see `Vmandn`
100                unsafe {
101                    zve64x_mask_helpers::execute_mask_logical_op(
102                        ext_state,
103                        vd,
104                        vs2,
105                        vs1,
106                        |a, b| a & b,
107                    );
108                }
109            }
110            Self::Vmor { vd, vs2, vs1 } => {
111                if !ext_state.vector_instructions_allowed() {
112                    Err(ExecutionError::IllegalInstruction {
113                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
114                    })?;
115                }
116                ext_state
117                    .vtype()
118                    .ok_or(ExecutionError::IllegalInstruction {
119                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
120                    })?;
121                // SAFETY: see `Vmandn`
122                unsafe {
123                    zve64x_mask_helpers::execute_mask_logical_op(
124                        ext_state,
125                        vd,
126                        vs2,
127                        vs1,
128                        |a, b| a | b,
129                    );
130                }
131            }
132            Self::Vmxor { vd, vs2, vs1 } => {
133                if !ext_state.vector_instructions_allowed() {
134                    Err(ExecutionError::IllegalInstruction {
135                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
136                    })?;
137                }
138                ext_state
139                    .vtype()
140                    .ok_or(ExecutionError::IllegalInstruction {
141                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
142                    })?;
143                // SAFETY: see `Vmandn`
144                unsafe {
145                    zve64x_mask_helpers::execute_mask_logical_op(
146                        ext_state,
147                        vd,
148                        vs2,
149                        vs1,
150                        |a, b| a ^ b,
151                    );
152                }
153            }
154            Self::Vmorn { vd, vs2, vs1 } => {
155                if !ext_state.vector_instructions_allowed() {
156                    Err(ExecutionError::IllegalInstruction {
157                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
158                    })?;
159                }
160                ext_state
161                    .vtype()
162                    .ok_or(ExecutionError::IllegalInstruction {
163                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
164                    })?;
165                // SAFETY: see `Vmandn`
166                unsafe {
167                    zve64x_mask_helpers::execute_mask_logical_op(
168                        ext_state,
169                        vd,
170                        vs2,
171                        vs1,
172                        |a, b| a | !b,
173                    );
174                }
175            }
176            Self::Vmnand { vd, vs2, vs1 } => {
177                if !ext_state.vector_instructions_allowed() {
178                    Err(ExecutionError::IllegalInstruction {
179                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
180                    })?;
181                }
182                ext_state
183                    .vtype()
184                    .ok_or(ExecutionError::IllegalInstruction {
185                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
186                    })?;
187                // SAFETY: see `Vmandn`
188                unsafe {
189                    zve64x_mask_helpers::execute_mask_logical_op(
190                        ext_state,
191                        vd,
192                        vs2,
193                        vs1,
194                        |a, b| !(a & b),
195                    );
196                }
197            }
198            Self::Vmnor { vd, vs2, vs1 } => {
199                if !ext_state.vector_instructions_allowed() {
200                    Err(ExecutionError::IllegalInstruction {
201                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
202                    })?;
203                }
204                ext_state
205                    .vtype()
206                    .ok_or(ExecutionError::IllegalInstruction {
207                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
208                    })?;
209                // SAFETY: see `Vmandn`
210                unsafe {
211                    zve64x_mask_helpers::execute_mask_logical_op(
212                        ext_state,
213                        vd,
214                        vs2,
215                        vs1,
216                        |a, b| !(a | b),
217                    );
218                }
219            }
220            Self::Vmxnor { vd, vs2, vs1 } => {
221                if !ext_state.vector_instructions_allowed() {
222                    Err(ExecutionError::IllegalInstruction {
223                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
224                    })?;
225                }
226                ext_state
227                    .vtype()
228                    .ok_or(ExecutionError::IllegalInstruction {
229                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
230                    })?;
231                // SAFETY: see `Vmandn`
232                unsafe {
233                    zve64x_mask_helpers::execute_mask_logical_op(
234                        ext_state,
235                        vd,
236                        vs2,
237                        vs1,
238                        |a, b| !(a ^ b),
239                    );
240                }
241            }
242            // vcpop.m (§16.2): count set bits in vs2 over active elements, write to GPR rd.
243            Self::Vcpop { rd, vs2, vm } => {
244                if !ext_state.vector_instructions_allowed() {
245                    Err(ExecutionError::IllegalInstruction {
246                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
247                    })?;
248                }
249                // vcpop/vfirst require a valid vtype to know vl, but do not use SEW.
250                ext_state
251                    .vtype()
252                    .ok_or(ExecutionError::IllegalInstruction {
253                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
254                    })?;
255                let vl = ext_state.vl();
256                let vstart = u32::from(ext_state.vstart());
257                // SAFETY: `vl <= VLMAX <= VLEN`, so `vl.div_ceil(8) <= VLENB`; `vstart <= vl`
258                // by spec invariant.
259                unsafe {
260                    zve64x_mask_helpers::execute_vcpop(regs, ext_state, rd, vs2, vm, vl, vstart);
261                }
262            }
263            // vfirst.m (§16.3): find lowest-numbered active set bit in vs2, write index to rd.
264            Self::Vfirst { rd, vs2, vm } => {
265                if !ext_state.vector_instructions_allowed() {
266                    Err(ExecutionError::IllegalInstruction {
267                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
268                    })?;
269                }
270                ext_state
271                    .vtype()
272                    .ok_or(ExecutionError::IllegalInstruction {
273                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
274                    })?;
275                let vl = ext_state.vl();
276                let vstart = u32::from(ext_state.vstart());
277                // SAFETY: same as `Vcpop`
278                unsafe {
279                    zve64x_mask_helpers::execute_vfirst(regs, ext_state, rd, vs2, vm, vl, vstart);
280                }
281            }
282            // vmsbf.m (§16.4): set-before-first mask bit.
283            // Constraints: vd != vs2 (overlap illegal), vm=false implies vd != v0.
284            Self::Vmsbf { vd, vs2, vm } => {
285                if !ext_state.vector_instructions_allowed() {
286                    Err(ExecutionError::IllegalInstruction {
287                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
288                    })?;
289                }
290                ext_state
291                    .vtype()
292                    .ok_or(ExecutionError::IllegalInstruction {
293                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
294                    })?;
295                // Spec §16.4: vmsbf/vmsif/vmsof with vstart != 0 raise an illegal instruction
296                // exception.
297                if ext_state.vstart() != 0 {
298                    Err(ExecutionError::IllegalInstruction {
299                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
300                    })?;
301                }
302                // Per spec §16.4: vd must not overlap vs2
303                if vd.bits() == vs2.bits() {
304                    Err(ExecutionError::IllegalInstruction {
305                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
306                    })?;
307                }
308                if !vm && vd.bits() == 0 {
309                    Err(ExecutionError::IllegalInstruction {
310                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
311                    })?;
312                }
313                let vl = ext_state.vl();
314                // SAFETY: `vd != vs2` checked above; `vd != v0` when masked checked above;
315                // `vstart == 0` checked above; `vl <= VLEN` so `vl.div_ceil(8) <= VLENB`.
316                unsafe {
317                    zve64x_mask_helpers::execute_vmsbf(ext_state, vd, vs2, vm, vl);
318                }
319            }
320            // vmsof.m (§16.5): set-only-first mask bit.
321            // Same overlap constraints as vmsbf.
322            Self::Vmsof { vd, vs2, vm } => {
323                if !ext_state.vector_instructions_allowed() {
324                    Err(ExecutionError::IllegalInstruction {
325                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
326                    })?;
327                }
328                ext_state
329                    .vtype()
330                    .ok_or(ExecutionError::IllegalInstruction {
331                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
332                    })?;
333                // Spec §16.4: vmsbf/vmsif/vmsof with vstart != 0 raise an illegal instruction
334                // exception.
335                if ext_state.vstart() != 0 {
336                    Err(ExecutionError::IllegalInstruction {
337                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
338                    })?;
339                }
340                if vd.bits() == vs2.bits() {
341                    Err(ExecutionError::IllegalInstruction {
342                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
343                    })?;
344                }
345                if !vm && vd.bits() == 0 {
346                    Err(ExecutionError::IllegalInstruction {
347                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
348                    })?;
349                }
350                let vl = ext_state.vl();
351                // SAFETY: see `Vmsbf`
352                unsafe {
353                    zve64x_mask_helpers::execute_vmsof(ext_state, vd, vs2, vm, vl);
354                }
355            }
356            // vmsif.m (§16.6): set-including-first mask bit.
357            // Same overlap constraints as vmsbf.
358            Self::Vmsif { vd, vs2, vm } => {
359                if !ext_state.vector_instructions_allowed() {
360                    Err(ExecutionError::IllegalInstruction {
361                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
362                    })?;
363                }
364                ext_state
365                    .vtype()
366                    .ok_or(ExecutionError::IllegalInstruction {
367                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
368                    })?;
369                // Spec §16.4: vmsbf/vmsif/vmsof with vstart != 0 raise an illegal instruction
370                // exception.
371                if ext_state.vstart() != 0 {
372                    Err(ExecutionError::IllegalInstruction {
373                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
374                    })?;
375                }
376                if vd.bits() == vs2.bits() {
377                    Err(ExecutionError::IllegalInstruction {
378                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
379                    })?;
380                }
381                if !vm && vd.bits() == 0 {
382                    Err(ExecutionError::IllegalInstruction {
383                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
384                    })?;
385                }
386                let vl = ext_state.vl();
387                // SAFETY: see `Vmsbf`
388                unsafe {
389                    zve64x_mask_helpers::execute_vmsif(ext_state, vd, vs2, vm, vl);
390                }
391            }
392            // viota.m (§16.8): write prefix popcount of vs2 bits as SEW-wide elements into vd.
393            // Constraints: vd must not overlap vs2 or v0 (when masked); vd alignment per LMUL;
394            // vstart must be zero (mandatory trap per spec §16.8); SEW must be wide enough
395            // to represent VLMAX-1.
396            Self::Viota { vd, vs2, vm } => {
397                if !ext_state.vector_instructions_allowed() {
398                    Err(ExecutionError::IllegalInstruction {
399                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
400                    })?;
401                }
402                let vtype = ext_state
403                    .vtype()
404                    .ok_or(ExecutionError::IllegalInstruction {
405                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
406                    })?;
407                // Spec §16.8: viota.m with vstart != 0 raises an illegal instruction exception.
408                if u32::from(ext_state.vstart()) != 0 {
409                    Err(ExecutionError::IllegalInstruction {
410                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
411                    })?;
412                }
413                let group_regs = vtype.vlmul().register_count();
414                let vd_idx = vd.bits();
415                if !vd_idx.is_multiple_of(group_regs) || vd_idx + group_regs > 32 {
416                    Err(ExecutionError::IllegalInstruction {
417                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
418                    })?;
419                }
420                // vd must not overlap vs2; vs2 is always a single mask register (group size 1).
421                let vd_start = u32::from(vd.bits());
422                let vs2_start = u32::from(vs2.bits());
423                if vd_start < vs2_start + 1 && vs2_start < vd_start + u32::from(group_regs) {
424                    Err(ExecutionError::IllegalInstruction {
425                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
426                    })?;
427                }
428                if !vm && vd.bits() == 0 {
429                    Err(ExecutionError::IllegalInstruction {
430                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
431                    })?;
432                }
433                let sew = vtype.vsew();
434                let sew_bits = u32::from(sew.bits());
435                let vlmax = vtype.vlmul().vlmax(ExtState::VLEN, sew_bits);
436                if u64::from(vlmax).unbounded_shr(sew_bits) != 0 {
437                    Err(ExecutionError::IllegalInstruction {
438                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
439                    })?;
440                }
441                let vl = ext_state.vl();
442                // SAFETY: vd alignment checked above; vd group does not overlap vs2 checked above;
443                // `vm=false` implies `vd != v0` checked above; vstart == 0 checked above;
444                // SEW wide enough to hold VLMAX-1 checked above;
445                // `vl <= VLMAX = group_regs * VLENB / sew_bytes`, all element indices valid.
446                unsafe {
447                    zve64x_mask_helpers::execute_viota(ext_state, vd, vs2, vm, vl, sew);
448                }
449            }
450            // vid.v (§16.9): write element index i as SEW-wide integer into vd[i].
451            // Constraints: vm=false implies vd != v0; vd alignment per LMUL.
452            Self::Vid { vd, vm } => {
453                if !ext_state.vector_instructions_allowed() {
454                    Err(ExecutionError::IllegalInstruction {
455                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
456                    })?;
457                }
458                let vtype = ext_state
459                    .vtype()
460                    .ok_or(ExecutionError::IllegalInstruction {
461                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
462                    })?;
463                let group_regs = vtype.vlmul().register_count();
464                let vd_idx = vd.bits();
465                if !vd_idx.is_multiple_of(group_regs) || vd_idx + group_regs > 32 {
466                    Err(ExecutionError::IllegalInstruction {
467                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
468                    })?;
469                }
470                if !vm && vd.bits() == 0 {
471                    Err(ExecutionError::IllegalInstruction {
472                        address: program_counter.old_pc(zve64x_helpers::INSTRUCTION_SIZE),
473                    })?;
474                }
475                let sew = vtype.vsew();
476                let vl = ext_state.vl();
477                let vstart = u32::from(ext_state.vstart());
478                // SAFETY: vd alignment checked above; `vm=false` implies `vd != v0` checked above;
479                // `vl <= VLMAX = group_regs * VLENB / sew_bytes`, all element indices valid.
480                unsafe {
481                    zve64x_mask_helpers::execute_vid(ext_state, vd, vm, vl, vstart, sew);
482                }
483            }
484        }
485
486        Ok(ControlFlow::Continue(Default::default()))
487    }
488}