polkavm_common/
program.rs

1use crate::utils::CowBytes;
2use crate::varint::{read_varint, write_varint, MAX_VARINT_LENGTH};
3use core::ops::Range;
4
5#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
6#[repr(u8)]
7pub enum Reg {
8    RA = 0,
9    SP = 1,
10    T0 = 2,
11    T1 = 3,
12    T2 = 4,
13    S0 = 5,
14    S1 = 6,
15    A0 = 7,
16    A1 = 8,
17    A2 = 9,
18    A3 = 10,
19    A4 = 11,
20    A5 = 12,
21}
22
23impl Reg {
24    #[inline]
25    pub const fn from_u8(value: u8) -> Option<Reg> {
26        match value {
27            0 => Some(Reg::RA),
28            1 => Some(Reg::SP),
29            2 => Some(Reg::T0),
30            3 => Some(Reg::T1),
31            4 => Some(Reg::T2),
32            5 => Some(Reg::S0),
33            6 => Some(Reg::S1),
34            7 => Some(Reg::A0),
35            8 => Some(Reg::A1),
36            9 => Some(Reg::A2),
37            10 => Some(Reg::A3),
38            11 => Some(Reg::A4),
39            12 => Some(Reg::A5),
40            _ => None,
41        }
42    }
43
44    pub const fn name(self) -> &'static str {
45        use Reg::*;
46        match self {
47            RA => "ra",
48            SP => "sp",
49            T0 => "t0",
50            T1 => "t1",
51            T2 => "t2",
52            S0 => "s0",
53            S1 => "s1",
54            A0 => "a0",
55            A1 => "a1",
56            A2 => "a2",
57            A3 => "a3",
58            A4 => "a4",
59            A5 => "a5",
60        }
61    }
62
63    /// List of all of the VM's registers.
64    pub const ALL: [Reg; 13] = {
65        use Reg::*;
66        [RA, SP, T0, T1, T2, S0, S1, A0, A1, A2, A3, A4, A5]
67    };
68
69    /// List of all input/output argument registers.
70    pub const ARG_REGS: [Reg; 9] = [Reg::A0, Reg::A1, Reg::A2, Reg::A3, Reg::A4, Reg::A5, Reg::T0, Reg::T1, Reg::T2];
71
72    pub const MAXIMUM_INPUT_REGS: usize = 9;
73    pub const MAXIMUM_OUTPUT_REGS: usize = 2;
74}
75
76impl core::fmt::Display for Reg {
77    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
78        fmt.write_str(self.name())
79    }
80}
81
82#[allow(clippy::partial_pub_fields)]
83#[doc(hidden)]
84pub struct VisitorHelper<'a, T> {
85    pub visitor: T,
86    reader: Reader<'a>,
87}
88
89impl<'a, T> VisitorHelper<'a, T> {
90    #[inline]
91    pub fn run<E>(
92        blob: &'a ProgramBlob<'a>,
93        visitor: T,
94        decode_table: &[fn(&mut Self) -> <T as InstructionVisitor>::ReturnTy; 256],
95    ) -> (T, <T as InstructionVisitor>::ReturnTy)
96    where
97        T: ParsingVisitor<E>,
98    {
99        let mut state = VisitorHelper {
100            visitor,
101            reader: blob.get_section_reader(blob.code.clone()),
102        };
103
104        let mut result = Ok(());
105        loop {
106            let Ok(opcode) = state.reader.read_byte() else { break };
107            result = state.visitor.on_pre_visit(state.reader.position - 1 - blob.code.start, opcode);
108            if result.is_err() {
109                break;
110            }
111
112            result = decode_table[opcode as usize](&mut state);
113            if result.is_err() {
114                break;
115            }
116
117            result = state.visitor.on_post_visit();
118            if result.is_err() {
119                break;
120            }
121        }
122
123        (state.visitor, result)
124    }
125
126    #[cold]
127    pub fn unknown_opcode<U, E>(&mut self) -> <T as InstructionVisitor>::ReturnTy
128    where
129        T: InstructionVisitor<ReturnTy = Result<U, E>>,
130        E: From<ProgramParseError>,
131    {
132        let error = ProgramParseError::unexpected_instruction(self.reader.position - 1);
133        Err(error.into())
134    }
135
136    #[inline(always)]
137    pub fn read_args_imm(&mut self) -> Result<u32, ProgramParseError> {
138        self.reader.read_varint()
139    }
140
141    #[inline(always)]
142    pub fn read_args_imm2(&mut self) -> Result<(u32, u32), ProgramParseError> {
143        let imm1 = self.reader.read_varint()?;
144        let imm2 = self.reader.read_varint()?;
145        Ok((imm1, imm2))
146    }
147
148    #[inline(always)]
149    pub fn read_args_reg_imm(&mut self) -> Result<(Reg, u32), ProgramParseError> {
150        let reg = self.reader.read_reg()?;
151        let imm = self.reader.read_varint()?;
152        Ok((reg, imm))
153    }
154
155    #[inline(always)]
156    pub fn read_args_reg_imm2(&mut self) -> Result<(Reg, u32, u32), ProgramParseError> {
157        let reg = self.reader.read_reg()?;
158        let imm1 = self.reader.read_varint()?;
159        let imm2 = self.reader.read_varint()?;
160        Ok((reg, imm1, imm2))
161    }
162
163    #[inline(always)]
164    pub fn read_args_regs2_imm(&mut self) -> Result<(Reg, Reg, u32), ProgramParseError> {
165        let (reg1, reg2) = self.reader.read_regs2()?;
166        let imm = self.reader.read_varint()?;
167        Ok((reg1, reg2, imm))
168    }
169
170    #[inline(always)]
171    pub fn read_args_regs3(&mut self) -> Result<(Reg, Reg, Reg), ProgramParseError> {
172        self.reader.read_regs3()
173    }
174
175    #[inline(always)]
176    pub fn read_args_regs2(&mut self) -> Result<(Reg, Reg), ProgramParseError> {
177        self.reader.read_regs2()
178    }
179}
180
181macro_rules! define_opcodes {
182    (@impl_shared $($name:ident = $value:expr,)+) => {
183        #[allow(non_camel_case_types)]
184        #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
185        #[repr(u8)]
186        pub enum Opcode {
187            $(
188                $name = $value,
189            )+
190        }
191
192        impl Opcode {
193            #[cfg_attr(feature = "alloc", inline)]
194            pub fn from_u8(byte: u8) -> Option<Opcode> {
195                if !IS_INSTRUCTION_VALID[byte as usize] {
196                    return None;
197                }
198
199                #[allow(unsafe_code)]
200                // SAFETY: We already checked that this opcode is valid, so this is safe.
201                unsafe {
202                    Some(core::mem::transmute(byte))
203                }
204            }
205        }
206
207        #[test]
208        fn test_opcode_from_u8() {
209            fn from_u8_naive(byte: u8) -> Option<Opcode> {
210                match byte {
211                    $($value => Some(Opcode::$name),)+
212                    _ => None
213                }
214            }
215
216            for byte in 0..=255 {
217                assert_eq!(from_u8_naive(byte), Opcode::from_u8(byte));
218            }
219        }
220
221        const IS_INSTRUCTION_VALID_CONST: [bool; 256] = {
222            let mut is_valid = [false; 256];
223            $(
224                is_valid[$value] = true;
225            )+
226            is_valid
227        };
228
229        #[cfg(feature = "alloc")]
230        static IS_INSTRUCTION_VALID: [bool; 256] = IS_INSTRUCTION_VALID_CONST;
231
232        #[cfg(not(feature = "alloc"))]
233        use IS_INSTRUCTION_VALID_CONST as IS_INSTRUCTION_VALID;
234    };
235
236    (
237        $d:tt
238
239        [$($name_argless:ident = $value_argless:expr,)+]
240        [$($name_reg_imm:ident = $value_reg_imm:expr,)+]
241        [$($name_reg_imm_imm:ident = $value_reg_imm_imm:expr,)+]
242        [$($name_reg_reg_imm:ident = $value_reg_reg_imm:expr,)+]
243        [$($name_reg_reg_reg:ident = $value_reg_reg_reg:expr,)+]
244        [$($name_imm:ident = $value_imm:expr,)+]
245        [$($name_imm_imm:ident = $value_imm_imm:expr,)+]
246        [$($name_reg_reg:ident = $value_reg_reg:expr,)+]
247    ) => {
248        pub trait ParsingVisitor<E>: InstructionVisitor<ReturnTy = Result<(), E>> {
249            fn on_pre_visit(&mut self, _offset: usize, _opcode: u8) -> Self::ReturnTy {
250                Ok(())
251            }
252
253            fn on_post_visit(&mut self) -> Self::ReturnTy {
254                Ok(())
255            }
256        }
257
258        pub trait InstructionVisitor {
259            type ReturnTy;
260
261            $(fn $name_argless(&mut self) -> Self::ReturnTy;)+
262            $(fn $name_reg_imm(&mut self, reg: Reg, imm: u32) -> Self::ReturnTy;)+
263            $(fn $name_reg_imm_imm(&mut self, reg: Reg, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
264            $(fn $name_reg_reg_imm(&mut self, reg1: Reg, reg2: Reg, imm: u32) -> Self::ReturnTy;)+
265            $(fn $name_reg_reg_reg(&mut self, reg1: Reg, reg2: Reg, reg3: Reg) -> Self::ReturnTy;)+
266            $(fn $name_imm(&mut self, imm: u32) -> Self::ReturnTy;)+
267            $(fn $name_imm_imm(&mut self, imm1: u32, imm2: u32) -> Self::ReturnTy;)+
268            $(fn $name_reg_reg(&mut self, reg1: Reg, reg2: Reg) -> Self::ReturnTy;)+
269
270        }
271
272        #[macro_export]
273        macro_rules! implement_instruction_visitor {
274            (impl<$d($visitor_ty_params:tt),*> $visitor_ty:ty, $method:ident) => {
275                impl<$d($visitor_ty_params),*> polkavm_common::program::InstructionVisitor for $visitor_ty {
276                    type ReturnTy = ();
277
278                    $(fn $name_argless(&mut self) -> Self::ReturnTy {
279                        self.$method(polkavm_common::program::Instruction::$name_argless);
280                    })+
281                    $(fn $name_reg_imm(&mut self, reg: Reg, imm: u32) -> Self::ReturnTy {
282                        self.$method(polkavm_common::program::Instruction::$name_reg_imm(reg, imm));
283                    })+
284                    $(fn $name_reg_imm_imm(&mut self, reg: Reg, imm1: u32, imm2: u32) -> Self::ReturnTy {
285                        self.$method(polkavm_common::program::Instruction::$name_reg_imm_imm(reg, imm1, imm2));
286                    })+
287                    $(fn $name_reg_reg_imm(&mut self, reg1: Reg, reg2: Reg, imm: u32) -> Self::ReturnTy {
288                        self.$method(polkavm_common::program::Instruction::$name_reg_reg_imm(reg1, reg2, imm));
289                    })+
290                    $(fn $name_reg_reg_reg(&mut self, reg1: Reg, reg2: Reg, reg3: Reg) -> Self::ReturnTy {
291                        self.$method(polkavm_common::program::Instruction::$name_reg_reg_reg(reg1, reg2, reg3));
292                    })+
293                    $(fn $name_imm(&mut self, imm: u32) -> Self::ReturnTy {
294                        self.$method(polkavm_common::program::Instruction::$name_imm(imm));
295                    })+
296                    $(fn $name_imm_imm(&mut self, imm1: u32, imm2: u32) -> Self::ReturnTy {
297                        self.$method(polkavm_common::program::Instruction::$name_imm_imm(imm1, imm2));
298                    })+
299                    $(fn $name_reg_reg(&mut self, reg1: Reg, reg2: Reg) -> Self::ReturnTy {
300                        self.$method(polkavm_common::program::Instruction::$name_reg_reg(reg1, reg2));
301                    })+
302                }
303            }
304        }
305
306        pub use implement_instruction_visitor;
307
308        #[derive(Copy, Clone, PartialEq, Eq, Debug)]
309        #[allow(non_camel_case_types)]
310        pub enum Instruction {
311            $($name_argless,)+
312            $($name_reg_imm(Reg, u32),)+
313            $($name_reg_imm_imm(Reg, u32, u32),)+
314            $($name_reg_reg_imm(Reg, Reg, u32),)+
315            $($name_reg_reg_reg(Reg, Reg, Reg),)+
316            $($name_imm(u32),)+
317            $($name_imm_imm(u32, u32),)+
318            $($name_reg_reg(Reg, Reg),)+
319        }
320
321        impl Instruction {
322            pub fn visit<T>(self, visitor: &mut T) -> T::ReturnTy where T: InstructionVisitor {
323                match self {
324                    $(Self::$name_argless => visitor.$name_argless(),)+
325                    $(Self::$name_reg_imm(reg, imm) => visitor.$name_reg_imm(reg, imm),)+
326                    $(Self::$name_reg_imm_imm(reg, imm1, imm2) => visitor.$name_reg_imm_imm(reg, imm1, imm2),)+
327                    $(Self::$name_reg_reg_imm(reg1, reg2, imm) => visitor.$name_reg_reg_imm(reg1, reg2, imm),)+
328                    $(Self::$name_reg_reg_reg(reg1, reg2, reg3) => visitor.$name_reg_reg_reg(reg1, reg2, reg3),)+
329                    $(Self::$name_imm(imm) => visitor.$name_imm(imm),)+
330                    $(Self::$name_imm_imm(imm1, imm2) => visitor.$name_imm_imm(imm1, imm2),)+
331                    $(Self::$name_reg_reg(reg1, reg2) => visitor.$name_reg_reg(reg1, reg2),)+
332                }
333            }
334
335            pub fn serialize_into(self, buffer: &mut [u8]) -> usize {
336                match self {
337                    $(Self::$name_argless => Self::serialize_argless(buffer, Opcode::$name_argless),)+
338                    $(Self::$name_reg_imm(reg, imm) => Self::serialize_reg_imm(buffer, Opcode::$name_reg_imm, reg, imm),)+
339                    $(Self::$name_reg_imm_imm(reg, imm1, imm2) => Self::serialize_reg_imm_imm(buffer, Opcode::$name_reg_imm_imm, reg, imm1, imm2),)+
340                    $(Self::$name_reg_reg_imm(reg1, reg2, imm) => Self::serialize_reg_reg_imm(buffer, Opcode::$name_reg_reg_imm, reg1, reg2, imm),)+
341                    $(Self::$name_reg_reg_reg(reg1, reg2, reg3) => Self::serialize_reg_reg_reg(buffer, Opcode::$name_reg_reg_reg, reg1, reg2, reg3),)+
342                    $(Self::$name_imm(imm) => Self::serialize_imm(buffer, Opcode::$name_imm, imm),)+
343                    $(Self::$name_imm_imm(imm1, imm2) => Self::serialize_imm_imm(buffer, Opcode::$name_imm_imm, imm1, imm2),)+
344                    $(Self::$name_reg_reg(reg1, reg2) => Self::serialize_reg_reg(buffer, Opcode::$name_reg_reg, reg1, reg2),)+
345
346                }
347            }
348
349            pub fn opcode(self) -> Opcode {
350                match self {
351                    $(Self::$name_argless => Opcode::$name_argless,)+
352                    $(Self::$name_reg_imm(..) => Opcode::$name_reg_imm,)+
353                    $(Self::$name_reg_imm_imm(..) => Opcode::$name_reg_imm_imm,)+
354                    $(Self::$name_reg_reg_imm(..) => Opcode::$name_reg_reg_imm,)+
355                    $(Self::$name_reg_reg_reg(..) => Opcode::$name_reg_reg_reg,)+
356                    $(Self::$name_imm(..) => Opcode::$name_imm,)+
357                    $(Self::$name_imm_imm(..) => Opcode::$name_imm_imm,)+
358                    $(Self::$name_reg_reg(..) => Opcode::$name_reg_reg,)+
359                }
360            }
361        }
362
363        impl core::fmt::Display for Instruction {
364            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
365                self.visit(fmt)
366            }
367        }
368
369        fn parse_instruction_impl(opcode: u8, reader: &mut Reader) -> Result<Instruction, ProgramParseError> {
370            Ok(match opcode {
371                $($value_argless => Instruction::$name_argless,)+
372                $($value_reg_imm => {
373                    let reg = reader.read_reg()?;
374                    let imm = reader.read_varint()?;
375                    Instruction::$name_reg_imm(reg, imm)
376                },)+
377                $($value_reg_imm_imm => {
378                    let reg = reader.read_reg()?;
379                    let imm1 = reader.read_varint()?;
380                    let imm2 = reader.read_varint()?;
381                    Instruction::$name_reg_imm_imm(reg, imm1, imm2)
382                },)+
383                $($value_reg_reg_imm => {
384                    let (reg1, reg2) = reader.read_regs2()?;
385                    let imm = reader.read_varint()?;
386                    Instruction::$name_reg_reg_imm(reg1, reg2, imm)
387                },)+
388                $($value_reg_reg_reg => {
389                    let (reg1, reg2) = reader.read_regs2()?;
390                    let reg3 = reader.read_reg()?;
391                    Instruction::$name_reg_reg_reg(reg1, reg2, reg3)
392                },)+
393                $($value_imm => {
394                    Instruction::$name_imm(reader.read_varint()?)
395                },)+
396                $($value_imm_imm => {
397                    let imm1 = reader.read_varint()?;
398                    let imm2 = reader.read_varint()?;
399                    Instruction::$name_imm_imm(imm1, imm2)
400                },)+
401                $($value_reg_reg => {
402                    let (reg1, reg2) = reader.read_regs2()?;
403                    Instruction::$name_reg_reg(reg1, reg2)
404                },)+
405                _ => return Err(ProgramParseError::unexpected_instruction(reader.position - 1))
406            })
407        }
408
409        pub mod asm {
410            use super::{Instruction, Reg};
411
412            $(
413                pub fn $name_argless() -> Instruction {
414                    Instruction::$name_argless
415                }
416            )+
417
418            $(
419                pub fn $name_reg_imm(reg: Reg, imm: u32) -> Instruction {
420                    Instruction::$name_reg_imm(reg, imm)
421                }
422            )+
423
424            $(
425                pub fn $name_reg_imm_imm(reg: Reg, imm1: u32, imm2: u32) -> Instruction {
426                    Instruction::$name_reg_imm_imm(reg, imm1, imm2)
427                }
428            )+
429
430            $(
431                pub fn $name_reg_reg_imm(reg1: Reg, reg2: Reg, imm: u32) -> Instruction {
432                    Instruction::$name_reg_reg_imm(reg1, reg2, imm)
433                }
434            )+
435
436            $(
437                pub fn $name_reg_reg_reg(reg1: Reg, reg2: Reg, reg3: Reg) -> Instruction {
438                    Instruction::$name_reg_reg_reg(reg1, reg2, reg3)
439                }
440            )+
441
442            $(
443                pub fn $name_imm(imm: u32) -> Instruction {
444                    Instruction::$name_imm(imm)
445                }
446            )+
447
448            $(
449                pub fn $name_imm_imm(imm1: u32, imm2: u32) -> Instruction {
450                    Instruction::$name_imm_imm(imm1, imm2)
451                }
452            )+
453
454            $(
455                pub fn $name_reg_reg(reg1: Reg, reg2: Reg) -> Instruction {
456                    Instruction::$name_reg_reg(reg1, reg2)
457                }
458            )+
459
460            pub fn ret() -> Instruction {
461                jump_indirect(Reg::RA, 0)
462            }
463        }
464
465        #[macro_export]
466        macro_rules! prepare_visitor {
467            ($table_name:ident, $visitor_ty:ident<$d($visitor_ty_params:tt),*>) => {{
468                use polkavm_common::program::{
469                    InstructionVisitor,
470                    VisitorHelper,
471                };
472
473                type ReturnTy<$d($visitor_ty_params),*> = <$visitor_ty<$d($visitor_ty_params),*> as InstructionVisitor>::ReturnTy;
474                type VisitFn<'_code, $d($visitor_ty_params),*> = fn(state: &mut VisitorHelper<'_code, $visitor_ty<$d($visitor_ty_params),*>>) -> ReturnTy<$d($visitor_ty_params),*>;
475
476                static $table_name: [VisitFn; 256] = {
477                    let mut table = [VisitorHelper::unknown_opcode as VisitFn; 256];
478                    $({
479                        // Putting all of the handlers in a single link section can make a big difference
480                        // when it comes to performance, even up to 10% in some cases. This will force the
481                        // compiler and the linker to put all of this code near each other, minimizing
482                        // instruction cache misses.
483                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
484                        fn $name_argless<'_code, $d($visitor_ty_params),*>(state: &mut VisitorHelper<'_code, $visitor_ty<$d($visitor_ty_params),*>>) -> ReturnTy<$d($visitor_ty_params),*>{
485                            state.visitor.$name_argless()
486                        }
487
488                        table[$value_argless] = $name_argless;
489                    })*
490
491                    $({
492                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
493                        fn $name_reg_imm<'_code, $d($visitor_ty_params),*>(state: &mut VisitorHelper<'_code, $visitor_ty<$d($visitor_ty_params),*>>) -> ReturnTy<$d($visitor_ty_params),*>{
494                            let (reg, imm) = state.read_args_reg_imm()?;
495                            state.visitor.$name_reg_imm(reg, imm)
496                        }
497
498                        table[$value_reg_imm] = $name_reg_imm;
499                    })*
500
501                    $({
502                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
503                        fn $name_reg_imm_imm<'_code, $d($visitor_ty_params),*>(state: &mut VisitorHelper<'_code, $visitor_ty<$d($visitor_ty_params),*>>) -> ReturnTy<$d($visitor_ty_params),*>{
504                            let (reg, imm1, imm2) = state.read_args_reg_imm2()?;
505                            state.visitor.$name_reg_imm_imm(reg, imm1, imm2)
506                        }
507
508                        table[$value_reg_imm_imm] = $name_reg_imm_imm;
509                    })*
510
511                    $({
512                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
513                        fn $name_reg_reg_imm<'_code, $d($visitor_ty_params),*>(state: &mut VisitorHelper<'_code, $visitor_ty<$d($visitor_ty_params),*>>) -> ReturnTy<$d($visitor_ty_params),*>{
514                            let (reg1, reg2, imm) = state.read_args_regs2_imm()?;
515                            state.visitor.$name_reg_reg_imm(reg1, reg2, imm)
516                        }
517
518                        table[$value_reg_reg_imm] = $name_reg_reg_imm;
519                    })*
520
521                    $({
522                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
523                        fn $name_reg_reg_reg<'_code, $d($visitor_ty_params),*>(state: &mut VisitorHelper<'_code, $visitor_ty<$d($visitor_ty_params),*>>) -> ReturnTy<$d($visitor_ty_params),*>{
524                            let (reg1, reg2, reg3) = state.read_args_regs3()?;
525                            state.visitor.$name_reg_reg_reg(reg1, reg2, reg3)
526                        }
527
528                        table[$value_reg_reg_reg] = $name_reg_reg_reg;
529                    })*
530
531                    $({
532                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
533                        fn $name_imm<'_code, $d($visitor_ty_params),*>(state: &mut VisitorHelper<'_code, $visitor_ty<$d($visitor_ty_params),*>>) -> ReturnTy<$d($visitor_ty_params),*>{
534                            let imm = state.read_args_imm()?;
535                            state.visitor.$name_imm(imm)
536                        }
537
538                        table[$value_imm] = $name_imm;
539                    })*
540
541                    $({
542                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
543                        fn $name_imm_imm<'_code, $d($visitor_ty_params),*>(state: &mut VisitorHelper<'_code, $visitor_ty<$d($visitor_ty_params),*>>) -> ReturnTy<$d($visitor_ty_params),*>{
544                            let (imm1, imm2) = state.read_args_imm2()?;
545                            state.visitor.$name_imm_imm(imm1, imm2)
546                        }
547
548                        table[$value_imm_imm] = $name_imm_imm;
549                    })*
550
551                    $({
552                        #[cfg_attr(target_os = "linux", link_section = concat!(".text.", stringify!($table_name)))]
553                        fn $name_reg_reg<'_code, $d($visitor_ty_params),*>(state: &mut VisitorHelper<'_code, $visitor_ty<$d($visitor_ty_params),*>>) -> ReturnTy<$d($visitor_ty_params),*>{
554                            let (reg1, reg2) = state.read_args_regs2()?;
555                            state.visitor.$name_reg_reg(reg1, reg2)
556                        }
557
558                        table[$value_reg_reg] = $name_reg_reg;
559                    })*
560
561                    table
562                };
563
564                #[inline]
565                fn run<$d($visitor_ty_params),*>(
566                    blob: &ProgramBlob,
567                    visitor: $visitor_ty<$d($visitor_ty_params),*>,
568                )
569                    -> ($visitor_ty<$d($visitor_ty_params),*>, <$visitor_ty<$d($visitor_ty_params),*> as InstructionVisitor>::ReturnTy)
570                {
571                    let decode_table: &[VisitFn; 256] = &$table_name;
572                    // SAFETY: Here we transmute the lifetimes which were unnecessarily extended to be 'static due to the table here being a `static`.
573                    let decode_table: &[VisitFn; 256] = unsafe { core::mem::transmute(decode_table) };
574
575                    VisitorHelper::run(blob, visitor, decode_table)
576                }
577
578                run
579            }};
580        }
581
582        pub use prepare_visitor;
583
584        define_opcodes!(
585            @impl_shared
586            $($name_argless = $value_argless,)+
587            $($name_reg_imm = $value_reg_imm,)+
588            $($name_reg_imm_imm = $value_reg_imm_imm,)+
589            $($name_reg_reg_imm = $value_reg_reg_imm,)+
590            $($name_reg_reg_reg = $value_reg_reg_reg,)+
591            $($name_imm = $value_imm,)+
592            $($name_imm_imm = $value_imm_imm,)+
593            $($name_reg_reg = $value_reg_reg,)+
594        );
595    }
596}
597
598// NOTE: The opcodes here are assigned roughly in the order of how common a given instruction is,
599// except the `trap` which is deliberately hardcoded as zero.
600define_opcodes! {
601    $
602
603    // Instructions with args: none
604    [
605        trap                                     = 0,
606        fallthrough                              = 17,
607    ]
608
609    // Instructions with args: reg, imm
610    [
611        call                                     = 6,
612        jump_indirect                            = 19,
613        load_imm                                 = 4,
614        load_u8                                  = 60,
615        load_i8                                  = 74,
616        load_u16                                 = 76,
617        load_i16                                 = 66,
618        load_u32                                 = 10,
619        store_u8                                 = 71,
620        store_u16                                = 69,
621        store_u32                                = 22,
622    ]
623
624    // Instructions with args: reg, imm, imm
625    [
626        branch_eq_imm                            = 7,
627        branch_not_eq_imm                        = 15,
628        branch_less_unsigned_imm                 = 44,
629        branch_less_signed_imm                   = 32,
630        branch_greater_or_equal_unsigned_imm     = 52,
631        branch_greater_or_equal_signed_imm       = 45,
632        branch_less_or_equal_signed_imm          = 46,
633        branch_less_or_equal_unsigned_imm        = 59,
634        branch_greater_signed_imm                = 53,
635        branch_greater_unsigned_imm              = 50,
636        store_imm_indirect_u8                    = 26,
637        store_imm_indirect_u16                   = 54,
638        store_imm_indirect_u32                   = 13,
639    ]
640
641    // Instructions with args: reg, reg, imm
642    [
643        store_indirect_u8                        = 16,
644        store_indirect_u16                       = 29,
645        store_indirect_u32                       = 3,
646        load_indirect_u8                         = 11,
647        load_indirect_i8                         = 21,
648        load_indirect_u16                        = 37,
649        load_indirect_i16                        = 33,
650        load_indirect_u32                        = 1,
651        call_indirect                            = 42,
652        add_imm                                  = 2,
653        and_imm                                  = 18,
654        xor_imm                                  = 31,
655        or_imm                                   = 49,
656        mul_imm                                  = 35,
657        mul_upper_signed_signed_imm              = 65,
658        mul_upper_unsigned_unsigned_imm          = 63,
659        set_less_than_unsigned_imm               = 27,
660        set_less_than_signed_imm                 = 56,
661        shift_logical_left_imm                   = 9,
662        shift_logical_right_imm                  = 14,
663        shift_arithmetic_right_imm               = 25,
664        negate_and_add_imm                       = 40,
665        set_greater_than_unsigned_imm            = 39,
666        set_greater_than_signed_imm              = 61,
667        shift_logical_right_imm_alt              = 72,
668        shift_arithmetic_right_imm_alt           = 80,
669        shift_logical_left_imm_alt               = 75,
670        branch_eq                                = 24,
671        branch_not_eq                            = 30,
672        branch_less_unsigned                     = 47,
673        branch_less_signed                       = 48,
674        branch_greater_or_equal_unsigned         = 41,
675        branch_greater_or_equal_signed           = 43,
676
677        cmov_if_zero_imm                         = 85,
678        cmov_if_not_zero_imm                     = 86,
679    ]
680
681    // Instructions with args: reg, reg, reg
682    [
683        add                                      = 8,
684        sub                                      = 20,
685        and                                      = 23,
686        xor                                      = 28,
687        or                                       = 12,
688        mul                                      = 34,
689        mul_upper_signed_signed                  = 67,
690        mul_upper_unsigned_unsigned              = 57,
691        mul_upper_signed_unsigned                = 81,
692        set_less_than_unsigned                   = 36,
693        set_less_than_signed                     = 58,
694        shift_logical_left                       = 55,
695        shift_logical_right                      = 51,
696        shift_arithmetic_right                   = 77,
697        div_unsigned                             = 68,
698        div_signed                               = 64,
699        rem_unsigned                             = 73,
700        rem_signed                               = 70,
701
702        cmov_if_zero                             = 83,
703        cmov_if_not_zero                         = 84,
704    ]
705
706    // Instructions with args: imm
707    [
708        jump                                     = 5,
709        ecalli                                   = 78,
710    ]
711
712    // Instructions with args: imm, imm
713    [
714        store_imm_u8                             = 62,
715        store_imm_u16                            = 79,
716        store_imm_u32                            = 38,
717    ]
718
719    // Instructions with args: reg, reg
720    [
721        move_reg                                 = 82,
722        sbrk                                     = 87,
723    ]
724}
725
726impl Opcode {
727    pub fn starts_new_basic_block(self) -> bool {
728        matches!(
729            self,
730            Self::trap
731                | Self::fallthrough
732                | Self::jump
733                | Self::jump_indirect
734                | Self::call
735                | Self::call_indirect
736                | Self::branch_eq
737                | Self::branch_eq_imm
738                | Self::branch_greater_or_equal_signed
739                | Self::branch_greater_or_equal_signed_imm
740                | Self::branch_greater_or_equal_unsigned
741                | Self::branch_greater_or_equal_unsigned_imm
742                | Self::branch_greater_signed_imm
743                | Self::branch_greater_unsigned_imm
744                | Self::branch_less_or_equal_signed_imm
745                | Self::branch_less_or_equal_unsigned_imm
746                | Self::branch_less_signed
747                | Self::branch_less_signed_imm
748                | Self::branch_less_unsigned
749                | Self::branch_less_unsigned_imm
750                | Self::branch_not_eq
751                | Self::branch_not_eq_imm
752        )
753    }
754}
755
756impl Instruction {
757    pub fn deserialize(input: &[u8]) -> Option<(usize, Self)> {
758        let mut reader = Reader { blob: input, position: 0 };
759
760        let opcode = reader.read_byte().ok()?;
761        let instruction = parse_instruction_impl(opcode, &mut reader).ok()?;
762        Some((reader.position, instruction))
763    }
764
765    fn serialize_argless(buffer: &mut [u8], opcode: Opcode) -> usize {
766        buffer[0] = opcode as u8;
767        1
768    }
769
770    fn serialize_reg_imm_imm(buffer: &mut [u8], opcode: Opcode, reg: Reg, imm1: u32, imm2: u32) -> usize {
771        buffer[0] = opcode as u8;
772        buffer[1] = reg as u8;
773        let mut position = 2;
774        position += write_varint(imm1, &mut buffer[position..]);
775        position += write_varint(imm2, &mut buffer[position..]);
776        position
777    }
778
779    fn serialize_reg_reg_reg(buffer: &mut [u8], opcode: Opcode, reg1: Reg, reg2: Reg, reg3: Reg) -> usize {
780        buffer[0] = opcode as u8;
781        buffer[1] = reg1 as u8 | (reg2 as u8) << 4;
782        buffer[2] = reg3 as u8;
783        3
784    }
785
786    fn serialize_reg_reg_imm(buffer: &mut [u8], opcode: Opcode, reg1: Reg, reg2: Reg, imm: u32) -> usize {
787        buffer[0] = opcode as u8;
788        buffer[1] = reg1 as u8 | (reg2 as u8) << 4;
789        write_varint(imm, &mut buffer[2..]) + 2
790    }
791
792    fn serialize_reg_imm(buffer: &mut [u8], opcode: Opcode, reg: Reg, imm: u32) -> usize {
793        buffer[0] = opcode as u8;
794        buffer[1] = reg as u8;
795        write_varint(imm, &mut buffer[2..]) + 2
796    }
797
798    fn serialize_imm(buffer: &mut [u8], opcode: Opcode, imm: u32) -> usize {
799        buffer[0] = opcode as u8;
800        write_varint(imm, &mut buffer[1..]) + 1
801    }
802
803    fn serialize_imm_imm(buffer: &mut [u8], opcode: Opcode, imm1: u32, imm2: u32) -> usize {
804        buffer[0] = opcode as u8;
805        let mut position = 1;
806        position += write_varint(imm1, &mut buffer[position..]);
807        position += write_varint(imm2, &mut buffer[position..]);
808        position
809    }
810
811    fn serialize_reg_reg(buffer: &mut [u8], opcode: Opcode, reg1: Reg, reg2: Reg) -> usize {
812        buffer[0] = opcode as u8;
813        buffer[1] = reg1 as u8 | (reg2 as u8) << 4;
814        2
815    }
816}
817
818pub const MAX_INSTRUCTION_LENGTH: usize = 2 + MAX_VARINT_LENGTH * 2;
819
820impl<'a> InstructionVisitor for core::fmt::Formatter<'a> {
821    type ReturnTy = core::fmt::Result;
822
823    fn trap(&mut self) -> Self::ReturnTy {
824        write!(self, "trap")
825    }
826
827    fn fallthrough(&mut self) -> Self::ReturnTy {
828        write!(self, "@:")
829    }
830
831    fn sbrk(&mut self, d: Reg, s: Reg) -> Self::ReturnTy {
832        write!(self, "{d} = sbrk {s}")
833    }
834
835    fn ecalli(&mut self, nth_import: u32) -> Self::ReturnTy {
836        write!(self, "ecalli {nth_import}")
837    }
838
839    fn set_less_than_unsigned(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
840        write!(self, "{d} = {s1} <u {s2}")
841    }
842
843    fn set_less_than_signed(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
844        write!(self, "{d} = {s1} <s {s2}")
845    }
846
847    fn shift_logical_right(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
848        write!(self, "{d} = {s1} >> {s2}")
849    }
850
851    fn shift_arithmetic_right(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
852        write!(self, "{d} = {s1} >>a {s2}")
853    }
854
855    fn shift_logical_left(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
856        write!(self, "{d} = {s1} << {s2}")
857    }
858
859    fn xor(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
860        write!(self, "{d} = {s1} ^ {s2}")
861    }
862
863    fn and(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
864        write!(self, "{d} = {s1} & {s2}")
865    }
866
867    fn or(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
868        write!(self, "{d} = {s1} | {s2}")
869    }
870
871    fn add(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
872        write!(self, "{d} = {s1} + {s2}")
873    }
874
875    fn sub(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
876        write!(self, "{d} = {s1} - {s2}")
877    }
878
879    fn mul(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
880        write!(self, "{d} = {s1} * {s2}")
881    }
882
883    fn mul_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
884        write!(self, "{d} = {s1} * {s2}")
885    }
886
887    fn mul_upper_signed_signed(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
888        write!(self, "{d} = ({s1} as i64 * {s2} as i64) >> 32")
889    }
890
891    fn mul_upper_signed_signed_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
892        write!(self, "{d} = ({s1} as i64 * {s2} as i64) >> 32", s2 = s2 as i32)
893    }
894
895    fn mul_upper_unsigned_unsigned(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
896        write!(self, "{d} = ({s1} as u64 * {s2} as u64) >> 32")
897    }
898
899    fn mul_upper_unsigned_unsigned_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
900        write!(self, "{d} = ({s1} as u64 * {s2} as u64) >> 32")
901    }
902
903    fn mul_upper_signed_unsigned(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
904        write!(self, "{d} = ({s1} as i64 * {s2} as u64) >> 32")
905    }
906
907    fn div_unsigned(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
908        write!(self, "{d} = {s1} /u {s2}")
909    }
910
911    fn div_signed(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
912        write!(self, "{d} = {s1} /s {s2}")
913    }
914
915    fn rem_unsigned(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
916        write!(self, "{d} = {s1} %u {s2}")
917    }
918
919    fn rem_signed(&mut self, d: Reg, s1: Reg, s2: Reg) -> Self::ReturnTy {
920        write!(self, "{d} = {s1} %s {s2}")
921    }
922
923    fn set_less_than_unsigned_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
924        write!(self, "{d} = {s1} <u 0x{s2:x}")
925    }
926
927    fn set_greater_than_unsigned_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
928        write!(self, "{d} = {s1} >u 0x{s2:x}")
929    }
930
931    fn set_less_than_signed_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
932        write!(self, "{d} = {s1} <s {s2}", s2 = s2 as i32)
933    }
934
935    fn set_greater_than_signed_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
936        write!(self, "{d} = {s1} >s {s2}", s2 = s2 as i32)
937    }
938
939    fn shift_logical_right_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
940        write!(self, "{d} = {s1} >> {s2}")
941    }
942
943    fn shift_logical_right_imm_alt(&mut self, d: Reg, s2: Reg, s1: u32) -> Self::ReturnTy {
944        write!(self, "{d} = {s1} >> {s2}")
945    }
946
947    fn shift_arithmetic_right_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
948        write!(self, "{d} = {s1} >>a {s2}")
949    }
950
951    fn shift_arithmetic_right_imm_alt(&mut self, d: Reg, s2: Reg, s1: u32) -> Self::ReturnTy {
952        write!(self, "{d} = {s1} >>a {s2}")
953    }
954
955    fn shift_logical_left_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
956        write!(self, "{d} = {s1} << {s2}")
957    }
958
959    fn shift_logical_left_imm_alt(&mut self, d: Reg, s2: Reg, s1: u32) -> Self::ReturnTy {
960        write!(self, "{d} = {s1} << {s2}")
961    }
962
963    fn or_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
964        write!(self, "{d} = {s1} | 0x{s2:x}")
965    }
966
967    fn and_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
968        write!(self, "{d} = {s1} & 0x{s2:x}")
969    }
970
971    fn xor_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
972        write!(self, "{d} = {s1} ^ 0x{s2:x}")
973    }
974
975    fn load_imm(&mut self, d: Reg, a: u32) -> Self::ReturnTy {
976        write!(self, "{d} = 0x{a:x}")
977    }
978
979    fn move_reg(&mut self, d: Reg, s: Reg) -> Self::ReturnTy {
980        write!(self, "{d} = {s}")
981    }
982
983    fn cmov_if_zero(&mut self, d: Reg, s: Reg, c: Reg) -> Self::ReturnTy {
984        write!(self, "{d} = {s} if {c} == 0")
985    }
986
987    fn cmov_if_not_zero(&mut self, d: Reg, s: Reg, c: Reg) -> Self::ReturnTy {
988        write!(self, "{d} = {s} if {c} != 0")
989    }
990
991    fn cmov_if_zero_imm(&mut self, d: Reg, c: Reg, s: u32) -> Self::ReturnTy {
992        write!(self, "{d} = {s} if {c} == 0")
993    }
994
995    fn cmov_if_not_zero_imm(&mut self, d: Reg, c: Reg, s: u32) -> Self::ReturnTy {
996        write!(self, "{d} = {s} if {c} != 0")
997    }
998
999    fn add_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
1000        if (s2 as i32) < 0 && (s2 as i32) > -4096 {
1001            write!(self, "{d} = {s1} - {s2}", s2 = -(s2 as i32))
1002        } else {
1003            write!(self, "{d} = {s1} + 0x{s2:x}")
1004        }
1005    }
1006
1007    fn negate_and_add_imm(&mut self, d: Reg, s1: Reg, s2: u32) -> Self::ReturnTy {
1008        if s2 == 0 {
1009            write!(self, "{d} = -{s1}")
1010        } else {
1011            write!(self, "{d} = -{s1} + {s2}")
1012        }
1013    }
1014
1015    fn store_imm_indirect_u8(&mut self, base: Reg, offset: u32, value: u32) -> Self::ReturnTy {
1016        write!(self, "u8 [{base} + {offset}] = {value}")
1017    }
1018
1019    fn store_imm_indirect_u16(&mut self, base: Reg, offset: u32, value: u32) -> Self::ReturnTy {
1020        write!(self, "u16 [{base} + {offset}] = {value}")
1021    }
1022
1023    fn store_imm_indirect_u32(&mut self, base: Reg, offset: u32, value: u32) -> Self::ReturnTy {
1024        write!(self, "u32 [{base} + {offset}] = {value}")
1025    }
1026
1027    fn store_indirect_u8(&mut self, src: Reg, base: Reg, offset: u32) -> Self::ReturnTy {
1028        if offset != 0 {
1029            write!(self, "u8 [{base} + {offset}] = {src}")
1030        } else {
1031            write!(self, "u8 [{base}] = {src}")
1032        }
1033    }
1034
1035    fn store_indirect_u16(&mut self, src: Reg, base: Reg, offset: u32) -> Self::ReturnTy {
1036        if offset != 0 {
1037            write!(self, "u16 [{base} + {offset}] = {src}")
1038        } else {
1039            write!(self, "u16 [{base}] = {src}")
1040        }
1041    }
1042
1043    fn store_indirect_u32(&mut self, src: Reg, base: Reg, offset: u32) -> Self::ReturnTy {
1044        if offset != 0 {
1045            write!(self, "u32 [{base} + {offset}] = {src}")
1046        } else {
1047            write!(self, "u32 [{base}] = {src}")
1048        }
1049    }
1050
1051    fn store_imm_u8(&mut self, value: u32, offset: u32) -> Self::ReturnTy {
1052        write!(self, "u8 [0x{offset:x}] = {value}")
1053    }
1054
1055    fn store_imm_u16(&mut self, value: u32, offset: u32) -> Self::ReturnTy {
1056        write!(self, "u16 [0x{offset:x}] = {value}")
1057    }
1058
1059    fn store_imm_u32(&mut self, value: u32, offset: u32) -> Self::ReturnTy {
1060        write!(self, "u32 [0x{offset:x}] = {value}")
1061    }
1062
1063    fn store_u8(&mut self, src: Reg, offset: u32) -> Self::ReturnTy {
1064        write!(self, "u8 [0x{offset:x}] = {src}")
1065    }
1066
1067    fn store_u16(&mut self, src: Reg, offset: u32) -> Self::ReturnTy {
1068        write!(self, "u16 [0x{offset:x}] = {src}")
1069    }
1070
1071    fn store_u32(&mut self, src: Reg, offset: u32) -> Self::ReturnTy {
1072        write!(self, "u32 [0x{offset:x}] = {src}")
1073    }
1074
1075    fn load_indirect_u8(&mut self, dst: Reg, base: Reg, offset: u32) -> Self::ReturnTy {
1076        if offset != 0 {
1077            write!(self, "{} = u8 [{} + {}]", dst, base, offset)
1078        } else {
1079            write!(self, "{} = u8 [{}]", dst, base)
1080        }
1081    }
1082
1083    fn load_indirect_i8(&mut self, dst: Reg, base: Reg, offset: u32) -> Self::ReturnTy {
1084        if offset != 0 {
1085            write!(self, "{} = i8 [{} + {}]", dst, base, offset)
1086        } else {
1087            write!(self, "{} = i8 [{}]", dst, base)
1088        }
1089    }
1090
1091    fn load_indirect_u16(&mut self, dst: Reg, base: Reg, offset: u32) -> Self::ReturnTy {
1092        if offset != 0 {
1093            write!(self, "{} = u16 [{} + {}]", dst, base, offset)
1094        } else {
1095            write!(self, "{} = u16 [{} ]", dst, base)
1096        }
1097    }
1098
1099    fn load_indirect_i16(&mut self, dst: Reg, base: Reg, offset: u32) -> Self::ReturnTy {
1100        if offset != 0 {
1101            write!(self, "{} = i16 [{} + {}]", dst, base, offset)
1102        } else {
1103            write!(self, "{} = i16 [{}]", dst, base)
1104        }
1105    }
1106
1107    fn load_indirect_u32(&mut self, dst: Reg, base: Reg, offset: u32) -> Self::ReturnTy {
1108        if offset != 0 {
1109            write!(self, "{} = u32 [{} + {}]", dst, base, offset)
1110        } else {
1111            write!(self, "{} = u32 [{}]", dst, base)
1112        }
1113    }
1114
1115    fn load_u8(&mut self, dst: Reg, offset: u32) -> Self::ReturnTy {
1116        write!(self, "{} = u8 [0x{:x}]", dst, offset)
1117    }
1118
1119    fn load_i8(&mut self, dst: Reg, offset: u32) -> Self::ReturnTy {
1120        write!(self, "{} = i8 [0x{:x}]", dst, offset)
1121    }
1122
1123    fn load_u16(&mut self, dst: Reg, offset: u32) -> Self::ReturnTy {
1124        write!(self, "{} = u16 [0x{:x}]", dst, offset)
1125    }
1126
1127    fn load_i16(&mut self, dst: Reg, offset: u32) -> Self::ReturnTy {
1128        write!(self, "{} = i16 [0x{:x}]", dst, offset)
1129    }
1130
1131    fn load_u32(&mut self, dst: Reg, offset: u32) -> Self::ReturnTy {
1132        write!(self, "{} = u32 [0x{:x}]", dst, offset)
1133    }
1134
1135    fn branch_less_unsigned(&mut self, s1: Reg, s2: Reg, imm: u32) -> Self::ReturnTy {
1136        write!(self, "if {} <u {}: jump @{:x}", s1, s2, imm)
1137    }
1138
1139    fn branch_less_signed(&mut self, s1: Reg, s2: Reg, imm: u32) -> Self::ReturnTy {
1140        write!(self, "if {} <s {}: jump @{:x}", s1, s2, imm)
1141    }
1142
1143    fn branch_less_unsigned_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1144        write!(self, "if {} <u {}: jump @{:x}", s1, s2, imm)
1145    }
1146
1147    fn branch_less_signed_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1148        write!(self, "if {} <s {}: jump @{:x}", s1, s2, imm)
1149    }
1150
1151    fn branch_greater_or_equal_unsigned(&mut self, s1: Reg, s2: Reg, imm: u32) -> Self::ReturnTy {
1152        write!(self, "if {} >=u {}: jump @{:x}", s1, s2, imm)
1153    }
1154
1155    fn branch_greater_or_equal_signed(&mut self, s1: Reg, s2: Reg, imm: u32) -> Self::ReturnTy {
1156        write!(self, "if {} >=s {}: jump @{:x}", s1, s2, imm)
1157    }
1158
1159    fn branch_greater_or_equal_unsigned_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1160        write!(self, "if {} >=u {}: jump @{:x}", s1, s2, imm)
1161    }
1162
1163    fn branch_greater_or_equal_signed_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1164        write!(self, "if {} >=s {}: jump @{:x}", s1, s2, imm)
1165    }
1166
1167    fn branch_eq(&mut self, s1: Reg, s2: Reg, imm: u32) -> Self::ReturnTy {
1168        write!(self, "if {} == {}: jump @{:x}", s1, s2, imm)
1169    }
1170
1171    fn branch_not_eq(&mut self, s1: Reg, s2: Reg, imm: u32) -> Self::ReturnTy {
1172        write!(self, "if {} != {}: jump @{:x}", s1, s2, imm)
1173    }
1174
1175    fn branch_eq_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1176        write!(self, "if {} == {}: jump @{:x}", s1, s2, imm)
1177    }
1178
1179    fn branch_not_eq_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1180        write!(self, "if {} != {}: jump @{:x}", s1, s2, imm)
1181    }
1182
1183    fn branch_less_or_equal_unsigned_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1184        write!(self, "if {} <=u {}: jump @{:x}", s1, s2, imm)
1185    }
1186
1187    fn branch_less_or_equal_signed_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1188        write!(self, "if {} <=s {}: jump @{:x}", s1, s2, imm)
1189    }
1190
1191    fn branch_greater_unsigned_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1192        write!(self, "if {} >u {}: jump @{:x}", s1, s2, imm)
1193    }
1194
1195    fn branch_greater_signed_imm(&mut self, s1: Reg, s2: u32, imm: u32) -> Self::ReturnTy {
1196        write!(self, "if {} >s {}: jump @{:x}", s1, s2, imm)
1197    }
1198
1199    fn jump(&mut self, target: u32) -> Self::ReturnTy {
1200        write!(self, "jump @{:x}", target)
1201    }
1202
1203    fn call(&mut self, ra: Reg, target: u32) -> Self::ReturnTy {
1204        match ra {
1205            Reg::RA => write!(self, "call @{:x}", target),
1206            _ => write!(self, "call @{:x}, {}", target, ra),
1207        }
1208    }
1209
1210    fn jump_indirect(&mut self, base: Reg, offset: u32) -> Self::ReturnTy {
1211        use Reg::*;
1212        match (base, offset) {
1213            (RA, 0) => write!(self, "ret"),
1214            (_, 0) => write!(self, "jump [{}]", base),
1215            (_, _) => write!(self, "jump [{} + {}]", base, offset),
1216        }
1217    }
1218
1219    fn call_indirect(&mut self, ra: Reg, base: Reg, offset: u32) -> Self::ReturnTy {
1220        use Reg::*;
1221        match (ra, base, offset) {
1222            (RA, _, 0) => write!(self, "call [{}]", base),
1223            (RA, _, _) => write!(self, "call [{} + {}]", base, offset),
1224            (_, _, 0) => write!(self, "call [{}], {}", base, ra),
1225            (_, _, _) => write!(self, "call [{} + {}], {}", base, offset, ra),
1226        }
1227    }
1228}
1229
1230#[derive(Debug)]
1231pub struct ProgramParseError(ProgramParseErrorKind);
1232
1233#[derive(Debug)]
1234enum ProgramParseErrorKind {
1235    FailedToReadVarint {
1236        offset: usize,
1237    },
1238    FailedToReadStringNonUtf {
1239        offset: usize,
1240    },
1241    FailedToReadInstructionArguments {
1242        offset: usize,
1243    },
1244    UnexpectedSection {
1245        offset: usize,
1246        section: u8,
1247    },
1248    UnexpectedInstruction {
1249        offset: usize,
1250    },
1251    UnexpectedEnd {
1252        offset: usize,
1253        expected_count: usize,
1254        actual_count: usize,
1255    },
1256    UnsupportedVersion {
1257        version: u8,
1258    },
1259    Other(&'static str),
1260}
1261
1262impl ProgramParseError {
1263    #[cold]
1264    #[inline]
1265    fn unexpected_instruction(offset: usize) -> ProgramParseError {
1266        ProgramParseError(ProgramParseErrorKind::UnexpectedInstruction { offset })
1267    }
1268
1269    #[cold]
1270    #[inline]
1271    fn failed_to_read_instruction_arguments(offset: usize) -> ProgramParseError {
1272        ProgramParseError(ProgramParseErrorKind::FailedToReadInstructionArguments { offset })
1273    }
1274
1275    #[cold]
1276    #[inline]
1277    fn failed_to_read_varint(offset: usize) -> ProgramParseError {
1278        ProgramParseError(ProgramParseErrorKind::FailedToReadVarint { offset })
1279    }
1280
1281    #[cold]
1282    #[inline]
1283    fn unexpected_end_of_file(offset: usize, expected_count: usize, actual_count: usize) -> ProgramParseError {
1284        ProgramParseError(ProgramParseErrorKind::UnexpectedEnd {
1285            offset,
1286            expected_count,
1287            actual_count,
1288        })
1289    }
1290}
1291
1292impl core::fmt::Display for ProgramParseError {
1293    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1294        match self.0 {
1295            ProgramParseErrorKind::FailedToReadVarint { offset } => {
1296                write!(
1297                    fmt,
1298                    "failed to parse program blob: failed to parse a varint at offset 0x{:x}",
1299                    offset
1300                )
1301            }
1302            ProgramParseErrorKind::FailedToReadStringNonUtf { offset } => {
1303                write!(
1304                    fmt,
1305                    "failed to parse program blob: failed to parse a string at offset 0x{:x} (not valid UTF-8)",
1306                    offset
1307                )
1308            }
1309            ProgramParseErrorKind::FailedToReadInstructionArguments { offset } => {
1310                write!(
1311                    fmt,
1312                    "failed to parse program blob: failed to parse instruction arguments at offset 0x{:x}",
1313                    offset
1314                )
1315            }
1316            ProgramParseErrorKind::UnexpectedSection { offset, section } => {
1317                write!(
1318                    fmt,
1319                    "failed to parse program blob: found unexpected section as offset 0x{:x}: 0x{:x}",
1320                    offset, section
1321                )
1322            }
1323            ProgramParseErrorKind::UnexpectedInstruction { offset } => {
1324                write!(
1325                    fmt,
1326                    "failed to parse program blob: failed to parse instruction at offset 0x{:x}",
1327                    offset
1328                )
1329            }
1330            ProgramParseErrorKind::UnexpectedEnd {
1331                offset,
1332                expected_count,
1333                actual_count,
1334            } => {
1335                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)
1336            }
1337            ProgramParseErrorKind::UnsupportedVersion { version } => {
1338                write!(fmt, "failed to parse program blob: unsupported version: {}", version)
1339            }
1340            ProgramParseErrorKind::Other(error) => {
1341                write!(fmt, "failed to parse program blob: {}", error)
1342            }
1343        }
1344    }
1345}
1346
1347#[cfg(feature = "std")]
1348impl std::error::Error for ProgramParseError {}
1349
1350#[derive(Clone, PartialEq, Eq, Debug)]
1351pub struct ProgramExport<'a> {
1352    jump_target: u32,
1353    symbol: ProgramSymbol<'a>,
1354}
1355
1356impl<'a> ProgramExport<'a> {
1357    pub fn new(jump_target: u32, symbol: ProgramSymbol<'a>) -> Self {
1358        Self { jump_target, symbol }
1359    }
1360
1361    pub fn jump_target(&self) -> u32 {
1362        self.jump_target
1363    }
1364
1365    pub fn symbol(&self) -> &ProgramSymbol<'a> {
1366        &self.symbol
1367    }
1368
1369    #[cfg(feature = "alloc")]
1370    pub fn into_owned(self) -> ProgramExport<'static> {
1371        ProgramExport {
1372            jump_target: self.jump_target,
1373            symbol: self.symbol.into_owned(),
1374        }
1375    }
1376}
1377
1378#[derive(Clone, PartialEq, Eq, Debug)]
1379pub struct ProgramImport<'a> {
1380    symbol: ProgramSymbol<'a>,
1381}
1382
1383impl<'a> ProgramImport<'a> {
1384    pub fn new(symbol: ProgramSymbol<'a>) -> Self {
1385        Self { symbol }
1386    }
1387
1388    pub fn symbol(&self) -> &ProgramSymbol<'a> {
1389        &self.symbol
1390    }
1391
1392    #[cfg(feature = "alloc")]
1393    pub fn into_owned(self) -> ProgramImport<'static> {
1394        ProgramImport {
1395            symbol: self.symbol.into_owned(),
1396        }
1397    }
1398}
1399
1400#[derive(Clone, PartialEq, Eq, Debug)]
1401pub struct ProgramSymbol<'a>(CowBytes<'a>);
1402
1403impl<'a> ProgramSymbol<'a> {
1404    pub fn new(bytes: CowBytes<'a>) -> Self {
1405        Self(bytes)
1406    }
1407
1408    pub fn into_inner(self) -> CowBytes<'a> {
1409        self.0
1410    }
1411
1412    #[cfg(feature = "alloc")]
1413    pub fn into_owned(self) -> ProgramSymbol<'static> {
1414        ProgramSymbol(self.0.into_owned())
1415    }
1416}
1417
1418impl<'a> From<&'a [u8]> for ProgramSymbol<'a> {
1419    fn from(symbol: &'a [u8]) -> Self {
1420        ProgramSymbol(symbol.into())
1421    }
1422}
1423
1424impl<'a> From<&'a str> for ProgramSymbol<'a> {
1425    fn from(symbol: &'a str) -> Self {
1426        ProgramSymbol(symbol.into())
1427    }
1428}
1429
1430#[cfg(feature = "alloc")]
1431impl<'a> From<alloc::vec::Vec<u8>> for ProgramSymbol<'a> {
1432    fn from(symbol: alloc::vec::Vec<u8>) -> Self {
1433        ProgramSymbol(symbol.into())
1434    }
1435}
1436
1437impl<'a> core::ops::Deref for ProgramSymbol<'a> {
1438    type Target = CowBytes<'a>;
1439
1440    fn deref(&self) -> &Self::Target {
1441        &self.0
1442    }
1443}
1444
1445impl<'a> core::fmt::Display for ProgramSymbol<'a> {
1446    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
1447        if let Ok(ident) = core::str::from_utf8(&self.0) {
1448            fmt.write_str("'")?;
1449            fmt.write_str(ident)?;
1450            fmt.write_str("'")?;
1451        } else {
1452            fmt.write_str("0x")?;
1453            for &byte in self.0.iter() {
1454                core::write!(fmt, "{:02x}", byte)?;
1455            }
1456        }
1457
1458        Ok(())
1459    }
1460}
1461
1462/// A partially deserialized PolkaVM program.
1463#[derive(Clone, Default)]
1464pub struct ProgramBlob<'a> {
1465    blob: CowBytes<'a>,
1466
1467    ro_data_size: u32,
1468    rw_data_size: u32,
1469    stack_size: u32,
1470
1471    ro_data: Range<usize>,
1472    rw_data: Range<usize>,
1473    exports: Range<usize>,
1474    imports: Range<usize>,
1475    code: Range<usize>,
1476    jump_table: Range<usize>,
1477
1478    debug_strings: Range<usize>,
1479    debug_line_program_ranges: Range<usize>,
1480    debug_line_programs: Range<usize>,
1481
1482    instruction_count: u32,
1483    basic_block_count: u32,
1484}
1485
1486#[derive(Clone)]
1487struct Reader<'a> {
1488    blob: &'a [u8],
1489    position: usize,
1490}
1491
1492impl<'a> Reader<'a> {
1493    fn skip(&mut self, count: usize) -> Result<(), ProgramParseError> {
1494        self.read_slice_as_range(count).map(|_| ())
1495    }
1496
1497    fn finish(&mut self) {
1498        self.position += self.blob.len();
1499        self.blob = b"";
1500    }
1501
1502    #[inline(always)]
1503    fn read_byte(&mut self) -> Result<u8, ProgramParseError> {
1504        Ok(self.read_slice(1)?[0])
1505    }
1506
1507    #[inline(always)]
1508    fn read_slice(&mut self, length: usize) -> Result<&'a [u8], ProgramParseError> {
1509        let Some(slice) = self.blob.get(..length) else {
1510            return Err(ProgramParseError::unexpected_end_of_file(self.position, length, self.blob.len()));
1511        };
1512
1513        self.position += length;
1514        self.blob = &self.blob[length..];
1515        Ok(slice)
1516    }
1517
1518    #[inline(always)]
1519    fn read_varint(&mut self) -> Result<u32, ProgramParseError> {
1520        let first_byte = self.read_byte()?;
1521        let Some((length, value)) = read_varint(self.blob, first_byte) else {
1522            return Err(ProgramParseError::failed_to_read_varint(self.position - 1));
1523        };
1524
1525        self.position += length;
1526        self.blob = &self.blob[length..];
1527        Ok(value)
1528    }
1529
1530    #[inline(always)]
1531    fn read_regs3(&mut self) -> Result<(Reg, Reg, Reg), ProgramParseError> {
1532        let data = self.read_slice(2)?;
1533        let reg1 = data[0] & 0b1111;
1534        let reg2 = data[0] >> 4;
1535        let reg3 = data[1];
1536        if let Some(reg1) = Reg::from_u8(reg1) {
1537            if let Some(reg2) = Reg::from_u8(reg2) {
1538                if let Some(reg3) = Reg::from_u8(reg3) {
1539                    return Ok((reg1, reg2, reg3));
1540                }
1541            }
1542        }
1543
1544        Err(ProgramParseError::failed_to_read_instruction_arguments(self.position - 2))
1545    }
1546
1547    #[inline(always)]
1548    fn read_reg(&mut self) -> Result<Reg, ProgramParseError> {
1549        let reg = self.read_byte()?;
1550        if let Some(reg) = Reg::from_u8(reg) {
1551            return Ok(reg);
1552        }
1553
1554        Err(ProgramParseError::failed_to_read_instruction_arguments(self.position - 1))
1555    }
1556
1557    #[inline(always)]
1558    fn read_regs2(&mut self) -> Result<(Reg, Reg), ProgramParseError> {
1559        let regs = self.read_byte()?;
1560        let reg1 = regs & 0b1111;
1561        let reg2 = regs >> 4;
1562        if let Some(reg1) = Reg::from_u8(reg1) {
1563            if let Some(reg2) = Reg::from_u8(reg2) {
1564                return Ok((reg1, reg2));
1565            }
1566        }
1567
1568        Err(ProgramParseError::failed_to_read_instruction_arguments(self.position - 1))
1569    }
1570
1571    fn read_bytes_with_length(&mut self) -> Result<&'a [u8], ProgramParseError> {
1572        let length = self.read_varint()? as usize;
1573        self.read_slice(length)
1574    }
1575
1576    fn read_string_with_length(&mut self) -> Result<&'a str, ProgramParseError> {
1577        let offset = self.position;
1578        let slice = self.read_bytes_with_length()?;
1579
1580        core::str::from_utf8(slice)
1581            .ok()
1582            .ok_or(ProgramParseError(ProgramParseErrorKind::FailedToReadStringNonUtf { offset }))
1583    }
1584
1585    fn read_slice_as_range(&mut self, count: usize) -> Result<Range<usize>, ProgramParseError> {
1586        if self.blob.len() < count {
1587            return Err(ProgramParseError::unexpected_end_of_file(self.position, count, self.blob.len()));
1588        };
1589
1590        let range = self.position..self.position + count;
1591        self.position += count;
1592        self.blob = &self.blob[count..];
1593        Ok(range)
1594    }
1595
1596    fn is_eof(&self) -> bool {
1597        self.blob.is_empty()
1598    }
1599
1600    fn read_section_range_into(
1601        &mut self,
1602        out_section: &mut u8,
1603        out_range: &mut Range<usize>,
1604        expected_section: u8,
1605    ) -> Result<(), ProgramParseError> {
1606        if *out_section == expected_section {
1607            let section_length = self.read_varint()? as usize;
1608            *out_range = self.read_slice_as_range(section_length)?;
1609            *out_section = self.read_byte()?;
1610        }
1611
1612        Ok(())
1613    }
1614}
1615
1616impl<'a> ProgramBlob<'a> {
1617    /// Parses the given bytes into a program blob.
1618    pub fn parse(bytes: impl Into<CowBytes<'a>>) -> Result<Self, ProgramParseError> {
1619        Self::parse_impl(bytes.into())
1620    }
1621
1622    /// Returns the original bytes from which this program blob was created from.
1623    pub fn as_bytes(&self) -> &[u8] {
1624        &self.blob
1625    }
1626
1627    #[inline(never)]
1628    fn parse_impl(blob: CowBytes<'a>) -> Result<Self, ProgramParseError> {
1629        if !blob.starts_with(&BLOB_MAGIC) {
1630            return Err(ProgramParseError(ProgramParseErrorKind::Other(
1631                "blob doesn't start with the expected magic bytes",
1632            )));
1633        }
1634
1635        let mut program = ProgramBlob {
1636            blob,
1637            ..ProgramBlob::default()
1638        };
1639
1640        let mut reader = Reader {
1641            blob: &program.blob[BLOB_MAGIC.len()..],
1642            position: BLOB_MAGIC.len(),
1643        };
1644
1645        let blob_version = reader.read_byte()?;
1646        if blob_version != BLOB_VERSION_V1 {
1647            return Err(ProgramParseError(ProgramParseErrorKind::UnsupportedVersion {
1648                version: blob_version,
1649            }));
1650        }
1651
1652        let mut section = reader.read_byte()?;
1653        if section == SECTION_MEMORY_CONFIG {
1654            let section_length = reader.read_varint()?;
1655            let position = reader.position;
1656            program.ro_data_size = reader.read_varint()?;
1657            program.rw_data_size = reader.read_varint()?;
1658            program.stack_size = reader.read_varint()?;
1659            if position + section_length as usize != reader.position {
1660                return Err(ProgramParseError(ProgramParseErrorKind::Other(
1661                    "the memory config section contains more data than expected",
1662                )));
1663            }
1664            section = reader.read_byte()?;
1665        }
1666
1667        reader.read_section_range_into(&mut section, &mut program.ro_data, SECTION_RO_DATA)?;
1668        reader.read_section_range_into(&mut section, &mut program.rw_data, SECTION_RW_DATA)?;
1669        reader.read_section_range_into(&mut section, &mut program.imports, SECTION_IMPORTS)?;
1670        reader.read_section_range_into(&mut section, &mut program.exports, SECTION_EXPORTS)?;
1671        reader.read_section_range_into(&mut section, &mut program.jump_table, SECTION_JUMP_TABLE)?;
1672
1673        if program.ro_data.len() > program.ro_data_size as usize {
1674            return Err(ProgramParseError(ProgramParseErrorKind::Other(
1675                "size of the read-only data payload exceeds the declared size of the section",
1676            )));
1677        }
1678
1679        if program.rw_data.len() > program.rw_data_size as usize {
1680            return Err(ProgramParseError(ProgramParseErrorKind::Other(
1681                "size of the read-write data payload exceeds the declared size of the section",
1682            )));
1683        }
1684
1685        if section == SECTION_CODE {
1686            let section_length = reader.read_varint()?;
1687            let initial_position = reader.position;
1688            let instruction_count = reader.read_varint()?;
1689            let basic_block_count = reader.read_varint()?;
1690            let header_size = (reader.position - initial_position) as u32;
1691            if section_length < header_size {
1692                return Err(ProgramParseError(ProgramParseErrorKind::Other("the code section is too short")));
1693            }
1694
1695            let body_length = section_length - header_size;
1696            if instruction_count > body_length {
1697                return Err(ProgramParseError(ProgramParseErrorKind::Other("invalid instruction count")));
1698            }
1699
1700            if basic_block_count > body_length {
1701                return Err(ProgramParseError(ProgramParseErrorKind::Other("invalid basic block count")));
1702            }
1703
1704            program.instruction_count = instruction_count;
1705            program.basic_block_count = basic_block_count;
1706            program.code = reader.read_slice_as_range(body_length as usize)?;
1707            section = reader.read_byte()?;
1708        }
1709
1710        reader.read_section_range_into(&mut section, &mut program.debug_strings, SECTION_OPT_DEBUG_STRINGS)?;
1711        reader.read_section_range_into(&mut section, &mut program.debug_line_programs, SECTION_OPT_DEBUG_LINE_PROGRAMS)?;
1712        reader.read_section_range_into(
1713            &mut section,
1714            &mut program.debug_line_program_ranges,
1715            SECTION_OPT_DEBUG_LINE_PROGRAM_RANGES,
1716        )?;
1717
1718        while (section & 0b10000000) != 0 {
1719            // We don't know this section, but it's optional, so just skip it.
1720            #[cfg(feature = "logging")]
1721            log::debug!("Skipping unsupported optional section: {}", section);
1722            let section_length = reader.read_varint()?;
1723            reader.skip(section_length as usize)?;
1724            section = reader.read_byte()?;
1725        }
1726
1727        if section == SECTION_END_OF_FILE {
1728            return Ok(program);
1729        }
1730
1731        Err(ProgramParseError(ProgramParseErrorKind::UnexpectedSection {
1732            offset: reader.position - 1,
1733            section,
1734        }))
1735    }
1736
1737    /// Returns the contents of the read-only data section.
1738    ///
1739    /// This only covers the initial non-zero portion of the section; use `ro_data_size` to get the full size.
1740    pub fn ro_data(&self) -> &[u8] {
1741        &self.blob[self.ro_data.clone()]
1742    }
1743
1744    /// Returns the size of the read-only data section.
1745    ///
1746    /// 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.
1747    pub fn ro_data_size(&self) -> u32 {
1748        self.ro_data_size
1749    }
1750
1751    /// Returns the contents of the read-write data section.
1752    ///
1753    /// This only covers the initial non-zero portion of the section; use `rw_data_size` to get the full size.
1754    pub fn rw_data(&self) -> &[u8] {
1755        &self.blob[self.rw_data.clone()]
1756    }
1757
1758    /// Returns the size of the read-write data section.
1759    ///
1760    /// 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.
1761    pub fn rw_data_size(&self) -> u32 {
1762        self.rw_data_size
1763    }
1764
1765    /// Returns the initial size of the stack.
1766    pub fn stack_size(&self) -> u32 {
1767        self.stack_size
1768    }
1769
1770    /// Returns the program code in its raw form.
1771    pub fn code(&self) -> &[u8] {
1772        &self.blob[self.code.clone()]
1773    }
1774
1775    /// Returns the number of instructions the code section should contain.
1776    ///
1777    /// NOTE: It is safe to preallocate memory based on this value as we make sure
1778    /// that it is no larger than the the physical size of the code section, however
1779    /// we do not verify that it is actually true, so it should *not* be blindly trusted!
1780    pub fn instruction_count(&self) -> u32 {
1781        self.instruction_count
1782    }
1783
1784    /// Returns the number of basic blocks the code section should contain.
1785    ///
1786    /// NOTE: It is safe to preallocate memory based on this value as we make sure
1787    /// that it is no larger than the the physical size of the code section, however
1788    /// we do not verify that it is actually true, so it should *not* be blindly trusted!
1789    pub fn basic_block_count(&self) -> u32 {
1790        self.basic_block_count
1791    }
1792
1793    fn get_section_reader(&self, range: Range<usize>) -> Reader {
1794        Reader {
1795            blob: &self.blob[range.start..range.end],
1796            position: range.start,
1797        }
1798    }
1799
1800    /// Returns an iterator over program imports.
1801    pub fn imports(&'_ self) -> impl Iterator<Item = Result<ProgramImport, ProgramParseError>> + Clone + '_ {
1802        #[derive(Clone)]
1803        enum State {
1804            Uninitialized,
1805            Pending(u32),
1806            Finished,
1807        }
1808
1809        #[derive(Clone)]
1810        struct ImportIterator<'a> {
1811            state: State,
1812            reader: Reader<'a>,
1813        }
1814
1815        impl<'a> ImportIterator<'a> {
1816            fn read_next(&mut self) -> Result<Option<ProgramImport<'a>>, ProgramParseError> {
1817                let remaining = match core::mem::replace(&mut self.state, State::Finished) {
1818                    State::Uninitialized => self.reader.read_varint()?,
1819                    State::Pending(remaining) => remaining,
1820                    State::Finished => return Ok(None),
1821                };
1822
1823                if remaining == 0 {
1824                    if !self.reader.is_eof() {
1825                        return Err(ProgramParseError(ProgramParseErrorKind::Other(
1826                            "the import section contains more data than expected",
1827                        )));
1828                    }
1829
1830                    return Ok(None);
1831                }
1832
1833                let symbol = self.reader.read_bytes_with_length()?;
1834                let import = ProgramImport { symbol: symbol.into() };
1835
1836                self.state = State::Pending(remaining - 1);
1837                Ok(Some(import))
1838            }
1839        }
1840
1841        impl<'a> Iterator for ImportIterator<'a> {
1842            type Item = Result<ProgramImport<'a>, ProgramParseError>;
1843            fn next(&mut self) -> Option<Self::Item> {
1844                self.read_next().transpose()
1845            }
1846        }
1847
1848        ImportIterator {
1849            state: if self.imports != (0_usize..0_usize) {
1850                State::Uninitialized
1851            } else {
1852                State::Finished
1853            },
1854            reader: self.get_section_reader(self.imports.clone()),
1855        }
1856    }
1857
1858    /// Returns an iterator over program exports.
1859    pub fn exports(&'_ self) -> impl Iterator<Item = Result<ProgramExport, ProgramParseError>> + Clone + '_ {
1860        #[derive(Clone)]
1861        enum State {
1862            Uninitialized,
1863            Pending(u32),
1864            Finished,
1865        }
1866
1867        #[derive(Clone)]
1868        struct ExportIterator<'a> {
1869            state: State,
1870            reader: Reader<'a>,
1871        }
1872
1873        impl<'a> ExportIterator<'a> {
1874            fn read_next(&mut self) -> Result<Option<ProgramExport<'a>>, ProgramParseError> {
1875                let remaining = match core::mem::replace(&mut self.state, State::Finished) {
1876                    State::Uninitialized => self.reader.read_varint()?,
1877                    State::Pending(remaining) => remaining,
1878                    State::Finished => return Ok(None),
1879                };
1880
1881                if remaining == 0 {
1882                    if !self.reader.is_eof() {
1883                        return Err(ProgramParseError(ProgramParseErrorKind::Other(
1884                            "the export section contains more data than expected",
1885                        )));
1886                    }
1887
1888                    return Ok(None);
1889                }
1890
1891                let jump_target = self.reader.read_varint()?;
1892                let symbol = self.reader.read_bytes_with_length()?;
1893                let export = ProgramExport {
1894                    jump_target,
1895                    symbol: symbol.into(),
1896                };
1897
1898                self.state = State::Pending(remaining - 1);
1899                Ok(Some(export))
1900            }
1901        }
1902
1903        impl<'a> Iterator for ExportIterator<'a> {
1904            type Item = Result<ProgramExport<'a>, ProgramParseError>;
1905            fn next(&mut self) -> Option<Self::Item> {
1906                self.read_next().transpose()
1907            }
1908        }
1909
1910        ExportIterator {
1911            state: if self.exports != (0_usize..0_usize) {
1912                State::Uninitialized
1913            } else {
1914                State::Finished
1915            },
1916            reader: self.get_section_reader(self.exports.clone()),
1917        }
1918    }
1919
1920    /// Returns an iterator over program instructions.
1921    pub fn instructions(&'_ self) -> impl Iterator<Item = Result<Instruction, ProgramParseError>> + Clone + '_ {
1922        #[derive(Clone)]
1923        struct CodeIterator<'a> {
1924            reader: Reader<'a>,
1925        }
1926
1927        impl<'a> Iterator for CodeIterator<'a> {
1928            type Item = Result<Instruction, ProgramParseError>;
1929            fn next(&mut self) -> Option<Self::Item> {
1930                if self.reader.is_eof() {
1931                    return None;
1932                }
1933
1934                let result = (|| -> Result<Instruction, ProgramParseError> {
1935                    let opcode = self.reader.read_byte()?;
1936                    parse_instruction_impl(opcode, &mut self.reader)
1937                })();
1938
1939                if result.is_err() {
1940                    self.reader.finish();
1941                }
1942
1943                Some(result)
1944            }
1945        }
1946
1947        CodeIterator {
1948            reader: self.get_section_reader(self.code.clone()),
1949        }
1950    }
1951
1952    /// The upper bound of how many entries there might be in this program's jump table, excluding the very first implicit entry.
1953    pub fn jump_table_upper_bound(&self) -> usize {
1954        self.jump_table.len()
1955    }
1956
1957    /// Returns an iterator over the jump table entries, excluding the very first implicit entry.
1958    pub fn jump_table(&'_ self) -> impl Iterator<Item = Result<u32, ProgramParseError>> + Clone + '_ {
1959        #[derive(Clone)]
1960        struct JumpTableIterator<'a> {
1961            reader: Reader<'a>,
1962        }
1963
1964        impl<'a> Iterator for JumpTableIterator<'a> {
1965            type Item = Result<u32, ProgramParseError>;
1966            fn next(&mut self) -> Option<Self::Item> {
1967                if self.reader.is_eof() {
1968                    return None;
1969                }
1970
1971                Some(self.reader.read_varint())
1972            }
1973        }
1974
1975        JumpTableIterator {
1976            reader: self.get_section_reader(self.jump_table.clone()),
1977        }
1978    }
1979
1980    /// Returns the debug string for the given relative offset.
1981    pub fn get_debug_string(&self, offset: u32) -> Result<&str, ProgramParseError> {
1982        let mut reader = self.get_section_reader(self.debug_strings.clone());
1983        reader.skip(offset as usize)?;
1984        reader.read_string_with_length()
1985    }
1986
1987    /// Returns the line program for the given instruction.
1988    pub fn get_debug_line_program_at(&self, nth_instruction: u32) -> Result<Option<LineProgram>, ProgramParseError> {
1989        if self.debug_line_program_ranges.is_empty() || self.debug_line_programs.is_empty() {
1990            return Ok(None);
1991        }
1992
1993        if self.blob[self.debug_line_programs.start] != VERSION_DEBUG_LINE_PROGRAM_V1 {
1994            return Err(ProgramParseError(ProgramParseErrorKind::Other(
1995                "the debug line programs section has an unsupported version",
1996            )));
1997        }
1998
1999        const ENTRY_SIZE: usize = 12;
2000
2001        let slice = &self.blob[self.debug_line_program_ranges.clone()];
2002        if slice.len() % ENTRY_SIZE != 0 {
2003            return Err(ProgramParseError(ProgramParseErrorKind::Other(
2004                "the debug function ranges section has an invalid size",
2005            )));
2006        }
2007
2008        let offset = binary_search(slice, ENTRY_SIZE, |xs| {
2009            let begin = u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]);
2010            if nth_instruction < begin {
2011                return core::cmp::Ordering::Greater;
2012            }
2013
2014            let end = u32::from_le_bytes([xs[4], xs[5], xs[6], xs[7]]);
2015            if nth_instruction >= end {
2016                return core::cmp::Ordering::Less;
2017            }
2018
2019            core::cmp::Ordering::Equal
2020        });
2021
2022        let Ok(offset) = offset else { return Ok(None) };
2023
2024        let xs = &slice[offset..offset + ENTRY_SIZE];
2025        let index_begin = u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]);
2026        let index_end = u32::from_le_bytes([xs[4], xs[5], xs[6], xs[7]]);
2027        let info_offset = u32::from_le_bytes([xs[8], xs[9], xs[10], xs[11]]);
2028
2029        if nth_instruction < index_begin || nth_instruction >= index_end {
2030            return Err(ProgramParseError(ProgramParseErrorKind::Other(
2031                "binary search for function debug info failed",
2032            )));
2033        }
2034
2035        let mut reader = self.get_section_reader(self.debug_line_programs.clone());
2036        reader.skip(info_offset as usize)?;
2037
2038        Ok(Some(LineProgram {
2039            entry_index: offset / ENTRY_SIZE,
2040            region_counter: 0,
2041            blob: self,
2042            reader,
2043            is_finished: false,
2044            program_counter: index_begin,
2045            stack: Default::default(),
2046            stack_depth: 0,
2047            mutation_depth: 0,
2048        }))
2049    }
2050
2051    /// Returns an owned program blob, possibly cloning it if it was deserialized in a zero-copy fashion.
2052    #[cfg(feature = "alloc")]
2053    pub fn into_owned(self) -> ProgramBlob<'static> {
2054        ProgramBlob {
2055            blob: self.blob.into_owned(),
2056
2057            ro_data_size: self.ro_data_size,
2058            rw_data_size: self.rw_data_size,
2059            stack_size: self.stack_size,
2060
2061            ro_data: self.ro_data,
2062            rw_data: self.rw_data,
2063            exports: self.exports,
2064            imports: self.imports,
2065            code: self.code,
2066            jump_table: self.jump_table,
2067
2068            debug_strings: self.debug_strings,
2069            debug_line_program_ranges: self.debug_line_program_ranges,
2070            debug_line_programs: self.debug_line_programs,
2071
2072            instruction_count: self.instruction_count,
2073            basic_block_count: self.basic_block_count,
2074        }
2075    }
2076}
2077
2078/// The source location.
2079#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2080pub enum SourceLocation<'a> {
2081    Path { path: &'a str },
2082    PathAndLine { path: &'a str, line: u32 },
2083    Full { path: &'a str, line: u32, column: u32 },
2084}
2085
2086impl<'a> SourceLocation<'a> {
2087    /// The path to the original source file.
2088    pub fn path(&self) -> &'a str {
2089        match *self {
2090            Self::Path { path, .. } => path,
2091            Self::PathAndLine { path, .. } => path,
2092            Self::Full { path, .. } => path,
2093        }
2094    }
2095
2096    /// The line in the original source file.
2097    pub fn line(&self) -> Option<u32> {
2098        match *self {
2099            Self::Path { .. } => None,
2100            Self::PathAndLine { line, .. } => Some(line),
2101            Self::Full { line, .. } => Some(line),
2102        }
2103    }
2104
2105    /// The column in the original source file.
2106    pub fn column(&self) -> Option<u32> {
2107        match *self {
2108            Self::Path { .. } => None,
2109            Self::PathAndLine { .. } => None,
2110            Self::Full { column, .. } => Some(column),
2111        }
2112    }
2113}
2114
2115impl<'a> core::fmt::Display for SourceLocation<'a> {
2116    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
2117        match *self {
2118            Self::Path { path } => fmt.write_str(path),
2119            Self::PathAndLine { path, line } => write!(fmt, "{}:{}", path, line),
2120            Self::Full { path, line, column } => write!(fmt, "{}:{}:{}", path, line, column),
2121        }
2122    }
2123}
2124
2125#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
2126pub enum FrameKind {
2127    Enter,
2128    Call,
2129    Line,
2130}
2131
2132pub struct FrameInfo<'a> {
2133    blob: &'a ProgramBlob<'a>,
2134    inner: &'a LineProgramFrame,
2135}
2136
2137impl<'a> FrameInfo<'a> {
2138    /// Returns the namespace of this location, if available.
2139    pub fn namespace(&self) -> Result<Option<&str>, ProgramParseError> {
2140        let namespace = self.blob.get_debug_string(self.inner.namespace_offset)?;
2141        if namespace.is_empty() {
2142            Ok(None)
2143        } else {
2144            Ok(Some(namespace))
2145        }
2146    }
2147
2148    /// Returns the function name of location without the namespace, if available.
2149    pub fn function_name_without_namespace(&self) -> Result<Option<&str>, ProgramParseError> {
2150        let function_name = self.blob.get_debug_string(self.inner.function_name_offset)?;
2151        if function_name.is_empty() {
2152            Ok(None)
2153        } else {
2154            Ok(Some(function_name))
2155        }
2156    }
2157
2158    /// Returns the offset into the debug strings section containing the source code path of this location, if available.
2159    pub fn path_debug_string_offset(&self) -> Option<u32> {
2160        if self.inner.path_offset == 0 {
2161            None
2162        } else {
2163            Some(self.inner.path_offset)
2164        }
2165    }
2166
2167    /// Returns the source code path of this location, if available.
2168    pub fn path(&self) -> Result<Option<&str>, ProgramParseError> {
2169        let path = self.blob.get_debug_string(self.inner.path_offset)?;
2170        if path.is_empty() {
2171            Ok(None)
2172        } else {
2173            Ok(Some(path))
2174        }
2175    }
2176
2177    /// Returns the source code line of this location, if available.
2178    pub fn line(&self) -> Option<u32> {
2179        if self.inner.line == 0 {
2180            None
2181        } else {
2182            Some(self.inner.line)
2183        }
2184    }
2185
2186    /// Returns the source code column of this location, if available.
2187    pub fn column(&self) -> Option<u32> {
2188        if self.inner.column == 0 {
2189            None
2190        } else {
2191            Some(self.inner.column)
2192        }
2193    }
2194
2195    pub fn kind(&self) -> FrameKind {
2196        self.inner.kind.unwrap_or(FrameKind::Line)
2197    }
2198
2199    /// Returns the full name of the function.
2200    pub fn full_name(&'_ self) -> Result<impl core::fmt::Display + '_, ProgramParseError> {
2201        Ok(DisplayName {
2202            prefix: self.namespace()?.unwrap_or(""),
2203            suffix: self.function_name_without_namespace()?.unwrap_or(""),
2204        })
2205    }
2206
2207    /// Returns the source location of where this frame comes from.
2208    pub fn location(&self) -> Result<Option<SourceLocation>, ProgramParseError> {
2209        if let Some(path) = self.path()? {
2210            if let Some(line) = self.line() {
2211                if let Some(column) = self.column() {
2212                    Ok(Some(SourceLocation::Full { path, line, column }))
2213                } else {
2214                    Ok(Some(SourceLocation::PathAndLine { path, line }))
2215                }
2216            } else {
2217                Ok(Some(SourceLocation::Path { path }))
2218            }
2219        } else {
2220            Ok(None)
2221        }
2222    }
2223}
2224
2225/// Debug information about a given region of bytecode.
2226pub struct RegionInfo<'a> {
2227    entry_index: usize,
2228    blob: &'a ProgramBlob<'a>,
2229    range: Range<u32>,
2230    frames: &'a [LineProgramFrame],
2231}
2232
2233impl<'a> RegionInfo<'a> {
2234    /// Returns the entry index of this region info within the parent line program object.
2235    pub fn entry_index(&self) -> usize {
2236        self.entry_index
2237    }
2238
2239    /// The range of instructions this region covers.
2240    pub fn instruction_range(&self) -> Range<u32> {
2241        self.range.clone()
2242    }
2243
2244    /// Returns an iterator over the frames this region covers.
2245    pub fn frames(&self) -> impl ExactSizeIterator<Item = FrameInfo> {
2246        self.frames.iter().map(|inner| FrameInfo { blob: self.blob, inner })
2247    }
2248}
2249
2250#[derive(Default)]
2251struct LineProgramFrame {
2252    kind: Option<FrameKind>,
2253    namespace_offset: u32,
2254    function_name_offset: u32,
2255    path_offset: u32,
2256    line: u32,
2257    column: u32,
2258}
2259
2260/// A line program state machine.
2261pub struct LineProgram<'a> {
2262    entry_index: usize,
2263    region_counter: usize,
2264    blob: &'a ProgramBlob<'a>,
2265    reader: Reader<'a>,
2266    is_finished: bool,
2267    program_counter: u32,
2268    // Support inline call stacks ~16 frames deep. Picked entirely arbitrarily.
2269    stack: [LineProgramFrame; 16],
2270    stack_depth: u32,
2271    mutation_depth: u32,
2272}
2273
2274impl<'a> LineProgram<'a> {
2275    /// Returns the entry index of this line program object.
2276    pub fn entry_index(&self) -> usize {
2277        self.entry_index
2278    }
2279
2280    /// Runs the line program until the next region becomes available, or until the program ends.
2281    pub fn run(&mut self) -> Result<Option<RegionInfo>, ProgramParseError> {
2282        struct SetTrueOnDrop<'a>(&'a mut bool);
2283        impl<'a> Drop for SetTrueOnDrop<'a> {
2284            fn drop(&mut self) {
2285                *self.0 = true;
2286            }
2287        }
2288
2289        if self.is_finished {
2290            return Ok(None);
2291        }
2292
2293        // Put an upper limit to how many instructions we'll process.
2294        const INSTRUCTION_LIMIT_PER_REGION: usize = 512;
2295
2296        let mark_as_finished_on_drop = SetTrueOnDrop(&mut self.is_finished);
2297        for _ in 0..INSTRUCTION_LIMIT_PER_REGION {
2298            let byte = match self.reader.read_byte() {
2299                Ok(byte) => byte,
2300                Err(error) => {
2301                    return Err(error);
2302                }
2303            };
2304
2305            let Some(opcode) = LineProgramOp::from_u8(byte) else {
2306                return Err(ProgramParseError(ProgramParseErrorKind::Other(
2307                    "found an unrecognized line program opcode",
2308                )));
2309            };
2310
2311            let (count, stack_depth) = match opcode {
2312                LineProgramOp::FinishProgram => {
2313                    return Ok(None);
2314                }
2315                LineProgramOp::SetMutationDepth => {
2316                    self.mutation_depth = self.reader.read_varint()?;
2317                    continue;
2318                }
2319                LineProgramOp::SetKindEnter => {
2320                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2321                        frame.kind = Some(FrameKind::Enter);
2322                    }
2323                    continue;
2324                }
2325                LineProgramOp::SetKindCall => {
2326                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2327                        frame.kind = Some(FrameKind::Call);
2328                    }
2329                    continue;
2330                }
2331                LineProgramOp::SetKindLine => {
2332                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2333                        frame.kind = Some(FrameKind::Line);
2334                    }
2335                    continue;
2336                }
2337                LineProgramOp::SetNamespace => {
2338                    let value = self.reader.read_varint()?;
2339                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2340                        frame.namespace_offset = value;
2341                    }
2342                    continue;
2343                }
2344                LineProgramOp::SetFunctionName => {
2345                    let value = self.reader.read_varint()?;
2346                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2347                        frame.function_name_offset = value;
2348                    }
2349                    continue;
2350                }
2351                LineProgramOp::SetPath => {
2352                    let value = self.reader.read_varint()?;
2353                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2354                        frame.path_offset = value;
2355                    }
2356                    continue;
2357                }
2358                LineProgramOp::SetLine => {
2359                    let value = self.reader.read_varint()?;
2360                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2361                        frame.line = value;
2362                    }
2363                    continue;
2364                }
2365                LineProgramOp::SetColumn => {
2366                    let value = self.reader.read_varint()?;
2367                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2368                        frame.column = value;
2369                    }
2370                    continue;
2371                }
2372                LineProgramOp::SetStackDepth => {
2373                    self.stack_depth = self.reader.read_varint()?;
2374                    continue;
2375                }
2376                LineProgramOp::IncrementLine => {
2377                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2378                        frame.line += 1;
2379                    }
2380                    continue;
2381                }
2382                LineProgramOp::AddLine => {
2383                    let value = self.reader.read_varint()?;
2384                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2385                        frame.line = frame.line.wrapping_add(value);
2386                    }
2387                    continue;
2388                }
2389                LineProgramOp::SubLine => {
2390                    let value = self.reader.read_varint()?;
2391                    if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
2392                        frame.line = frame.line.wrapping_sub(value);
2393                    }
2394                    continue;
2395                }
2396                LineProgramOp::FinishInstruction => (1, self.stack_depth),
2397                LineProgramOp::FinishMultipleInstructions => {
2398                    let count = self.reader.read_varint()?;
2399                    (count, self.stack_depth)
2400                }
2401                LineProgramOp::FinishInstructionAndIncrementStackDepth => {
2402                    let depth = self.stack_depth;
2403                    self.stack_depth = self.stack_depth.saturating_add(1);
2404                    (1, depth)
2405                }
2406                LineProgramOp::FinishMultipleInstructionsAndIncrementStackDepth => {
2407                    let count = self.reader.read_varint()?;
2408                    let depth = self.stack_depth;
2409                    self.stack_depth = self.stack_depth.saturating_add(1);
2410                    (count, depth)
2411                }
2412                LineProgramOp::FinishInstructionAndDecrementStackDepth => {
2413                    let depth = self.stack_depth;
2414                    self.stack_depth = self.stack_depth.saturating_sub(1);
2415                    (1, depth)
2416                }
2417                LineProgramOp::FinishMultipleInstructionsAndDecrementStackDepth => {
2418                    let count = self.reader.read_varint()?;
2419                    let depth = self.stack_depth;
2420                    self.stack_depth = self.stack_depth.saturating_sub(1);
2421                    (count, depth)
2422                }
2423            };
2424
2425            let range = self.program_counter..self.program_counter + count;
2426            self.program_counter += count;
2427
2428            let frames = &self.stack[..core::cmp::min(stack_depth as usize, self.stack.len())];
2429            core::mem::forget(mark_as_finished_on_drop);
2430
2431            let entry_index = self.region_counter;
2432            self.region_counter += 1;
2433            return Ok(Some(RegionInfo {
2434                entry_index,
2435                blob: self.blob,
2436                range,
2437                frames,
2438            }));
2439        }
2440
2441        Err(ProgramParseError(ProgramParseErrorKind::Other(
2442            "found a line program with too many instructions",
2443        )))
2444    }
2445}
2446
2447struct DisplayName<'a> {
2448    prefix: &'a str,
2449    suffix: &'a str,
2450}
2451
2452impl<'a> core::fmt::Display for DisplayName<'a> {
2453    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
2454        fmt.write_str(self.prefix)?;
2455        if !self.prefix.is_empty() {
2456            fmt.write_str("::")?;
2457        }
2458        fmt.write_str(self.suffix)
2459    }
2460}
2461
2462/// A binary search implementation which can work on chunks of items, and guarantees that it
2463/// will always return the first item if there are multiple identical consecutive items.
2464fn binary_search(slice: &[u8], chunk_size: usize, compare: impl Fn(&[u8]) -> core::cmp::Ordering) -> Result<usize, usize> {
2465    let mut size = slice.len() / chunk_size;
2466    if size == 0 {
2467        return Err(0);
2468    }
2469
2470    let mut base = 0_usize;
2471    while size > 1 {
2472        let half = size / 2;
2473        let mid = base + half;
2474        let item = &slice[mid * chunk_size..(mid + 1) * chunk_size];
2475        match compare(item) {
2476            core::cmp::Ordering::Greater => {
2477                // The value we're looking for is to the left of the midpoint.
2478                size -= half;
2479            }
2480            core::cmp::Ordering::Less => {
2481                // The value we're looking for is to the right of the midpoint.
2482                size -= half;
2483                base = mid;
2484            }
2485            core::cmp::Ordering::Equal => {
2486                // We've found the value, but it might not be the first value.
2487                let previous_item = &slice[(mid - 1) * chunk_size..mid * chunk_size];
2488                if compare(previous_item) != core::cmp::Ordering::Equal {
2489                    // It is the first value.
2490                    return Ok(mid * chunk_size);
2491                }
2492
2493                // It's not the first value. Let's continue.
2494                //
2495                // We could do a linear search here which in the average case
2496                // would probably be faster, but keeping it as a binary search
2497                // will avoid a worst-case O(n) scenario.
2498                size -= half;
2499            }
2500        }
2501    }
2502
2503    let item = &slice[base * chunk_size..(base + 1) * chunk_size];
2504    let ord = compare(item);
2505    if ord == core::cmp::Ordering::Equal {
2506        Ok(base * chunk_size)
2507    } else {
2508        Err((base + usize::from(ord == core::cmp::Ordering::Less)) * chunk_size)
2509    }
2510}
2511
2512#[cfg(test)]
2513extern crate std;
2514
2515#[cfg(test)]
2516proptest::proptest! {
2517    #![proptest_config(proptest::prelude::ProptestConfig::with_cases(20000))]
2518    #[allow(clippy::ignored_unit_patterns)]
2519    #[test]
2520    fn test_binary_search(needle: u8, mut xs: std::vec::Vec<u8>) {
2521        xs.sort();
2522        let binary_result = binary_search(&xs, 1, |slice| slice[0].cmp(&needle));
2523        let mut linear_result = Err(0);
2524        for (index, value) in xs.iter().copied().enumerate() {
2525            #[allow(clippy::comparison_chain)]
2526            if value == needle {
2527                linear_result = Ok(index);
2528                break;
2529            } else if value < needle {
2530                linear_result = Err(index + 1);
2531                continue;
2532            } else {
2533                break;
2534            }
2535        }
2536
2537        assert_eq!(binary_result, linear_result, "linear search = {:?}, binary search = {:?}, needle = {}, xs = {:?}", linear_result, binary_result, needle, xs);
2538    }
2539}
2540
2541/// The magic bytes with which every program blob must start with.
2542pub const BLOB_MAGIC: [u8; 4] = [b'P', b'V', b'M', b'\0'];
2543
2544pub const SECTION_MEMORY_CONFIG: u8 = 1;
2545pub const SECTION_RO_DATA: u8 = 2;
2546pub const SECTION_RW_DATA: u8 = 3;
2547pub const SECTION_IMPORTS: u8 = 4;
2548pub const SECTION_EXPORTS: u8 = 5;
2549pub const SECTION_JUMP_TABLE: u8 = 6;
2550pub const SECTION_CODE: u8 = 7;
2551pub const SECTION_OPT_DEBUG_STRINGS: u8 = 128;
2552pub const SECTION_OPT_DEBUG_LINE_PROGRAMS: u8 = 129;
2553pub const SECTION_OPT_DEBUG_LINE_PROGRAM_RANGES: u8 = 130;
2554pub const SECTION_END_OF_FILE: u8 = 0;
2555
2556pub const BLOB_VERSION_V1: u8 = 1;
2557
2558pub const VERSION_DEBUG_LINE_PROGRAM_V1: u8 = 1;
2559
2560#[derive(Copy, Clone, Debug)]
2561pub enum LineProgramOp {
2562    FinishProgram = 0,
2563    SetMutationDepth = 1,
2564    SetKindEnter = 2,
2565    SetKindCall = 3,
2566    SetKindLine = 4,
2567    SetNamespace = 5,
2568    SetFunctionName = 6,
2569    SetPath = 7,
2570    SetLine = 8,
2571    SetColumn = 9,
2572    SetStackDepth = 10,
2573    IncrementLine = 11,
2574    AddLine = 12,
2575    SubLine = 13,
2576    FinishInstruction = 14,
2577    FinishMultipleInstructions = 15,
2578    FinishInstructionAndIncrementStackDepth = 16,
2579    FinishMultipleInstructionsAndIncrementStackDepth = 17,
2580    FinishInstructionAndDecrementStackDepth = 18,
2581    FinishMultipleInstructionsAndDecrementStackDepth = 19,
2582}
2583
2584impl LineProgramOp {
2585    #[inline]
2586    pub const fn from_u8(value: u8) -> Option<Self> {
2587        match value {
2588            0 => Some(Self::FinishProgram),
2589            1 => Some(Self::SetMutationDepth),
2590            2 => Some(Self::SetKindEnter),
2591            3 => Some(Self::SetKindCall),
2592            4 => Some(Self::SetKindLine),
2593            5 => Some(Self::SetNamespace),
2594            6 => Some(Self::SetFunctionName),
2595            7 => Some(Self::SetPath),
2596            8 => Some(Self::SetLine),
2597            9 => Some(Self::SetColumn),
2598            10 => Some(Self::SetStackDepth),
2599            11 => Some(Self::IncrementLine),
2600            12 => Some(Self::AddLine),
2601            13 => Some(Self::SubLine),
2602            14 => Some(Self::FinishInstruction),
2603            15 => Some(Self::FinishMultipleInstructions),
2604            16 => Some(Self::FinishInstructionAndIncrementStackDepth),
2605            17 => Some(Self::FinishMultipleInstructionsAndIncrementStackDepth),
2606            18 => Some(Self::FinishInstructionAndDecrementStackDepth),
2607            19 => Some(Self::FinishMultipleInstructionsAndDecrementStackDepth),
2608            _ => None,
2609        }
2610    }
2611}