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