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