polkavm_common/
program.rs

1use crate::abi::{VM_CODE_ADDRESS_ALIGNMENT, VM_MAXIMUM_CODE_SIZE, VM_MAXIMUM_IMPORT_COUNT, VM_MAXIMUM_JUMP_TABLE_ENTRIES};
2use crate::utils::ArcBytes;
3use crate::varint::{read_simple_varint, read_varint, write_simple_varint, MAX_VARINT_LENGTH};
4use core::fmt::Write;
5use core::ops::Range;
6
7#[derive(Copy, Clone)]
8#[repr(transparent)]
9pub struct RawReg(u32);
10
11impl Eq for RawReg {}
12impl PartialEq for RawReg {
13    fn eq(&self, rhs: &Self) -> bool {
14        self.get() == rhs.get()
15    }
16}
17
18impl RawReg {
19    #[inline]
20    pub const fn get(self) -> Reg {
21        let mut value = self.0 & 0b1111;
22        if value > 12 {
23            value = 12;
24        }
25
26        let Some(reg) = Reg::from_raw(value) else { unreachable!() };
27        reg
28    }
29
30    #[inline]
31    pub const fn raw_unparsed(self) -> u32 {
32        self.0
33    }
34}
35
36impl From<Reg> for RawReg {
37    fn from(reg: Reg) -> Self {
38        Self(reg as u32)
39    }
40}
41
42impl From<RawReg> for Reg {
43    fn from(reg: RawReg) -> Self {
44        reg.get()
45    }
46}
47
48impl core::fmt::Debug for RawReg {
49    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
50        write!(fmt, "{} (0x{:x})", self.get(), self.0)
51    }
52}
53
54impl core::fmt::Display for RawReg {
55    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
56        self.get().fmt(fmt)
57    }
58}
59
60#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
61#[repr(u32)]
62pub enum Reg {
63    RA = 0,
64    SP = 1,
65    T0 = 2,
66    T1 = 3,
67    T2 = 4,
68    S0 = 5,
69    S1 = 6,
70    A0 = 7,
71    A1 = 8,
72    A2 = 9,
73    A3 = 10,
74    A4 = 11,
75    A5 = 12,
76}
77
78impl Reg {
79    #[inline]
80    pub const fn to_usize(self) -> usize {
81        self as usize
82    }
83
84    #[inline]
85    pub const fn to_u32(self) -> u32 {
86        self as u32
87    }
88
89    #[inline]
90    pub const fn raw(self) -> RawReg {
91        RawReg(self as u32)
92    }
93
94    #[inline]
95    pub const fn from_raw(value: u32) -> Option<Reg> {
96        Some(match value {
97            0 => Reg::RA,
98            1 => Reg::SP,
99            2 => Reg::T0,
100            3 => Reg::T1,
101            4 => Reg::T2,
102            5 => Reg::S0,
103            6 => Reg::S1,
104            7 => Reg::A0,
105            8 => Reg::A1,
106            9 => Reg::A2,
107            10 => Reg::A3,
108            11 => Reg::A4,
109            12 => Reg::A5,
110            _ => return None,
111        })
112    }
113
114    pub const fn name(self) -> &'static str {
115        use Reg::*;
116        match self {
117            RA => "ra",
118            SP => "sp",
119            T0 => "t0",
120            T1 => "t1",
121            T2 => "t2",
122            S0 => "s0",
123            S1 => "s1",
124            A0 => "a0",
125            A1 => "a1",
126            A2 => "a2",
127            A3 => "a3",
128            A4 => "a4",
129            A5 => "a5",
130        }
131    }
132
133    pub const fn name_non_abi(self) -> &'static str {
134        use Reg::*;
135        match self {
136            RA => "r0",
137            SP => "r1",
138            T0 => "r2",
139            T1 => "r3",
140            T2 => "r4",
141            S0 => "r5",
142            S1 => "r6",
143            A0 => "r7",
144            A1 => "r8",
145            A2 => "r9",
146            A3 => "r10",
147            A4 => "r11",
148            A5 => "r12",
149        }
150    }
151
152    /// List of all of the VM's registers.
153    pub const ALL: [Reg; 13] = {
154        use Reg::*;
155        [RA, SP, T0, T1, T2, S0, S1, A0, A1, A2, A3, A4, A5]
156    };
157
158    /// List of all input/output argument registers.
159    pub const ARG_REGS: [Reg; 9] = [Reg::A0, Reg::A1, Reg::A2, Reg::A3, Reg::A4, Reg::A5, Reg::T0, Reg::T1, Reg::T2];
160
161    pub const MAXIMUM_INPUT_REGS: usize = 9;
162    pub const MAXIMUM_OUTPUT_REGS: usize = 2;
163}
164
165impl core::fmt::Display for Reg {
166    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
167        fmt.write_str(self.name())
168    }
169}
170
171#[inline(never)]
172#[cold]
173fn find_next_offset_unbounded(bitmask: &[u8], code_len: u32, mut offset: u32) -> u32 {
174    while let Some(&byte) = bitmask.get(offset as usize >> 3) {
175        let shift = offset & 7;
176        let mask = byte >> shift;
177        if mask == 0 {
178            offset += 8 - shift;
179        } else {
180            offset += mask.trailing_zeros();
181            break;
182        }
183    }
184
185    core::cmp::min(code_len, offset)
186}
187
188#[inline(never)]
189fn visitor_step_slow<T>(
190    state: &mut <T as OpcodeVisitor>::State,
191    code: &[u8],
192    bitmask: &[u8],
193    offset: u32,
194    opcode_visitor: T,
195) -> (u32, <T as OpcodeVisitor>::ReturnTy, bool)
196where
197    T: OpcodeVisitor,
198{
199    if offset as usize >= code.len() {
200        return (offset + 1, visitor_step_invalid_instruction(state, offset, opcode_visitor), true);
201    }
202
203    debug_assert!(code.len() <= u32::MAX as usize);
204    debug_assert_eq!(bitmask.len(), (code.len() + 7) / 8);
205    debug_assert!(offset as usize <= code.len());
206    debug_assert!(get_bit_for_offset(bitmask, code.len(), offset), "bit at {offset} is zero");
207
208    let (skip, mut is_next_instruction_invalid) = parse_bitmask_slow(bitmask, code.len(), offset);
209    let chunk = &code[offset as usize..core::cmp::min(offset as usize + 17, code.len())];
210    let opcode = chunk[0];
211
212    if is_next_instruction_invalid && offset as usize + skip as usize + 1 >= code.len() {
213        // This is the last instruction.
214        if !opcode_visitor
215            .instruction_set()
216            .opcode_from_u8(opcode)
217            .unwrap_or(Opcode::trap)
218            .can_fallthrough()
219        {
220            // We can't fallthrough, so there's no need to inject a trap after this instruction.
221            is_next_instruction_invalid = false;
222        }
223    }
224
225    let mut t: [u8; 16] = [0; 16];
226    t[..chunk.len() - 1].copy_from_slice(&chunk[1..]);
227    let chunk = u128::from_le_bytes([
228        t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15],
229    ]);
230
231    debug_assert!(
232        opcode_visitor.instruction_set().opcode_from_u8(opcode).is_some()
233            || !is_jump_target_valid(opcode_visitor.instruction_set(), code, bitmask, offset + skip + 1)
234    );
235
236    (
237        offset + skip + 1,
238        opcode_visitor.dispatch(state, usize::from(opcode), chunk, offset, skip),
239        is_next_instruction_invalid,
240    )
241}
242
243#[cfg_attr(not(debug_assertions), inline(always))]
244fn visitor_step_fast<T>(
245    state: &mut <T as OpcodeVisitor>::State,
246    code: &[u8],
247    bitmask: &[u8],
248    offset: u32,
249    opcode_visitor: T,
250) -> (u32, <T as OpcodeVisitor>::ReturnTy, bool)
251where
252    T: OpcodeVisitor,
253{
254    debug_assert!(code.len() <= u32::MAX as usize);
255    debug_assert_eq!(bitmask.len(), (code.len() + 7) / 8);
256    debug_assert!(offset as usize <= code.len());
257    debug_assert!(get_bit_for_offset(bitmask, code.len(), offset), "bit at {offset} is zero");
258
259    debug_assert!(offset as usize + 32 <= code.len());
260
261    let Some(chunk) = code.get(offset as usize..offset as usize + 32) else {
262        unreachable!()
263    };
264    let Some(skip) = parse_bitmask_fast(bitmask, offset) else {
265        unreachable!()
266    };
267    let opcode = usize::from(chunk[0]);
268
269    // NOTE: This should produce the same assembly as the unsafe `read_unaligned`.
270    let chunk = u128::from_le_bytes([
271        chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7], chunk[8], chunk[9], chunk[10], chunk[11], chunk[12],
272        chunk[13], chunk[14], chunk[15], chunk[16],
273    ]);
274
275    debug_assert!(skip <= BITMASK_MAX);
276    debug_assert!(
277        opcode_visitor.instruction_set().opcode_from_u8(opcode as u8).is_some()
278            || !is_jump_target_valid(opcode_visitor.instruction_set(), code, bitmask, offset + skip + 1)
279    );
280    let result = opcode_visitor.dispatch(state, opcode, chunk, offset, skip);
281
282    let next_offset = offset + skip + 1;
283    let is_next_instruction_invalid = skip == 24 && !get_bit_for_offset(bitmask, code.len(), next_offset);
284    (next_offset, result, is_next_instruction_invalid)
285}
286
287#[cfg_attr(not(debug_assertions), inline(always))]
288#[cold]
289fn visitor_step_invalid_instruction<T>(state: &mut <T as OpcodeVisitor>::State, offset: u32, opcode_visitor: T) -> T::ReturnTy
290where
291    T: OpcodeVisitor,
292{
293    opcode_visitor.dispatch(state, INVALID_INSTRUCTION_INDEX as usize, 0, offset, 0)
294}
295
296#[cfg_attr(not(debug_assertions), inline(always))]
297fn visitor_step_runner<T, const FAST_PATH: bool>(
298    state: &mut <T as OpcodeVisitor>::State,
299    code: &[u8],
300    bitmask: &[u8],
301    mut offset: u32,
302    opcode_visitor: T,
303) -> u32
304where
305    T: OpcodeVisitor<ReturnTy = ()>,
306{
307    let (next_offset, (), is_next_instruction_invalid) = if FAST_PATH {
308        visitor_step_fast(state, code, bitmask, offset, opcode_visitor)
309    } else {
310        visitor_step_slow(state, code, bitmask, offset, opcode_visitor)
311    };
312
313    offset = next_offset;
314    if is_next_instruction_invalid {
315        visitor_step_invalid_instruction(state, offset, opcode_visitor);
316        if (offset as usize) < code.len() {
317            let next_offset = find_next_offset_unbounded(bitmask, code.len() as u32, offset);
318            debug_assert!(next_offset > offset);
319            offset = next_offset;
320        }
321    }
322
323    offset
324}
325
326// Having this be never inlined makes it easier to analyze the resulting assembly/machine code,
327// and it also seems to make the code mariginally faster for some reason.
328#[inline(never)]
329fn visitor_run<T>(state: &mut <T as OpcodeVisitor>::State, blob: &ProgramBlob, opcode_visitor: T)
330where
331    T: OpcodeVisitor<ReturnTy = ()>,
332{
333    let code = blob.code();
334    let bitmask = blob.bitmask();
335
336    let mut offset = 0;
337    if !get_bit_for_offset(bitmask, code.len(), 0) {
338        visitor_step_invalid_instruction(state, 0, opcode_visitor);
339        offset = find_next_offset_unbounded(bitmask, code.len() as u32, 0);
340    }
341
342    while offset as usize + 32 <= code.len() {
343        offset = visitor_step_runner::<T, true>(state, code, bitmask, offset, opcode_visitor);
344    }
345
346    while (offset as usize) < code.len() {
347        offset = visitor_step_runner::<T, false>(state, code, bitmask, offset, opcode_visitor);
348    }
349}
350
351#[inline(always)]
352fn sign_extend_at(value: u32, bits_to_cut: u32) -> u32 {
353    (((u64::from(value) << bits_to_cut) as u32 as i32).wrapping_shr(bits_to_cut)) as u32
354}
355
356type LookupEntry = u32;
357const EMPTY_LOOKUP_ENTRY: LookupEntry = 0;
358
359#[repr(transparent)]
360struct LookupTable([LookupEntry; 256]);
361
362impl LookupTable {
363    const fn pack(imm1_bits: u32, imm1_skip: u32, imm2_bits: u32) -> LookupEntry {
364        assert!(imm1_bits <= 0b111111);
365        assert!(imm2_bits <= 0b111111);
366        assert!(imm1_skip <= 0b111111);
367        (imm1_bits) | ((imm1_skip) << 6) | ((imm2_bits) << 12)
368    }
369
370    #[inline(always)]
371    fn unpack(entry: LookupEntry) -> (u32, u32, u32) {
372        (entry & 0b111111, (entry >> 6) & 0b111111, (entry >> 12) & 0b111111)
373    }
374
375    const fn build(offset: i32) -> Self {
376        const fn min_u32(a: u32, b: u32) -> u32 {
377            if a < b {
378                a
379            } else {
380                b
381            }
382        }
383
384        const fn clamp_i32(range: core::ops::RangeInclusive<i32>, value: i32) -> i32 {
385            if value < *range.start() {
386                *range.start()
387            } else if value > *range.end() {
388                *range.end()
389            } else {
390                value
391            }
392        }
393
394        const fn sign_extend_cutoff_for_length(length: u32) -> u32 {
395            match length {
396                0 => 32,
397                1 => 24,
398                2 => 16,
399                3 => 8,
400                4 => 0,
401                _ => unreachable!(),
402            }
403        }
404
405        let mut output = [EMPTY_LOOKUP_ENTRY; 256];
406        let mut skip = 0;
407        while skip <= 0b11111 {
408            let mut aux = 0;
409            while aux <= 0b111 {
410                let imm1_length = min_u32(4, aux);
411                let imm2_length = clamp_i32(0..=4, skip as i32 - imm1_length as i32 - offset) as u32;
412                let imm1_bits = sign_extend_cutoff_for_length(imm1_length);
413                let imm2_bits = sign_extend_cutoff_for_length(imm2_length);
414                let imm1_skip = imm1_length * 8;
415
416                let index = Self::get_lookup_index(skip, aux);
417                output[index as usize] = Self::pack(imm1_bits, imm1_skip, imm2_bits);
418                aux += 1;
419            }
420            skip += 1;
421        }
422
423        LookupTable(output)
424    }
425
426    #[inline(always)]
427    const fn get_lookup_index(skip: u32, aux: u32) -> u32 {
428        debug_assert!(skip <= 0b11111);
429        let index = skip | ((aux & 0b111) << 5);
430        debug_assert!(index <= 0xff);
431        index
432    }
433
434    #[inline(always)]
435    fn get(&self, skip: u32, aux: u32) -> (u32, u32, u32) {
436        let index = Self::get_lookup_index(skip, aux);
437        debug_assert!((index as usize) < self.0.len());
438
439        #[allow(unsafe_code)]
440        // SAFETY: `index` is composed of a 5-bit `skip` and 3-bit `aux`,
441        // which gives us 8 bits in total, and the table's length is 256,
442        // so out of bounds access in impossible.
443        Self::unpack(*unsafe { self.0.get_unchecked(index as usize) })
444    }
445}
446
447static TABLE_1: LookupTable = LookupTable::build(1);
448static TABLE_2: LookupTable = LookupTable::build(2);
449
450#[inline(always)]
451pub fn read_args_imm(chunk: u128, skip: u32) -> u32 {
452    read_simple_varint(chunk as u32, skip)
453}
454
455#[inline(always)]
456pub fn read_args_offset(chunk: u128, instruction_offset: u32, skip: u32) -> u32 {
457    instruction_offset.wrapping_add(read_args_imm(chunk, skip))
458}
459
460#[inline(always)]
461pub fn read_args_imm2(chunk: u128, skip: u32) -> (u32, u32) {
462    let (imm1_bits, imm1_skip, imm2_bits) = TABLE_1.get(skip, chunk as u32);
463    let chunk = chunk >> 8;
464    let chunk = chunk as u64;
465    let imm1 = sign_extend_at(chunk as u32, imm1_bits);
466    let chunk = chunk >> imm1_skip;
467    let imm2 = sign_extend_at(chunk as u32, imm2_bits);
468    (imm1, imm2)
469}
470
471#[inline(always)]
472pub fn read_args_reg_imm(chunk: u128, skip: u32) -> (RawReg, u32) {
473    let chunk = chunk as u64;
474    let reg = RawReg(chunk as u32);
475    let chunk = chunk >> 8;
476    let (_, _, imm_bits) = TABLE_1.get(skip, 0);
477    let imm = sign_extend_at(chunk as u32, imm_bits);
478    (reg, imm)
479}
480
481#[inline(always)]
482pub fn read_args_reg_imm2(chunk: u128, skip: u32) -> (RawReg, u32, u32) {
483    let reg = RawReg(chunk as u32);
484    let (imm1_bits, imm1_skip, imm2_bits) = TABLE_1.get(skip, chunk as u32 >> 4);
485    let chunk = chunk >> 8;
486    let chunk = chunk as u64;
487    let imm1 = sign_extend_at(chunk as u32, imm1_bits);
488    let chunk = chunk >> imm1_skip;
489    let imm2 = sign_extend_at(chunk as u32, imm2_bits);
490    (reg, imm1, imm2)
491}
492
493#[inline(always)]
494pub fn read_args_reg_imm_offset(chunk: u128, instruction_offset: u32, skip: u32) -> (RawReg, u32, u32) {
495    let (reg, imm1, imm2) = read_args_reg_imm2(chunk, skip);
496    let imm2 = instruction_offset.wrapping_add(imm2);
497    (reg, imm1, imm2)
498}
499
500#[inline(always)]
501pub fn read_args_regs2_imm2(chunk: u128, skip: u32) -> (RawReg, RawReg, u32, u32) {
502    let (reg1, reg2, imm1_aux) = {
503        let value = chunk as u32;
504        (RawReg(value), RawReg(value >> 4), value >> 8)
505    };
506
507    let (imm1_bits, imm1_skip, imm2_bits) = TABLE_2.get(skip, imm1_aux);
508    let chunk = chunk >> 16;
509    let chunk = chunk as u64;
510    let imm1 = sign_extend_at(chunk as u32, imm1_bits);
511    let chunk = chunk >> imm1_skip;
512    let imm2 = sign_extend_at(chunk as u32, imm2_bits);
513    (reg1, reg2, imm1, imm2)
514}
515
516#[inline(always)]
517pub fn read_args_reg_imm64(chunk: u128, _skip: u32) -> (RawReg, u64) {
518    let reg = RawReg(chunk as u32);
519    let imm = (chunk >> 8) as u64;
520    (reg, imm)
521}
522
523#[inline(always)]
524pub fn read_args_regs2_imm(chunk: u128, skip: u32) -> (RawReg, RawReg, u32) {
525    let chunk = chunk as u64;
526    let (reg1, reg2) = {
527        let value = chunk as u32;
528        (RawReg(value), RawReg(value >> 4))
529    };
530    let chunk = chunk >> 8;
531    let (_, _, imm_bits) = TABLE_1.get(skip, 0);
532    let imm = sign_extend_at(chunk as u32, imm_bits);
533    (reg1, reg2, imm)
534}
535
536#[inline(always)]
537pub fn read_args_regs2_offset(chunk: u128, instruction_offset: u32, skip: u32) -> (RawReg, RawReg, u32) {
538    let (reg1, reg2, imm) = read_args_regs2_imm(chunk, skip);
539    let imm = instruction_offset.wrapping_add(imm);
540    (reg1, reg2, imm)
541}
542
543#[inline(always)]
544pub fn read_args_regs3(chunk: u128) -> (RawReg, RawReg, RawReg) {
545    let chunk = chunk as u32;
546    let (reg2, reg3, reg1) = (RawReg(chunk), RawReg(chunk >> 4), RawReg(chunk >> 8));
547    (reg1, reg2, reg3)
548}
549
550#[inline(always)]
551pub fn read_args_regs2(chunk: u128) -> (RawReg, RawReg) {
552    let chunk = chunk as u32;
553    let (reg1, reg2) = (RawReg(chunk), RawReg(chunk >> 4));
554    (reg1, reg2)
555}
556
557#[cfg(kani)]
558mod kani {
559    use core::cmp::min;
560
561    fn clamp<T>(range: core::ops::RangeInclusive<T>, value: T) -> T
562    where
563        T: PartialOrd + Copy,
564    {
565        if value < *range.start() {
566            *range.start()
567        } else if value > *range.end() {
568            *range.end()
569        } else {
570            value
571        }
572    }
573
574    fn read<O, L>(slice: &[u8], offset: O, length: L) -> u32
575    where
576        O: TryInto<usize>,
577        L: TryInto<usize>,
578    {
579        let offset = offset.try_into().unwrap_or_else(|_| unreachable!());
580        let length = length.try_into().unwrap_or_else(|_| unreachable!());
581        let slice = &slice[offset..offset + length];
582        match length {
583            0 => 0,
584            1 => slice[0] as u32,
585            2 => u16::from_le_bytes([slice[0], slice[1]]) as u32,
586            3 => u32::from_le_bytes([slice[0], slice[1], slice[2], 0]),
587            4 => u32::from_le_bytes([slice[0], slice[1], slice[2], slice[3]]),
588            _ => unreachable!(),
589        }
590    }
591
592    fn sext<L>(value: u32, length: L) -> u32
593    where
594        L: Into<i64>,
595    {
596        match length.into() {
597            0 => 0,
598            1 => value as u8 as i8 as i32 as u32,
599            2 => value as u16 as i16 as i32 as u32,
600            3 => (((value << 8) as i32) >> 8) as u32,
601            4 => value,
602            _ => unreachable!(),
603        }
604    }
605
606    macro_rules! args {
607        () => {{
608            let code: [u8; 16] = kani::any();
609            let chunk = u128::from_le_bytes(code);
610            let skip: u32 = kani::any_where(|x| *x <= super::BITMASK_MAX);
611
612            (code, chunk, skip)
613        }};
614    }
615
616    #[kani::proof]
617    fn verify_read_args_imm() {
618        fn simple_read_args_imm(code: &[u8], skip: u32) -> u32 {
619            let imm_length = min(4, skip);
620            sext(read(code, 0, imm_length), imm_length)
621        }
622
623        let (code, chunk, skip) = args!();
624        assert_eq!(super::read_args_imm(chunk, skip), simple_read_args_imm(&code, skip));
625    }
626
627    #[kani::proof]
628    fn verify_read_args_imm2() {
629        fn simple_read_args_imm2(code: &[u8], skip: i32) -> (u32, u32) {
630            let imm1_length = min(4, i32::from(code[0]) & 0b111);
631            let imm2_length = clamp(0..=4, skip - imm1_length - 1);
632            let imm1 = sext(read(code, 1, imm1_length), imm1_length);
633            let imm2 = sext(read(code, 1 + imm1_length, imm2_length), imm2_length);
634            (imm1, imm2)
635        }
636
637        let (code, chunk, skip) = args!();
638        assert_eq!(super::read_args_imm2(chunk, skip), simple_read_args_imm2(&code, skip as i32));
639    }
640
641    #[kani::proof]
642    fn verify_read_args_reg_imm() {
643        fn simple_read_args_reg_imm(code: &[u8], skip: i32) -> (u8, u32) {
644            let reg = min(12, code[0] & 0b1111);
645            let imm_length = clamp(0..=4, skip - 1);
646            let imm = sext(read(code, 1, imm_length), imm_length);
647            (reg, imm)
648        }
649
650        let (code, chunk, skip) = args!();
651        let (reg, imm) = super::read_args_reg_imm(chunk, skip);
652        let reg = reg.get() as u8;
653        assert_eq!((reg, imm), simple_read_args_reg_imm(&code, skip as i32));
654    }
655
656    #[kani::proof]
657    fn verify_read_args_reg_imm2() {
658        fn simple_read_args_reg_imm2(code: &[u8], skip: i32) -> (u8, u32, u32) {
659            let reg = min(12, code[0] & 0b1111);
660            let imm1_length = min(4, i32::from(code[0] >> 4) & 0b111);
661            let imm2_length = clamp(0..=4, skip - imm1_length - 1);
662            let imm1 = sext(read(code, 1, imm1_length), imm1_length);
663            let imm2 = sext(read(code, 1 + imm1_length, imm2_length), imm2_length);
664            (reg, imm1, imm2)
665        }
666
667        let (code, chunk, skip) = args!();
668        let (reg, imm1, imm2) = super::read_args_reg_imm2(chunk, skip);
669        let reg = reg.get() as u8;
670        assert_eq!((reg, imm1, imm2), simple_read_args_reg_imm2(&code, skip as i32));
671    }
672
673    #[kani::proof]
674    fn verify_read_args_regs2_imm2() {
675        fn simple_read_args_regs2_imm2(code: &[u8], skip: i32) -> (u8, u8, u32, u32) {
676            let reg1 = min(12, code[0] & 0b1111);
677            let reg2 = min(12, code[0] >> 4);
678            let imm1_length = min(4, i32::from(code[1]) & 0b111);
679            let imm2_length = clamp(0..=4, skip - imm1_length - 2);
680            let imm1 = sext(read(code, 2, imm1_length), imm1_length);
681            let imm2 = sext(read(code, 2 + imm1_length, imm2_length), imm2_length);
682            (reg1, reg2, imm1, imm2)
683        }
684
685        let (code, chunk, skip) = args!();
686        let (reg1, reg2, imm1, imm2) = super::read_args_regs2_imm2(chunk, skip);
687        let reg1 = reg1.get() as u8;
688        let reg2 = reg2.get() as u8;
689        assert_eq!((reg1, reg2, imm1, imm2), simple_read_args_regs2_imm2(&code, skip as i32))
690    }
691
692    #[kani::proof]
693    fn verify_read_args_regs2_imm() {
694        fn simple_read_args_regs2_imm(code: &[u8], skip: u32) -> (u8, u8, u32) {
695            let reg1 = min(12, code[0] & 0b1111);
696            let reg2 = min(12, code[0] >> 4);
697            let imm_length = clamp(0..=4, skip as i32 - 1);
698            let imm = sext(read(code, 1, imm_length), imm_length);
699            (reg1, reg2, imm)
700        }
701
702        let (code, chunk, skip) = args!();
703        let (reg1, reg2, imm) = super::read_args_regs2_imm(chunk, skip);
704        let reg1 = reg1.get() as u8;
705        let reg2 = reg2.get() as u8;
706        assert_eq!((reg1, reg2, imm), simple_read_args_regs2_imm(&code, skip));
707    }
708
709    #[kani::proof]
710    fn verify_read_args_regs3() {
711        fn simple_read_args_regs3(code: &[u8]) -> (u8, u8, u8) {
712            let reg2 = min(12, code[0] & 0b1111);
713            let reg3 = min(12, code[0] >> 4);
714            let reg1 = min(12, code[1] & 0b1111);
715            (reg1, reg2, reg3)
716        }
717
718        let (code, chunk, _) = args!();
719        let (reg1, reg2, reg3) = super::read_args_regs3(chunk);
720        let reg1 = reg1.get() as u8;
721        let reg2 = reg2.get() as u8;
722        let reg3 = reg3.get() as u8;
723        assert_eq!((reg1, reg2, reg3), simple_read_args_regs3(&code));
724    }
725
726    #[kani::proof]
727    fn verify_read_args_regs2() {
728        fn simple_read_args_regs2(code: &[u8]) -> (u8, u8) {
729            let reg1 = min(12, code[0] & 0b1111);
730            let reg2 = min(12, code[0] >> 4);
731            (reg1, reg2)
732        }
733
734        let (code, chunk, _) = args!();
735        let (reg1, reg2) = super::read_args_regs2(chunk);
736        let reg1 = reg1.get() as u8;
737        let reg2 = reg2.get() as u8;
738        assert_eq!((reg1, reg2), simple_read_args_regs2(&code));
739    }
740}
741
742/// The lowest level visitor; dispatches directly on opcode numbers.
743pub trait OpcodeVisitor: Copy {
744    type State;
745    type ReturnTy;
746    type InstructionSet: InstructionSet;
747
748    fn instruction_set(self) -> Self::InstructionSet;
749    fn dispatch(self, state: &mut Self::State, opcode: usize, chunk: u128, offset: u32, skip: u32) -> Self::ReturnTy;
750}
751
752macro_rules! define_opcodes {
753    (@impl_instruction_set $instruction_set:ident [$($instruction_set_tag:tt),+] $([$($tag:tt),+] $name:ident = $value:expr,)+) => {
754        impl $instruction_set {
755            #[doc(hidden)]
756            pub const IS_INSTRUCTION_VALID_CONST: [bool; 256] = {
757                let mut is_valid = [false; 256];
758                let b = [$($instruction_set_tag),+];
759                $(
760                    is_valid[$value] = {
761                        let a = [$($tag),+];
762                        let mut found = false;
763                        let mut i = 0;
764                        'outer: while i < a.len() {
765                            let mut j = 0;
766                            while j < b.len() {
767                                if a[i] == b[j] {
768                                    found = true;
769                                    break 'outer;
770                                }
771                                j += 1;
772                            }
773                            i += 1;
774                        }
775                        found
776                    };
777                )+
778                is_valid
779            };
780        }
781
782        impl InstructionSet for $instruction_set {
783            #[cfg_attr(feature = "alloc", inline)]
784            fn opcode_from_u8(self, byte: u8) -> Option<Opcode> {
785                static IS_INSTRUCTION_VALID: [bool; 256] = $instruction_set::IS_INSTRUCTION_VALID_CONST;
786
787                if !IS_INSTRUCTION_VALID[byte as usize] {
788                    return None;
789                }
790
791                #[allow(unsafe_code)]
792                // SAFETY: We already checked that this opcode is valid, so this is safe.
793                unsafe {
794                    Some(core::mem::transmute(byte))
795                }
796            }
797        }
798    };
799
800    (@impl_shared $([$($tag:tt),+] $name:ident = $value:expr,)+) => {
801        #[allow(non_camel_case_types)]
802        #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
803        #[repr(u8)]
804        pub enum Opcode {
805            $(
806                $name = $value,
807            )+
808        }
809
810        impl Opcode {
811            pub fn from_u8_any(byte: u8) -> Option<Opcode> {
812                match byte {
813                    $($value => Some(Opcode::$name),)+
814                    _ => None
815                }
816            }
817        }
818
819        define_opcodes!(@impl_instruction_set ISA32_V1         [I_32, I_SBRK]  $([$($tag),+] $name = $value,)+);
820        define_opcodes!(@impl_instruction_set ISA32_V1_NoSbrk  [I_32]          $([$($tag),+] $name = $value,)+);
821        define_opcodes!(@impl_instruction_set ISA64_V1         [I_64, I_SBRK]  $([$($tag),+] $name = $value,)+);
822        define_opcodes!(@impl_instruction_set ISA64_V1_NoSbrk  [I_64]          $([$($tag),+] $name = $value,)+);
823
824        #[test]
825        fn test_opcode_from_u8() {
826            for byte in 0..=255 {
827                if let Some(opcode) = Opcode::from_u8_any(byte) {
828                    assert_eq!(ISA32_V1.opcode_from_u8(byte).unwrap_or(opcode), opcode);
829                    assert_eq!(ISA32_V1_NoSbrk.opcode_from_u8(byte).unwrap_or(opcode), opcode);
830                    assert_eq!(ISA64_V1.opcode_from_u8(byte).unwrap_or(opcode), opcode);
831                } else {
832                    assert_eq!(ISA32_V1.opcode_from_u8(byte), None);
833                    assert_eq!(ISA32_V1_NoSbrk.opcode_from_u8(byte), None);
834                    assert_eq!(ISA64_V1.opcode_from_u8(byte), None);
835                }
836            }
837
838            assert!(ISA32_V1.opcode_from_u8(Opcode::sbrk as u8).is_some());
839            assert!(ISA32_V1_NoSbrk.opcode_from_u8(Opcode::sbrk as u8).is_none());
840        }
841    };
842
843    (
844        $d:tt
845
846        [$([$($tag_argless:tt),+] $name_argless:ident = $value_argless:expr,)+]
847        [$([$($tag_reg_imm:tt),+] $name_reg_imm:ident = $value_reg_imm:expr,)+]
848        [$([$($tag_reg_imm_offset:tt),+] $name_reg_imm_offset:ident = $value_reg_imm_offset:expr,)+]
849        [$([$($tag_reg_imm_imm:tt),+] $name_reg_imm_imm:ident = $value_reg_imm_imm:expr,)+]
850        [$([$($tag_reg_reg_imm:tt),+] $name_reg_reg_imm:ident = $value_reg_reg_imm:expr,)+]
851        [$([$($tag_reg_reg_offset:tt),+] $name_reg_reg_offset:ident = $value_reg_reg_offset:expr,)+]
852        [$([$($tag_reg_reg_reg:tt),+] $name_reg_reg_reg:ident = $value_reg_reg_reg:expr,)+]
853        [$([$($tag_offset:tt),+] $name_offset:ident = $value_offset:expr,)+]
854        [$([$($tag_imm:tt),+] $name_imm:ident = $value_imm:expr,)+]
855        [$([$($tag_imm_imm:tt),+] $name_imm_imm:ident = $value_imm_imm:expr,)+]
856        [$([$($tag_reg_reg:tt),+] $name_reg_reg:ident = $value_reg_reg:expr,)+]
857        [$([$($tag_reg_reg_imm_imm:tt),+] $name_reg_reg_imm_imm:ident = $value_reg_reg_imm_imm:expr,)+]
858        [$([$($tag_reg_imm64:tt),+] $name_reg_imm64:ident = $value_reg_imm64:expr,)+]
859    ) => {
860        pub trait ParsingVisitor {
861            type ReturnTy;
862
863            $(fn $name_argless(&mut self, offset: u32, args_length: u32) -> Self::ReturnTy;)+
864            $(fn $name_reg_imm(&mut self, offset: u32, args_length: u32, reg: RawReg, imm: u32) -> Self::ReturnTy;)+
865            $(fn $name_reg_imm_offset(&mut self, offset: u32, args_length: u32, reg: RawReg, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
866            $(fn $name_reg_imm_imm(&mut self, offset: u32, args_length: u32, reg: RawReg, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
867            $(fn $name_reg_reg_imm(&mut self, offset: u32, args_length: u32, reg1: RawReg, reg2: RawReg, imm: u32) -> Self::ReturnTy;)+
868            $(fn $name_reg_reg_offset(&mut self, offset: u32, args_length: u32, reg1: RawReg, reg2: RawReg, imm: u32) -> Self::ReturnTy;)+
869            $(fn $name_reg_reg_reg(&mut self, offset: u32, args_length: u32, reg1: RawReg, reg2: RawReg, reg3: RawReg) -> Self::ReturnTy;)+
870            $(fn $name_offset(&mut self, offset: u32, args_length: u32, imm: u32) -> Self::ReturnTy;)+
871            $(fn $name_imm(&mut self, offset: u32, args_length: u32, imm: u32) -> Self::ReturnTy;)+
872            $(fn $name_imm_imm(&mut self, offset: u32, args_length: u32, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
873            $(fn $name_reg_reg(&mut self, offset: u32, args_length: u32, reg1: RawReg, reg2: RawReg) -> Self::ReturnTy;)+
874            $(fn $name_reg_reg_imm_imm(&mut self, offset: u32, args_length: u32, reg1: RawReg, reg2: RawReg, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
875            $(fn $name_reg_imm64(&mut self, offset: u32, args_length: u32, reg: RawReg, imm: u64) -> Self::ReturnTy;)+
876
877            fn invalid(&mut self, offset: u32, args_length: u32) -> Self::ReturnTy;
878        }
879
880        pub trait InstructionVisitor {
881            type ReturnTy;
882
883            $(fn $name_argless(&mut self) -> Self::ReturnTy;)+
884            $(fn $name_reg_imm(&mut self, reg: RawReg, imm: u32) -> Self::ReturnTy;)+
885            $(fn $name_reg_imm_offset(&mut self, reg: RawReg, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
886            $(fn $name_reg_imm_imm(&mut self, reg: RawReg, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
887            $(fn $name_reg_reg_imm(&mut self, reg1: RawReg, reg2: RawReg, imm: u32) -> Self::ReturnTy;)+
888            $(fn $name_reg_reg_offset(&mut self, reg1: RawReg, reg2: RawReg, imm: u32) -> Self::ReturnTy;)+
889            $(fn $name_reg_reg_reg(&mut self, reg1: RawReg, reg2: RawReg, reg3: RawReg) -> Self::ReturnTy;)+
890            $(fn $name_offset(&mut self, imm: u32) -> Self::ReturnTy;)+
891            $(fn $name_imm(&mut self, imm: u32) -> Self::ReturnTy;)+
892            $(fn $name_imm_imm(&mut self, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
893            $(fn $name_reg_reg(&mut self, reg1: RawReg, reg2: RawReg) -> Self::ReturnTy;)+
894            $(fn $name_reg_reg_imm_imm(&mut self, reg1: RawReg, reg2: RawReg, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
895            $(fn $name_reg_imm64(&mut self, reg: RawReg, imm: u64) -> Self::ReturnTy;)+
896
897            fn invalid(&mut self) -> Self::ReturnTy;
898        }
899
900        #[derive(Copy, Clone, PartialEq, Eq, Debug)]
901        #[allow(non_camel_case_types)]
902        #[repr(u32)]
903        pub enum Instruction {
904            $($name_argless = $value_argless,)+
905            $($name_reg_imm(RawReg, u32) = $value_reg_imm,)+
906            $($name_reg_imm_offset(RawReg, u32, u32) = $value_reg_imm_offset,)+
907            $($name_reg_imm_imm(RawReg, u32, u32) = $value_reg_imm_imm,)+
908            $($name_reg_reg_imm(RawReg, RawReg, u32) = $value_reg_reg_imm,)+
909            $($name_reg_reg_offset(RawReg, RawReg, u32) = $value_reg_reg_offset,)+
910            $($name_reg_reg_reg(RawReg, RawReg, RawReg) = $value_reg_reg_reg,)+
911            $($name_offset(u32) = $value_offset,)+
912            $($name_imm(u32) = $value_imm,)+
913            $($name_imm_imm(u32, u32) = $value_imm_imm,)+
914            $($name_reg_reg(RawReg, RawReg) = $value_reg_reg,)+
915            $($name_reg_reg_imm_imm(RawReg, RawReg, u32, u32) = $value_reg_reg_imm_imm,)+
916            $($name_reg_imm64(RawReg, u64) = $value_reg_imm64,)+
917            invalid = INVALID_INSTRUCTION_INDEX as u32,
918        }
919
920        impl Instruction {
921            pub fn visit<T>(self, visitor: &mut T) -> T::ReturnTy where T: InstructionVisitor {
922                match self {
923                    $(Self::$name_argless => visitor.$name_argless(),)+
924                    $(Self::$name_reg_imm(reg, imm) => visitor.$name_reg_imm(reg, imm),)+
925                    $(Self::$name_reg_imm_offset(reg, imm1, imm2) => visitor.$name_reg_imm_offset(reg, imm1, imm2),)+
926                    $(Self::$name_reg_imm_imm(reg, imm1, imm2) => visitor.$name_reg_imm_imm(reg, imm1, imm2),)+
927                    $(Self::$name_reg_reg_imm(reg1, reg2, imm) => visitor.$name_reg_reg_imm(reg1, reg2, imm),)+
928                    $(Self::$name_reg_reg_offset(reg1, reg2, imm) => visitor.$name_reg_reg_offset(reg1, reg2, imm),)+
929                    $(Self::$name_reg_reg_reg(reg1, reg2, reg3) => visitor.$name_reg_reg_reg(reg1, reg2, reg3),)+
930                    $(Self::$name_offset(imm) => visitor.$name_offset(imm),)+
931                    $(Self::$name_imm(imm) => visitor.$name_imm(imm),)+
932                    $(Self::$name_imm_imm(imm1, imm2) => visitor.$name_imm_imm(imm1, imm2),)+
933                    $(Self::$name_reg_reg(reg1, reg2) => visitor.$name_reg_reg(reg1, reg2),)+
934                    $(Self::$name_reg_reg_imm_imm(reg1, reg2, imm1, imm2) => visitor.$name_reg_reg_imm_imm(reg1, reg2, imm1, imm2),)+
935                    $(Self::$name_reg_imm64(reg, imm) => visitor.$name_reg_imm64(reg, imm),)+
936                    Self::invalid => visitor.invalid(),
937                }
938            }
939
940            pub fn serialize_into(self, position: u32, buffer: &mut [u8]) -> usize {
941                match self {
942                    $(Self::$name_argless => Self::serialize_argless(buffer, Opcode::$name_argless),)+
943                    $(Self::$name_reg_imm(reg, imm) => Self::serialize_reg_imm(buffer, Opcode::$name_reg_imm, reg, imm),)+
944                    $(Self::$name_reg_imm_offset(reg, imm1, imm2) => Self::serialize_reg_imm_offset(buffer, position, Opcode::$name_reg_imm_offset, reg, imm1, imm2),)+
945                    $(Self::$name_reg_imm_imm(reg, imm1, imm2) => Self::serialize_reg_imm_imm(buffer, Opcode::$name_reg_imm_imm, reg, imm1, imm2),)+
946                    $(Self::$name_reg_reg_imm(reg1, reg2, imm) => Self::serialize_reg_reg_imm(buffer, Opcode::$name_reg_reg_imm, reg1, reg2, imm),)+
947                    $(Self::$name_reg_reg_offset(reg1, reg2, imm) => Self::serialize_reg_reg_offset(buffer, position, Opcode::$name_reg_reg_offset, reg1, reg2, imm),)+
948                    $(Self::$name_reg_reg_reg(reg1, reg2, reg3) => Self::serialize_reg_reg_reg(buffer, Opcode::$name_reg_reg_reg, reg1, reg2, reg3),)+
949                    $(Self::$name_offset(imm) => Self::serialize_offset(buffer, position, Opcode::$name_offset, imm),)+
950                    $(Self::$name_imm(imm) => Self::serialize_imm(buffer, Opcode::$name_imm, imm),)+
951                    $(Self::$name_imm_imm(imm1, imm2) => Self::serialize_imm_imm(buffer, Opcode::$name_imm_imm, imm1, imm2),)+
952                    $(Self::$name_reg_reg(reg1, reg2) => Self::serialize_reg_reg(buffer, Opcode::$name_reg_reg, reg1, reg2),)+
953                    $(Self::$name_reg_reg_imm_imm(reg1, reg2, imm1, imm2) => Self::serialize_reg_reg_imm_imm(buffer, Opcode::$name_reg_reg_imm_imm, reg1, reg2, imm1, imm2),)+
954                    $(Self::$name_reg_imm64(reg, imm) => Self::serialize_reg_imm64(buffer, Opcode::$name_reg_imm64, reg, imm),)+
955                    Self::invalid => Self::serialize_argless(buffer, Opcode::trap),
956
957                }
958            }
959
960            pub fn opcode(self) -> Opcode {
961                match self {
962                    $(Self::$name_argless => Opcode::$name_argless,)+
963                    $(Self::$name_reg_imm(..) => Opcode::$name_reg_imm,)+
964                    $(Self::$name_reg_imm_offset(..) => Opcode::$name_reg_imm_offset,)+
965                    $(Self::$name_reg_imm_imm(..) => Opcode::$name_reg_imm_imm,)+
966                    $(Self::$name_reg_reg_imm(..) => Opcode::$name_reg_reg_imm,)+
967                    $(Self::$name_reg_reg_offset(..) => Opcode::$name_reg_reg_offset,)+
968                    $(Self::$name_reg_reg_reg(..) => Opcode::$name_reg_reg_reg,)+
969                    $(Self::$name_offset(..) => Opcode::$name_offset,)+
970                    $(Self::$name_imm(..) => Opcode::$name_imm,)+
971                    $(Self::$name_imm_imm(..) => Opcode::$name_imm_imm,)+
972                    $(Self::$name_reg_reg(..) => Opcode::$name_reg_reg,)+
973                    $(Self::$name_reg_reg_imm_imm(..) => Opcode::$name_reg_reg_imm_imm,)+
974                    $(Self::$name_reg_imm64(..) => Opcode::$name_reg_imm64,)+
975                    Self::invalid => Opcode::trap,
976                }
977            }
978        }
979
980        pub mod asm {
981            use super::{Instruction, Reg};
982
983            $(
984                pub fn $name_argless() -> Instruction {
985                    Instruction::$name_argless
986                }
987            )+
988
989            $(
990                pub fn $name_reg_imm(reg: Reg, imm: u32) -> Instruction {
991                    Instruction::$name_reg_imm(reg.into(), imm)
992                }
993            )+
994
995            $(
996                pub fn $name_reg_imm_offset(reg: Reg, imm1: u32, imm2: u32) -> Instruction {
997                    Instruction::$name_reg_imm_offset(reg.into(), imm1, imm2)
998                }
999            )+
1000
1001            $(
1002                pub fn $name_reg_imm_imm(reg: Reg, imm1: u32, imm2: u32) -> Instruction {
1003                    Instruction::$name_reg_imm_imm(reg.into(), imm1, imm2)
1004                }
1005            )+
1006
1007            $(
1008                pub fn $name_reg_reg_imm(reg1: Reg, reg2: Reg, imm: u32) -> Instruction {
1009                    Instruction::$name_reg_reg_imm(reg1.into(), reg2.into(), imm)
1010                }
1011            )+
1012
1013            $(
1014                pub fn $name_reg_reg_offset(reg1: Reg, reg2: Reg, imm: u32) -> Instruction {
1015                    Instruction::$name_reg_reg_offset(reg1.into(), reg2.into(), imm)
1016                }
1017            )+
1018
1019            $(
1020                pub fn $name_reg_reg_reg(reg1: Reg, reg2: Reg, reg3: Reg) -> Instruction {
1021                    Instruction::$name_reg_reg_reg(reg1.into(), reg2.into(), reg3.into())
1022                }
1023            )+
1024
1025            $(
1026                pub fn $name_offset(imm: u32) -> Instruction {
1027                    Instruction::$name_offset(imm)
1028                }
1029            )+
1030
1031            $(
1032                pub fn $name_imm(imm: u32) -> Instruction {
1033                    Instruction::$name_imm(imm)
1034                }
1035            )+
1036
1037            $(
1038                pub fn $name_imm_imm(imm1: u32, imm2: u32) -> Instruction {
1039                    Instruction::$name_imm_imm(imm1, imm2)
1040                }
1041            )+
1042
1043            $(
1044                pub fn $name_reg_reg(reg1: Reg, reg2: Reg) -> Instruction {
1045                    Instruction::$name_reg_reg(reg1.into(), reg2.into())
1046                }
1047            )+
1048
1049            $(
1050                pub fn $name_reg_reg_imm_imm(reg1: Reg, reg2: Reg, imm1: u32, imm2: u32) -> Instruction {
1051                    Instruction::$name_reg_reg_imm_imm(reg1.into(), reg2.into(), imm1, imm2)
1052                }
1053            )+
1054
1055            $(
1056                pub fn $name_reg_imm64(reg: Reg, imm: u64) -> Instruction {
1057                    Instruction::$name_reg_imm64(reg.into(), imm)
1058                }
1059            )+
1060
1061            pub fn ret() -> Instruction {
1062                jump_indirect(Reg::RA, 0)
1063            }
1064        }
1065
1066        #[macro_export]
1067        macro_rules! build_static_dispatch_table {
1068            ($table_name:ident, $instruction_set:tt, $visitor_ty:ident<$d($visitor_ty_params:tt),*>) => {{
1069                use $crate::program::{
1070                    ParsingVisitor
1071                };
1072
1073                type ReturnTy<$d($visitor_ty_params),*> = <$visitor_ty<$d($visitor_ty_params),*> as ParsingVisitor>::ReturnTy;
1074                type VisitFn<$d($visitor_ty_params),*> = fn(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, args_length: u32);
1075
1076                #[derive(Copy, Clone)]
1077                struct DispatchTable<'a>(&'a [VisitFn<'a>; 257]);
1078
1079                impl<'a> $crate::program::OpcodeVisitor for DispatchTable<'a> {
1080                    type State = $visitor_ty<'a>;
1081                    type ReturnTy = ();
1082                    type InstructionSet = $instruction_set;
1083
1084                    #[inline]
1085                    fn instruction_set(self) -> Self::InstructionSet {
1086                        $instruction_set
1087                    }
1088
1089                    #[inline]
1090                    fn dispatch(self, state: &mut $visitor_ty<'a>, opcode: usize, chunk: u128, offset: u32, skip: u32) {
1091                        self.0[opcode](state, chunk, offset, skip)
1092                    }
1093                }
1094
1095                static $table_name: [VisitFn; 257] = {
1096                    let mut table = [invalid_instruction as VisitFn; 257];
1097
1098                    $({
1099                        // Putting all of the handlers in a single link section can make a big difference
1100                        // when it comes to performance, even up to 10% in some cases. This will force the
1101                        // compiler and the linker to put all of this code near each other, minimizing
1102                        // instruction cache misses.
1103                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1104                        fn $name_argless<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, _chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1105                            state.$name_argless(instruction_offset, skip)
1106                        }
1107
1108                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_argless] {
1109                            table[$value_argless] = $name_argless;
1110                        }
1111                    })*
1112
1113                    $({
1114                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1115                        fn $name_reg_imm<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1116                            let (reg, imm) = $crate::program::read_args_reg_imm(chunk, skip);
1117                            state.$name_reg_imm(instruction_offset, skip, reg, imm)
1118                        }
1119
1120                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_reg_imm] {
1121                            table[$value_reg_imm] = $name_reg_imm;
1122                        }
1123                    })*
1124
1125                    $({
1126                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1127                        fn $name_reg_imm_offset<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1128                            let (reg, imm1, imm2) = $crate::program::read_args_reg_imm_offset(chunk, instruction_offset, skip);
1129                            state.$name_reg_imm_offset(instruction_offset, skip, reg, imm1, imm2)
1130                        }
1131
1132                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_reg_imm_offset] {
1133                            table[$value_reg_imm_offset] = $name_reg_imm_offset;
1134                        }
1135                    })*
1136
1137                    $({
1138                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1139                        fn $name_reg_imm_imm<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1140                            let (reg, imm1, imm2) = $crate::program::read_args_reg_imm2(chunk, skip);
1141                            state.$name_reg_imm_imm(instruction_offset, skip, reg, imm1, imm2)
1142                        }
1143
1144                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_reg_imm_imm] {
1145                            table[$value_reg_imm_imm] = $name_reg_imm_imm;
1146                        }
1147                    })*
1148
1149                    $({
1150                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1151                        fn $name_reg_reg_imm<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1152                            let (reg1, reg2, imm) = $crate::program::read_args_regs2_imm(chunk, skip);
1153                            state.$name_reg_reg_imm(instruction_offset, skip, reg1, reg2, imm)
1154                        }
1155
1156                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_reg_reg_imm] {
1157                            table[$value_reg_reg_imm] = $name_reg_reg_imm;
1158                        }
1159                    })*
1160
1161                    $({
1162                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1163                        fn $name_reg_reg_offset<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1164                            let (reg1, reg2, imm) = $crate::program::read_args_regs2_offset(chunk, instruction_offset, skip);
1165                            state.$name_reg_reg_offset(instruction_offset, skip, reg1, reg2, imm)
1166                        }
1167
1168                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_reg_reg_offset] {
1169                            table[$value_reg_reg_offset] = $name_reg_reg_offset;
1170                        }
1171                    })*
1172
1173                    $({
1174                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1175                        fn $name_reg_reg_reg<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1176                            let (reg1, reg2, reg3) = $crate::program::read_args_regs3(chunk);
1177                            state.$name_reg_reg_reg(instruction_offset, skip, reg1, reg2, reg3)
1178                        }
1179
1180                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_reg_reg_reg] {
1181                            table[$value_reg_reg_reg] = $name_reg_reg_reg;
1182                        }
1183                    })*
1184
1185                    $({
1186                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1187                        fn $name_offset<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1188                            let imm = $crate::program::read_args_offset(chunk, instruction_offset, skip);
1189                            state.$name_offset(instruction_offset, skip, imm)
1190                        }
1191
1192                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_offset] {
1193                            table[$value_offset] = $name_offset;
1194                        }
1195                    })*
1196
1197                    $({
1198                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1199                        fn $name_imm<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1200                            let imm = $crate::program::read_args_imm(chunk, skip);
1201                            state.$name_imm(instruction_offset, skip, imm)
1202                        }
1203
1204                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_imm] {
1205                            table[$value_imm] = $name_imm;
1206                        }
1207                    })*
1208
1209                    $({
1210                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1211                        fn $name_imm_imm<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1212                            let (imm1, imm2) = $crate::program::read_args_imm2(chunk, skip);
1213                            state.$name_imm_imm(instruction_offset, skip, imm1, imm2)
1214                        }
1215
1216                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_imm_imm] {
1217                            table[$value_imm_imm] = $name_imm_imm;
1218                        }
1219                    })*
1220
1221                    $({
1222                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1223                        fn $name_reg_reg<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1224                            let (reg1, reg2) = $crate::program::read_args_regs2(chunk);
1225                            state.$name_reg_reg(instruction_offset, skip, reg1, reg2)
1226                        }
1227
1228                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_reg_reg] {
1229                            table[$value_reg_reg] = $name_reg_reg;
1230                        }
1231                    })*
1232
1233                    $({
1234                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1235                        fn $name_reg_reg_imm_imm<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1236                            let (reg1, reg2, imm1, imm2) = $crate::program::read_args_regs2_imm2(chunk, skip);
1237                            state.$name_reg_reg_imm_imm(instruction_offset, skip, reg1, reg2, imm1, imm2)
1238                        }
1239
1240                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_reg_reg_imm_imm] {
1241                            table[$value_reg_reg_imm_imm] = $name_reg_reg_imm_imm;
1242                        }
1243                    })*
1244
1245                    $({
1246                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1247                        fn $name_reg_imm64<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1248                            let (reg, imm) = $crate::program::read_args_reg_imm64(chunk, skip);
1249                            state.$name_reg_imm64(instruction_offset, skip, reg, imm)
1250                        }
1251
1252                        if $instruction_set::IS_INSTRUCTION_VALID_CONST[$value_reg_imm64] {
1253                            table[$value_reg_imm64] = $name_reg_imm64;
1254                        }
1255                    })*
1256
1257                    #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
1258                    #[cold]
1259                    fn invalid_instruction<$d($visitor_ty_params),*>(state: &mut $visitor_ty<$d($visitor_ty_params),*>, _chunk: u128, instruction_offset: u32, skip: u32) -> ReturnTy<$d($visitor_ty_params),*>{
1260                        state.invalid(instruction_offset, skip)
1261                    }
1262
1263                    table
1264                };
1265
1266                #[inline]
1267                #[allow(unsafe_code)]
1268                // SAFETY: Here we transmute the lifetimes which were unnecessarily extended to be 'static due to the table here being a `static`.
1269                fn transmute_lifetime<'a>(table: DispatchTable<'static>) -> DispatchTable<'a> {
1270                    unsafe { core::mem::transmute(&$table_name) }
1271                }
1272
1273                transmute_lifetime(DispatchTable(&$table_name))
1274            }};
1275        }
1276
1277        pub use build_static_dispatch_table;
1278
1279        #[derive(Copy, Clone)]
1280        struct EnumVisitor<I> {
1281            instruction_set: I
1282        }
1283
1284        impl<'a, I> OpcodeVisitor for EnumVisitor<I> where I: InstructionSet {
1285            type State = ();
1286            type ReturnTy = Instruction;
1287            type InstructionSet = I;
1288
1289            fn instruction_set(self) -> Self::InstructionSet {
1290                self.instruction_set
1291            }
1292
1293            fn dispatch(self, _state: &mut (), opcode: usize, chunk: u128, offset: u32, skip: u32) -> Instruction {
1294                if self.instruction_set().opcode_from_u8(opcode as u8).is_none() {
1295                    return Instruction::invalid
1296                }
1297
1298                match opcode {
1299                    $(
1300                        $value_argless => Instruction::$name_argless,
1301                    )+
1302                    $(
1303                        $value_reg_imm => {
1304                            let (reg, imm) = $crate::program::read_args_reg_imm(chunk, skip);
1305                            Instruction::$name_reg_imm(reg, imm)
1306                        },
1307                    )+
1308                    $(
1309                        $value_reg_imm_offset => {
1310                            let (reg, imm1, imm2) = $crate::program::read_args_reg_imm_offset(chunk, offset, skip);
1311                            Instruction::$name_reg_imm_offset(reg, imm1, imm2)
1312                        },
1313                    )+
1314                    $(
1315                        $value_reg_imm_imm => {
1316                            let (reg, imm1, imm2) = $crate::program::read_args_reg_imm2(chunk, skip);
1317                            Instruction::$name_reg_imm_imm(reg, imm1, imm2)
1318                        },
1319                    )+
1320                    $(
1321                        $value_reg_reg_imm => {
1322                            let (reg1, reg2, imm) = $crate::program::read_args_regs2_imm(chunk, skip);
1323                            Instruction::$name_reg_reg_imm(reg1, reg2, imm)
1324                        }
1325                    )+
1326                    $(
1327                        $value_reg_reg_offset => {
1328                            let (reg1, reg2, imm) = $crate::program::read_args_regs2_offset(chunk, offset, skip);
1329                            Instruction::$name_reg_reg_offset(reg1, reg2, imm)
1330                        }
1331                    )+
1332                    $(
1333                        $value_reg_reg_reg => {
1334                            let (reg1, reg2, reg3) = $crate::program::read_args_regs3(chunk);
1335                            Instruction::$name_reg_reg_reg(reg1, reg2, reg3)
1336                        }
1337                    )+
1338                    $(
1339                        $value_offset => {
1340                            let imm = $crate::program::read_args_offset(chunk, offset, skip);
1341                            Instruction::$name_offset(imm)
1342                        }
1343                    )+
1344                    $(
1345                        $value_imm => {
1346                            let imm = $crate::program::read_args_imm(chunk, skip);
1347                            Instruction::$name_imm(imm)
1348                        }
1349                    )+
1350                    $(
1351                        $value_imm_imm => {
1352                            let (imm1, imm2) = $crate::program::read_args_imm2(chunk, skip);
1353                            Instruction::$name_imm_imm(imm1, imm2)
1354                        }
1355                    )+
1356                    $(
1357                        $value_reg_reg => {
1358                            let (reg1, reg2) = $crate::program::read_args_regs2(chunk);
1359                            Instruction::$name_reg_reg(reg1, reg2)
1360                        }
1361                    )+
1362                    $(
1363                        $value_reg_reg_imm_imm => {
1364                            let (reg1, reg2, imm1, imm2) = $crate::program::read_args_regs2_imm2(chunk, skip);
1365                            Instruction::$name_reg_reg_imm_imm(reg1, reg2, imm1, imm2)
1366                        }
1367                    )+
1368                    $(
1369                        $value_reg_imm64 => {
1370                            let (reg, imm) = $crate::program::read_args_reg_imm64(chunk, skip);
1371                            Instruction::$name_reg_imm64(reg, imm)
1372                        }
1373                    )+
1374                    _ => Instruction::invalid,
1375                }
1376            }
1377        }
1378
1379        define_opcodes!(
1380            @impl_shared
1381            $([$($tag_argless),+] $name_argless = $value_argless,)+
1382            $([$($tag_reg_imm),+] $name_reg_imm = $value_reg_imm,)+
1383            $([$($tag_reg_imm_offset),+] $name_reg_imm_offset = $value_reg_imm_offset,)+
1384            $([$($tag_reg_imm_imm),+] $name_reg_imm_imm = $value_reg_imm_imm,)+
1385            $([$($tag_reg_reg_imm),+] $name_reg_reg_imm = $value_reg_reg_imm,)+
1386            $([$($tag_reg_reg_offset),+] $name_reg_reg_offset = $value_reg_reg_offset,)+
1387            $([$($tag_reg_reg_reg),+] $name_reg_reg_reg = $value_reg_reg_reg,)+
1388            $([$($tag_offset),+] $name_offset = $value_offset,)+
1389            $([$($tag_imm),+] $name_imm = $value_imm,)+
1390            $([$($tag_imm_imm),+] $name_imm_imm = $value_imm_imm,)+
1391            $([$($tag_reg_reg),+] $name_reg_reg = $value_reg_reg,)+
1392            $([$($tag_reg_reg_imm_imm),+] $name_reg_reg_imm_imm = $value_reg_reg_imm_imm,)+
1393            $([$($tag_reg_imm64),+] $name_reg_imm64 = $value_reg_imm64,)+
1394        );
1395    }
1396}
1397
1398#[inline]
1399fn parse_instruction<I>(instruction_set: I, code: &[u8], bitmask: &[u8], offset: u32) -> (u32, Instruction, bool)
1400where
1401    I: InstructionSet,
1402{
1403    let visitor = EnumVisitor { instruction_set };
1404    if offset as usize + 32 <= code.len() {
1405        visitor_step_fast(&mut (), code, bitmask, offset, visitor)
1406    } else {
1407        visitor_step_slow(&mut (), code, bitmask, offset, visitor)
1408    }
1409}
1410
1411const INVALID_INSTRUCTION_INDEX: u32 = 256;
1412
1413// Constants so that `define_opcodes` works. The exact values don't matter.
1414const I_32: usize = 0;
1415const I_64: usize = 1;
1416const I_SBRK: usize = 2;
1417
1418// NOTE: The opcodes here are assigned roughly in the order of how common a given instruction is,
1419// except the `trap` which is deliberately hardcoded as zero.
1420define_opcodes! {
1421    $
1422
1423    // Instructions with args: none
1424    [
1425        [I_64, I_32] trap                                     = 0,
1426        [I_64, I_32] fallthrough                              = 1,
1427        [I_64, I_32] memset                                   = 2,
1428    ]
1429
1430    // Instructions with args: reg, imm
1431    [
1432        [I_64, I_32] jump_indirect                            = 50,
1433        [I_64, I_32] load_imm                                 = 51,
1434        [I_64, I_32] load_u8                                  = 52,
1435        [I_64, I_32] load_i8                                  = 53,
1436        [I_64, I_32] load_u16                                 = 54,
1437        [I_64, I_32] load_i16                                 = 55,
1438        [I_64, I_32] load_i32                                 = 57,
1439        [I_64]       load_u32                                 = 56,
1440        [I_64]       load_u64                                 = 58,
1441        [I_64, I_32] store_u8                                 = 59,
1442        [I_64, I_32] store_u16                                = 60,
1443        [I_64, I_32] store_u32                                = 61,
1444        [I_64]       store_u64                                = 62,
1445    ]
1446
1447    // Instructions with args: reg, imm, offset
1448    [
1449        [I_64, I_32] load_imm_and_jump                        = 80,
1450        [I_64, I_32] branch_eq_imm                            = 81,
1451        [I_64, I_32] branch_not_eq_imm                        = 82,
1452        [I_64, I_32] branch_less_unsigned_imm                 = 83,
1453        [I_64, I_32] branch_less_signed_imm                   = 87,
1454        [I_64, I_32] branch_greater_or_equal_unsigned_imm     = 85,
1455        [I_64, I_32] branch_greater_or_equal_signed_imm       = 89,
1456        [I_64, I_32] branch_less_or_equal_signed_imm          = 88,
1457        [I_64, I_32] branch_less_or_equal_unsigned_imm        = 84,
1458        [I_64, I_32] branch_greater_signed_imm                = 90,
1459        [I_64, I_32] branch_greater_unsigned_imm              = 86,
1460    ]
1461
1462    // Instructions with args: reg, imm, imm
1463    [
1464        [I_64, I_32] store_imm_indirect_u8                    = 70,
1465        [I_64, I_32] store_imm_indirect_u16                   = 71,
1466        [I_64, I_32] store_imm_indirect_u32                   = 72,
1467        [I_64]       store_imm_indirect_u64                   = 73,
1468    ]
1469
1470    // Instructions with args: reg, reg, imm
1471    [
1472        [I_64, I_32] store_indirect_u8                        = 120,
1473        [I_64, I_32] store_indirect_u16                       = 121,
1474        [I_64, I_32] store_indirect_u32                       = 122,
1475        [I_64]       store_indirect_u64                       = 123,
1476        [I_64, I_32] load_indirect_u8                         = 124,
1477        [I_64, I_32] load_indirect_i8                         = 125,
1478        [I_64, I_32] load_indirect_u16                        = 126,
1479        [I_64, I_32] load_indirect_i16                        = 127,
1480        [I_64, I_32] load_indirect_i32                        = 129,
1481        [I_64]       load_indirect_u32                        = 128,
1482        [I_64]       load_indirect_u64                        = 130,
1483        [I_64, I_32] add_imm_32                               = 131,
1484        [I_64]       add_imm_64                               = 149,
1485        [I_64, I_32] and_imm                                  = 132,
1486        [I_64, I_32] xor_imm                                  = 133,
1487        [I_64, I_32] or_imm                                   = 134,
1488        [I_64, I_32] mul_imm_32                               = 135,
1489        [I_64]       mul_imm_64                               = 150,
1490        [I_64, I_32] set_less_than_unsigned_imm               = 136,
1491        [I_64, I_32] set_less_than_signed_imm                 = 137,
1492        [I_64, I_32] shift_logical_left_imm_32                = 138,
1493        [I_64]       shift_logical_left_imm_64                = 151,
1494        [I_64, I_32] shift_logical_right_imm_32               = 139,
1495        [I_64]       shift_logical_right_imm_64               = 152,
1496        [I_64, I_32] shift_arithmetic_right_imm_32            = 140,
1497        [I_64]       shift_arithmetic_right_imm_64            = 153,
1498        [I_64, I_32] negate_and_add_imm_32                    = 141,
1499        [I_64]       negate_and_add_imm_64                    = 154,
1500        [I_64, I_32] set_greater_than_unsigned_imm            = 142,
1501        [I_64, I_32] set_greater_than_signed_imm              = 143,
1502        [I_64, I_32] shift_logical_right_imm_alt_32           = 145,
1503        [I_64]       shift_logical_right_imm_alt_64           = 156,
1504        [I_64, I_32] shift_arithmetic_right_imm_alt_32        = 146,
1505        [I_64]       shift_arithmetic_right_imm_alt_64        = 157,
1506        [I_64, I_32] shift_logical_left_imm_alt_32            = 144,
1507        [I_64]       shift_logical_left_imm_alt_64            = 155,
1508
1509        [I_64, I_32] cmov_if_zero_imm                         = 147,
1510        [I_64, I_32] cmov_if_not_zero_imm                     = 148,
1511
1512        [I_64, I_32] rotate_right_imm_32                      = 160,
1513        [I_64, I_32] rotate_right_imm_alt_32                  = 161,
1514        [I_64]       rotate_right_imm_64                      = 158,
1515        [I_64]       rotate_right_imm_alt_64                  = 159,
1516    ]
1517
1518    // Instructions with args: reg, reg, offset
1519    [
1520        [I_64, I_32] branch_eq                                = 170,
1521        [I_64, I_32] branch_not_eq                            = 171,
1522        [I_64, I_32] branch_less_unsigned                     = 172,
1523        [I_64, I_32] branch_less_signed                       = 173,
1524        [I_64, I_32] branch_greater_or_equal_unsigned         = 174,
1525        [I_64, I_32] branch_greater_or_equal_signed           = 175,
1526    ]
1527
1528    // Instructions with args: reg, reg, reg
1529    [
1530        [I_64, I_32] add_32                                   = 190,
1531        [I_64]       add_64                                   = 200,
1532        [I_64, I_32] sub_32                                   = 191,
1533        [I_64]       sub_64                                   = 201,
1534        [I_64, I_32] and                                      = 210,
1535        [I_64, I_32] xor                                      = 211,
1536        [I_64, I_32] or                                       = 212,
1537        [I_64, I_32] mul_32                                   = 192,
1538        [I_64]       mul_64                                   = 202,
1539        [I_32, I_64] mul_upper_signed_signed                  = 213,
1540        [I_32, I_64] mul_upper_unsigned_unsigned              = 214,
1541        [I_32, I_64] mul_upper_signed_unsigned                = 215,
1542        [I_64, I_32] set_less_than_unsigned                   = 216,
1543        [I_64, I_32] set_less_than_signed                     = 217,
1544        [I_64, I_32] shift_logical_left_32                    = 197,
1545        [I_64]       shift_logical_left_64                    = 207,
1546        [I_64, I_32] shift_logical_right_32                   = 198,
1547        [I_64]       shift_logical_right_64                   = 208,
1548        [I_64, I_32] shift_arithmetic_right_32                = 199,
1549        [I_64]       shift_arithmetic_right_64                = 209,
1550        [I_64, I_32] div_unsigned_32                          = 193,
1551        [I_64]       div_unsigned_64                          = 203,
1552        [I_64, I_32] div_signed_32                            = 194,
1553        [I_64]       div_signed_64                            = 204,
1554        [I_64, I_32] rem_unsigned_32                          = 195,
1555        [I_64]       rem_unsigned_64                          = 205,
1556        [I_64, I_32] rem_signed_32                            = 196,
1557        [I_64]       rem_signed_64                            = 206,
1558
1559        [I_64, I_32] cmov_if_zero                             = 218,
1560        [I_64, I_32] cmov_if_not_zero                         = 219,
1561
1562        [I_64, I_32] and_inverted                             = 224,
1563        [I_64, I_32] or_inverted                              = 225,
1564        [I_64, I_32] xnor                                     = 226,
1565        [I_64, I_32] maximum                                  = 227,
1566        [I_64, I_32] maximum_unsigned                         = 228,
1567        [I_64, I_32] minimum                                  = 229,
1568        [I_64, I_32] minimum_unsigned                         = 230,
1569        [I_64, I_32] rotate_left_32                           = 221,
1570        [I_64]       rotate_left_64                           = 220,
1571        [I_64, I_32] rotate_right_32                          = 223,
1572        [I_64]       rotate_right_64                          = 222,
1573    ]
1574
1575    // Instructions with args: offset
1576    [
1577        [I_64, I_32] jump                                     = 40,
1578    ]
1579
1580    // Instructions with args: imm
1581    [
1582        [I_64, I_32] ecalli                                   = 10,
1583    ]
1584
1585    // Instructions with args: imm, imm
1586    [
1587        [I_64, I_32] store_imm_u8                             = 30,
1588        [I_64, I_32] store_imm_u16                            = 31,
1589        [I_64, I_32] store_imm_u32                            = 32,
1590        [I_64]       store_imm_u64                            = 33,
1591    ]
1592
1593    // Instructions with args: reg, reg
1594    [
1595        [I_64, I_32] move_reg                                 = 100,
1596        [I_SBRK]     sbrk                                     = 101,
1597        [I_64, I_32] count_leading_zero_bits_32               = 105,
1598        [I_64]       count_leading_zero_bits_64               = 104,
1599        [I_64, I_32] count_trailing_zero_bits_32              = 107,
1600        [I_64]       count_trailing_zero_bits_64              = 106,
1601        [I_64, I_32] count_set_bits_32                        = 103,
1602        [I_64]       count_set_bits_64                        = 102,
1603        [I_64, I_32] sign_extend_8                            = 108,
1604        [I_64, I_32] sign_extend_16                           = 109,
1605        [I_64, I_32] zero_extend_16                           = 110,
1606        [I_64, I_32] reverse_byte                             = 111,
1607    ]
1608
1609    // Instructions with args: reg, reg, imm, imm
1610    [
1611        [I_64, I_32] load_imm_and_jump_indirect               = 180,
1612    ]
1613
1614    // Instruction with args: reg, imm64
1615    [
1616        [I_64] load_imm64                                     = 20,
1617    ]
1618}
1619
1620impl Opcode {
1621    pub fn can_fallthrough(self) -> bool {
1622        !matches!(
1623            self,
1624            Self::trap | Self::jump | Self::jump_indirect | Self::load_imm_and_jump | Self::load_imm_and_jump_indirect
1625        )
1626    }
1627
1628    pub fn starts_new_basic_block(self) -> bool {
1629        matches!(
1630            self,
1631            Self::trap
1632                | Self::fallthrough
1633                | Self::jump
1634                | Self::jump_indirect
1635                | Self::load_imm_and_jump
1636                | Self::load_imm_and_jump_indirect
1637                | Self::branch_eq
1638                | Self::branch_eq_imm
1639                | Self::branch_greater_or_equal_signed
1640                | Self::branch_greater_or_equal_signed_imm
1641                | Self::branch_greater_or_equal_unsigned
1642                | Self::branch_greater_or_equal_unsigned_imm
1643                | Self::branch_greater_signed_imm
1644                | Self::branch_greater_unsigned_imm
1645                | Self::branch_less_or_equal_signed_imm
1646                | Self::branch_less_or_equal_unsigned_imm
1647                | Self::branch_less_signed
1648                | Self::branch_less_signed_imm
1649                | Self::branch_less_unsigned
1650                | Self::branch_less_unsigned_imm
1651                | Self::branch_not_eq
1652                | Self::branch_not_eq_imm
1653        )
1654    }
1655}
1656
1657impl core::fmt::Display for Instruction {
1658    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1659        self.visit(&mut InstructionFormatter {
1660            format: &Default::default(),
1661            fmt,
1662        })
1663    }
1664}
1665
1666impl Instruction {
1667    pub fn display<'a>(self, format: &'a InstructionFormat<'a>) -> impl core::fmt::Display + 'a {
1668        struct Inner<'a, 'b> {
1669            instruction: Instruction,
1670            format: &'a InstructionFormat<'b>,
1671        }
1672
1673        impl<'a, 'b> core::fmt::Display for Inner<'a, 'b> {
1674            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1675                self.instruction.visit(&mut InstructionFormatter { format: self.format, fmt })
1676            }
1677        }
1678
1679        Inner { instruction: self, format }
1680    }
1681
1682    pub fn starts_new_basic_block(self) -> bool {
1683        self.opcode().starts_new_basic_block()
1684    }
1685
1686    fn serialize_argless(buffer: &mut [u8], opcode: Opcode) -> usize {
1687        buffer[0] = opcode as u8;
1688        1
1689    }
1690
1691    fn serialize_reg_imm_offset(buffer: &mut [u8], position: u32, opcode: Opcode, reg: RawReg, imm1: u32, imm2: u32) -> usize {
1692        let imm2 = imm2.wrapping_sub(position);
1693        buffer[0] = opcode as u8;
1694        let mut position = 2;
1695        let imm1_length = write_simple_varint(imm1, &mut buffer[position..]);
1696        position += imm1_length;
1697        buffer[1] = reg.0 as u8 | (imm1_length << 4) as u8;
1698        position += write_simple_varint(imm2, &mut buffer[position..]);
1699        position
1700    }
1701
1702    fn serialize_reg_imm_imm(buffer: &mut [u8], opcode: Opcode, reg: RawReg, imm1: u32, imm2: u32) -> usize {
1703        buffer[0] = opcode as u8;
1704        let mut position = 2;
1705        let imm1_length = write_simple_varint(imm1, &mut buffer[position..]);
1706        position += imm1_length;
1707        buffer[1] = reg.0 as u8 | (imm1_length << 4) as u8;
1708        position += write_simple_varint(imm2, &mut buffer[position..]);
1709        position
1710    }
1711    fn serialize_reg_reg_imm_imm(buffer: &mut [u8], opcode: Opcode, reg1: RawReg, reg2: RawReg, imm1: u32, imm2: u32) -> usize {
1712        buffer[0] = opcode as u8;
1713        buffer[1] = reg1.0 as u8 | (reg2.0 as u8) << 4;
1714        let mut position = 3;
1715        let imm1_length = write_simple_varint(imm1, &mut buffer[position..]);
1716        buffer[2] = imm1_length as u8;
1717        position += imm1_length;
1718        position += write_simple_varint(imm2, &mut buffer[position..]);
1719        position
1720    }
1721
1722    fn serialize_reg_imm64(buffer: &mut [u8], opcode: Opcode, reg: RawReg, imm: u64) -> usize {
1723        buffer[0] = opcode as u8;
1724        buffer[1] = reg.0 as u8;
1725        buffer[2..10].copy_from_slice(&imm.to_le_bytes());
1726        10
1727    }
1728
1729    fn serialize_reg_reg_reg(buffer: &mut [u8], opcode: Opcode, reg1: RawReg, reg2: RawReg, reg3: RawReg) -> usize {
1730        buffer[0] = opcode as u8;
1731        buffer[1] = reg2.0 as u8 | (reg3.0 as u8) << 4;
1732        buffer[2] = reg1.0 as u8;
1733        3
1734    }
1735
1736    fn serialize_reg_reg_imm(buffer: &mut [u8], opcode: Opcode, reg1: RawReg, reg2: RawReg, imm: u32) -> usize {
1737        buffer[0] = opcode as u8;
1738        buffer[1] = reg1.0 as u8 | (reg2.0 as u8) << 4;
1739        write_simple_varint(imm, &mut buffer[2..]) + 2
1740    }
1741
1742    fn serialize_reg_reg_offset(buffer: &mut [u8], position: u32, opcode: Opcode, reg1: RawReg, reg2: RawReg, imm: u32) -> usize {
1743        let imm = imm.wrapping_sub(position);
1744        buffer[0] = opcode as u8;
1745        buffer[1] = reg1.0 as u8 | (reg2.0 as u8) << 4;
1746        write_simple_varint(imm, &mut buffer[2..]) + 2
1747    }
1748
1749    fn serialize_reg_imm(buffer: &mut [u8], opcode: Opcode, reg: RawReg, imm: u32) -> usize {
1750        buffer[0] = opcode as u8;
1751        buffer[1] = reg.0 as u8;
1752        write_simple_varint(imm, &mut buffer[2..]) + 2
1753    }
1754
1755    fn serialize_offset(buffer: &mut [u8], position: u32, opcode: Opcode, imm: u32) -> usize {
1756        let imm = imm.wrapping_sub(position);
1757        buffer[0] = opcode as u8;
1758        write_simple_varint(imm, &mut buffer[1..]) + 1
1759    }
1760
1761    fn serialize_imm(buffer: &mut [u8], opcode: Opcode, imm: u32) -> usize {
1762        buffer[0] = opcode as u8;
1763        write_simple_varint(imm, &mut buffer[1..]) + 1
1764    }
1765
1766    fn serialize_imm_imm(buffer: &mut [u8], opcode: Opcode, imm1: u32, imm2: u32) -> usize {
1767        buffer[0] = opcode as u8;
1768        let mut position = 2;
1769        let imm1_length = write_simple_varint(imm1, &mut buffer[position..]);
1770        buffer[1] = imm1_length as u8;
1771        position += imm1_length;
1772        position += write_simple_varint(imm2, &mut buffer[position..]);
1773        position
1774    }
1775
1776    fn serialize_reg_reg(buffer: &mut [u8], opcode: Opcode, reg1: RawReg, reg2: RawReg) -> usize {
1777        buffer[0] = opcode as u8;
1778        buffer[1] = reg1.0 as u8 | (reg2.0 as u8) << 4;
1779        2
1780    }
1781}
1782
1783pub const MAX_INSTRUCTION_LENGTH: usize = 2 + MAX_VARINT_LENGTH * 2;
1784
1785#[non_exhaustive]
1786pub struct InstructionFormat<'a> {
1787    pub prefer_non_abi_reg_names: bool,
1788    pub prefer_unaliased: bool,
1789    pub jump_target_formatter: Option<&'a dyn Fn(u32, &mut core::fmt::Formatter) -> core::fmt::Result>,
1790    pub is_64_bit: bool,
1791}
1792
1793impl<'a> Default for InstructionFormat<'a> {
1794    fn default() -> Self {
1795        InstructionFormat {
1796            prefer_non_abi_reg_names: false,
1797            prefer_unaliased: false,
1798            jump_target_formatter: None,
1799            is_64_bit: true,
1800        }
1801    }
1802}
1803
1804struct InstructionFormatter<'a, 'b, 'c> {
1805    format: &'a InstructionFormat<'c>,
1806    fmt: &'a mut core::fmt::Formatter<'b>,
1807}
1808
1809impl<'a, 'b, 'c> InstructionFormatter<'a, 'b, 'c> {
1810    fn format_reg(&self, reg: RawReg) -> &'static str {
1811        if self.format.prefer_non_abi_reg_names {
1812            reg.get().name_non_abi()
1813        } else {
1814            reg.get().name()
1815        }
1816    }
1817
1818    fn format_jump(&self, imm: u32) -> impl core::fmt::Display + 'a {
1819        struct Formatter<'a>(Option<&'a dyn Fn(u32, &mut core::fmt::Formatter) -> core::fmt::Result>, u32);
1820        impl<'a> core::fmt::Display for Formatter<'a> {
1821            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1822                if let Some(f) = self.0 {
1823                    f(self.1, fmt)
1824                } else {
1825                    write!(fmt, "{}", self.1)
1826                }
1827            }
1828        }
1829
1830        Formatter(self.format.jump_target_formatter, imm)
1831    }
1832
1833    fn format_imm(&self, imm: u32) -> impl core::fmt::Display {
1834        struct Formatter {
1835            imm: u32,
1836            is_64_bit: bool,
1837        }
1838
1839        impl core::fmt::Display for Formatter {
1840            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1841                use crate::cast::cast;
1842
1843                if self.imm == 0 {
1844                    write!(fmt, "{}", self.imm)
1845                } else if !self.is_64_bit {
1846                    write!(fmt, "0x{:x}", self.imm)
1847                } else {
1848                    let imm: i32 = cast(self.imm).to_signed();
1849                    let imm: i64 = cast(imm).to_i64_sign_extend();
1850                    write!(fmt, "0x{:x}", imm)
1851                }
1852            }
1853        }
1854
1855        Formatter {
1856            imm,
1857            is_64_bit: self.format.is_64_bit,
1858        }
1859    }
1860}
1861
1862impl<'a, 'b, 'c> core::fmt::Write for InstructionFormatter<'a, 'b, 'c> {
1863    fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
1864        self.fmt.write_str(s)
1865    }
1866}
1867
1868impl<'a, 'b, 'c> InstructionVisitor for InstructionFormatter<'a, 'b, 'c> {
1869    type ReturnTy = core::fmt::Result;
1870
1871    fn trap(&mut self) -> Self::ReturnTy {
1872        write!(self, "trap")
1873    }
1874
1875    fn fallthrough(&mut self) -> Self::ReturnTy {
1876        write!(self, "fallthrough")
1877    }
1878
1879    fn sbrk(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
1880        let d = self.format_reg(d);
1881        let s = self.format_reg(s);
1882        write!(self, "{d} = sbrk {s}")
1883    }
1884
1885    fn memset(&mut self) -> Self::ReturnTy {
1886        write!(self, "[a0..a0 + a2] = u8 a1")
1887    }
1888
1889    fn ecalli(&mut self, nth_import: u32) -> Self::ReturnTy {
1890        write!(self, "ecalli {nth_import}")
1891    }
1892
1893    fn set_less_than_unsigned(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1894        let d = self.format_reg(d);
1895        let s1 = self.format_reg(s1);
1896        let s2 = self.format_reg(s2);
1897        write!(self, "{d} = {s1} <u {s2}")
1898    }
1899
1900    fn set_less_than_signed(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1901        let d = self.format_reg(d);
1902        let s1 = self.format_reg(s1);
1903        let s2 = self.format_reg(s2);
1904        write!(self, "{d} = {s1} <s {s2}")
1905    }
1906
1907    fn shift_logical_right_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1908        let d = self.format_reg(d);
1909        let s1 = self.format_reg(s1);
1910        let s2 = self.format_reg(s2);
1911        write!(self, "{d} = {s1} >> {s2}")
1912    }
1913
1914    fn shift_arithmetic_right_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1915        let d = self.format_reg(d);
1916        let s1 = self.format_reg(s1);
1917        let s2 = self.format_reg(s2);
1918        write!(self, "{d} = {s1} >>a {s2}")
1919    }
1920
1921    fn shift_logical_left_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1922        let d = self.format_reg(d);
1923        let s1 = self.format_reg(s1);
1924        let s2 = self.format_reg(s2);
1925        write!(self, "{d} = {s1} << {s2}")
1926    }
1927
1928    fn shift_logical_right_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1929        let d = self.format_reg(d);
1930        let s1 = self.format_reg(s1);
1931        let s2 = self.format_reg(s2);
1932        if self.format.is_64_bit {
1933            write!(self, "i32 {d} = {s1} >> {s2}")
1934        } else {
1935            write!(self, "{d} = {s1} >> {s2}")
1936        }
1937    }
1938
1939    fn shift_arithmetic_right_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1940        let d = self.format_reg(d);
1941        let s1 = self.format_reg(s1);
1942        let s2 = self.format_reg(s2);
1943        if self.format.is_64_bit {
1944            write!(self, "i32 {d} = {s1} >>a {s2}")
1945        } else {
1946            write!(self, "{d} = {s1} >>a {s2}")
1947        }
1948    }
1949
1950    fn shift_logical_left_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1951        let d = self.format_reg(d);
1952        let s1 = self.format_reg(s1);
1953        let s2 = self.format_reg(s2);
1954        if self.format.is_64_bit {
1955            write!(self, "i32 {d} = {s1} << {s2}")
1956        } else {
1957            write!(self, "{d} = {s1} << {s2}")
1958        }
1959    }
1960
1961    fn xor(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1962        let d = self.format_reg(d);
1963        let s1 = self.format_reg(s1);
1964        let s2 = self.format_reg(s2);
1965        write!(self, "{d} = {s1} ^ {s2}")
1966    }
1967
1968    fn and(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1969        let d = self.format_reg(d);
1970        let s1 = self.format_reg(s1);
1971        let s2 = self.format_reg(s2);
1972        write!(self, "{d} = {s1} & {s2}")
1973    }
1974
1975    fn or(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1976        let d = self.format_reg(d);
1977        let s1 = self.format_reg(s1);
1978        let s2 = self.format_reg(s2);
1979        write!(self, "{d} = {s1} | {s2}")
1980    }
1981
1982    fn add_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1983        let d = self.format_reg(d);
1984        let s1 = self.format_reg(s1);
1985        let s2 = self.format_reg(s2);
1986        if self.format.is_64_bit {
1987            write!(self, "i32 {d} = {s1} + {s2}")
1988        } else {
1989            write!(self, "{d} = {s1} + {s2}")
1990        }
1991    }
1992
1993    fn add_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
1994        let d = self.format_reg(d);
1995        let s1 = self.format_reg(s1);
1996        let s2 = self.format_reg(s2);
1997        write!(self, "{d} = {s1} + {s2}")
1998    }
1999
2000    fn sub_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2001        let d = self.format_reg(d);
2002        let s1 = self.format_reg(s1);
2003        let s2 = self.format_reg(s2);
2004        if self.format.is_64_bit {
2005            write!(self, "i32 {d} = {s1} - {s2}")
2006        } else {
2007            write!(self, "{d} = {s1} - {s2}")
2008        }
2009    }
2010
2011    fn sub_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2012        let d = self.format_reg(d);
2013        let s1 = self.format_reg(s1);
2014        let s2 = self.format_reg(s2);
2015        write!(self, "{d} = {s1} - {s2}")
2016    }
2017
2018    fn mul_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2019        let d = self.format_reg(d);
2020        let s1 = self.format_reg(s1);
2021        let s2 = self.format_reg(s2);
2022        if self.format.is_64_bit {
2023            write!(self, "i32 {d} = {s1} * {s2}")
2024        } else {
2025            write!(self, "{d} = {s1} * {s2}")
2026        }
2027    }
2028
2029    fn mul_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2030        let d = self.format_reg(d);
2031        let s1 = self.format_reg(s1);
2032        let s2 = self.format_reg(s2);
2033        write!(self, "{d} = {s1} * {s2}")
2034    }
2035
2036    fn mul_imm_32(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2037        let d = self.format_reg(d);
2038        let s1 = self.format_reg(s1);
2039        let s2 = self.format_imm(s2);
2040        if self.format.is_64_bit {
2041            write!(self, "i32 {d} = {s1} * {s2}")
2042        } else {
2043            write!(self, "{d} = {s1} * {s2}")
2044        }
2045    }
2046
2047    fn mul_imm_64(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2048        let d = self.format_reg(d);
2049        let s1 = self.format_reg(s1);
2050        let s2 = self.format_imm(s2);
2051        write!(self, "{d} = {s1} * {s2}")
2052    }
2053
2054    fn mul_upper_signed_signed(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2055        let d = self.format_reg(d);
2056        let s1 = self.format_reg(s1);
2057        let s2 = self.format_reg(s2);
2058        write!(self, "{d} = {s1} mulh {s2}")
2059    }
2060
2061    fn mul_upper_unsigned_unsigned(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2062        let d = self.format_reg(d);
2063        let s1 = self.format_reg(s1);
2064        let s2 = self.format_reg(s2);
2065        write!(self, "{d} = {s1} mulhu {s2}")
2066    }
2067
2068    fn mul_upper_signed_unsigned(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2069        let d = self.format_reg(d);
2070        let s1 = self.format_reg(s1);
2071        let s2 = self.format_reg(s2);
2072        write!(self, "{d} = {s1} mulhsu {s2}")
2073    }
2074
2075    fn div_unsigned_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2076        let d = self.format_reg(d);
2077        let s1 = self.format_reg(s1);
2078        let s2 = self.format_reg(s2);
2079        if self.format.is_64_bit {
2080            write!(self, "i32 {d} = {s1} /u {s2}")
2081        } else {
2082            write!(self, "{d} = {s1} /u {s2}")
2083        }
2084    }
2085
2086    fn div_signed_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2087        let d = self.format_reg(d);
2088        let s1 = self.format_reg(s1);
2089        let s2 = self.format_reg(s2);
2090        if self.format.is_64_bit {
2091            write!(self, "i32 {d} = {s1} /s {s2}")
2092        } else {
2093            write!(self, "{d} = {s1} /s {s2}")
2094        }
2095    }
2096
2097    fn rem_unsigned_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2098        let d = self.format_reg(d);
2099        let s1 = self.format_reg(s1);
2100        let s2 = self.format_reg(s2);
2101        if self.format.is_64_bit {
2102            write!(self, "i32 {d} = {s1} %u {s2}")
2103        } else {
2104            write!(self, "{d} = {s1} %u {s2}")
2105        }
2106    }
2107
2108    fn rem_signed_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2109        let d = self.format_reg(d);
2110        let s1 = self.format_reg(s1);
2111        let s2 = self.format_reg(s2);
2112        if self.format.is_64_bit {
2113            write!(self, "i32 {d} = {s1} %s {s2}")
2114        } else {
2115            write!(self, "{d} = {s1} %s {s2}")
2116        }
2117    }
2118
2119    fn div_unsigned_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2120        let d = self.format_reg(d);
2121        let s1 = self.format_reg(s1);
2122        let s2 = self.format_reg(s2);
2123        write!(self, "{d} = {s1} /u {s2}")
2124    }
2125
2126    fn div_signed_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2127        let d = self.format_reg(d);
2128        let s1 = self.format_reg(s1);
2129        let s2 = self.format_reg(s2);
2130        write!(self, "{d} = {s1} /s {s2}")
2131    }
2132
2133    fn rem_unsigned_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2134        let d = self.format_reg(d);
2135        let s1 = self.format_reg(s1);
2136        let s2 = self.format_reg(s2);
2137        write!(self, "{d} = {s1} %u {s2}")
2138    }
2139
2140    fn rem_signed_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2141        let d = self.format_reg(d);
2142        let s1 = self.format_reg(s1);
2143        let s2 = self.format_reg(s2);
2144        write!(self, "{d} = {s1} %s {s2}")
2145    }
2146
2147    fn and_inverted(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2148        let d = self.format_reg(d);
2149        let s1 = self.format_reg(s1);
2150        let s2 = self.format_reg(s2);
2151        write!(self, "{d} = {s1} & ~{s2}")
2152    }
2153
2154    fn or_inverted(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2155        let d = self.format_reg(d);
2156        let s1 = self.format_reg(s1);
2157        let s2 = self.format_reg(s2);
2158        write!(self, "{d} = {s1} | ~{s2}")
2159    }
2160
2161    fn xnor(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2162        let d = self.format_reg(d);
2163        let s1 = self.format_reg(s1);
2164        let s2 = self.format_reg(s2);
2165        write!(self, "{d} = ~({s1} ^ {s2})")
2166    }
2167
2168    fn maximum(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2169        let d = self.format_reg(d);
2170        let s1 = self.format_reg(s1);
2171        let s2 = self.format_reg(s2);
2172        write!(self, "{d} = maxs({s1}, {s2})")
2173    }
2174
2175    fn maximum_unsigned(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2176        let d = self.format_reg(d);
2177        let s1 = self.format_reg(s1);
2178        let s2 = self.format_reg(s2);
2179        write!(self, "{d} = maxu({s1}, {s2})")
2180    }
2181
2182    fn minimum(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2183        let d = self.format_reg(d);
2184        let s1 = self.format_reg(s1);
2185        let s2 = self.format_reg(s2);
2186        write!(self, "{d} = mins({s1}, {s2})")
2187    }
2188
2189    fn minimum_unsigned(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2190        let d = self.format_reg(d);
2191        let s1 = self.format_reg(s1);
2192        let s2 = self.format_reg(s2);
2193        write!(self, "{d} = minu({s1}, {s2})")
2194    }
2195
2196    fn rotate_left_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2197        let d = self.format_reg(d);
2198        let s1 = self.format_reg(s1);
2199        let s2 = self.format_reg(s2);
2200        if self.format.is_64_bit {
2201            write!(self, "i32 {d} = {s1} <<r {s2}")
2202        } else {
2203            write!(self, "{d} = {s1} <<r {s2}")
2204        }
2205    }
2206
2207    fn rotate_left_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2208        let d = self.format_reg(d);
2209        let s1 = self.format_reg(s1);
2210        let s2 = self.format_reg(s2);
2211        write!(self, "{d} = {s1} <<r {s2}")
2212    }
2213
2214    fn rotate_right_32(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2215        let d = self.format_reg(d);
2216        let s1 = self.format_reg(s1);
2217        let s2 = self.format_reg(s2);
2218        if self.format.is_64_bit {
2219            write!(self, "i32 {d} = {s1} >>r {s2}")
2220        } else {
2221            write!(self, "{d} = {s1} >>r {s2}")
2222        }
2223    }
2224
2225    fn rotate_right_64(&mut self, d: RawReg, s1: RawReg, s2: RawReg) -> Self::ReturnTy {
2226        let d = self.format_reg(d);
2227        let s1 = self.format_reg(s1);
2228        let s2 = self.format_reg(s2);
2229        write!(self, "{d} = {s1} >>r {s2}")
2230    }
2231
2232    fn set_less_than_unsigned_imm(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2233        let d = self.format_reg(d);
2234        let s1 = self.format_reg(s1);
2235        let s2 = self.format_imm(s2);
2236        write!(self, "{d} = {s1} <u {s2}")
2237    }
2238
2239    fn set_greater_than_unsigned_imm(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2240        let d = self.format_reg(d);
2241        let s1 = self.format_reg(s1);
2242        let s2 = self.format_imm(s2);
2243        write!(self, "{d} = {s1} >u {s2}")
2244    }
2245
2246    fn set_less_than_signed_imm(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2247        let d = self.format_reg(d);
2248        let s1 = self.format_reg(s1);
2249        let s2 = self.format_imm(s2);
2250        write!(self, "{d} = {s1} <s {s2}")
2251    }
2252
2253    fn set_greater_than_signed_imm(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2254        let d = self.format_reg(d);
2255        let s1 = self.format_reg(s1);
2256        let s2 = self.format_imm(s2);
2257        write!(self, "{d} = {s1} >s {s2}")
2258    }
2259
2260    fn shift_logical_right_imm_32(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2261        let d = self.format_reg(d);
2262        let s1 = self.format_reg(s1);
2263        let s2 = self.format_imm(s2);
2264        if self.format.is_64_bit {
2265            write!(self, "i32 {d} = {s1} >> {s2}")
2266        } else {
2267            write!(self, "{d} = {s1} >> {s2}")
2268        }
2269    }
2270
2271    fn shift_logical_right_imm_alt_32(&mut self, d: RawReg, s2: RawReg, s1: u32) -> Self::ReturnTy {
2272        let d = self.format_reg(d);
2273        let s1 = self.format_imm(s1);
2274        let s2 = self.format_reg(s2);
2275        if self.format.is_64_bit {
2276            write!(self, "i32 {d} = {s1} >> {s2}")
2277        } else {
2278            write!(self, "{d} = {s1} >> {s2}")
2279        }
2280    }
2281
2282    fn shift_arithmetic_right_imm_32(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2283        let d = self.format_reg(d);
2284        let s1 = self.format_reg(s1);
2285        let s2 = self.format_imm(s2);
2286        if self.format.is_64_bit {
2287            write!(self, "i32 {d} = {s1} >>a {s2}")
2288        } else {
2289            write!(self, "{d} = {s1} >>a {s2}")
2290        }
2291    }
2292
2293    fn shift_logical_right_imm_64(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2294        let d = self.format_reg(d);
2295        let s1 = self.format_reg(s1);
2296        let s2 = self.format_imm(s2);
2297        write!(self, "{d} = {s1} >> {s2}")
2298    }
2299
2300    fn shift_logical_right_imm_alt_64(&mut self, d: RawReg, s2: RawReg, s1: u32) -> Self::ReturnTy {
2301        let d = self.format_reg(d);
2302        let s1 = self.format_imm(s1);
2303        let s2 = self.format_reg(s2);
2304        write!(self, "{d} = {s1} >> {s2}")
2305    }
2306
2307    fn shift_arithmetic_right_imm_64(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2308        let d = self.format_reg(d);
2309        let s1 = self.format_reg(s1);
2310        let s2 = self.format_imm(s2);
2311        write!(self, "{d} = {s1} >>a {s2}")
2312    }
2313
2314    fn shift_arithmetic_right_imm_alt_32(&mut self, d: RawReg, s2: RawReg, s1: u32) -> Self::ReturnTy {
2315        let d = self.format_reg(d);
2316        let s1 = self.format_imm(s1);
2317        let s2 = self.format_reg(s2);
2318        if self.format.is_64_bit {
2319            write!(self, "i32 {d} = {s1} >>a {s2}")
2320        } else {
2321            write!(self, "{d} = {s1} >>a {s2}")
2322        }
2323    }
2324
2325    fn shift_logical_left_imm_32(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2326        let d = self.format_reg(d);
2327        let s1 = self.format_reg(s1);
2328        let s2 = self.format_imm(s2);
2329        if self.format.is_64_bit {
2330            write!(self, "i32 {d} = {s1} << {s2}")
2331        } else {
2332            write!(self, "{d} = {s1} << {s2}")
2333        }
2334    }
2335
2336    fn shift_logical_left_imm_alt_32(&mut self, d: RawReg, s2: RawReg, s1: u32) -> Self::ReturnTy {
2337        let d = self.format_reg(d);
2338        let s1 = self.format_imm(s1);
2339        let s2 = self.format_reg(s2);
2340        if self.format.is_64_bit {
2341            write!(self, "i32 {d} = {s1} << {s2}")
2342        } else {
2343            write!(self, "{d} = {s1} << {s2}")
2344        }
2345    }
2346
2347    fn shift_arithmetic_right_imm_alt_64(&mut self, d: RawReg, s2: RawReg, s1: u32) -> Self::ReturnTy {
2348        let d = self.format_reg(d);
2349        let s1 = self.format_imm(s1);
2350        let s2 = self.format_reg(s2);
2351        write!(self, "{d} = {s1} >>a {s2}")
2352    }
2353
2354    fn shift_logical_left_imm_64(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2355        let d = self.format_reg(d);
2356        let s1 = self.format_reg(s1);
2357        let s2 = self.format_imm(s2);
2358        write!(self, "{d} = {s1} << {s2}")
2359    }
2360
2361    fn shift_logical_left_imm_alt_64(&mut self, d: RawReg, s2: RawReg, s1: u32) -> Self::ReturnTy {
2362        let d = self.format_reg(d);
2363        let s1 = self.format_imm(s1);
2364        let s2 = self.format_reg(s2);
2365        write!(self, "{d} = {s1} << {s2}")
2366    }
2367
2368    fn or_imm(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2369        let d = self.format_reg(d);
2370        let s1 = self.format_reg(s1);
2371        let s2 = self.format_imm(s2);
2372        write!(self, "{d} = {s1} | {s2}")
2373    }
2374
2375    fn and_imm(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2376        let d = self.format_reg(d);
2377        let s1 = self.format_reg(s1);
2378        let s2 = self.format_imm(s2);
2379        write!(self, "{d} = {s1} & {s2}")
2380    }
2381
2382    fn xor_imm(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2383        let d = self.format_reg(d);
2384        let s1 = self.format_reg(s1);
2385        let s2 = self.format_imm(s2);
2386        write!(self, "{d} = {s1} ^ {s2}")
2387    }
2388
2389    fn load_imm(&mut self, d: RawReg, a: u32) -> Self::ReturnTy {
2390        let d = self.format_reg(d);
2391        let a = self.format_imm(a);
2392        write!(self, "{d} = {a}")
2393    }
2394
2395    fn load_imm64(&mut self, d: RawReg, a: u64) -> Self::ReturnTy {
2396        let d = self.format_reg(d);
2397        write!(self, "{d} = 0x{a:x}")
2398    }
2399
2400    fn move_reg(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2401        let d = self.format_reg(d);
2402        let s = self.format_reg(s);
2403        write!(self, "{d} = {s}")
2404    }
2405
2406    fn count_leading_zero_bits_32(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2407        let d = self.format_reg(d);
2408        let s = self.format_reg(s);
2409        if self.format.is_64_bit {
2410            write!(self, "i32 {d} = clz {s}")
2411        } else {
2412            write!(self, "{d} = clz {s}")
2413        }
2414    }
2415
2416    fn count_leading_zero_bits_64(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2417        let d = self.format_reg(d);
2418        let s = self.format_reg(s);
2419        write!(self, "{d} = clz {s}")
2420    }
2421
2422    fn count_trailing_zero_bits_32(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2423        let d = self.format_reg(d);
2424        let s = self.format_reg(s);
2425        if self.format.is_64_bit {
2426            write!(self, "i32 {d} = ctz {s}")
2427        } else {
2428            write!(self, "{d} = ctz {s}")
2429        }
2430    }
2431
2432    fn count_trailing_zero_bits_64(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2433        let d = self.format_reg(d);
2434        let s = self.format_reg(s);
2435        write!(self, "{d} = ctz {s}")
2436    }
2437
2438    fn count_set_bits_32(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2439        let d = self.format_reg(d);
2440        let s = self.format_reg(s);
2441        if self.format.is_64_bit {
2442            write!(self, "i32 {d} = cpop {s}")
2443        } else {
2444            write!(self, "{d} = cpop {s}")
2445        }
2446    }
2447
2448    fn count_set_bits_64(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2449        let d = self.format_reg(d);
2450        let s = self.format_reg(s);
2451        write!(self, "{d} = cpop {s}")
2452    }
2453
2454    fn sign_extend_8(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2455        let d = self.format_reg(d);
2456        let s = self.format_reg(s);
2457        write!(self, "{d} = sext.b {s}")
2458    }
2459
2460    fn sign_extend_16(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2461        let d = self.format_reg(d);
2462        let s = self.format_reg(s);
2463        write!(self, "{d} = sext.h {s}")
2464    }
2465
2466    fn zero_extend_16(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2467        let d = self.format_reg(d);
2468        let s = self.format_reg(s);
2469        write!(self, "{d} = zext.h {s}")
2470    }
2471
2472    fn reverse_byte(&mut self, d: RawReg, s: RawReg) -> Self::ReturnTy {
2473        let d = self.format_reg(d);
2474        let s = self.format_reg(s);
2475        write!(self, "{d} = reverse {s}")
2476    }
2477
2478    fn cmov_if_zero(&mut self, d: RawReg, s: RawReg, c: RawReg) -> Self::ReturnTy {
2479        let d = self.format_reg(d);
2480        let s = self.format_reg(s);
2481        let c = self.format_reg(c);
2482        write!(self, "{d} = {s} if {c} == 0")
2483    }
2484
2485    fn cmov_if_not_zero(&mut self, d: RawReg, s: RawReg, c: RawReg) -> Self::ReturnTy {
2486        let d = self.format_reg(d);
2487        let s = self.format_reg(s);
2488        let c = self.format_reg(c);
2489        write!(self, "{d} = {s} if {c} != 0")
2490    }
2491
2492    fn cmov_if_zero_imm(&mut self, d: RawReg, c: RawReg, s: u32) -> Self::ReturnTy {
2493        let d = self.format_reg(d);
2494        let c = self.format_reg(c);
2495        let s = self.format_imm(s);
2496        write!(self, "{d} = {s} if {c} == 0")
2497    }
2498
2499    fn cmov_if_not_zero_imm(&mut self, d: RawReg, c: RawReg, s: u32) -> Self::ReturnTy {
2500        let d = self.format_reg(d);
2501        let c = self.format_reg(c);
2502        let s = self.format_imm(s);
2503        write!(self, "{d} = {s} if {c} != 0")
2504    }
2505
2506    fn rotate_right_imm_32(&mut self, d: RawReg, s: RawReg, c: u32) -> Self::ReturnTy {
2507        let d = self.format_reg(d);
2508        let s = self.format_reg(s);
2509        let c = self.format_imm(c);
2510        if self.format.is_64_bit {
2511            write!(self, "i32 {d} = {s} >>r {c}")
2512        } else {
2513            write!(self, "{d} = {s} >> {c}")
2514        }
2515    }
2516
2517    fn rotate_right_imm_alt_32(&mut self, d: RawReg, c: RawReg, s: u32) -> Self::ReturnTy {
2518        let d = self.format_reg(d);
2519        let c = self.format_reg(c);
2520        let s = self.format_imm(s);
2521        if self.format.is_64_bit {
2522            write!(self, "i32 {d} = {s} >>r {c}")
2523        } else {
2524            write!(self, "{d} = {s} >> {c}")
2525        }
2526    }
2527
2528    fn rotate_right_imm_64(&mut self, d: RawReg, s: RawReg, c: u32) -> Self::ReturnTy {
2529        let d = self.format_reg(d);
2530        let s = self.format_reg(s);
2531        let c = self.format_imm(c);
2532        write!(self, "{d} = {s} >>r {c}")
2533    }
2534
2535    fn rotate_right_imm_alt_64(&mut self, d: RawReg, c: RawReg, s: u32) -> Self::ReturnTy {
2536        let d = self.format_reg(d);
2537        let c = self.format_reg(c);
2538        let s = self.format_imm(s);
2539        write!(self, "{d} = {s} >>r {c}")
2540    }
2541
2542    fn add_imm_64(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2543        let d = self.format_reg(d);
2544        let s1 = self.format_reg(s1);
2545        if !self.format.prefer_unaliased && i64::from(s2) < 0 && i64::from(s2) > -4096 {
2546            write!(self, "{d} = {s1} - {s2}", s2 = -i64::from(s2))
2547        } else {
2548            let s2 = self.format_imm(s2);
2549            write!(self, "{d} = {s1} + {s2}")
2550        }
2551    }
2552
2553    fn add_imm_32(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2554        let d = self.format_reg(d);
2555        let s1 = self.format_reg(s1);
2556        let prefix = if self.format.is_64_bit { "i32 " } else { "" };
2557        if !self.format.prefer_unaliased && i64::from(s2) < 0 && i64::from(s2) > -4096 {
2558            write!(self, "{prefix}{d} = {s1} - {s2}", s2 = -i64::from(s2))
2559        } else {
2560            let s2 = self.format_imm(s2);
2561            write!(self, "{prefix}{d} = {s1} + {s2}")
2562        }
2563    }
2564
2565    fn negate_and_add_imm_32(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2566        let d = self.format_reg(d);
2567        let s1 = self.format_reg(s1);
2568        let prefix = if self.format.is_64_bit { "i32 " } else { "" };
2569        if !self.format.prefer_unaliased && s2 == 0 {
2570            write!(self, "{prefix}{d} = -{s1}")
2571        } else {
2572            let s2 = self.format_imm(s2);
2573            write!(self, "{prefix}{d} = {s2} - {s1}")
2574        }
2575    }
2576
2577    fn negate_and_add_imm_64(&mut self, d: RawReg, s1: RawReg, s2: u32) -> Self::ReturnTy {
2578        let d = self.format_reg(d);
2579        let s1 = self.format_reg(s1);
2580        if !self.format.prefer_unaliased && s2 == 0 {
2581            write!(self, "{d} = -{s1}")
2582        } else {
2583            let s2 = self.format_imm(s2);
2584            write!(self, "{d} = {s2} - {s1}")
2585        }
2586    }
2587
2588    fn store_imm_indirect_u8(&mut self, base: RawReg, offset: u32, value: u32) -> Self::ReturnTy {
2589        let base = self.format_reg(base);
2590        let value = self.format_imm(value);
2591        write!(self, "u8 [{base} + {offset}] = {value}")
2592    }
2593
2594    fn store_imm_indirect_u16(&mut self, base: RawReg, offset: u32, value: u32) -> Self::ReturnTy {
2595        let base = self.format_reg(base);
2596        let value = self.format_imm(value);
2597        write!(self, "u16 [{base} + {offset}] = {value}")
2598    }
2599
2600    fn store_imm_indirect_u32(&mut self, base: RawReg, offset: u32, value: u32) -> Self::ReturnTy {
2601        let base = self.format_reg(base);
2602        let value = self.format_imm(value);
2603        write!(self, "u32 [{base} + {offset}] = {value}")
2604    }
2605
2606    fn store_imm_indirect_u64(&mut self, base: RawReg, offset: u32, value: u32) -> Self::ReturnTy {
2607        let base = self.format_reg(base);
2608        let value = self.format_imm(value);
2609        write!(self, "u64 [{base} + {offset}] = {value}")
2610    }
2611
2612    fn store_indirect_u8(&mut self, src: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2613        let base = self.format_reg(base);
2614        if self.format.prefer_unaliased || offset != 0 {
2615            let offset = self.format_imm(offset);
2616            write!(self, "u8 [{base} + {offset}] = {src}")
2617        } else {
2618            write!(self, "u8 [{base}] = {src}")
2619        }
2620    }
2621
2622    fn store_indirect_u16(&mut self, src: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2623        let src = self.format_reg(src);
2624        let base = self.format_reg(base);
2625        if self.format.prefer_unaliased || offset != 0 {
2626            let offset = self.format_imm(offset);
2627            write!(self, "u16 [{base} + {offset}] = {src}")
2628        } else {
2629            write!(self, "u16 [{base}] = {src}")
2630        }
2631    }
2632
2633    fn store_indirect_u32(&mut self, src: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2634        let src = self.format_reg(src);
2635        let base = self.format_reg(base);
2636        if self.format.prefer_unaliased || offset != 0 {
2637            let offset = self.format_imm(offset);
2638            write!(self, "u32 [{base} + {offset}] = {src}")
2639        } else {
2640            write!(self, "u32 [{base}] = {src}")
2641        }
2642    }
2643
2644    fn store_indirect_u64(&mut self, src: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2645        let src = self.format_reg(src);
2646        let base = self.format_reg(base);
2647        if self.format.prefer_unaliased || offset != 0 {
2648            let offset = self.format_imm(offset);
2649            write!(self, "u64 [{base} + {offset}] = {src}")
2650        } else {
2651            write!(self, "u64 [{base}] = {src}")
2652        }
2653    }
2654
2655    fn store_imm_u8(&mut self, offset: u32, value: u32) -> Self::ReturnTy {
2656        let offset = self.format_imm(offset);
2657        let value = self.format_imm(value);
2658        write!(self, "u8 [{offset}] = {value}")
2659    }
2660
2661    fn store_imm_u16(&mut self, offset: u32, value: u32) -> Self::ReturnTy {
2662        let offset = self.format_imm(offset);
2663        let value = self.format_imm(value);
2664        write!(self, "u16 [{offset}] = {value}")
2665    }
2666
2667    fn store_imm_u32(&mut self, offset: u32, value: u32) -> Self::ReturnTy {
2668        let offset = self.format_imm(offset);
2669        let value = self.format_imm(value);
2670        write!(self, "u32 [{offset}] = {value}")
2671    }
2672
2673    fn store_imm_u64(&mut self, offset: u32, value: u32) -> Self::ReturnTy {
2674        let offset = self.format_imm(offset);
2675        let value = self.format_imm(value);
2676        write!(self, "u64 [{offset}] = {value}")
2677    }
2678
2679    fn store_u8(&mut self, src: RawReg, offset: u32) -> Self::ReturnTy {
2680        let src = self.format_reg(src);
2681        let offset = self.format_imm(offset);
2682        write!(self, "u8 [{offset}] = {src}")
2683    }
2684
2685    fn store_u16(&mut self, src: RawReg, offset: u32) -> Self::ReturnTy {
2686        let src = self.format_reg(src);
2687        let offset = self.format_imm(offset);
2688        write!(self, "u16 [{offset}] = {src}")
2689    }
2690
2691    fn store_u32(&mut self, src: RawReg, offset: u32) -> Self::ReturnTy {
2692        let src = self.format_reg(src);
2693        let offset = self.format_imm(offset);
2694        write!(self, "u32 [{offset}] = {src}")
2695    }
2696
2697    fn store_u64(&mut self, src: RawReg, offset: u32) -> Self::ReturnTy {
2698        let src = self.format_reg(src);
2699        let offset = self.format_imm(offset);
2700        write!(self, "u64 [{offset}] = {src}")
2701    }
2702
2703    fn load_indirect_u8(&mut self, dst: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2704        let dst = self.format_reg(dst);
2705        let base = self.format_reg(base);
2706        if self.format.prefer_unaliased || offset != 0 {
2707            let offset = self.format_imm(offset);
2708            write!(self, "{} = u8 [{} + {}]", dst, base, offset)
2709        } else {
2710            write!(self, "{} = u8 [{}]", dst, base)
2711        }
2712    }
2713
2714    fn load_indirect_i8(&mut self, dst: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2715        let dst = self.format_reg(dst);
2716        let base = self.format_reg(base);
2717        if self.format.prefer_unaliased || offset != 0 {
2718            let offset = self.format_imm(offset);
2719            write!(self, "{} = i8 [{} + {}]", dst, base, offset)
2720        } else {
2721            write!(self, "{} = i8 [{}]", dst, base)
2722        }
2723    }
2724
2725    fn load_indirect_u16(&mut self, dst: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2726        let dst = self.format_reg(dst);
2727        let base = self.format_reg(base);
2728        if self.format.prefer_unaliased || offset != 0 {
2729            let offset = self.format_imm(offset);
2730            write!(self, "{} = u16 [{} + {}]", dst, base, offset)
2731        } else {
2732            write!(self, "{} = u16 [{} ]", dst, base)
2733        }
2734    }
2735
2736    fn load_indirect_i16(&mut self, dst: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2737        let dst = self.format_reg(dst);
2738        let base = self.format_reg(base);
2739        if self.format.prefer_unaliased || offset != 0 {
2740            let offset = self.format_imm(offset);
2741            write!(self, "{} = i16 [{} + {}]", dst, base, offset)
2742        } else {
2743            write!(self, "{} = i16 [{}]", dst, base)
2744        }
2745    }
2746
2747    fn load_indirect_u32(&mut self, dst: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2748        let dst = self.format_reg(dst);
2749        let base = self.format_reg(base);
2750        if self.format.prefer_unaliased || offset != 0 {
2751            let offset = self.format_imm(offset);
2752            write!(self, "{} = u32 [{} + {}]", dst, base, offset)
2753        } else {
2754            write!(self, "{} = u32 [{}]", dst, base)
2755        }
2756    }
2757
2758    fn load_indirect_i32(&mut self, dst: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2759        let dst = self.format_reg(dst);
2760        let base = self.format_reg(base);
2761        if self.format.prefer_unaliased || offset != 0 {
2762            let offset = self.format_imm(offset);
2763            write!(self, "{} = i32 [{} + {}]", dst, base, offset)
2764        } else {
2765            write!(self, "{} = i32 [{}]", dst, base)
2766        }
2767    }
2768
2769    fn load_indirect_u64(&mut self, dst: RawReg, base: RawReg, offset: u32) -> Self::ReturnTy {
2770        let dst = self.format_reg(dst);
2771        let base = self.format_reg(base);
2772        if self.format.prefer_unaliased || offset != 0 {
2773            let offset = self.format_imm(offset);
2774            write!(self, "{} = u64 [{} + {}]", dst, base, offset)
2775        } else {
2776            write!(self, "{} = u64 [{}]", dst, base)
2777        }
2778    }
2779
2780    fn load_u8(&mut self, dst: RawReg, offset: u32) -> Self::ReturnTy {
2781        let dst = self.format_reg(dst);
2782        let offset = self.format_imm(offset);
2783        write!(self, "{} = u8 [{}]", dst, offset)
2784    }
2785
2786    fn load_i8(&mut self, dst: RawReg, offset: u32) -> Self::ReturnTy {
2787        let dst = self.format_reg(dst);
2788        let offset = self.format_imm(offset);
2789        write!(self, "{} = i8 [{}]", dst, offset)
2790    }
2791
2792    fn load_u16(&mut self, dst: RawReg, offset: u32) -> Self::ReturnTy {
2793        let dst = self.format_reg(dst);
2794        let offset = self.format_imm(offset);
2795        write!(self, "{} = u16 [{}]", dst, offset)
2796    }
2797
2798    fn load_i16(&mut self, dst: RawReg, offset: u32) -> Self::ReturnTy {
2799        let dst = self.format_reg(dst);
2800        let offset = self.format_imm(offset);
2801        write!(self, "{} = i16 [{}]", dst, offset)
2802    }
2803
2804    fn load_i32(&mut self, dst: RawReg, offset: u32) -> Self::ReturnTy {
2805        let dst = self.format_reg(dst);
2806        let offset = self.format_imm(offset);
2807        write!(self, "{} = i32 [{}]", dst, offset)
2808    }
2809
2810    fn load_u32(&mut self, dst: RawReg, offset: u32) -> Self::ReturnTy {
2811        let dst = self.format_reg(dst);
2812        let offset = self.format_imm(offset);
2813        write!(self, "{} = u32 [{}]", dst, offset)
2814    }
2815
2816    fn load_u64(&mut self, dst: RawReg, offset: u32) -> Self::ReturnTy {
2817        let dst = self.format_reg(dst);
2818        let offset = self.format_imm(offset);
2819        write!(self, "{} = u64 [{}]", dst, offset)
2820    }
2821
2822    fn branch_less_unsigned(&mut self, s1: RawReg, s2: RawReg, imm: u32) -> Self::ReturnTy {
2823        let s1 = self.format_reg(s1);
2824        let s2 = self.format_reg(s2);
2825        let imm = self.format_jump(imm);
2826        write!(self, "jump {} if {} <u {}", imm, s1, s2)
2827    }
2828
2829    fn branch_less_signed(&mut self, s1: RawReg, s2: RawReg, imm: u32) -> Self::ReturnTy {
2830        let s1 = self.format_reg(s1);
2831        let s2 = self.format_reg(s2);
2832        let imm = self.format_jump(imm);
2833        write!(self, "jump {} if {} <s {}", imm, s1, s2)
2834    }
2835
2836    fn branch_less_unsigned_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2837        let s1 = self.format_reg(s1);
2838        let imm = self.format_jump(imm);
2839        write!(self, "jump {} if {} <u {}", imm, s1, s2)
2840    }
2841
2842    fn branch_less_signed_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2843        let s1 = self.format_reg(s1);
2844        let imm = self.format_jump(imm);
2845        write!(self, "jump {} if {} <s {}", imm, s1, s2)
2846    }
2847
2848    fn branch_greater_or_equal_unsigned(&mut self, s1: RawReg, s2: RawReg, imm: u32) -> Self::ReturnTy {
2849        let s1 = self.format_reg(s1);
2850        let s2 = self.format_reg(s2);
2851        let imm = self.format_jump(imm);
2852        write!(self, "jump {} if {} >=u {}", imm, s1, s2)
2853    }
2854
2855    fn branch_greater_or_equal_signed(&mut self, s1: RawReg, s2: RawReg, imm: u32) -> Self::ReturnTy {
2856        let s1 = self.format_reg(s1);
2857        let s2 = self.format_reg(s2);
2858        let imm = self.format_jump(imm);
2859        write!(self, "jump {} if {} >=s {}", imm, s1, s2)
2860    }
2861
2862    fn branch_greater_or_equal_unsigned_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2863        let s1 = self.format_reg(s1);
2864        let imm = self.format_jump(imm);
2865        write!(self, "jump {} if {} >=u {}", imm, s1, s2)
2866    }
2867
2868    fn branch_greater_or_equal_signed_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2869        let s1 = self.format_reg(s1);
2870        let imm = self.format_jump(imm);
2871        write!(self, "jump {} if {} >=s {}", imm, s1, s2)
2872    }
2873
2874    fn branch_eq(&mut self, s1: RawReg, s2: RawReg, imm: u32) -> Self::ReturnTy {
2875        let s1 = self.format_reg(s1);
2876        let s2 = self.format_reg(s2);
2877        let imm = self.format_jump(imm);
2878        write!(self, "jump {} if {} == {}", imm, s1, s2)
2879    }
2880
2881    fn branch_not_eq(&mut self, s1: RawReg, s2: RawReg, imm: u32) -> Self::ReturnTy {
2882        let s1 = self.format_reg(s1);
2883        let s2 = self.format_reg(s2);
2884        let imm = self.format_jump(imm);
2885        write!(self, "jump {} if {} != {}", imm, s1, s2)
2886    }
2887
2888    fn branch_eq_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2889        let s1 = self.format_reg(s1);
2890        let imm = self.format_jump(imm);
2891        write!(self, "jump {} if {} == {}", imm, s1, s2)
2892    }
2893
2894    fn branch_not_eq_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2895        let s1 = self.format_reg(s1);
2896        let imm = self.format_jump(imm);
2897        write!(self, "jump {} if {} != {}", imm, s1, s2)
2898    }
2899
2900    fn branch_less_or_equal_unsigned_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2901        let s1 = self.format_reg(s1);
2902        let imm = self.format_jump(imm);
2903        write!(self, "jump {} if {} <=u {}", imm, s1, s2)
2904    }
2905
2906    fn branch_less_or_equal_signed_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2907        let s1 = self.format_reg(s1);
2908        let imm = self.format_jump(imm);
2909        write!(self, "jump {} if {} <=s {}", imm, s1, s2)
2910    }
2911
2912    fn branch_greater_unsigned_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2913        let s1 = self.format_reg(s1);
2914        let imm = self.format_jump(imm);
2915        write!(self, "jump {} if {} >u {}", imm, s1, s2)
2916    }
2917
2918    fn branch_greater_signed_imm(&mut self, s1: RawReg, s2: u32, imm: u32) -> Self::ReturnTy {
2919        let s1 = self.format_reg(s1);
2920        let imm = self.format_jump(imm);
2921        write!(self, "jump {} if {} >s {}", imm, s1, s2)
2922    }
2923
2924    fn jump(&mut self, target: u32) -> Self::ReturnTy {
2925        let target = self.format_jump(target);
2926        write!(self, "jump {}", target)
2927    }
2928
2929    fn load_imm_and_jump(&mut self, ra: RawReg, value: u32, target: u32) -> Self::ReturnTy {
2930        let ra = self.format_reg(ra);
2931        let target = self.format_jump(target);
2932        write!(self, "{ra} = {value}, jump {target}")
2933    }
2934
2935    fn jump_indirect(&mut self, base: RawReg, offset: u32) -> Self::ReturnTy {
2936        if !self.format.prefer_unaliased {
2937            match (base, offset) {
2938                (_, 0) if base == Reg::RA.into() => return write!(self, "ret"),
2939                (_, 0) => return write!(self, "jump [{}]", self.format_reg(base)),
2940                (_, _) => {}
2941            }
2942        }
2943
2944        let offset = self.format_imm(offset);
2945        write!(self, "jump [{} + {}]", self.format_reg(base), offset)
2946    }
2947
2948    fn load_imm_and_jump_indirect(&mut self, ra: RawReg, base: RawReg, value: u32, offset: u32) -> Self::ReturnTy {
2949        let ra = self.format_reg(ra);
2950        let base = self.format_reg(base);
2951        if ra != base {
2952            if !self.format.prefer_unaliased && offset == 0 {
2953                write!(self, "{ra} = {value}, jump [{base}]")
2954            } else {
2955                let offset = self.format_imm(offset);
2956                write!(self, "{ra} = {value}, jump [{base} + {offset}]")
2957            }
2958        } else if !self.format.prefer_unaliased && offset == 0 {
2959            write!(self, "tmp = {base}, {ra} = {value}, jump [tmp]")
2960        } else {
2961            let offset = self.format_imm(offset);
2962            write!(self, "tmp = {base}, {ra} = {value}, jump [tmp + {offset}]")
2963        }
2964    }
2965
2966    fn invalid(&mut self) -> Self::ReturnTy {
2967        write!(self, "invalid")
2968    }
2969}
2970
2971#[derive(Debug)]
2972pub struct ProgramParseError(ProgramParseErrorKind);
2973
2974#[derive(Debug)]
2975enum ProgramParseErrorKind {
2976    FailedToReadVarint {
2977        offset: usize,
2978    },
2979    FailedToReadStringNonUtf {
2980        offset: usize,
2981    },
2982    UnexpectedSection {
2983        offset: usize,
2984        section: u8,
2985    },
2986    UnexpectedEnd {
2987        offset: usize,
2988        expected_count: usize,
2989        actual_count: usize,
2990    },
2991    UnsupportedVersion {
2992        version: u8,
2993    },
2994    Other(&'static str),
2995}
2996
2997impl ProgramParseError {
2998    #[cold]
2999    #[inline]
3000    fn failed_to_read_varint(offset: usize) -> ProgramParseError {
3001        ProgramParseError(ProgramParseErrorKind::FailedToReadVarint { offset })
3002    }
3003
3004    #[cold]
3005    #[inline]
3006    fn unexpected_end_of_file(offset: usize, expected_count: usize, actual_count: usize) -> ProgramParseError {
3007        ProgramParseError(ProgramParseErrorKind::UnexpectedEnd {
3008            offset,
3009            expected_count,
3010            actual_count,
3011        })
3012    }
3013}
3014
3015impl core::fmt::Display for ProgramParseError {
3016    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
3017        match self.0 {
3018            ProgramParseErrorKind::FailedToReadVarint { offset } => {
3019                write!(
3020                    fmt,
3021                    "failed to parse program blob: failed to parse a varint at offset 0x{:x}",
3022                    offset
3023                )
3024            }
3025            ProgramParseErrorKind::FailedToReadStringNonUtf { offset } => {
3026                write!(
3027                    fmt,
3028                    "failed to parse program blob: failed to parse a string at offset 0x{:x} (not valid UTF-8)",
3029                    offset
3030                )
3031            }
3032            ProgramParseErrorKind::UnexpectedSection { offset, section } => {
3033                write!(
3034                    fmt,
3035                    "failed to parse program blob: found unexpected section as offset 0x{:x}: 0x{:x}",
3036                    offset, section
3037                )
3038            }
3039            ProgramParseErrorKind::UnexpectedEnd {
3040                offset,
3041                expected_count,
3042                actual_count,
3043            } => {
3044                write!(fmt, "failed to parse program blob: unexpected end of file at offset 0x{:x}: expected to be able to read at least {} bytes, found {} bytes", offset, expected_count, actual_count)
3045            }
3046            ProgramParseErrorKind::UnsupportedVersion { version } => {
3047                write!(fmt, "failed to parse program blob: unsupported version: {}", version)
3048            }
3049            ProgramParseErrorKind::Other(error) => {
3050                write!(fmt, "failed to parse program blob: {}", error)
3051            }
3052        }
3053    }
3054}
3055
3056#[cfg(feature = "std")]
3057impl std::error::Error for ProgramParseError {}
3058
3059#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
3060#[repr(transparent)]
3061pub struct ProgramCounter(pub u32);
3062
3063impl core::fmt::Display for ProgramCounter {
3064    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
3065        self.0.fmt(fmt)
3066    }
3067}
3068
3069#[derive(Clone, PartialEq, Eq, Debug)]
3070pub struct ProgramExport<T> {
3071    program_counter: ProgramCounter,
3072    symbol: ProgramSymbol<T>,
3073}
3074
3075impl<T> ProgramExport<T>
3076where
3077    T: AsRef<[u8]>,
3078{
3079    pub fn new(program_counter: ProgramCounter, symbol: ProgramSymbol<T>) -> Self {
3080        Self { program_counter, symbol }
3081    }
3082
3083    pub fn program_counter(&self) -> ProgramCounter {
3084        self.program_counter
3085    }
3086
3087    pub fn symbol(&self) -> &ProgramSymbol<T> {
3088        &self.symbol
3089    }
3090}
3091
3092impl<T> PartialEq<str> for ProgramExport<T>
3093where
3094    T: AsRef<[u8]>,
3095{
3096    fn eq(&self, rhs: &str) -> bool {
3097        self.symbol.as_bytes() == rhs.as_bytes()
3098    }
3099}
3100
3101#[derive(Clone, PartialEq, Eq, Debug)]
3102pub struct ProgramSymbol<T>(T);
3103
3104impl<T> ProgramSymbol<T>
3105where
3106    T: AsRef<[u8]>,
3107{
3108    pub fn new(bytes: T) -> Self {
3109        Self(bytes)
3110    }
3111
3112    pub fn into_inner(self) -> T {
3113        self.0
3114    }
3115
3116    pub fn as_bytes(&self) -> &[u8] {
3117        self.0.as_ref()
3118    }
3119}
3120
3121impl<T> PartialEq<str> for ProgramSymbol<T>
3122where
3123    T: AsRef<[u8]>,
3124{
3125    fn eq(&self, rhs: &str) -> bool {
3126        self.as_bytes() == rhs.as_bytes()
3127    }
3128}
3129
3130impl<'a, T> PartialEq<&'a str> for ProgramSymbol<T>
3131where
3132    T: AsRef<[u8]>,
3133{
3134    fn eq(&self, rhs: &&'a str) -> bool {
3135        self.as_bytes() == rhs.as_bytes()
3136    }
3137}
3138
3139impl<T> core::fmt::Display for ProgramSymbol<T>
3140where
3141    T: AsRef<[u8]>,
3142{
3143    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
3144        let bytes = self.0.as_ref();
3145        if let Ok(ident) = core::str::from_utf8(bytes) {
3146            fmt.write_str("'")?;
3147            fmt.write_str(ident)?;
3148            fmt.write_str("'")?;
3149        } else {
3150            fmt.write_str("0x")?;
3151            for &byte in bytes.iter() {
3152                core::write!(fmt, "{:02x}", byte)?;
3153            }
3154        }
3155
3156        Ok(())
3157    }
3158}
3159
3160/// A partially deserialized PolkaVM program.
3161#[derive(Clone, Default)]
3162pub struct ProgramBlob {
3163    #[cfg(feature = "unique-id")]
3164    unique_id: u64,
3165
3166    is_64_bit: bool,
3167
3168    ro_data_size: u32,
3169    rw_data_size: u32,
3170    stack_size: u32,
3171
3172    ro_data: ArcBytes,
3173    rw_data: ArcBytes,
3174    code: ArcBytes,
3175    jump_table: ArcBytes,
3176    jump_table_entry_size: u8,
3177    bitmask: ArcBytes,
3178    import_offsets: ArcBytes,
3179    import_symbols: ArcBytes,
3180    exports: ArcBytes,
3181
3182    debug_strings: ArcBytes,
3183    debug_line_program_ranges: ArcBytes,
3184    debug_line_programs: ArcBytes,
3185}
3186
3187struct Reader<'a, T>
3188where
3189    T: ?Sized,
3190{
3191    blob: &'a T,
3192    position: usize,
3193}
3194
3195impl<'a, T> Clone for Reader<'a, T>
3196where
3197    T: ?Sized,
3198{
3199    fn clone(&self) -> Self {
3200        Reader {
3201            blob: self.blob,
3202            position: self.position,
3203        }
3204    }
3205}
3206
3207impl<'a, T> From<&'a T> for Reader<'a, T> {
3208    fn from(blob: &'a T) -> Self {
3209        Self { blob, position: 0 }
3210    }
3211}
3212
3213impl<'a, T> Reader<'a, T>
3214where
3215    T: ?Sized + AsRef<[u8]>,
3216{
3217    fn skip(&mut self, count: usize) -> Result<(), ProgramParseError> {
3218        self.read_slice_as_range(count).map(|_| ())
3219    }
3220
3221    #[inline(always)]
3222    fn read_byte(&mut self) -> Result<u8, ProgramParseError> {
3223        Ok(self.read_slice(1)?[0])
3224    }
3225
3226    #[inline(always)]
3227    fn read_slice(&mut self, length: usize) -> Result<&'a [u8], ProgramParseError> {
3228        let blob = &self.blob.as_ref()[self.position..];
3229        let Some(slice) = blob.get(..length) else {
3230            return Err(ProgramParseError::unexpected_end_of_file(self.position, length, blob.len()));
3231        };
3232
3233        self.position += length;
3234        Ok(slice)
3235    }
3236
3237    #[inline(always)]
3238    fn read_varint(&mut self) -> Result<u32, ProgramParseError> {
3239        let first_byte = self.read_byte()?;
3240        let Some((length, value)) = read_varint(&self.blob.as_ref()[self.position..], first_byte) else {
3241            return Err(ProgramParseError::failed_to_read_varint(self.position - 1));
3242        };
3243
3244        self.position += length;
3245        Ok(value)
3246    }
3247
3248    fn read_bytes_with_length(&mut self) -> Result<&'a [u8], ProgramParseError> {
3249        let length = self.read_varint()? as usize;
3250        self.read_slice(length)
3251    }
3252
3253    fn read_string_with_length(&mut self) -> Result<&'a str, ProgramParseError> {
3254        let offset = self.position;
3255        let slice = self.read_bytes_with_length()?;
3256
3257        core::str::from_utf8(slice)
3258            .ok()
3259            .ok_or(ProgramParseError(ProgramParseErrorKind::FailedToReadStringNonUtf { offset }))
3260    }
3261
3262    fn read_slice_as_range(&mut self, count: usize) -> Result<Range<usize>, ProgramParseError> {
3263        let blob = &self.blob.as_ref()[self.position..];
3264        if blob.len() < count {
3265            return Err(ProgramParseError::unexpected_end_of_file(self.position, count, blob.len()));
3266        };
3267
3268        let range = self.position..self.position + count;
3269        self.position += count;
3270        Ok(range)
3271    }
3272}
3273
3274impl<'a> Reader<'a, ArcBytes> {
3275    fn read_slice_as_bytes(&mut self, length: usize) -> Result<ArcBytes, ProgramParseError> {
3276        let range = self.read_slice_as_range(length)?;
3277        Ok(self.blob.subslice(range))
3278    }
3279
3280    fn read_section_as_bytes(&mut self, out_section: &mut u8, expected_section: u8) -> Result<ArcBytes, ProgramParseError> {
3281        if *out_section != expected_section {
3282            return Ok(ArcBytes::default());
3283        }
3284
3285        let section_length = self.read_varint()? as usize;
3286        let range = self.read_slice_as_range(section_length)?;
3287        *out_section = self.read_byte()?;
3288
3289        Ok(self.blob.subslice(range))
3290    }
3291}
3292
3293#[derive(Copy, Clone)]
3294pub struct Imports<'a> {
3295    offsets: &'a [u8],
3296    symbols: &'a [u8],
3297}
3298
3299impl<'a> Imports<'a> {
3300    pub fn is_empty(&self) -> bool {
3301        self.len() == 0
3302    }
3303
3304    pub fn len(&self) -> u32 {
3305        (self.offsets.len() / 4) as u32
3306    }
3307
3308    pub fn get(&self, index: u32) -> Option<ProgramSymbol<&'a [u8]>> {
3309        let offset_start = index.checked_mul(4)?;
3310        let offset_end = offset_start.checked_add(4)?;
3311        let xs = self.offsets.get(offset_start as usize..offset_end as usize)?;
3312        let offset = u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]) as usize;
3313        let next_offset = offset_end
3314            .checked_add(4)
3315            .and_then(|next_offset_end| self.offsets.get(offset_end as usize..next_offset_end as usize))
3316            .map_or(self.symbols.len(), |xs| u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]) as usize);
3317
3318        let symbol = self.symbols.get(offset..next_offset)?;
3319        Some(ProgramSymbol::new(symbol))
3320    }
3321
3322    pub fn iter(&self) -> ImportsIter<'a> {
3323        ImportsIter { imports: *self, index: 0 }
3324    }
3325}
3326
3327impl<'a> IntoIterator for Imports<'a> {
3328    type Item = Option<ProgramSymbol<&'a [u8]>>;
3329    type IntoIter = ImportsIter<'a>;
3330
3331    fn into_iter(self) -> Self::IntoIter {
3332        self.iter()
3333    }
3334}
3335
3336impl<'a> IntoIterator for &'a Imports<'a> {
3337    type Item = Option<ProgramSymbol<&'a [u8]>>;
3338    type IntoIter = ImportsIter<'a>;
3339
3340    fn into_iter(self) -> Self::IntoIter {
3341        self.iter()
3342    }
3343}
3344
3345pub struct ImportsIter<'a> {
3346    imports: Imports<'a>,
3347    index: u32,
3348}
3349
3350impl<'a> Iterator for ImportsIter<'a> {
3351    type Item = Option<ProgramSymbol<&'a [u8]>>;
3352    fn next(&mut self) -> Option<Self::Item> {
3353        if self.index >= self.imports.len() {
3354            None
3355        } else {
3356            let value = self.imports.get(self.index);
3357            self.index += 1;
3358            Some(value)
3359        }
3360    }
3361}
3362
3363#[derive(Copy, Clone)]
3364pub struct JumpTable<'a> {
3365    blob: &'a [u8],
3366    entry_size: u32,
3367}
3368
3369impl<'a> JumpTable<'a> {
3370    pub fn is_empty(&self) -> bool {
3371        self.len() == 0
3372    }
3373
3374    pub fn len(&self) -> u32 {
3375        if self.entry_size == 0 {
3376            0
3377        } else {
3378            self.blob.len() as u32 / self.entry_size
3379        }
3380    }
3381
3382    pub fn get_by_address(&self, address: u32) -> Option<ProgramCounter> {
3383        if address & (VM_CODE_ADDRESS_ALIGNMENT - 1) != 0 || address == 0 {
3384            return None;
3385        }
3386
3387        self.get_by_index((address - VM_CODE_ADDRESS_ALIGNMENT) / VM_CODE_ADDRESS_ALIGNMENT)
3388    }
3389
3390    pub fn get_by_index(&self, index: u32) -> Option<ProgramCounter> {
3391        if self.entry_size == 0 {
3392            return None;
3393        }
3394
3395        let start = index.checked_mul(self.entry_size)?;
3396        let end = start.checked_add(self.entry_size)?;
3397        self.blob
3398            .get(start as usize..end as usize)
3399            .map(|xs| match xs.len() {
3400                1 => u32::from(xs[0]),
3401                2 => u32::from(u16::from_le_bytes([xs[0], xs[1]])),
3402                3 => u32::from_le_bytes([xs[0], xs[1], xs[2], 0]),
3403                4 => u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]),
3404                _ => unreachable!(),
3405            })
3406            .map(ProgramCounter)
3407    }
3408
3409    pub fn iter(&self) -> JumpTableIter<'a> {
3410        JumpTableIter {
3411            jump_table: *self,
3412            index: 0,
3413        }
3414    }
3415}
3416
3417impl<'a> IntoIterator for JumpTable<'a> {
3418    type Item = ProgramCounter;
3419    type IntoIter = JumpTableIter<'a>;
3420
3421    fn into_iter(self) -> Self::IntoIter {
3422        self.iter()
3423    }
3424}
3425
3426impl<'a> IntoIterator for &'a JumpTable<'a> {
3427    type Item = ProgramCounter;
3428    type IntoIter = JumpTableIter<'a>;
3429
3430    fn into_iter(self) -> Self::IntoIter {
3431        self.iter()
3432    }
3433}
3434
3435pub struct JumpTableIter<'a> {
3436    jump_table: JumpTable<'a>,
3437    index: u32,
3438}
3439
3440impl<'a> Iterator for JumpTableIter<'a> {
3441    type Item = ProgramCounter;
3442    fn next(&mut self) -> Option<Self::Item> {
3443        let value = self.jump_table.get_by_index(self.index)?;
3444        self.index += 1;
3445        Some(value)
3446    }
3447}
3448
3449pub const BITMASK_MAX: u32 = 24;
3450
3451pub fn get_bit_for_offset(bitmask: &[u8], code_len: usize, offset: u32) -> bool {
3452    let Some(byte) = bitmask.get(offset as usize >> 3) else {
3453        return false;
3454    };
3455
3456    if offset as usize > code_len {
3457        return false;
3458    }
3459
3460    let shift = offset & 7;
3461    ((byte >> shift) & 1) == 1
3462}
3463
3464fn get_previous_instruction_skip(bitmask: &[u8], offset: u32) -> Option<u32> {
3465    let shift = offset & 7;
3466    let mut mask = u32::from(bitmask[offset as usize >> 3]) << 24;
3467    if offset >= 8 {
3468        mask |= u32::from(bitmask[(offset as usize >> 3) - 1]) << 16;
3469    }
3470    if offset >= 16 {
3471        mask |= u32::from(bitmask[(offset as usize >> 3) - 2]) << 8;
3472    }
3473    if offset >= 24 {
3474        mask |= u32::from(bitmask[(offset as usize >> 3) - 3]);
3475    }
3476
3477    mask <<= 8 - shift;
3478    mask >>= 1;
3479    let skip = mask.leading_zeros() - 1;
3480    if skip > BITMASK_MAX {
3481        None
3482    } else {
3483        Some(skip)
3484    }
3485}
3486
3487#[test]
3488fn test_get_previous_instruction_skip() {
3489    assert_eq!(get_previous_instruction_skip(&[0b00000001], 0), None);
3490    assert_eq!(get_previous_instruction_skip(&[0b00000011], 0), None);
3491    assert_eq!(get_previous_instruction_skip(&[0b00000010], 1), None);
3492    assert_eq!(get_previous_instruction_skip(&[0b00000011], 1), Some(0));
3493    assert_eq!(get_previous_instruction_skip(&[0b00000001], 1), Some(0));
3494    assert_eq!(get_previous_instruction_skip(&[0b00000001, 0b00000001], 8), Some(7));
3495    assert_eq!(get_previous_instruction_skip(&[0b00000001, 0b00000000], 8), Some(7));
3496}
3497
3498pub trait InstructionSet: Copy {
3499    fn opcode_from_u8(self, byte: u8) -> Option<Opcode>;
3500}
3501
3502#[allow(non_camel_case_types)]
3503#[derive(Copy, Clone, Debug, Default)]
3504pub struct ISA32_V1;
3505
3506#[allow(non_camel_case_types)]
3507#[derive(Copy, Clone, Debug, Default)]
3508pub struct ISA32_V1_NoSbrk;
3509
3510#[allow(non_camel_case_types)]
3511#[derive(Copy, Clone, Debug, Default)]
3512pub struct ISA64_V1;
3513
3514#[allow(non_camel_case_types)]
3515#[derive(Copy, Clone, Debug, Default)]
3516pub struct ISA64_V1_NoSbrk;
3517
3518pub type DefaultInstructionSet = ISA32_V1;
3519
3520/// Returns whether a jump to a given `offset` is allowed.
3521#[inline]
3522pub fn is_jump_target_valid<I>(instruction_set: I, code: &[u8], bitmask: &[u8], offset: u32) -> bool
3523where
3524    I: InstructionSet,
3525{
3526    if !get_bit_for_offset(bitmask, code.len(), offset) {
3527        // We can't jump if there's no instruction here.
3528        return false;
3529    }
3530
3531    if offset == 0 {
3532        // This is the very first instruction, so we can always jump here.
3533        return true;
3534    }
3535
3536    let Some(skip) = get_previous_instruction_skip(bitmask, offset) else {
3537        // We can't jump if there's no previous instruction in range.
3538        return false;
3539    };
3540
3541    let Some(opcode) = instruction_set.opcode_from_u8(code[offset as usize - skip as usize - 1]) else {
3542        // We can't jump after an invalid instruction.
3543        return false;
3544    };
3545
3546    if !opcode.starts_new_basic_block() {
3547        // We can't jump after this instruction.
3548        return false;
3549    }
3550
3551    true
3552}
3553
3554#[inline]
3555pub fn find_start_of_basic_block<I>(instruction_set: I, code: &[u8], bitmask: &[u8], mut offset: u32) -> Option<u32>
3556where
3557    I: InstructionSet,
3558{
3559    if !get_bit_for_offset(bitmask, code.len(), offset) {
3560        // We can't jump if there's no instruction here.
3561        return None;
3562    }
3563
3564    if offset == 0 {
3565        // This is the very first instruction, so we can always jump here.
3566        return Some(0);
3567    }
3568
3569    loop {
3570        // We can't jump if there's no previous instruction in range.
3571        let skip = get_previous_instruction_skip(bitmask, offset)?;
3572        let previous_offset = offset - skip - 1;
3573        let opcode = instruction_set
3574            .opcode_from_u8(code[previous_offset as usize])
3575            .unwrap_or(Opcode::trap);
3576        if opcode.starts_new_basic_block() {
3577            // We can jump after this instruction.
3578            return Some(offset);
3579        }
3580
3581        offset = previous_offset;
3582        if offset == 0 {
3583            return Some(0);
3584        }
3585    }
3586}
3587
3588#[test]
3589fn test_is_jump_target_valid() {
3590    fn assert_get_previous_instruction_skip_matches_instruction_parser(code: &[u8], bitmask: &[u8]) {
3591        for instruction in Instructions::new(DefaultInstructionSet::default(), code, bitmask, 0, false) {
3592            match instruction.kind {
3593                Instruction::trap => {
3594                    let skip = get_previous_instruction_skip(bitmask, instruction.offset.0);
3595                    if let Some(skip) = skip {
3596                        let previous_offset = instruction.offset.0 - skip - 1;
3597                        assert_eq!(
3598                            Instructions::new(DefaultInstructionSet::default(), code, bitmask, previous_offset, true)
3599                                .next()
3600                                .unwrap(),
3601                            ParsedInstruction {
3602                                kind: Instruction::trap,
3603                                offset: ProgramCounter(previous_offset),
3604                                next_offset: instruction.offset,
3605                            }
3606                        );
3607                    } else {
3608                        for skip in 0..=24 {
3609                            let Some(previous_offset) = instruction.offset.0.checked_sub(skip + 1) else {
3610                                continue;
3611                            };
3612                            assert_eq!(
3613                                Instructions::new(DefaultInstructionSet::default(), code, bitmask, previous_offset, true)
3614                                    .next()
3615                                    .unwrap()
3616                                    .kind,
3617                                Instruction::invalid,
3618                            );
3619                        }
3620                    }
3621                }
3622                Instruction::invalid => {}
3623                _ => unreachable!(),
3624            }
3625        }
3626    }
3627
3628    macro_rules! gen {
3629        ($code_length:expr, $bits:expr) => {{
3630            let mut bitmask = [0; ($code_length + 7) / 8];
3631            for bit in $bits {
3632                let bit: usize = bit;
3633                assert!(bit < $code_length);
3634                bitmask[bit / 8] |= (1 << (bit % 8));
3635            }
3636
3637            let code = [Opcode::trap as u8; $code_length];
3638            assert_get_previous_instruction_skip_matches_instruction_parser(&code, &bitmask);
3639            (code, bitmask)
3640        }};
3641    }
3642
3643    // Make sure the helper macro works correctly.
3644    assert_eq!(gen!(1, [0]).1, [0b00000001]);
3645    assert_eq!(gen!(2, [1]).1, [0b00000010]);
3646    assert_eq!(gen!(8, [7]).1, [0b10000000]);
3647    assert_eq!(gen!(9, [8]).1, [0b00000000, 0b00000001]);
3648    assert_eq!(gen!(10, [9]).1, [0b00000000, 0b00000010]);
3649    assert_eq!(gen!(10, [2, 9]).1, [0b00000100, 0b00000010]);
3650
3651    macro_rules! assert_valid {
3652        ($code_length:expr, $bits:expr, $offset:expr) => {{
3653            let (code, bitmask) = gen!($code_length, $bits);
3654            assert!(is_jump_target_valid(DefaultInstructionSet::default(), &code, &bitmask, $offset));
3655        }};
3656    }
3657
3658    macro_rules! assert_invalid {
3659        ($code_length:expr, $bits:expr, $offset:expr) => {{
3660            let (code, bitmask) = gen!($code_length, $bits);
3661            assert!(!is_jump_target_valid(DefaultInstructionSet::default(), &code, &bitmask, $offset));
3662        }};
3663    }
3664
3665    assert_valid!(1, [0], 0);
3666    assert_invalid!(1, [], 0);
3667    assert_valid!(2, [0, 1], 1);
3668    assert_invalid!(2, [1], 1);
3669    assert_valid!(8, [0, 7], 7);
3670    assert_valid!(9, [0, 8], 8);
3671    assert_valid!(25, [0, 24], 24);
3672    assert_valid!(26, [0, 25], 25);
3673    assert_invalid!(27, [0, 26], 26);
3674
3675    assert!(is_jump_target_valid(
3676        DefaultInstructionSet::default(),
3677        &[Opcode::load_imm as u8],
3678        &[0b00000001],
3679        0
3680    ));
3681
3682    assert!(!is_jump_target_valid(
3683        DefaultInstructionSet::default(),
3684        &[Opcode::load_imm as u8, Opcode::load_imm as u8],
3685        &[0b00000011],
3686        1
3687    ));
3688
3689    assert!(is_jump_target_valid(
3690        DefaultInstructionSet::default(),
3691        &[Opcode::trap as u8, Opcode::load_imm as u8],
3692        &[0b00000011],
3693        1
3694    ));
3695}
3696
3697#[cfg_attr(not(debug_assertions), inline(always))]
3698fn parse_bitmask_slow(bitmask: &[u8], code_length: usize, offset: u32) -> (u32, bool) {
3699    let mut offset = offset as usize + 1;
3700    let mut is_next_instruction_invalid = true;
3701    let origin = offset;
3702    while let Some(&byte) = bitmask.get(offset >> 3) {
3703        let shift = offset & 7;
3704        let mask = byte >> shift;
3705        if mask == 0 {
3706            offset += 8 - shift;
3707            if (offset - origin) < BITMASK_MAX as usize {
3708                continue;
3709            }
3710        } else {
3711            offset += mask.trailing_zeros() as usize;
3712            is_next_instruction_invalid = offset >= code_length || (offset - origin) > BITMASK_MAX as usize;
3713        }
3714        break;
3715    }
3716
3717    use core::cmp::min;
3718    let offset = min(offset, code_length);
3719    let skip = min((offset - origin) as u32, BITMASK_MAX);
3720    (skip, is_next_instruction_invalid)
3721}
3722
3723#[cfg_attr(not(debug_assertions), inline(always))]
3724pub(crate) fn parse_bitmask_fast(bitmask: &[u8], mut offset: u32) -> Option<u32> {
3725    debug_assert!(offset < u32::MAX);
3726    debug_assert!(get_bit_for_offset(bitmask, offset as usize + 1, offset));
3727    offset += 1;
3728
3729    let bitmask = bitmask.get(offset as usize >> 3..(offset as usize >> 3) + 4)?;
3730    let shift = offset & 7;
3731    let mask: u32 = (u32::from_le_bytes([bitmask[0], bitmask[1], bitmask[2], bitmask[3]]) >> shift) | (1 << BITMASK_MAX);
3732    Some(mask.trailing_zeros())
3733}
3734
3735#[test]
3736fn test_parse_bitmask() {
3737    #[track_caller]
3738    fn parse_both(bitmask: &[u8], offset: u32) -> u32 {
3739        let result_fast = parse_bitmask_fast(bitmask, offset).unwrap();
3740        let result_slow = parse_bitmask_slow(bitmask, bitmask.len() * 8, offset).0;
3741        assert_eq!(result_fast, result_slow);
3742
3743        result_fast
3744    }
3745
3746    assert_eq!(parse_both(&[0b00000011, 0, 0, 0], 0), 0);
3747    assert_eq!(parse_both(&[0b00000101, 0, 0, 0], 0), 1);
3748    assert_eq!(parse_both(&[0b10000001, 0, 0, 0], 0), 6);
3749    assert_eq!(parse_both(&[0b00000001, 1, 0, 0], 0), 7);
3750    assert_eq!(parse_both(&[0b00000001, 1 << 7, 0, 0], 0), 14);
3751    assert_eq!(parse_both(&[0b00000001, 0, 1, 0], 0), 15);
3752    assert_eq!(parse_both(&[0b00000001, 0, 1 << 7, 0], 0), 22);
3753    assert_eq!(parse_both(&[0b00000001, 0, 0, 1], 0), 23);
3754
3755    assert_eq!(parse_both(&[0b11000000, 0, 0, 0, 0], 6), 0);
3756    assert_eq!(parse_both(&[0b01000000, 1, 0, 0, 0], 6), 1);
3757
3758    assert_eq!(parse_both(&[0b10000000, 1, 0, 0, 0], 7), 0);
3759    assert_eq!(parse_both(&[0b10000000, 1 << 1, 0, 0, 0], 7), 1);
3760}
3761
3762#[derive(Clone)]
3763pub struct Instructions<'a, I> {
3764    code: &'a [u8],
3765    bitmask: &'a [u8],
3766    offset: u32,
3767    invalid_offset: Option<u32>,
3768    is_bounded: bool,
3769    is_done: bool,
3770    instruction_set: I,
3771}
3772
3773#[derive(Copy, Clone, PartialEq, Eq, Debug)]
3774pub struct ParsedInstruction {
3775    pub kind: Instruction,
3776    pub offset: ProgramCounter,
3777    pub next_offset: ProgramCounter,
3778}
3779
3780impl core::ops::Deref for ParsedInstruction {
3781    type Target = Instruction;
3782
3783    #[inline]
3784    fn deref(&self) -> &Self::Target {
3785        &self.kind
3786    }
3787}
3788
3789impl core::fmt::Display for ParsedInstruction {
3790    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
3791        write!(fmt, "{:>7}: {}", self.offset, self.kind)
3792    }
3793}
3794
3795impl<'a, I> Instructions<'a, I>
3796where
3797    I: InstructionSet,
3798{
3799    #[inline]
3800    pub fn new_bounded(instruction_set: I, code: &'a [u8], bitmask: &'a [u8], offset: u32) -> Self {
3801        Self::new(instruction_set, code, bitmask, offset, true)
3802    }
3803
3804    #[inline]
3805    pub fn new_unbounded(instruction_set: I, code: &'a [u8], bitmask: &'a [u8], offset: u32) -> Self {
3806        Self::new(instruction_set, code, bitmask, offset, false)
3807    }
3808
3809    #[inline]
3810    fn new(instruction_set: I, code: &'a [u8], bitmask: &'a [u8], offset: u32, is_bounded: bool) -> Self {
3811        assert!(code.len() <= u32::MAX as usize);
3812        assert_eq!(bitmask.len(), (code.len() + 7) / 8);
3813
3814        let is_valid = get_bit_for_offset(bitmask, code.len(), offset);
3815        let mut is_done = false;
3816        let (offset, invalid_offset) = if is_valid {
3817            (offset, None)
3818        } else if is_bounded {
3819            is_done = true;
3820            (core::cmp::min(offset + 1, code.len() as u32), Some(offset))
3821        } else {
3822            let next_offset = find_next_offset_unbounded(bitmask, code.len() as u32, offset);
3823            debug_assert!(
3824                next_offset as usize == code.len() || get_bit_for_offset(bitmask, code.len(), next_offset),
3825                "bit at {offset} is zero"
3826            );
3827            (next_offset, Some(offset))
3828        };
3829
3830        Self {
3831            code,
3832            bitmask,
3833            offset,
3834            invalid_offset,
3835            is_bounded,
3836            is_done,
3837            instruction_set,
3838        }
3839    }
3840
3841    #[inline]
3842    pub fn offset(&self) -> u32 {
3843        self.invalid_offset.unwrap_or(self.offset)
3844    }
3845
3846    #[inline]
3847    pub fn visit<T>(&mut self, visitor: &mut T) -> Option<<T as InstructionVisitor>::ReturnTy>
3848    where
3849        T: InstructionVisitor,
3850    {
3851        // TODO: Make this directly dispatched?
3852        Some(self.next()?.visit(visitor))
3853    }
3854}
3855
3856impl<'a, I> Iterator for Instructions<'a, I>
3857where
3858    I: InstructionSet,
3859{
3860    type Item = ParsedInstruction;
3861
3862    #[inline(always)]
3863    fn next(&mut self) -> Option<Self::Item> {
3864        if let Some(offset) = self.invalid_offset.take() {
3865            return Some(ParsedInstruction {
3866                kind: Instruction::invalid,
3867                offset: ProgramCounter(offset),
3868                next_offset: ProgramCounter(self.offset),
3869            });
3870        }
3871
3872        if self.is_done || self.offset as usize >= self.code.len() {
3873            return None;
3874        }
3875
3876        let offset = self.offset;
3877        debug_assert!(get_bit_for_offset(self.bitmask, self.code.len(), offset), "bit at {offset} is zero");
3878
3879        let (next_offset, instruction, is_next_instruction_invalid) =
3880            parse_instruction(self.instruction_set, self.code, self.bitmask, self.offset);
3881        debug_assert!(next_offset > self.offset);
3882
3883        if !is_next_instruction_invalid {
3884            self.offset = next_offset;
3885            debug_assert!(
3886                self.offset as usize == self.code.len() || get_bit_for_offset(self.bitmask, self.code.len(), self.offset),
3887                "bit at {} is zero",
3888                self.offset
3889            );
3890        } else {
3891            if next_offset as usize == self.code.len() {
3892                self.offset = self.code.len() as u32 + 1;
3893            } else if self.is_bounded {
3894                self.is_done = true;
3895                if instruction.opcode().can_fallthrough() {
3896                    self.offset = self.code.len() as u32;
3897                } else {
3898                    self.offset = next_offset;
3899                }
3900            } else {
3901                self.offset = find_next_offset_unbounded(self.bitmask, self.code.len() as u32, next_offset);
3902                debug_assert!(
3903                    self.offset as usize == self.code.len() || get_bit_for_offset(self.bitmask, self.code.len(), self.offset),
3904                    "bit at {} is zero",
3905                    self.offset
3906                );
3907            }
3908
3909            if instruction.opcode().can_fallthrough() {
3910                self.invalid_offset = Some(next_offset);
3911            }
3912        }
3913
3914        Some(ParsedInstruction {
3915            kind: instruction,
3916            offset: ProgramCounter(offset),
3917            next_offset: ProgramCounter(next_offset),
3918        })
3919    }
3920
3921    fn size_hint(&self) -> (usize, Option<usize>) {
3922        (0, Some(self.code.len() - core::cmp::min(self.offset() as usize, self.code.len())))
3923    }
3924}
3925
3926#[test]
3927fn test_instructions_iterator_with_implicit_trap() {
3928    for is_bounded in [false, true] {
3929        let mut i = Instructions::new(
3930            DefaultInstructionSet::default(),
3931            &[Opcode::fallthrough as u8],
3932            &[0b00000001],
3933            0,
3934            is_bounded,
3935        );
3936        assert_eq!(
3937            i.next(),
3938            Some(ParsedInstruction {
3939                kind: Instruction::fallthrough,
3940                offset: ProgramCounter(0),
3941                next_offset: ProgramCounter(1),
3942            })
3943        );
3944
3945        assert_eq!(
3946            i.next(),
3947            Some(ParsedInstruction {
3948                kind: Instruction::invalid,
3949                offset: ProgramCounter(1),
3950                next_offset: ProgramCounter(2),
3951            })
3952        );
3953
3954        assert_eq!(i.next(), None);
3955    }
3956}
3957
3958#[test]
3959fn test_instructions_iterator_without_implicit_trap() {
3960    for is_bounded in [false, true] {
3961        let mut i = Instructions::new(
3962            DefaultInstructionSet::default(),
3963            &[Opcode::trap as u8],
3964            &[0b00000001],
3965            0,
3966            is_bounded,
3967        );
3968        assert_eq!(
3969            i.next(),
3970            Some(ParsedInstruction {
3971                kind: Instruction::trap,
3972                offset: ProgramCounter(0),
3973                next_offset: ProgramCounter(1),
3974            })
3975        );
3976
3977        assert_eq!(i.next(), None);
3978    }
3979}
3980
3981#[test]
3982fn test_instructions_iterator_very_long_bitmask_bounded() {
3983    let mut code = [0_u8; 64];
3984    code[0] = Opcode::fallthrough as u8;
3985    let mut bitmask = [0_u8; 8];
3986    bitmask[0] = 0b00000001;
3987    bitmask[7] = 0b10000000;
3988
3989    let mut i = Instructions::new(DefaultInstructionSet::default(), &code, &bitmask, 0, true);
3990    assert_eq!(
3991        i.next(),
3992        Some(ParsedInstruction {
3993            kind: Instruction::fallthrough,
3994            offset: ProgramCounter(0),
3995            next_offset: ProgramCounter(25),
3996        })
3997    );
3998
3999    assert_eq!(
4000        i.next(),
4001        Some(ParsedInstruction {
4002            kind: Instruction::invalid,
4003            offset: ProgramCounter(25),
4004            next_offset: ProgramCounter(64),
4005        })
4006    );
4007
4008    assert_eq!(i.next(), None);
4009}
4010
4011#[test]
4012fn test_instructions_iterator_very_long_bitmask_unbounded() {
4013    let mut code = [0_u8; 64];
4014    code[0] = Opcode::fallthrough as u8;
4015    let mut bitmask = [0_u8; 8];
4016    bitmask[0] = 0b00000001;
4017    bitmask[7] = 0b10000000;
4018
4019    let mut i = Instructions::new(DefaultInstructionSet::default(), &code, &bitmask, 0, false);
4020    assert_eq!(
4021        i.next(),
4022        Some(ParsedInstruction {
4023            kind: Instruction::fallthrough,
4024            offset: ProgramCounter(0),
4025            next_offset: ProgramCounter(25),
4026        })
4027    );
4028
4029    assert_eq!(
4030        i.next(),
4031        Some(ParsedInstruction {
4032            kind: Instruction::invalid,
4033            offset: ProgramCounter(25),
4034            next_offset: ProgramCounter(63),
4035        })
4036    );
4037
4038    assert_eq!(
4039        i.next(),
4040        Some(ParsedInstruction {
4041            kind: Instruction::trap,
4042            offset: ProgramCounter(63),
4043            next_offset: ProgramCounter(64),
4044        })
4045    );
4046
4047    assert_eq!(i.next(), None);
4048}
4049
4050#[test]
4051fn test_instructions_iterator_start_at_invalid_offset_bounded() {
4052    let mut i = Instructions::new(DefaultInstructionSet::default(), &[Opcode::trap as u8; 8], &[0b10000001], 1, true);
4053    assert_eq!(
4054        i.next(),
4055        Some(ParsedInstruction {
4056            kind: Instruction::invalid,
4057            offset: ProgramCounter(1),
4058            // Since a bounded iterator doesn't scan forward it just assumes the next offset.
4059            next_offset: ProgramCounter(2),
4060        })
4061    );
4062
4063    assert_eq!(i.next(), None);
4064}
4065
4066#[test]
4067fn test_instructions_iterator_start_at_invalid_offset_unbounded() {
4068    let mut i = Instructions::new(DefaultInstructionSet::default(), &[Opcode::trap as u8; 8], &[0b10000001], 1, false);
4069    assert_eq!(
4070        i.next(),
4071        Some(ParsedInstruction {
4072            kind: Instruction::invalid,
4073            offset: ProgramCounter(1),
4074            next_offset: ProgramCounter(7),
4075        })
4076    );
4077
4078    assert_eq!(
4079        i.next(),
4080        Some(ParsedInstruction {
4081            kind: Instruction::trap,
4082            offset: ProgramCounter(7),
4083            next_offset: ProgramCounter(8),
4084        })
4085    );
4086
4087    assert_eq!(i.next(), None);
4088}
4089
4090#[test]
4091fn test_instructions_iterator_does_not_emit_unnecessary_invalid_instructions_if_bounded_and_ends_with_a_trap() {
4092    let code = [Opcode::trap as u8; 32];
4093    let bitmask = [0b00000001, 0b00000000, 0b00000000, 0b00000100];
4094    let mut i = Instructions::new(DefaultInstructionSet::default(), &code, &bitmask, 0, true);
4095    assert_eq!(i.offset(), 0);
4096    assert_eq!(
4097        i.next(),
4098        Some(ParsedInstruction {
4099            kind: Instruction::trap,
4100            offset: ProgramCounter(0),
4101            next_offset: ProgramCounter(25)
4102        })
4103    );
4104    assert_eq!(i.offset(), 25);
4105    assert_eq!(i.next(), None);
4106}
4107
4108#[test]
4109fn test_instructions_iterator_does_not_emit_unnecessary_invalid_instructions_if_unbounded_and_ends_with_a_trap() {
4110    let code = [Opcode::trap as u8; 32];
4111    let bitmask = [0b00000001, 0b00000000, 0b00000000, 0b00000100];
4112    let mut i = Instructions::new(DefaultInstructionSet::default(), &code, &bitmask, 0, false);
4113    assert_eq!(i.offset(), 0);
4114    assert_eq!(
4115        i.next(),
4116        Some(ParsedInstruction {
4117            kind: Instruction::trap,
4118            offset: ProgramCounter(0),
4119            next_offset: ProgramCounter(25)
4120        })
4121    );
4122    assert_eq!(i.offset(), 26);
4123    assert_eq!(
4124        i.next(),
4125        Some(ParsedInstruction {
4126            kind: Instruction::trap,
4127            offset: ProgramCounter(26),
4128            next_offset: ProgramCounter(32)
4129        })
4130    );
4131    assert_eq!(i.next(), None);
4132}
4133
4134#[derive(Clone, Default)]
4135#[non_exhaustive]
4136pub struct ProgramParts {
4137    pub is_64_bit: bool,
4138    pub ro_data_size: u32,
4139    pub rw_data_size: u32,
4140    pub stack_size: u32,
4141
4142    pub ro_data: ArcBytes,
4143    pub rw_data: ArcBytes,
4144    pub code_and_jump_table: ArcBytes,
4145    pub import_offsets: ArcBytes,
4146    pub import_symbols: ArcBytes,
4147    pub exports: ArcBytes,
4148
4149    pub debug_strings: ArcBytes,
4150    pub debug_line_program_ranges: ArcBytes,
4151    pub debug_line_programs: ArcBytes,
4152}
4153
4154impl ProgramParts {
4155    pub fn from_bytes(blob: ArcBytes) -> Result<Self, ProgramParseError> {
4156        if !blob.starts_with(&BLOB_MAGIC) {
4157            return Err(ProgramParseError(ProgramParseErrorKind::Other(
4158                "blob doesn't start with the expected magic bytes",
4159            )));
4160        }
4161
4162        let mut reader = Reader {
4163            blob: &blob,
4164            position: BLOB_MAGIC.len(),
4165        };
4166
4167        let blob_version = reader.read_byte()?;
4168        let is_64_bit = if blob_version == BLOB_VERSION_V1_32 {
4169            false
4170        } else if blob_version == BLOB_VERSION_V1_64 {
4171            true
4172        } else {
4173            return Err(ProgramParseError(ProgramParseErrorKind::UnsupportedVersion {
4174                version: blob_version,
4175            }));
4176        };
4177
4178        let blob_len = BlobLen::from_le_bytes(reader.read_slice(BLOB_LEN_SIZE)?.try_into().unwrap());
4179        if blob_len != blob.len() as u64 {
4180            return Err(ProgramParseError(ProgramParseErrorKind::Other(
4181                "blob size doesn't match the blob length metadata",
4182            )));
4183        }
4184
4185        let mut parts = ProgramParts {
4186            is_64_bit,
4187            ..ProgramParts::default()
4188        };
4189
4190        let mut section = reader.read_byte()?;
4191        if section == SECTION_MEMORY_CONFIG {
4192            let section_length = reader.read_varint()?;
4193            let position = reader.position;
4194            parts.ro_data_size = reader.read_varint()?;
4195            parts.rw_data_size = reader.read_varint()?;
4196            parts.stack_size = reader.read_varint()?;
4197            if position + section_length as usize != reader.position {
4198                return Err(ProgramParseError(ProgramParseErrorKind::Other(
4199                    "the memory config section contains more data than expected",
4200                )));
4201            }
4202            section = reader.read_byte()?;
4203        }
4204
4205        parts.ro_data = reader.read_section_as_bytes(&mut section, SECTION_RO_DATA)?;
4206        parts.rw_data = reader.read_section_as_bytes(&mut section, SECTION_RW_DATA)?;
4207
4208        if section == SECTION_IMPORTS {
4209            let section_length = reader.read_varint()? as usize;
4210            let section_start = reader.position;
4211            let import_count = reader.read_varint()?;
4212            if import_count > VM_MAXIMUM_IMPORT_COUNT {
4213                return Err(ProgramParseError(ProgramParseErrorKind::Other("too many imports")));
4214            }
4215
4216            let Some(import_offsets_size) = import_count.checked_mul(4) else {
4217                return Err(ProgramParseError(ProgramParseErrorKind::Other("the imports section is invalid")));
4218            };
4219
4220            parts.import_offsets = reader.read_slice_as_bytes(import_offsets_size as usize)?;
4221            let Some(import_symbols_size) = section_length.checked_sub(reader.position - section_start) else {
4222                return Err(ProgramParseError(ProgramParseErrorKind::Other("the imports section is invalid")));
4223            };
4224
4225            parts.import_symbols = reader.read_slice_as_bytes(import_symbols_size)?;
4226            section = reader.read_byte()?;
4227        }
4228
4229        parts.exports = reader.read_section_as_bytes(&mut section, SECTION_EXPORTS)?;
4230        parts.code_and_jump_table = reader.read_section_as_bytes(&mut section, SECTION_CODE_AND_JUMP_TABLE)?;
4231        parts.debug_strings = reader.read_section_as_bytes(&mut section, SECTION_OPT_DEBUG_STRINGS)?;
4232        parts.debug_line_programs = reader.read_section_as_bytes(&mut section, SECTION_OPT_DEBUG_LINE_PROGRAMS)?;
4233        parts.debug_line_program_ranges = reader.read_section_as_bytes(&mut section, SECTION_OPT_DEBUG_LINE_PROGRAM_RANGES)?;
4234
4235        while (section & 0b10000000) != 0 {
4236            // We don't know this section, but it's optional, so just skip it.
4237            #[cfg(feature = "logging")]
4238            log::debug!("Skipping unsupported optional section: {}", section);
4239            let section_length = reader.read_varint()?;
4240            reader.skip(section_length as usize)?;
4241            section = reader.read_byte()?;
4242        }
4243
4244        if section != SECTION_END_OF_FILE {
4245            return Err(ProgramParseError(ProgramParseErrorKind::UnexpectedSection {
4246                offset: reader.position - 1,
4247                section,
4248            }));
4249        }
4250
4251        Ok(parts)
4252    }
4253}
4254
4255impl ProgramBlob {
4256    /// Parses the blob length information from the given `raw_blob` bytes.
4257    ///
4258    /// Returns `None` if `raw_blob` doesn't contain enough bytes to read the length.
4259    pub fn blob_length(raw_blob: &[u8]) -> Option<BlobLen> {
4260        let end = BLOB_LEN_OFFSET + BLOB_LEN_SIZE;
4261        if raw_blob.len() < end {
4262            return None;
4263        }
4264        Some(BlobLen::from_le_bytes(raw_blob[BLOB_LEN_OFFSET..end].try_into().unwrap()))
4265    }
4266
4267    /// Parses the given bytes into a program blob.
4268    pub fn parse(bytes: ArcBytes) -> Result<Self, ProgramParseError> {
4269        let parts = ProgramParts::from_bytes(bytes)?;
4270        Self::from_parts(parts)
4271    }
4272
4273    /// Creates a program blob from parts.
4274    pub fn from_parts(parts: ProgramParts) -> Result<Self, ProgramParseError> {
4275        let mut blob = ProgramBlob {
4276            #[cfg(feature = "unique-id")]
4277            unique_id: 0,
4278
4279            is_64_bit: parts.is_64_bit,
4280
4281            ro_data_size: parts.ro_data_size,
4282            rw_data_size: parts.rw_data_size,
4283            stack_size: parts.stack_size,
4284
4285            ro_data: parts.ro_data,
4286            rw_data: parts.rw_data,
4287            exports: parts.exports,
4288            import_symbols: parts.import_symbols,
4289            import_offsets: parts.import_offsets,
4290            code: Default::default(),
4291            jump_table: Default::default(),
4292            jump_table_entry_size: Default::default(),
4293            bitmask: Default::default(),
4294
4295            debug_strings: parts.debug_strings,
4296            debug_line_program_ranges: parts.debug_line_program_ranges,
4297            debug_line_programs: parts.debug_line_programs,
4298        };
4299
4300        if blob.ro_data.len() > blob.ro_data_size as usize {
4301            return Err(ProgramParseError(ProgramParseErrorKind::Other(
4302                "size of the read-only data payload exceeds the declared size of the section",
4303            )));
4304        }
4305
4306        if blob.rw_data.len() > blob.rw_data_size as usize {
4307            return Err(ProgramParseError(ProgramParseErrorKind::Other(
4308                "size of the read-write data payload exceeds the declared size of the section",
4309            )));
4310        }
4311
4312        if parts.code_and_jump_table.is_empty() {
4313            return Err(ProgramParseError(ProgramParseErrorKind::Other("no code found")));
4314        }
4315
4316        {
4317            let mut reader = Reader {
4318                blob: &parts.code_and_jump_table,
4319                position: 0,
4320            };
4321
4322            let initial_position = reader.position;
4323            let jump_table_entry_count = reader.read_varint()?;
4324            if jump_table_entry_count > VM_MAXIMUM_JUMP_TABLE_ENTRIES {
4325                return Err(ProgramParseError(ProgramParseErrorKind::Other(
4326                    "the jump table section is too long",
4327                )));
4328            }
4329
4330            let jump_table_entry_size = reader.read_byte()?;
4331            let code_length = reader.read_varint()?;
4332            if code_length > VM_MAXIMUM_CODE_SIZE {
4333                return Err(ProgramParseError(ProgramParseErrorKind::Other("the code section is too long")));
4334            }
4335
4336            if !matches!(jump_table_entry_size, 0..=4) {
4337                return Err(ProgramParseError(ProgramParseErrorKind::Other("invalid jump table entry size")));
4338            }
4339
4340            let Some(jump_table_length) = jump_table_entry_count.checked_mul(u32::from(jump_table_entry_size)) else {
4341                return Err(ProgramParseError(ProgramParseErrorKind::Other("the jump table is too long")));
4342            };
4343
4344            blob.jump_table_entry_size = jump_table_entry_size;
4345            blob.jump_table = reader.read_slice_as_bytes(jump_table_length as usize)?;
4346            blob.code = reader.read_slice_as_bytes(code_length as usize)?;
4347
4348            let bitmask_length = parts.code_and_jump_table.len() - (reader.position - initial_position);
4349            blob.bitmask = reader.read_slice_as_bytes(bitmask_length)?;
4350
4351            let mut expected_bitmask_length = blob.code.len() / 8;
4352            let is_bitmask_padded = blob.code.len() % 8 != 0;
4353            expected_bitmask_length += usize::from(is_bitmask_padded);
4354
4355            if blob.bitmask.len() != expected_bitmask_length {
4356                return Err(ProgramParseError(ProgramParseErrorKind::Other(
4357                    "the bitmask length doesn't match the code length",
4358                )));
4359            }
4360
4361            if is_bitmask_padded {
4362                let last_byte = *blob.bitmask.last().unwrap();
4363                let padding_bits = blob.bitmask.len() * 8 - blob.code.len();
4364                let padding_mask = ((0b10000000_u8 as i8) >> (padding_bits - 1)) as u8;
4365                if last_byte & padding_mask != 0 {
4366                    return Err(ProgramParseError(ProgramParseErrorKind::Other(
4367                        "the bitmask is padded with non-zero bits",
4368                    )));
4369                }
4370            }
4371        }
4372
4373        #[cfg(feature = "unique-id")]
4374        {
4375            static ID_COUNTER: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(0);
4376            blob.unique_id = ID_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
4377        }
4378
4379        Ok(blob)
4380    }
4381
4382    #[cfg(feature = "unique-id")]
4383    /// Returns an unique ID of the program blob.
4384    ///
4385    /// This is an automatically incremented counter every time a `ProgramBlob` is created.
4386    pub fn unique_id(&self) -> u64 {
4387        self.unique_id
4388    }
4389
4390    /// Returns whether the blob contains a 64-bit program.
4391    pub fn is_64_bit(&self) -> bool {
4392        self.is_64_bit
4393    }
4394
4395    /// Calculates an unique hash of the program blob.
4396    pub fn unique_hash(&self, include_debug: bool) -> crate::hasher::Hash {
4397        let ProgramBlob {
4398            #[cfg(feature = "unique-id")]
4399                unique_id: _,
4400            is_64_bit,
4401            ro_data_size,
4402            rw_data_size,
4403            stack_size,
4404            ro_data,
4405            rw_data,
4406            code,
4407            jump_table,
4408            jump_table_entry_size,
4409            bitmask,
4410            import_offsets,
4411            import_symbols,
4412            exports,
4413            debug_strings,
4414            debug_line_program_ranges,
4415            debug_line_programs,
4416        } = self;
4417
4418        let mut hasher = crate::hasher::Hasher::new();
4419
4420        hasher.update_u32_array([
4421            1_u32, // VERSION
4422            u32::from(*is_64_bit),
4423            *ro_data_size,
4424            *rw_data_size,
4425            *stack_size,
4426            ro_data.len() as u32,
4427            rw_data.len() as u32,
4428            code.len() as u32,
4429            jump_table.len() as u32,
4430            u32::from(*jump_table_entry_size),
4431            bitmask.len() as u32,
4432            import_offsets.len() as u32,
4433            import_symbols.len() as u32,
4434            exports.len() as u32,
4435        ]);
4436
4437        hasher.update(ro_data);
4438        hasher.update(rw_data);
4439        hasher.update(code);
4440        hasher.update(jump_table);
4441        hasher.update(bitmask);
4442        hasher.update(import_offsets);
4443        hasher.update(import_symbols);
4444        hasher.update(exports);
4445
4446        if include_debug {
4447            hasher.update_u32_array([
4448                debug_strings.len() as u32,
4449                debug_line_program_ranges.len() as u32,
4450                debug_line_programs.len() as u32,
4451            ]);
4452
4453            hasher.update(debug_strings);
4454            hasher.update(debug_line_program_ranges);
4455            hasher.update(debug_line_programs);
4456        }
4457
4458        hasher.finalize()
4459    }
4460
4461    /// Returns the contents of the read-only data section.
4462    ///
4463    /// This only covers the initial non-zero portion of the section; use `ro_data_size` to get the full size.
4464    pub fn ro_data(&self) -> &[u8] {
4465        &self.ro_data
4466    }
4467
4468    /// Returns the size of the read-only data section.
4469    ///
4470    /// This can be larger than the length of `ro_data`, in which case the rest of the space is assumed to be filled with zeros.
4471    pub fn ro_data_size(&self) -> u32 {
4472        self.ro_data_size
4473    }
4474
4475    /// Returns the contents of the read-write data section.
4476    ///
4477    /// This only covers the initial non-zero portion of the section; use `rw_data_size` to get the full size.
4478    pub fn rw_data(&self) -> &[u8] {
4479        &self.rw_data
4480    }
4481
4482    /// Returns the size of the read-write data section.
4483    ///
4484    /// This can be larger than the length of `rw_data`, in which case the rest of the space is assumed to be filled with zeros.
4485    pub fn rw_data_size(&self) -> u32 {
4486        self.rw_data_size
4487    }
4488
4489    /// Returns the initial size of the stack.
4490    pub fn stack_size(&self) -> u32 {
4491        self.stack_size
4492    }
4493
4494    /// Returns the program code in its raw form.
4495    pub fn code(&self) -> &[u8] {
4496        &self.code
4497    }
4498
4499    #[cfg(feature = "export-internals-for-testing")]
4500    #[doc(hidden)]
4501    pub fn set_code(&mut self, code: ArcBytes) {
4502        self.code = code;
4503    }
4504
4505    /// Returns the code bitmask in its raw form.
4506    pub fn bitmask(&self) -> &[u8] {
4507        &self.bitmask
4508    }
4509
4510    pub fn imports(&self) -> Imports {
4511        Imports {
4512            offsets: &self.import_offsets,
4513            symbols: &self.import_symbols,
4514        }
4515    }
4516
4517    /// Returns an iterator over program exports.
4518    pub fn exports(&self) -> impl Iterator<Item = ProgramExport<&[u8]>> + Clone {
4519        #[derive(Clone)]
4520        enum State {
4521            Uninitialized,
4522            Pending(u32),
4523            Finished,
4524        }
4525
4526        #[derive(Clone)]
4527        struct ExportIterator<'a> {
4528            state: State,
4529            reader: Reader<'a, [u8]>,
4530        }
4531
4532        impl<'a> Iterator for ExportIterator<'a> {
4533            type Item = ProgramExport<&'a [u8]>;
4534            fn next(&mut self) -> Option<Self::Item> {
4535                let remaining = match core::mem::replace(&mut self.state, State::Finished) {
4536                    State::Uninitialized => self.reader.read_varint().ok()?,
4537                    State::Pending(remaining) => remaining,
4538                    State::Finished => return None,
4539                };
4540
4541                if remaining == 0 {
4542                    return None;
4543                }
4544
4545                let target_code_offset = self.reader.read_varint().ok()?;
4546                let symbol = self.reader.read_bytes_with_length().ok()?;
4547                let export = ProgramExport {
4548                    program_counter: ProgramCounter(target_code_offset),
4549                    symbol: ProgramSymbol::new(symbol),
4550                };
4551
4552                self.state = State::Pending(remaining - 1);
4553                Some(export)
4554            }
4555        }
4556
4557        ExportIterator {
4558            state: if !self.exports.is_empty() {
4559                State::Uninitialized
4560            } else {
4561                State::Finished
4562            },
4563            reader: Reader {
4564                blob: &self.exports,
4565                position: 0,
4566            },
4567        }
4568    }
4569
4570    /// Visits every instrution in the program.
4571    #[cfg_attr(not(debug_assertions), inline(always))]
4572    pub fn visit<T>(&self, dispatch_table: T, visitor: &mut T::State)
4573    where
4574        T: OpcodeVisitor<ReturnTy = ()>,
4575    {
4576        visitor_run(visitor, self, dispatch_table);
4577    }
4578
4579    /// Returns an iterator over all of the instructions in the program.
4580    ///
4581    /// WARNING: this is unbounded and has O(n) complexity; just creating this iterator can iterate over the whole program, even if `next` is never called!
4582    #[inline]
4583    pub fn instructions<I>(&self, instruction_set: I) -> Instructions<I>
4584    where
4585        I: InstructionSet,
4586    {
4587        Instructions::new_unbounded(instruction_set, self.code(), self.bitmask(), 0)
4588    }
4589
4590    /// Returns an interator over instructions starting at a given offset.
4591    ///
4592    /// This iterator is bounded and has O(1) complexity.
4593    #[inline]
4594    pub fn instructions_bounded_at<I>(&self, instruction_set: I, offset: ProgramCounter) -> Instructions<I>
4595    where
4596        I: InstructionSet,
4597    {
4598        Instructions::new_bounded(instruction_set, self.code(), self.bitmask(), offset.0)
4599    }
4600
4601    /// Returns whether the given program counter is a valid target for a jump.
4602    pub fn is_jump_target_valid<I>(&self, instruction_set: I, target: ProgramCounter) -> bool
4603    where
4604        I: InstructionSet,
4605    {
4606        is_jump_target_valid(instruction_set, self.code(), self.bitmask(), target.0)
4607    }
4608
4609    /// Returns a jump table.
4610    pub fn jump_table(&self) -> JumpTable {
4611        JumpTable {
4612            blob: &self.jump_table,
4613            entry_size: u32::from(self.jump_table_entry_size),
4614        }
4615    }
4616
4617    /// Returns the debug string for the given relative offset.
4618    pub fn get_debug_string(&self, offset: u32) -> Result<&str, ProgramParseError> {
4619        let mut reader = Reader {
4620            blob: &self.debug_strings,
4621            position: 0,
4622        };
4623        reader.skip(offset as usize)?;
4624        reader.read_string_with_length()
4625    }
4626
4627    /// Returns the line program for the given instruction.
4628    pub fn get_debug_line_program_at(&self, program_counter: ProgramCounter) -> Result<Option<LineProgram>, ProgramParseError> {
4629        let program_counter = program_counter.0;
4630        if self.debug_line_program_ranges.is_empty() || self.debug_line_programs.is_empty() {
4631            return Ok(None);
4632        }
4633
4634        if self.debug_line_programs[0] != VERSION_DEBUG_LINE_PROGRAM_V1 {
4635            return Err(ProgramParseError(ProgramParseErrorKind::Other(
4636                "the debug line programs section has an unsupported version",
4637            )));
4638        }
4639
4640        const ENTRY_SIZE: usize = 12;
4641
4642        let slice = &self.debug_line_program_ranges;
4643        if slice.len() % ENTRY_SIZE != 0 {
4644            return Err(ProgramParseError(ProgramParseErrorKind::Other(
4645                "the debug function ranges section has an invalid size",
4646            )));
4647        }
4648
4649        let offset = binary_search(slice, ENTRY_SIZE, |xs| {
4650            let begin = u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]);
4651            if program_counter < begin {
4652                return core::cmp::Ordering::Greater;
4653            }
4654
4655            let end = u32::from_le_bytes([xs[4], xs[5], xs[6], xs[7]]);
4656            if program_counter >= end {
4657                return core::cmp::Ordering::Less;
4658            }
4659
4660            core::cmp::Ordering::Equal
4661        });
4662
4663        let Ok(offset) = offset else { return Ok(None) };
4664
4665        let xs = &slice[offset..offset + ENTRY_SIZE];
4666        let index_begin = u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]);
4667        let index_end = u32::from_le_bytes([xs[4], xs[5], xs[6], xs[7]]);
4668        let info_offset = u32::from_le_bytes([xs[8], xs[9], xs[10], xs[11]]);
4669
4670        if program_counter < index_begin || program_counter >= index_end {
4671            return Err(ProgramParseError(ProgramParseErrorKind::Other(
4672                "binary search for function debug info failed",
4673            )));
4674        }
4675
4676        let mut reader = Reader {
4677            blob: &self.debug_line_programs,
4678            position: 0,
4679        };
4680
4681        reader.skip(info_offset as usize)?;
4682
4683        Ok(Some(LineProgram {
4684            entry_index: offset / ENTRY_SIZE,
4685            region_counter: 0,
4686            blob: self,
4687            reader,
4688            is_finished: false,
4689            program_counter: index_begin,
4690            stack: Default::default(),
4691            stack_depth: 0,
4692            mutation_depth: 0,
4693        }))
4694    }
4695}
4696
4697/// The source location.
4698#[derive(Copy, Clone, PartialEq, Eq, Debug)]
4699pub enum SourceLocation<'a> {
4700    Path { path: &'a str },
4701    PathAndLine { path: &'a str, line: u32 },
4702    Full { path: &'a str, line: u32, column: u32 },
4703}
4704
4705impl<'a> SourceLocation<'a> {
4706    /// The path to the original source file.
4707    pub fn path(&self) -> &'a str {
4708        match *self {
4709            Self::Path { path, .. } => path,
4710            Self::PathAndLine { path, .. } => path,
4711            Self::Full { path, .. } => path,
4712        }
4713    }
4714
4715    /// The line in the original source file.
4716    pub fn line(&self) -> Option<u32> {
4717        match *self {
4718            Self::Path { .. } => None,
4719            Self::PathAndLine { line, .. } => Some(line),
4720            Self::Full { line, .. } => Some(line),
4721        }
4722    }
4723
4724    /// The column in the original source file.
4725    pub fn column(&self) -> Option<u32> {
4726        match *self {
4727            Self::Path { .. } => None,
4728            Self::PathAndLine { .. } => None,
4729            Self::Full { column, .. } => Some(column),
4730        }
4731    }
4732}
4733
4734impl<'a> core::fmt::Display for SourceLocation<'a> {
4735    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
4736        match *self {
4737            Self::Path { path } => fmt.write_str(path),
4738            Self::PathAndLine { path, line } => write!(fmt, "{}:{}", path, line),
4739            Self::Full { path, line, column } => write!(fmt, "{}:{}:{}", path, line, column),
4740        }
4741    }
4742}
4743
4744#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
4745pub enum FrameKind {
4746    Enter,
4747    Call,
4748    Line,
4749}
4750
4751pub struct FrameInfo<'a> {
4752    blob: &'a ProgramBlob,
4753    inner: &'a LineProgramFrame,
4754}
4755
4756impl<'a> FrameInfo<'a> {
4757    /// Returns the namespace of this location, if available.
4758    pub fn namespace(&self) -> Result<Option<&str>, ProgramParseError> {
4759        let namespace = self.blob.get_debug_string(self.inner.namespace_offset)?;
4760        if namespace.is_empty() {
4761            Ok(None)
4762        } else {
4763            Ok(Some(namespace))
4764        }
4765    }
4766
4767    /// Returns the function name of location without the namespace, if available.
4768    pub fn function_name_without_namespace(&self) -> Result<Option<&str>, ProgramParseError> {
4769        let function_name = self.blob.get_debug_string(self.inner.function_name_offset)?;
4770        if function_name.is_empty() {
4771            Ok(None)
4772        } else {
4773            Ok(Some(function_name))
4774        }
4775    }
4776
4777    /// Returns the offset into the debug strings section containing the source code path of this location, if available.
4778    pub fn path_debug_string_offset(&self) -> Option<u32> {
4779        if self.inner.path_offset == 0 {
4780            None
4781        } else {
4782            Some(self.inner.path_offset)
4783        }
4784    }
4785
4786    /// Returns the source code path of this location, if available.
4787    pub fn path(&self) -> Result<Option<&str>, ProgramParseError> {
4788        let path = self.blob.get_debug_string(self.inner.path_offset)?;
4789        if path.is_empty() {
4790            Ok(None)
4791        } else {
4792            Ok(Some(path))
4793        }
4794    }
4795
4796    /// Returns the source code line of this location, if available.
4797    pub fn line(&self) -> Option<u32> {
4798        if self.inner.line == 0 {
4799            None
4800        } else {
4801            Some(self.inner.line)
4802        }
4803    }
4804
4805    /// Returns the source code column of this location, if available.
4806    pub fn column(&self) -> Option<u32> {
4807        if self.inner.column == 0 {
4808            None
4809        } else {
4810            Some(self.inner.column)
4811        }
4812    }
4813
4814    pub fn kind(&self) -> FrameKind {
4815        self.inner.kind.unwrap_or(FrameKind::Line)
4816    }
4817
4818    /// Returns the full name of the function.
4819    pub fn full_name(&'_ self) -> Result<impl core::fmt::Display + '_, ProgramParseError> {
4820        Ok(DisplayName {
4821            prefix: self.namespace()?.unwrap_or(""),
4822            suffix: self.function_name_without_namespace()?.unwrap_or(""),
4823        })
4824    }
4825
4826    /// Returns the source location of where this frame comes from.
4827    pub fn location(&self) -> Result<Option<SourceLocation>, ProgramParseError> {
4828        if let Some(path) = self.path()? {
4829            if let Some(line) = self.line() {
4830                if let Some(column) = self.column() {
4831                    Ok(Some(SourceLocation::Full { path, line, column }))
4832                } else {
4833                    Ok(Some(SourceLocation::PathAndLine { path, line }))
4834                }
4835            } else {
4836                Ok(Some(SourceLocation::Path { path }))
4837            }
4838        } else {
4839            Ok(None)
4840        }
4841    }
4842}
4843
4844/// Debug information about a given region of bytecode.
4845pub struct RegionInfo<'a> {
4846    entry_index: usize,
4847    blob: &'a ProgramBlob,
4848    range: Range<ProgramCounter>,
4849    frames: &'a [LineProgramFrame],
4850}
4851
4852impl<'a> RegionInfo<'a> {
4853    /// Returns the entry index of this region info within the parent line program object.
4854    pub fn entry_index(&self) -> usize {
4855        self.entry_index
4856    }
4857
4858    /// The range of instructions this region covers.
4859    pub fn instruction_range(&self) -> Range<ProgramCounter> {
4860        self.range.clone()
4861    }
4862
4863    /// Returns an iterator over the frames this region covers.
4864    pub fn frames(&self) -> impl ExactSizeIterator<Item = FrameInfo> {
4865        self.frames.iter().map(|inner| FrameInfo { blob: self.blob, inner })
4866    }
4867}
4868
4869#[derive(Default)]
4870struct LineProgramFrame {
4871    kind: Option<FrameKind>,
4872    namespace_offset: u32,
4873    function_name_offset: u32,
4874    path_offset: u32,
4875    line: u32,
4876    column: u32,
4877}
4878
4879/// A line program state machine.
4880pub struct LineProgram<'a> {
4881    entry_index: usize,
4882    region_counter: usize,
4883    blob: &'a ProgramBlob,
4884    reader: Reader<'a, ArcBytes>,
4885    is_finished: bool,
4886    program_counter: u32,
4887    // Support inline call stacks ~16 frames deep. Picked entirely arbitrarily.
4888    stack: [LineProgramFrame; 16],
4889    stack_depth: u32,
4890    mutation_depth: u32,
4891}
4892
4893impl<'a> LineProgram<'a> {
4894    /// Returns the entry index of this line program object.
4895    pub fn entry_index(&self) -> usize {
4896        self.entry_index
4897    }
4898
4899    /// Runs the line program until the next region becomes available, or until the program ends.
4900    pub fn run(&mut self) -> Result<Option<RegionInfo>, ProgramParseError> {
4901        struct SetTrueOnDrop<'a>(&'a mut bool);
4902        impl<'a> Drop for SetTrueOnDrop<'a> {
4903            fn drop(&mut self) {
4904                *self.0 = true;
4905            }
4906        }
4907
4908        if self.is_finished {
4909            return Ok(None);
4910        }
4911
4912        // Put an upper limit to how many instructions we'll process.
4913        const INSTRUCTION_LIMIT_PER_REGION: usize = 512;
4914
4915        let mark_as_finished_on_drop = SetTrueOnDrop(&mut self.is_finished);
4916        for _ in 0..INSTRUCTION_LIMIT_PER_REGION {
4917            let byte = match self.reader.read_byte() {
4918                Ok(byte) => byte,
4919                Err(error) => {
4920                    return Err(error);
4921                }
4922            };
4923
4924            let Some(opcode) = LineProgramOp::from_u8(byte) else {
4925                return Err(ProgramParseError(ProgramParseErrorKind::Other(
4926                    "found an unrecognized line program opcode",
4927                )));
4928            };
4929
4930            let (count, stack_depth) = match opcode {
4931                LineProgramOp::FinishProgram => {
4932                    return Ok(None);
4933                }
4934                LineProgramOp::SetMutationDepth => {
4935                    self.mutation_depth = self.reader.read_varint()?;
4936                    continue;
4937                }
4938                LineProgramOp::SetKindEnter => {
4939                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
4940                        frame.kind = Some(FrameKind::Enter);
4941                    }
4942                    continue;
4943                }
4944                LineProgramOp::SetKindCall => {
4945                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
4946                        frame.kind = Some(FrameKind::Call);
4947                    }
4948                    continue;
4949                }
4950                LineProgramOp::SetKindLine => {
4951                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
4952                        frame.kind = Some(FrameKind::Line);
4953                    }
4954                    continue;
4955                }
4956                LineProgramOp::SetNamespace => {
4957                    let value = self.reader.read_varint()?;
4958                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
4959                        frame.namespace_offset = value;
4960                    }
4961                    continue;
4962                }
4963                LineProgramOp::SetFunctionName => {
4964                    let value = self.reader.read_varint()?;
4965                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
4966                        frame.function_name_offset = value;
4967                    }
4968                    continue;
4969                }
4970                LineProgramOp::SetPath => {
4971                    let value = self.reader.read_varint()?;
4972                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
4973                        frame.path_offset = value;
4974                    }
4975                    continue;
4976                }
4977                LineProgramOp::SetLine => {
4978                    let value = self.reader.read_varint()?;
4979                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
4980                        frame.line = value;
4981                    }
4982                    continue;
4983                }
4984                LineProgramOp::SetColumn => {
4985                    let value = self.reader.read_varint()?;
4986                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
4987                        frame.column = value;
4988                    }
4989                    continue;
4990                }
4991                LineProgramOp::SetStackDepth => {
4992                    self.stack_depth = self.reader.read_varint()?;
4993                    continue;
4994                }
4995                LineProgramOp::IncrementLine => {
4996                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
4997                        frame.line += 1;
4998                    }
4999                    continue;
5000                }
5001                LineProgramOp::AddLine => {
5002                    let value = self.reader.read_varint()?;
5003                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5004                        frame.line = frame.line.wrapping_add(value);
5005                    }
5006                    continue;
5007                }
5008                LineProgramOp::SubLine => {
5009                    let value = self.reader.read_varint()?;
5010                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5011                        frame.line = frame.line.wrapping_sub(value);
5012                    }
5013                    continue;
5014                }
5015                LineProgramOp::FinishInstruction => (1, self.stack_depth),
5016                LineProgramOp::FinishMultipleInstructions => {
5017                    let count = self.reader.read_varint()?;
5018                    (count, self.stack_depth)
5019                }
5020                LineProgramOp::FinishInstructionAndIncrementStackDepth => {
5021                    let depth = self.stack_depth;
5022                    self.stack_depth = self.stack_depth.saturating_add(1);
5023                    (1, depth)
5024                }
5025                LineProgramOp::FinishMultipleInstructionsAndIncrementStackDepth => {
5026                    let count = self.reader.read_varint()?;
5027                    let depth = self.stack_depth;
5028                    self.stack_depth = self.stack_depth.saturating_add(1);
5029                    (count, depth)
5030                }
5031                LineProgramOp::FinishInstructionAndDecrementStackDepth => {
5032                    let depth = self.stack_depth;
5033                    self.stack_depth = self.stack_depth.saturating_sub(1);
5034                    (1, depth)
5035                }
5036                LineProgramOp::FinishMultipleInstructionsAndDecrementStackDepth => {
5037                    let count = self.reader.read_varint()?;
5038                    let depth = self.stack_depth;
5039                    self.stack_depth = self.stack_depth.saturating_sub(1);
5040                    (count, depth)
5041                }
5042            };
5043
5044            let range = ProgramCounter(self.program_counter)..ProgramCounter(self.program_counter + count);
5045            self.program_counter += count;
5046
5047            let frames = &self.stack[..core::cmp::min(stack_depth as usize, self.stack.len())];
5048            core::mem::forget(mark_as_finished_on_drop);
5049
5050            let entry_index = self.region_counter;
5051            self.region_counter += 1;
5052            return Ok(Some(RegionInfo {
5053                entry_index,
5054                blob: self.blob,
5055                range,
5056                frames,
5057            }));
5058        }
5059
5060        Err(ProgramParseError(ProgramParseErrorKind::Other(
5061            "found a line program with too many instructions",
5062        )))
5063    }
5064}
5065
5066struct DisplayName<'a> {
5067    prefix: &'a str,
5068    suffix: &'a str,
5069}
5070
5071impl<'a> core::fmt::Display for DisplayName<'a> {
5072    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
5073        fmt.write_str(self.prefix)?;
5074        if !self.prefix.is_empty() {
5075            fmt.write_str("::")?;
5076        }
5077        fmt.write_str(self.suffix)
5078    }
5079}
5080
5081/// A binary search implementation which can work on chunks of items, and guarantees that it
5082/// will always return the first item if there are multiple identical consecutive items.
5083fn binary_search(slice: &[u8], chunk_size: usize, compare: impl Fn(&[u8]) -> core::cmp::Ordering) -> Result<usize, usize> {
5084    let mut size = slice.len() / chunk_size;
5085    if size == 0 {
5086        return Err(0);
5087    }
5088
5089    let mut base = 0_usize;
5090    while size > 1 {
5091        let half = size / 2;
5092        let mid = base + half;
5093        let item = &slice[mid * chunk_size..(mid + 1) * chunk_size];
5094        match compare(item) {
5095            core::cmp::Ordering::Greater => {
5096                // The value we're looking for is to the left of the midpoint.
5097                size -= half;
5098            }
5099            core::cmp::Ordering::Less => {
5100                // The value we're looking for is to the right of the midpoint.
5101                size -= half;
5102                base = mid;
5103            }
5104            core::cmp::Ordering::Equal => {
5105                // We've found the value, but it might not be the first value.
5106                let previous_item = &slice[(mid - 1) * chunk_size..mid * chunk_size];
5107                if compare(previous_item) != core::cmp::Ordering::Equal {
5108                    // It is the first value.
5109                    return Ok(mid * chunk_size);
5110                }
5111
5112                // It's not the first value. Let's continue.
5113                //
5114                // We could do a linear search here which in the average case
5115                // would probably be faster, but keeping it as a binary search
5116                // will avoid a worst-case O(n) scenario.
5117                size -= half;
5118            }
5119        }
5120    }
5121
5122    let item = &slice[base * chunk_size..(base + 1) * chunk_size];
5123    let ord = compare(item);
5124    if ord == core::cmp::Ordering::Equal {
5125        Ok(base * chunk_size)
5126    } else {
5127        Err((base + usize::from(ord == core::cmp::Ordering::Less)) * chunk_size)
5128    }
5129}
5130
5131#[cfg(test)]
5132extern crate std;
5133
5134#[cfg(test)]
5135proptest::proptest! {
5136    #![proptest_config(proptest::prelude::ProptestConfig::with_cases(20000))]
5137    #[allow(clippy::ignored_unit_patterns)]
5138    #[test]
5139    fn test_binary_search(needle: u8, mut xs: std::vec::Vec<u8>) {
5140        xs.sort();
5141        let binary_result = binary_search(&xs, 1, |slice| slice[0].cmp(&needle));
5142        let mut linear_result = Err(0);
5143        for (index, value) in xs.iter().copied().enumerate() {
5144            #[allow(clippy::comparison_chain)]
5145            if value == needle {
5146                linear_result = Ok(index);
5147                break;
5148            } else if value < needle {
5149                linear_result = Err(index + 1);
5150                continue;
5151            } else {
5152                break;
5153            }
5154        }
5155
5156        assert_eq!(binary_result, linear_result, "linear search = {:?}, binary search = {:?}, needle = {}, xs = {:?}", linear_result, binary_result, needle, xs);
5157    }
5158}
5159
5160/// The magic bytes with which every program blob must start with.
5161pub const BLOB_MAGIC: [u8; 4] = [b'P', b'V', b'M', b'\0'];
5162
5163/// The blob length is the length of the blob itself encoded as an 64bit LE integer.
5164/// By embedding this metadata into the header, program blobs stay opaque,
5165/// however this information can still easily be retrieved.
5166/// Found at offset 5 after the magic bytes and version number.
5167pub type BlobLen = u64;
5168pub const BLOB_LEN_SIZE: usize = core::mem::size_of::<BlobLen>();
5169pub const BLOB_LEN_OFFSET: usize = BLOB_MAGIC.len() + 1;
5170
5171pub const SECTION_MEMORY_CONFIG: u8 = 1;
5172pub const SECTION_RO_DATA: u8 = 2;
5173pub const SECTION_RW_DATA: u8 = 3;
5174pub const SECTION_IMPORTS: u8 = 4;
5175pub const SECTION_EXPORTS: u8 = 5;
5176pub const SECTION_CODE_AND_JUMP_TABLE: u8 = 6;
5177pub const SECTION_OPT_DEBUG_STRINGS: u8 = 128;
5178pub const SECTION_OPT_DEBUG_LINE_PROGRAMS: u8 = 129;
5179pub const SECTION_OPT_DEBUG_LINE_PROGRAM_RANGES: u8 = 130;
5180pub const SECTION_END_OF_FILE: u8 = 0;
5181
5182pub const BLOB_VERSION_V1_64: u8 = 0;
5183pub const BLOB_VERSION_V1_32: u8 = 1;
5184
5185pub const VERSION_DEBUG_LINE_PROGRAM_V1: u8 = 1;
5186
5187#[derive(Copy, Clone, Debug)]
5188pub enum LineProgramOp {
5189    FinishProgram = 0,
5190    SetMutationDepth = 1,
5191    SetKindEnter = 2,
5192    SetKindCall = 3,
5193    SetKindLine = 4,
5194    SetNamespace = 5,
5195    SetFunctionName = 6,
5196    SetPath = 7,
5197    SetLine = 8,
5198    SetColumn = 9,
5199    SetStackDepth = 10,
5200    IncrementLine = 11,
5201    AddLine = 12,
5202    SubLine = 13,
5203    FinishInstruction = 14,
5204    FinishMultipleInstructions = 15,
5205    FinishInstructionAndIncrementStackDepth = 16,
5206    FinishMultipleInstructionsAndIncrementStackDepth = 17,
5207    FinishInstructionAndDecrementStackDepth = 18,
5208    FinishMultipleInstructionsAndDecrementStackDepth = 19,
5209}
5210
5211impl LineProgramOp {
5212    #[inline]
5213    pub const fn from_u8(value: u8) -> Option<Self> {
5214        match value {
5215            0 => Some(Self::FinishProgram),
5216            1 => Some(Self::SetMutationDepth),
5217            2 => Some(Self::SetKindEnter),
5218            3 => Some(Self::SetKindCall),
5219            4 => Some(Self::SetKindLine),
5220            5 => Some(Self::SetNamespace),
5221            6 => Some(Self::SetFunctionName),
5222            7 => Some(Self::SetPath),
5223            8 => Some(Self::SetLine),
5224            9 => Some(Self::SetColumn),
5225            10 => Some(Self::SetStackDepth),
5226            11 => Some(Self::IncrementLine),
5227            12 => Some(Self::AddLine),
5228            13 => Some(Self::SubLine),
5229            14 => Some(Self::FinishInstruction),
5230            15 => Some(Self::FinishMultipleInstructions),
5231            16 => Some(Self::FinishInstructionAndIncrementStackDepth),
5232            17 => Some(Self::FinishMultipleInstructionsAndIncrementStackDepth),
5233            18 => Some(Self::FinishInstructionAndDecrementStackDepth),
5234            19 => Some(Self::FinishMultipleInstructionsAndDecrementStackDepth),
5235            _ => None,
5236        }
5237    }
5238}