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