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