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