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().div_ceil(8));
209 debug_assert!(offset as usize <= code.len());
210 debug_assert!(get_bit_for_offset(bitmask, code.len(), offset), "bit at {offset} is zero");
211
212 let (skip, mut is_next_instruction_invalid) = parse_bitmask_slow(bitmask, code.len(), offset);
213 let chunk = &code[offset as usize..core::cmp::min(offset as usize + 17, code.len())];
214 let opcode = chunk[0];
215
216 if is_next_instruction_invalid && offset as usize + skip as usize + 1 >= code.len() {
217 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().div_ceil(8));
260 debug_assert!(offset as usize <= code.len());
261 debug_assert!(get_bit_for_offset(bitmask, code.len(), offset), "bit at {offset} is zero");
262
263 debug_assert!(offset as usize + 32 <= code.len());
264
265 let Some(chunk) = code.get(offset as usize..offset as usize + 32) else {
266 unreachable!()
267 };
268 let Some(skip) = parse_bitmask_fast(bitmask, offset) else {
269 unreachable!()
270 };
271 let opcode = usize::from(chunk[0]);
272
273 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
3126impl core::error::Error for ProgramParseError {}
3127
3128#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
3129#[repr(transparent)]
3130pub struct ProgramCounter(pub u32);
3131
3132impl core::fmt::Display for ProgramCounter {
3133 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
3134 self.0.fmt(fmt)
3135 }
3136}
3137
3138#[derive(Clone, PartialEq, Eq, Debug)]
3139pub struct ProgramExport<T> {
3140 program_counter: ProgramCounter,
3141 symbol: ProgramSymbol<T>,
3142}
3143
3144impl<T> ProgramExport<T>
3145where
3146 T: AsRef<[u8]>,
3147{
3148 pub fn new(program_counter: ProgramCounter, symbol: ProgramSymbol<T>) -> Self {
3149 Self { program_counter, symbol }
3150 }
3151
3152 pub fn program_counter(&self) -> ProgramCounter {
3153 self.program_counter
3154 }
3155
3156 pub fn symbol(&self) -> &ProgramSymbol<T> {
3157 &self.symbol
3158 }
3159}
3160
3161impl<T> PartialEq<str> for ProgramExport<T>
3162where
3163 T: AsRef<[u8]>,
3164{
3165 fn eq(&self, rhs: &str) -> bool {
3166 self.symbol.as_bytes() == rhs.as_bytes()
3167 }
3168}
3169
3170#[derive(Clone, PartialEq, Eq, Debug)]
3171pub struct ProgramSymbol<T>(T);
3172
3173impl<T> ProgramSymbol<T>
3174where
3175 T: AsRef<[u8]>,
3176{
3177 pub fn new(bytes: T) -> Self {
3178 Self(bytes)
3179 }
3180
3181 pub fn into_inner(self) -> T {
3182 self.0
3183 }
3184
3185 pub fn as_bytes(&self) -> &[u8] {
3186 self.0.as_ref()
3187 }
3188}
3189
3190impl<T> PartialEq<str> for ProgramSymbol<T>
3191where
3192 T: AsRef<[u8]>,
3193{
3194 fn eq(&self, rhs: &str) -> bool {
3195 self.as_bytes() == rhs.as_bytes()
3196 }
3197}
3198
3199impl<'a, T> PartialEq<&'a str> for ProgramSymbol<T>
3200where
3201 T: AsRef<[u8]>,
3202{
3203 fn eq(&self, rhs: &&'a str) -> bool {
3204 self.as_bytes() == rhs.as_bytes()
3205 }
3206}
3207
3208impl<T> core::fmt::Display for ProgramSymbol<T>
3209where
3210 T: AsRef<[u8]>,
3211{
3212 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
3213 let bytes = self.0.as_ref();
3214 if let Ok(ident) = core::str::from_utf8(bytes) {
3215 fmt.write_str("'")?;
3216 fmt.write_str(ident)?;
3217 fmt.write_str("'")?;
3218 } else {
3219 fmt.write_str("0x")?;
3220 for &byte in bytes.iter() {
3221 core::write!(fmt, "{:02x}", byte)?;
3222 }
3223 }
3224
3225 Ok(())
3226 }
3227}
3228
3229#[derive(Clone, Default)]
3231pub struct ProgramBlob {
3232 #[cfg(feature = "unique-id")]
3233 unique_id: u64,
3234
3235 is_64_bit: bool,
3236
3237 ro_data_size: u32,
3238 rw_data_size: u32,
3239 stack_size: u32,
3240
3241 ro_data: ArcBytes,
3242 rw_data: ArcBytes,
3243 code: ArcBytes,
3244 jump_table: ArcBytes,
3245 jump_table_entry_size: u8,
3246 bitmask: ArcBytes,
3247 import_offsets: ArcBytes,
3248 import_symbols: ArcBytes,
3249 exports: ArcBytes,
3250
3251 debug_strings: ArcBytes,
3252 debug_line_program_ranges: ArcBytes,
3253 debug_line_programs: ArcBytes,
3254}
3255
3256struct Reader<'a, T>
3257where
3258 T: ?Sized,
3259{
3260 blob: &'a T,
3261 position: usize,
3262}
3263
3264impl<'a, T> Clone for Reader<'a, T>
3265where
3266 T: ?Sized,
3267{
3268 fn clone(&self) -> Self {
3269 Reader {
3270 blob: self.blob,
3271 position: self.position,
3272 }
3273 }
3274}
3275
3276impl<'a, T> From<&'a T> for Reader<'a, T> {
3277 fn from(blob: &'a T) -> Self {
3278 Self { blob, position: 0 }
3279 }
3280}
3281
3282impl<'a, T> Reader<'a, T>
3283where
3284 T: ?Sized + AsRef<[u8]>,
3285{
3286 fn skip(&mut self, count: usize) -> Result<(), ProgramParseError> {
3287 self.read_slice_as_range(count).map(|_| ())
3288 }
3289
3290 #[inline(always)]
3291 fn read_byte(&mut self) -> Result<u8, ProgramParseError> {
3292 Ok(self.read_slice(1)?[0])
3293 }
3294
3295 #[inline(always)]
3296 fn read_slice(&mut self, length: usize) -> Result<&'a [u8], ProgramParseError> {
3297 let blob = &self.blob.as_ref()[self.position..];
3298 let Some(slice) = blob.get(..length) else {
3299 return Err(ProgramParseError::unexpected_end_of_file(self.position, length, blob.len()));
3300 };
3301
3302 self.position += length;
3303 Ok(slice)
3304 }
3305
3306 #[inline(always)]
3307 fn read_varint(&mut self) -> Result<u32, ProgramParseError> {
3308 let first_byte = self.read_byte()?;
3309 let Some((length, value)) = read_varint(&self.blob.as_ref()[self.position..], first_byte) else {
3310 return Err(ProgramParseError::failed_to_read_varint(self.position - 1));
3311 };
3312
3313 self.position += length;
3314 Ok(value)
3315 }
3316
3317 fn read_bytes_with_length(&mut self) -> Result<&'a [u8], ProgramParseError> {
3318 let length = self.read_varint()? as usize;
3319 self.read_slice(length)
3320 }
3321
3322 fn read_string_with_length(&mut self) -> Result<&'a str, ProgramParseError> {
3323 let offset = self.position;
3324 let slice = self.read_bytes_with_length()?;
3325
3326 core::str::from_utf8(slice)
3327 .ok()
3328 .ok_or(ProgramParseError(ProgramParseErrorKind::FailedToReadStringNonUtf { offset }))
3329 }
3330
3331 fn read_slice_as_range(&mut self, count: usize) -> Result<Range<usize>, ProgramParseError> {
3332 let blob = &self.blob.as_ref()[self.position..];
3333 if blob.len() < count {
3334 return Err(ProgramParseError::unexpected_end_of_file(self.position, count, blob.len()));
3335 };
3336
3337 let range = self.position..self.position + count;
3338 self.position += count;
3339 Ok(range)
3340 }
3341}
3342
3343impl<'a> Reader<'a, ArcBytes> {
3344 fn read_slice_as_bytes(&mut self, length: usize) -> Result<ArcBytes, ProgramParseError> {
3345 let range = self.read_slice_as_range(length)?;
3346 Ok(self.blob.subslice(range))
3347 }
3348
3349 fn read_section_as_bytes(&mut self, out_section: &mut u8, expected_section: u8) -> Result<ArcBytes, ProgramParseError> {
3350 if *out_section != expected_section {
3351 return Ok(ArcBytes::default());
3352 }
3353
3354 let section_length = self.read_varint()? as usize;
3355 let range = self.read_slice_as_range(section_length)?;
3356 *out_section = self.read_byte()?;
3357
3358 Ok(self.blob.subslice(range))
3359 }
3360}
3361
3362#[derive(Copy, Clone)]
3363pub struct Imports<'a> {
3364 offsets: &'a [u8],
3365 symbols: &'a [u8],
3366}
3367
3368impl<'a> Imports<'a> {
3369 pub fn is_empty(&self) -> bool {
3370 self.len() == 0
3371 }
3372
3373 pub fn len(&self) -> u32 {
3374 (self.offsets.len() / 4) as u32
3375 }
3376
3377 pub fn get(&self, index: u32) -> Option<ProgramSymbol<&'a [u8]>> {
3378 let offset_start = index.checked_mul(4)?;
3379 let offset_end = offset_start.checked_add(4)?;
3380 let xs = self.offsets.get(offset_start as usize..offset_end as usize)?;
3381 let offset = u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]) as usize;
3382 let next_offset = offset_end
3383 .checked_add(4)
3384 .and_then(|next_offset_end| self.offsets.get(offset_end as usize..next_offset_end as usize))
3385 .map_or(self.symbols.len(), |xs| u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]) as usize);
3386
3387 let symbol = self.symbols.get(offset..next_offset)?;
3388 Some(ProgramSymbol::new(symbol))
3389 }
3390
3391 pub fn iter(&self) -> ImportsIter<'a> {
3392 ImportsIter { imports: *self, index: 0 }
3393 }
3394}
3395
3396impl<'a> IntoIterator for Imports<'a> {
3397 type Item = Option<ProgramSymbol<&'a [u8]>>;
3398 type IntoIter = ImportsIter<'a>;
3399
3400 fn into_iter(self) -> Self::IntoIter {
3401 self.iter()
3402 }
3403}
3404
3405impl<'a> IntoIterator for &'a Imports<'a> {
3406 type Item = Option<ProgramSymbol<&'a [u8]>>;
3407 type IntoIter = ImportsIter<'a>;
3408
3409 fn into_iter(self) -> Self::IntoIter {
3410 self.iter()
3411 }
3412}
3413
3414pub struct ImportsIter<'a> {
3415 imports: Imports<'a>,
3416 index: u32,
3417}
3418
3419impl<'a> Iterator for ImportsIter<'a> {
3420 type Item = Option<ProgramSymbol<&'a [u8]>>;
3421 fn next(&mut self) -> Option<Self::Item> {
3422 if self.index >= self.imports.len() {
3423 None
3424 } else {
3425 let value = self.imports.get(self.index);
3426 self.index += 1;
3427 Some(value)
3428 }
3429 }
3430}
3431
3432#[derive(Copy, Clone)]
3433pub struct JumpTable<'a> {
3434 blob: &'a [u8],
3435 entry_size: u32,
3436}
3437
3438impl<'a> JumpTable<'a> {
3439 pub fn is_empty(&self) -> bool {
3440 self.len() == 0
3441 }
3442
3443 pub fn len(&self) -> u32 {
3444 if self.entry_size == 0 {
3445 0
3446 } else {
3447 self.blob.len() as u32 / self.entry_size
3448 }
3449 }
3450
3451 pub fn get_by_address(&self, address: u32) -> Option<ProgramCounter> {
3452 if address & (VM_CODE_ADDRESS_ALIGNMENT - 1) != 0 || address == 0 {
3453 return None;
3454 }
3455
3456 self.get_by_index((address - VM_CODE_ADDRESS_ALIGNMENT) / VM_CODE_ADDRESS_ALIGNMENT)
3457 }
3458
3459 pub fn get_by_index(&self, index: u32) -> Option<ProgramCounter> {
3460 if self.entry_size == 0 {
3461 return None;
3462 }
3463
3464 let start = index.checked_mul(self.entry_size)?;
3465 let end = start.checked_add(self.entry_size)?;
3466 self.blob
3467 .get(start as usize..end as usize)
3468 .map(|xs| match xs.len() {
3469 1 => u32::from(xs[0]),
3470 2 => u32::from(u16::from_le_bytes([xs[0], xs[1]])),
3471 3 => u32::from_le_bytes([xs[0], xs[1], xs[2], 0]),
3472 4 => u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]),
3473 _ => unreachable!(),
3474 })
3475 .map(ProgramCounter)
3476 }
3477
3478 pub fn iter(&self) -> JumpTableIter<'a> {
3479 JumpTableIter {
3480 jump_table: *self,
3481 index: 0,
3482 }
3483 }
3484}
3485
3486impl<'a> IntoIterator for JumpTable<'a> {
3487 type Item = ProgramCounter;
3488 type IntoIter = JumpTableIter<'a>;
3489
3490 fn into_iter(self) -> Self::IntoIter {
3491 self.iter()
3492 }
3493}
3494
3495impl<'a> IntoIterator for &'a JumpTable<'a> {
3496 type Item = ProgramCounter;
3497 type IntoIter = JumpTableIter<'a>;
3498
3499 fn into_iter(self) -> Self::IntoIter {
3500 self.iter()
3501 }
3502}
3503
3504pub struct JumpTableIter<'a> {
3505 jump_table: JumpTable<'a>,
3506 index: u32,
3507}
3508
3509impl<'a> Iterator for JumpTableIter<'a> {
3510 type Item = ProgramCounter;
3511 fn next(&mut self) -> Option<Self::Item> {
3512 let value = self.jump_table.get_by_index(self.index)?;
3513 self.index += 1;
3514 Some(value)
3515 }
3516}
3517
3518pub const BITMASK_MAX: u32 = 24;
3519
3520pub fn get_bit_for_offset(bitmask: &[u8], code_len: usize, offset: u32) -> bool {
3521 let Some(byte) = bitmask.get(offset as usize >> 3) else {
3522 return false;
3523 };
3524
3525 if offset as usize > code_len {
3526 return false;
3527 }
3528
3529 let shift = offset & 7;
3530 ((byte >> shift) & 1) == 1
3531}
3532
3533fn get_previous_instruction_skip(bitmask: &[u8], offset: u32) -> Option<u32> {
3534 let shift = offset & 7;
3535 let mut mask = u32::from(bitmask[offset as usize >> 3]) << 24;
3536 if offset >= 8 {
3537 mask |= u32::from(bitmask[(offset as usize >> 3) - 1]) << 16;
3538 }
3539 if offset >= 16 {
3540 mask |= u32::from(bitmask[(offset as usize >> 3) - 2]) << 8;
3541 }
3542 if offset >= 24 {
3543 mask |= u32::from(bitmask[(offset as usize >> 3) - 3]);
3544 }
3545
3546 mask <<= 8 - shift;
3547 mask >>= 1;
3548 let skip = mask.leading_zeros() - 1;
3549 if skip > BITMASK_MAX {
3550 None
3551 } else {
3552 Some(skip)
3553 }
3554}
3555
3556#[test]
3557fn test_get_previous_instruction_skip() {
3558 assert_eq!(get_previous_instruction_skip(&[0b00000001], 0), None);
3559 assert_eq!(get_previous_instruction_skip(&[0b00000011], 0), None);
3560 assert_eq!(get_previous_instruction_skip(&[0b00000010], 1), None);
3561 assert_eq!(get_previous_instruction_skip(&[0b00000011], 1), Some(0));
3562 assert_eq!(get_previous_instruction_skip(&[0b00000001], 1), Some(0));
3563 assert_eq!(get_previous_instruction_skip(&[0b00000001, 0b00000001], 8), Some(7));
3564 assert_eq!(get_previous_instruction_skip(&[0b00000001, 0b00000000], 8), Some(7));
3565}
3566
3567pub trait InstructionSet: Copy {
3568 fn opcode_from_u8(self, byte: u8) -> Option<Opcode>;
3569}
3570
3571#[allow(non_camel_case_types)]
3572#[derive(Copy, Clone, Debug, Default)]
3573pub struct ISA32_V1;
3574
3575#[allow(non_camel_case_types)]
3576#[derive(Copy, Clone, Debug, Default)]
3577pub struct ISA32_V1_NoSbrk;
3578
3579#[allow(non_camel_case_types)]
3580#[derive(Copy, Clone, Debug, Default)]
3581pub struct ISA64_V1;
3582
3583#[allow(non_camel_case_types)]
3584#[derive(Copy, Clone, Debug, Default)]
3585pub struct ISA64_V1_NoSbrk;
3586
3587pub type DefaultInstructionSet = ISA32_V1;
3588
3589#[inline]
3591pub fn is_jump_target_valid<I>(instruction_set: I, code: &[u8], bitmask: &[u8], offset: u32) -> bool
3592where
3593 I: InstructionSet,
3594{
3595 if !get_bit_for_offset(bitmask, code.len(), offset) {
3596 return false;
3598 }
3599
3600 if offset == 0 {
3601 return true;
3603 }
3604
3605 let Some(skip) = get_previous_instruction_skip(bitmask, offset) else {
3606 return false;
3608 };
3609
3610 let Some(opcode) = instruction_set.opcode_from_u8(code[offset as usize - skip as usize - 1]) else {
3611 return false;
3613 };
3614
3615 if !opcode.starts_new_basic_block() {
3616 return false;
3618 }
3619
3620 true
3621}
3622
3623#[inline]
3624pub fn find_start_of_basic_block<I>(instruction_set: I, code: &[u8], bitmask: &[u8], mut offset: u32) -> Option<u32>
3625where
3626 I: InstructionSet,
3627{
3628 if !get_bit_for_offset(bitmask, code.len(), offset) {
3629 return None;
3631 }
3632
3633 if offset == 0 {
3634 return Some(0);
3636 }
3637
3638 loop {
3639 let skip = get_previous_instruction_skip(bitmask, offset)?;
3641 let previous_offset = offset - skip - 1;
3642 let opcode = instruction_set
3643 .opcode_from_u8(code[previous_offset as usize])
3644 .unwrap_or(Opcode::trap);
3645 if opcode.starts_new_basic_block() {
3646 return Some(offset);
3648 }
3649
3650 offset = previous_offset;
3651 if offset == 0 {
3652 return Some(0);
3653 }
3654 }
3655}
3656
3657#[test]
3658fn test_is_jump_target_valid() {
3659 fn assert_get_previous_instruction_skip_matches_instruction_parser(code: &[u8], bitmask: &[u8]) {
3660 for instruction in Instructions::new(DefaultInstructionSet::default(), code, bitmask, 0, false) {
3661 match instruction.kind {
3662 Instruction::trap => {
3663 let skip = get_previous_instruction_skip(bitmask, instruction.offset.0);
3664 if let Some(skip) = skip {
3665 let previous_offset = instruction.offset.0 - skip - 1;
3666 assert_eq!(
3667 Instructions::new(DefaultInstructionSet::default(), code, bitmask, previous_offset, true)
3668 .next()
3669 .unwrap(),
3670 ParsedInstruction {
3671 kind: Instruction::trap,
3672 offset: ProgramCounter(previous_offset),
3673 next_offset: instruction.offset,
3674 }
3675 );
3676 } else {
3677 for skip in 0..=24 {
3678 let Some(previous_offset) = instruction.offset.0.checked_sub(skip + 1) else {
3679 continue;
3680 };
3681 assert_eq!(
3682 Instructions::new(DefaultInstructionSet::default(), code, bitmask, previous_offset, true)
3683 .next()
3684 .unwrap()
3685 .kind,
3686 Instruction::invalid,
3687 );
3688 }
3689 }
3690 }
3691 Instruction::invalid => {}
3692 _ => unreachable!(),
3693 }
3694 }
3695 }
3696
3697 macro_rules! g {
3698 ($code_length:expr, $bits:expr) => {{
3699 let mut bitmask = [0; {
3700 let value: usize = $code_length;
3701 value.div_ceil(8)
3702 }];
3703 for bit in $bits {
3704 let bit: usize = bit;
3705 assert!(bit < $code_length);
3706 bitmask[bit / 8] |= (1 << (bit % 8));
3707 }
3708
3709 let code = [Opcode::trap as u8; $code_length];
3710 assert_get_previous_instruction_skip_matches_instruction_parser(&code, &bitmask);
3711 (code, bitmask)
3712 }};
3713 }
3714
3715 assert_eq!(g!(1, [0]).1, [0b00000001]);
3717 assert_eq!(g!(2, [1]).1, [0b00000010]);
3718 assert_eq!(g!(8, [7]).1, [0b10000000]);
3719 assert_eq!(g!(9, [8]).1, [0b00000000, 0b00000001]);
3720 assert_eq!(g!(10, [9]).1, [0b00000000, 0b00000010]);
3721 assert_eq!(g!(10, [2, 9]).1, [0b00000100, 0b00000010]);
3722
3723 macro_rules! assert_valid {
3724 ($code_length:expr, $bits:expr, $offset:expr) => {{
3725 let (code, bitmask) = g!($code_length, $bits);
3726 assert!(is_jump_target_valid(DefaultInstructionSet::default(), &code, &bitmask, $offset));
3727 }};
3728 }
3729
3730 macro_rules! assert_invalid {
3731 ($code_length:expr, $bits:expr, $offset:expr) => {{
3732 let (code, bitmask) = g!($code_length, $bits);
3733 assert!(!is_jump_target_valid(DefaultInstructionSet::default(), &code, &bitmask, $offset));
3734 }};
3735 }
3736
3737 assert_valid!(1, [0], 0);
3738 assert_invalid!(1, [], 0);
3739 assert_valid!(2, [0, 1], 1);
3740 assert_invalid!(2, [1], 1);
3741 assert_valid!(8, [0, 7], 7);
3742 assert_valid!(9, [0, 8], 8);
3743 assert_valid!(25, [0, 24], 24);
3744 assert_valid!(26, [0, 25], 25);
3745 assert_invalid!(27, [0, 26], 26);
3746
3747 assert!(is_jump_target_valid(
3748 DefaultInstructionSet::default(),
3749 &[Opcode::load_imm as u8],
3750 &[0b00000001],
3751 0
3752 ));
3753
3754 assert!(!is_jump_target_valid(
3755 DefaultInstructionSet::default(),
3756 &[Opcode::load_imm as u8, Opcode::load_imm as u8],
3757 &[0b00000011],
3758 1
3759 ));
3760
3761 assert!(is_jump_target_valid(
3762 DefaultInstructionSet::default(),
3763 &[Opcode::trap as u8, Opcode::load_imm as u8],
3764 &[0b00000011],
3765 1
3766 ));
3767}
3768
3769#[cfg_attr(not(debug_assertions), inline(always))]
3770fn parse_bitmask_slow(bitmask: &[u8], code_length: usize, offset: u32) -> (u32, bool) {
3771 let mut offset = offset as usize + 1;
3772 let mut is_next_instruction_invalid = true;
3773 let origin = offset;
3774 while let Some(&byte) = bitmask.get(offset >> 3) {
3775 let shift = offset & 7;
3776 let mask = byte >> shift;
3777 if mask == 0 {
3778 offset += 8 - shift;
3779 if (offset - origin) < BITMASK_MAX as usize {
3780 continue;
3781 }
3782 } else {
3783 offset += mask.trailing_zeros() as usize;
3784 is_next_instruction_invalid = offset >= code_length || (offset - origin) > BITMASK_MAX as usize;
3785 }
3786 break;
3787 }
3788
3789 use core::cmp::min;
3790 let offset = min(offset, code_length);
3791 let skip = min((offset - origin) as u32, BITMASK_MAX);
3792 (skip, is_next_instruction_invalid)
3793}
3794
3795#[cfg_attr(not(debug_assertions), inline(always))]
3796pub(crate) fn parse_bitmask_fast(bitmask: &[u8], mut offset: u32) -> Option<u32> {
3797 debug_assert!(offset < u32::MAX);
3798 debug_assert!(get_bit_for_offset(bitmask, offset as usize + 1, offset));
3799 offset += 1;
3800
3801 let bitmask = bitmask.get(offset as usize >> 3..(offset as usize >> 3) + 4)?;
3802 let shift = offset & 7;
3803 let mask: u32 = (u32::from_le_bytes([bitmask[0], bitmask[1], bitmask[2], bitmask[3]]) >> shift) | (1 << BITMASK_MAX);
3804 Some(mask.trailing_zeros())
3805}
3806
3807#[test]
3808fn test_parse_bitmask() {
3809 #[track_caller]
3810 fn parse_both(bitmask: &[u8], offset: u32) -> u32 {
3811 let result_fast = parse_bitmask_fast(bitmask, offset).unwrap();
3812 let result_slow = parse_bitmask_slow(bitmask, bitmask.len() * 8, offset).0;
3813 assert_eq!(result_fast, result_slow);
3814
3815 result_fast
3816 }
3817
3818 assert_eq!(parse_both(&[0b00000011, 0, 0, 0], 0), 0);
3819 assert_eq!(parse_both(&[0b00000101, 0, 0, 0], 0), 1);
3820 assert_eq!(parse_both(&[0b10000001, 0, 0, 0], 0), 6);
3821 assert_eq!(parse_both(&[0b00000001, 1, 0, 0], 0), 7);
3822 assert_eq!(parse_both(&[0b00000001, 1 << 7, 0, 0], 0), 14);
3823 assert_eq!(parse_both(&[0b00000001, 0, 1, 0], 0), 15);
3824 assert_eq!(parse_both(&[0b00000001, 0, 1 << 7, 0], 0), 22);
3825 assert_eq!(parse_both(&[0b00000001, 0, 0, 1], 0), 23);
3826
3827 assert_eq!(parse_both(&[0b11000000, 0, 0, 0, 0], 6), 0);
3828 assert_eq!(parse_both(&[0b01000000, 1, 0, 0, 0], 6), 1);
3829
3830 assert_eq!(parse_both(&[0b10000000, 1, 0, 0, 0], 7), 0);
3831 assert_eq!(parse_both(&[0b10000000, 1 << 1, 0, 0, 0], 7), 1);
3832}
3833
3834#[derive(Clone)]
3835pub struct Instructions<'a, I> {
3836 code: &'a [u8],
3837 bitmask: &'a [u8],
3838 offset: u32,
3839 invalid_offset: Option<u32>,
3840 is_bounded: bool,
3841 is_done: bool,
3842 instruction_set: I,
3843}
3844
3845#[derive(Copy, Clone, PartialEq, Eq, Debug)]
3846pub struct ParsedInstruction {
3847 pub kind: Instruction,
3848 pub offset: ProgramCounter,
3849 pub next_offset: ProgramCounter,
3850}
3851
3852impl core::ops::Deref for ParsedInstruction {
3853 type Target = Instruction;
3854
3855 #[inline]
3856 fn deref(&self) -> &Self::Target {
3857 &self.kind
3858 }
3859}
3860
3861impl core::fmt::Display for ParsedInstruction {
3862 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
3863 write!(fmt, "{:>7}: {}", self.offset, self.kind)
3864 }
3865}
3866
3867impl<'a, I> Instructions<'a, I>
3868where
3869 I: InstructionSet,
3870{
3871 #[inline]
3872 pub fn new_bounded(instruction_set: I, code: &'a [u8], bitmask: &'a [u8], offset: u32) -> Self {
3873 Self::new(instruction_set, code, bitmask, offset, true)
3874 }
3875
3876 #[inline]
3877 pub fn new_unbounded(instruction_set: I, code: &'a [u8], bitmask: &'a [u8], offset: u32) -> Self {
3878 Self::new(instruction_set, code, bitmask, offset, false)
3879 }
3880
3881 #[inline]
3882 fn new(instruction_set: I, code: &'a [u8], bitmask: &'a [u8], offset: u32, is_bounded: bool) -> Self {
3883 assert!(code.len() <= u32::MAX as usize);
3884 assert_eq!(bitmask.len(), code.len().div_ceil(8));
3885
3886 let is_valid = get_bit_for_offset(bitmask, code.len(), offset);
3887 let mut is_done = false;
3888 let (offset, invalid_offset) = if is_valid {
3889 (offset, None)
3890 } else if is_bounded {
3891 is_done = true;
3892 (core::cmp::min(offset + 1, code.len() as u32), Some(offset))
3893 } else {
3894 let next_offset = find_next_offset_unbounded(bitmask, code.len() as u32, offset);
3895 debug_assert!(
3896 next_offset as usize == code.len() || get_bit_for_offset(bitmask, code.len(), next_offset),
3897 "bit at {offset} is zero"
3898 );
3899 (next_offset, Some(offset))
3900 };
3901
3902 Self {
3903 code,
3904 bitmask,
3905 offset,
3906 invalid_offset,
3907 is_bounded,
3908 is_done,
3909 instruction_set,
3910 }
3911 }
3912
3913 #[inline]
3914 pub fn offset(&self) -> u32 {
3915 self.invalid_offset.unwrap_or(self.offset)
3916 }
3917
3918 #[inline]
3919 pub fn visit<T>(&mut self, visitor: &mut T) -> Option<<T as InstructionVisitor>::ReturnTy>
3920 where
3921 T: InstructionVisitor,
3922 {
3923 Some(self.next()?.visit(visitor))
3925 }
3926}
3927
3928impl<'a, I> Iterator for Instructions<'a, I>
3929where
3930 I: InstructionSet,
3931{
3932 type Item = ParsedInstruction;
3933
3934 #[inline(always)]
3935 fn next(&mut self) -> Option<Self::Item> {
3936 if let Some(offset) = self.invalid_offset.take() {
3937 return Some(ParsedInstruction {
3938 kind: Instruction::invalid,
3939 offset: ProgramCounter(offset),
3940 next_offset: ProgramCounter(self.offset),
3941 });
3942 }
3943
3944 if self.is_done || self.offset as usize >= self.code.len() {
3945 return None;
3946 }
3947
3948 let offset = self.offset;
3949 debug_assert!(get_bit_for_offset(self.bitmask, self.code.len(), offset), "bit at {offset} is zero");
3950
3951 let (next_offset, instruction, is_next_instruction_invalid) =
3952 parse_instruction(self.instruction_set, self.code, self.bitmask, self.offset);
3953 debug_assert!(next_offset > self.offset);
3954
3955 if !is_next_instruction_invalid {
3956 self.offset = next_offset;
3957 debug_assert!(
3958 self.offset as usize == self.code.len() || get_bit_for_offset(self.bitmask, self.code.len(), self.offset),
3959 "bit at {} is zero",
3960 self.offset
3961 );
3962 } else {
3963 if next_offset as usize == self.code.len() {
3964 self.offset = self.code.len() as u32 + 1;
3965 } else if self.is_bounded {
3966 self.is_done = true;
3967 if instruction.opcode().can_fallthrough() {
3968 self.offset = self.code.len() as u32;
3969 } else {
3970 self.offset = next_offset;
3971 }
3972 } else {
3973 self.offset = find_next_offset_unbounded(self.bitmask, self.code.len() as u32, next_offset);
3974 debug_assert!(
3975 self.offset as usize == self.code.len() || get_bit_for_offset(self.bitmask, self.code.len(), self.offset),
3976 "bit at {} is zero",
3977 self.offset
3978 );
3979 }
3980
3981 if instruction.opcode().can_fallthrough() {
3982 self.invalid_offset = Some(next_offset);
3983 }
3984 }
3985
3986 Some(ParsedInstruction {
3987 kind: instruction,
3988 offset: ProgramCounter(offset),
3989 next_offset: ProgramCounter(next_offset),
3990 })
3991 }
3992
3993 fn size_hint(&self) -> (usize, Option<usize>) {
3994 (0, Some(self.code.len() - core::cmp::min(self.offset() as usize, self.code.len())))
3995 }
3996}
3997
3998#[test]
3999fn test_instructions_iterator_with_implicit_trap() {
4000 for is_bounded in [false, true] {
4001 let mut i = Instructions::new(
4002 DefaultInstructionSet::default(),
4003 &[Opcode::fallthrough as u8],
4004 &[0b00000001],
4005 0,
4006 is_bounded,
4007 );
4008 assert_eq!(
4009 i.next(),
4010 Some(ParsedInstruction {
4011 kind: Instruction::fallthrough,
4012 offset: ProgramCounter(0),
4013 next_offset: ProgramCounter(1),
4014 })
4015 );
4016
4017 assert_eq!(
4018 i.next(),
4019 Some(ParsedInstruction {
4020 kind: Instruction::invalid,
4021 offset: ProgramCounter(1),
4022 next_offset: ProgramCounter(2),
4023 })
4024 );
4025
4026 assert_eq!(i.next(), None);
4027 }
4028}
4029
4030#[test]
4031fn test_instructions_iterator_without_implicit_trap() {
4032 for is_bounded in [false, true] {
4033 let mut i = Instructions::new(
4034 DefaultInstructionSet::default(),
4035 &[Opcode::trap as u8],
4036 &[0b00000001],
4037 0,
4038 is_bounded,
4039 );
4040 assert_eq!(
4041 i.next(),
4042 Some(ParsedInstruction {
4043 kind: Instruction::trap,
4044 offset: ProgramCounter(0),
4045 next_offset: ProgramCounter(1),
4046 })
4047 );
4048
4049 assert_eq!(i.next(), None);
4050 }
4051}
4052
4053#[test]
4054fn test_instructions_iterator_very_long_bitmask_bounded() {
4055 let mut code = [0_u8; 64];
4056 code[0] = Opcode::fallthrough as u8;
4057 let mut bitmask = [0_u8; 8];
4058 bitmask[0] = 0b00000001;
4059 bitmask[7] = 0b10000000;
4060
4061 let mut i = Instructions::new(DefaultInstructionSet::default(), &code, &bitmask, 0, true);
4062 assert_eq!(
4063 i.next(),
4064 Some(ParsedInstruction {
4065 kind: Instruction::fallthrough,
4066 offset: ProgramCounter(0),
4067 next_offset: ProgramCounter(25),
4068 })
4069 );
4070
4071 assert_eq!(
4072 i.next(),
4073 Some(ParsedInstruction {
4074 kind: Instruction::invalid,
4075 offset: ProgramCounter(25),
4076 next_offset: ProgramCounter(64),
4077 })
4078 );
4079
4080 assert_eq!(i.next(), None);
4081}
4082
4083#[test]
4084fn test_instructions_iterator_very_long_bitmask_unbounded() {
4085 let mut code = [0_u8; 64];
4086 code[0] = Opcode::fallthrough as u8;
4087 let mut bitmask = [0_u8; 8];
4088 bitmask[0] = 0b00000001;
4089 bitmask[7] = 0b10000000;
4090
4091 let mut i = Instructions::new(DefaultInstructionSet::default(), &code, &bitmask, 0, false);
4092 assert_eq!(
4093 i.next(),
4094 Some(ParsedInstruction {
4095 kind: Instruction::fallthrough,
4096 offset: ProgramCounter(0),
4097 next_offset: ProgramCounter(25),
4098 })
4099 );
4100
4101 assert_eq!(
4102 i.next(),
4103 Some(ParsedInstruction {
4104 kind: Instruction::invalid,
4105 offset: ProgramCounter(25),
4106 next_offset: ProgramCounter(63),
4107 })
4108 );
4109
4110 assert_eq!(
4111 i.next(),
4112 Some(ParsedInstruction {
4113 kind: Instruction::trap,
4114 offset: ProgramCounter(63),
4115 next_offset: ProgramCounter(64),
4116 })
4117 );
4118
4119 assert_eq!(i.next(), None);
4120}
4121
4122#[test]
4123fn test_instructions_iterator_start_at_invalid_offset_bounded() {
4124 let mut i = Instructions::new(DefaultInstructionSet::default(), &[Opcode::trap as u8; 8], &[0b10000001], 1, true);
4125 assert_eq!(
4126 i.next(),
4127 Some(ParsedInstruction {
4128 kind: Instruction::invalid,
4129 offset: ProgramCounter(1),
4130 next_offset: ProgramCounter(2),
4132 })
4133 );
4134
4135 assert_eq!(i.next(), None);
4136}
4137
4138#[test]
4139fn test_instructions_iterator_start_at_invalid_offset_unbounded() {
4140 let mut i = Instructions::new(DefaultInstructionSet::default(), &[Opcode::trap as u8; 8], &[0b10000001], 1, false);
4141 assert_eq!(
4142 i.next(),
4143 Some(ParsedInstruction {
4144 kind: Instruction::invalid,
4145 offset: ProgramCounter(1),
4146 next_offset: ProgramCounter(7),
4147 })
4148 );
4149
4150 assert_eq!(
4151 i.next(),
4152 Some(ParsedInstruction {
4153 kind: Instruction::trap,
4154 offset: ProgramCounter(7),
4155 next_offset: ProgramCounter(8),
4156 })
4157 );
4158
4159 assert_eq!(i.next(), None);
4160}
4161
4162#[test]
4163fn test_instructions_iterator_does_not_emit_unnecessary_invalid_instructions_if_bounded_and_ends_with_a_trap() {
4164 let code = [Opcode::trap as u8; 32];
4165 let bitmask = [0b00000001, 0b00000000, 0b00000000, 0b00000100];
4166 let mut i = Instructions::new(DefaultInstructionSet::default(), &code, &bitmask, 0, true);
4167 assert_eq!(i.offset(), 0);
4168 assert_eq!(
4169 i.next(),
4170 Some(ParsedInstruction {
4171 kind: Instruction::trap,
4172 offset: ProgramCounter(0),
4173 next_offset: ProgramCounter(25)
4174 })
4175 );
4176 assert_eq!(i.offset(), 25);
4177 assert_eq!(i.next(), None);
4178}
4179
4180#[test]
4181fn test_instructions_iterator_does_not_emit_unnecessary_invalid_instructions_if_unbounded_and_ends_with_a_trap() {
4182 let code = [Opcode::trap as u8; 32];
4183 let bitmask = [0b00000001, 0b00000000, 0b00000000, 0b00000100];
4184 let mut i = Instructions::new(DefaultInstructionSet::default(), &code, &bitmask, 0, false);
4185 assert_eq!(i.offset(), 0);
4186 assert_eq!(
4187 i.next(),
4188 Some(ParsedInstruction {
4189 kind: Instruction::trap,
4190 offset: ProgramCounter(0),
4191 next_offset: ProgramCounter(25)
4192 })
4193 );
4194 assert_eq!(i.offset(), 26);
4195 assert_eq!(
4196 i.next(),
4197 Some(ParsedInstruction {
4198 kind: Instruction::trap,
4199 offset: ProgramCounter(26),
4200 next_offset: ProgramCounter(32)
4201 })
4202 );
4203 assert_eq!(i.next(), None);
4204}
4205
4206#[derive(Copy, Clone, PartialEq, Eq, Debug)]
4207pub enum EstimateInterpreterMemoryUsageArgs {
4208 UnboundedCache {
4209 instruction_count: u32,
4210 basic_block_count: u32,
4211 page_size: u32,
4212 },
4213 BoundedCache {
4214 instruction_count: u32,
4215 basic_block_count: u32,
4216 max_cache_size_bytes: u32,
4217 max_block_size: u32,
4218 page_size: u32,
4219 },
4220}
4221
4222#[derive(Copy, Clone, PartialEq, Eq, Debug)]
4223pub struct ProgramMemoryInfo {
4224 pub baseline_ram_consumption: u32,
4225 pub purgeable_ram_consumption: u32,
4226}
4227
4228#[derive(Clone, Default)]
4229#[non_exhaustive]
4230pub struct ProgramParts {
4231 pub is_64_bit: bool,
4232 pub ro_data_size: u32,
4233 pub rw_data_size: u32,
4234 pub stack_size: u32,
4235
4236 pub ro_data: ArcBytes,
4237 pub rw_data: ArcBytes,
4238 pub code_and_jump_table: ArcBytes,
4239 pub import_offsets: ArcBytes,
4240 pub import_symbols: ArcBytes,
4241 pub exports: ArcBytes,
4242
4243 pub debug_strings: ArcBytes,
4244 pub debug_line_program_ranges: ArcBytes,
4245 pub debug_line_programs: ArcBytes,
4246}
4247
4248impl ProgramParts {
4249 pub fn from_bytes(blob: ArcBytes) -> Result<Self, ProgramParseError> {
4250 if !blob.starts_with(&BLOB_MAGIC) {
4251 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4252 "blob doesn't start with the expected magic bytes",
4253 )));
4254 }
4255
4256 let mut reader = Reader {
4257 blob: &blob,
4258 position: BLOB_MAGIC.len(),
4259 };
4260
4261 let blob_version = reader.read_byte()?;
4262 let is_64_bit = if blob_version == BLOB_VERSION_V1_32 {
4263 false
4264 } else if blob_version == BLOB_VERSION_V1_64 {
4265 true
4266 } else {
4267 return Err(ProgramParseError(ProgramParseErrorKind::UnsupportedVersion {
4268 version: blob_version,
4269 }));
4270 };
4271
4272 let blob_len = BlobLen::from_le_bytes(reader.read_slice(BLOB_LEN_SIZE)?.try_into().unwrap());
4273 if blob_len != blob.len() as u64 {
4274 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4275 "blob size doesn't match the blob length metadata",
4276 )));
4277 }
4278
4279 let mut parts = ProgramParts {
4280 is_64_bit,
4281 ..ProgramParts::default()
4282 };
4283
4284 let mut section = reader.read_byte()?;
4285 if section == SECTION_MEMORY_CONFIG {
4286 let section_length = reader.read_varint()?;
4287 let position = reader.position;
4288 parts.ro_data_size = reader.read_varint()?;
4289 parts.rw_data_size = reader.read_varint()?;
4290 parts.stack_size = reader.read_varint()?;
4291 if position + section_length as usize != reader.position {
4292 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4293 "the memory config section contains more data than expected",
4294 )));
4295 }
4296 section = reader.read_byte()?;
4297 }
4298
4299 parts.ro_data = reader.read_section_as_bytes(&mut section, SECTION_RO_DATA)?;
4300 parts.rw_data = reader.read_section_as_bytes(&mut section, SECTION_RW_DATA)?;
4301
4302 if section == SECTION_IMPORTS {
4303 let section_length = reader.read_varint()? as usize;
4304 let section_start = reader.position;
4305 let import_count = reader.read_varint()?;
4306 if import_count > VM_MAXIMUM_IMPORT_COUNT {
4307 return Err(ProgramParseError(ProgramParseErrorKind::Other("too many imports")));
4308 }
4309
4310 let Some(import_offsets_size) = import_count.checked_mul(4) else {
4311 return Err(ProgramParseError(ProgramParseErrorKind::Other("the imports section is invalid")));
4312 };
4313
4314 parts.import_offsets = reader.read_slice_as_bytes(import_offsets_size as usize)?;
4315 let Some(import_symbols_size) = section_length.checked_sub(reader.position - section_start) else {
4316 return Err(ProgramParseError(ProgramParseErrorKind::Other("the imports section is invalid")));
4317 };
4318
4319 parts.import_symbols = reader.read_slice_as_bytes(import_symbols_size)?;
4320 section = reader.read_byte()?;
4321 }
4322
4323 parts.exports = reader.read_section_as_bytes(&mut section, SECTION_EXPORTS)?;
4324 parts.code_and_jump_table = reader.read_section_as_bytes(&mut section, SECTION_CODE_AND_JUMP_TABLE)?;
4325 parts.debug_strings = reader.read_section_as_bytes(&mut section, SECTION_OPT_DEBUG_STRINGS)?;
4326 parts.debug_line_programs = reader.read_section_as_bytes(&mut section, SECTION_OPT_DEBUG_LINE_PROGRAMS)?;
4327 parts.debug_line_program_ranges = reader.read_section_as_bytes(&mut section, SECTION_OPT_DEBUG_LINE_PROGRAM_RANGES)?;
4328
4329 while (section & 0b10000000) != 0 {
4330 #[cfg(feature = "logging")]
4332 log::debug!("Skipping unsupported optional section: {}", section);
4333 let section_length = reader.read_varint()?;
4334 reader.skip(section_length as usize)?;
4335 section = reader.read_byte()?;
4336 }
4337
4338 if section != SECTION_END_OF_FILE {
4339 return Err(ProgramParseError(ProgramParseErrorKind::UnexpectedSection {
4340 offset: reader.position - 1,
4341 section,
4342 }));
4343 }
4344
4345 Ok(parts)
4346 }
4347}
4348
4349impl ProgramBlob {
4350 pub fn blob_length(raw_blob: &[u8]) -> Option<BlobLen> {
4354 let end = BLOB_LEN_OFFSET + BLOB_LEN_SIZE;
4355 if raw_blob.len() < end {
4356 return None;
4357 }
4358 Some(BlobLen::from_le_bytes(raw_blob[BLOB_LEN_OFFSET..end].try_into().unwrap()))
4359 }
4360
4361 pub fn parse(bytes: ArcBytes) -> Result<Self, ProgramParseError> {
4363 let parts = ProgramParts::from_bytes(bytes)?;
4364 Self::from_parts(parts)
4365 }
4366
4367 pub fn from_parts(parts: ProgramParts) -> Result<Self, ProgramParseError> {
4369 let mut blob = ProgramBlob {
4370 #[cfg(feature = "unique-id")]
4371 unique_id: 0,
4372
4373 is_64_bit: parts.is_64_bit,
4374
4375 ro_data_size: parts.ro_data_size,
4376 rw_data_size: parts.rw_data_size,
4377 stack_size: parts.stack_size,
4378
4379 ro_data: parts.ro_data,
4380 rw_data: parts.rw_data,
4381 exports: parts.exports,
4382 import_symbols: parts.import_symbols,
4383 import_offsets: parts.import_offsets,
4384 code: Default::default(),
4385 jump_table: Default::default(),
4386 jump_table_entry_size: Default::default(),
4387 bitmask: Default::default(),
4388
4389 debug_strings: parts.debug_strings,
4390 debug_line_program_ranges: parts.debug_line_program_ranges,
4391 debug_line_programs: parts.debug_line_programs,
4392 };
4393
4394 if blob.ro_data.len() > blob.ro_data_size as usize {
4395 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4396 "size of the read-only data payload exceeds the declared size of the section",
4397 )));
4398 }
4399
4400 if blob.rw_data.len() > blob.rw_data_size as usize {
4401 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4402 "size of the read-write data payload exceeds the declared size of the section",
4403 )));
4404 }
4405
4406 if parts.code_and_jump_table.is_empty() {
4407 return Err(ProgramParseError(ProgramParseErrorKind::Other("no code found")));
4408 }
4409
4410 {
4411 let mut reader = Reader {
4412 blob: &parts.code_and_jump_table,
4413 position: 0,
4414 };
4415
4416 let initial_position = reader.position;
4417 let jump_table_entry_count = reader.read_varint()?;
4418 if jump_table_entry_count > VM_MAXIMUM_JUMP_TABLE_ENTRIES {
4419 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4420 "the jump table section is too long",
4421 )));
4422 }
4423
4424 let jump_table_entry_size = reader.read_byte()?;
4425 let code_length = reader.read_varint()?;
4426 if code_length > VM_MAXIMUM_CODE_SIZE {
4427 return Err(ProgramParseError(ProgramParseErrorKind::Other("the code section is too long")));
4428 }
4429
4430 if !matches!(jump_table_entry_size, 0..=4) {
4431 return Err(ProgramParseError(ProgramParseErrorKind::Other("invalid jump table entry size")));
4432 }
4433
4434 let Some(jump_table_length) = jump_table_entry_count.checked_mul(u32::from(jump_table_entry_size)) else {
4435 return Err(ProgramParseError(ProgramParseErrorKind::Other("the jump table is too long")));
4436 };
4437
4438 blob.jump_table_entry_size = jump_table_entry_size;
4439 blob.jump_table = reader.read_slice_as_bytes(jump_table_length as usize)?;
4440 blob.code = reader.read_slice_as_bytes(code_length as usize)?;
4441
4442 let bitmask_length = parts.code_and_jump_table.len() - (reader.position - initial_position);
4443 blob.bitmask = reader.read_slice_as_bytes(bitmask_length)?;
4444
4445 let mut expected_bitmask_length = blob.code.len() / 8;
4446 let is_bitmask_padded = blob.code.len() % 8 != 0;
4447 expected_bitmask_length += usize::from(is_bitmask_padded);
4448
4449 if blob.bitmask.len() != expected_bitmask_length {
4450 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4451 "the bitmask length doesn't match the code length",
4452 )));
4453 }
4454
4455 if is_bitmask_padded {
4456 let last_byte = *blob.bitmask.last().unwrap();
4457 let padding_bits = blob.bitmask.len() * 8 - blob.code.len();
4458 let padding_mask = ((0b10000000_u8 as i8) >> (padding_bits - 1)) as u8;
4459 if last_byte & padding_mask != 0 {
4460 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4461 "the bitmask is padded with non-zero bits",
4462 )));
4463 }
4464 }
4465 }
4466
4467 #[cfg(feature = "unique-id")]
4468 {
4469 static ID_COUNTER: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(0);
4470 blob.unique_id = ID_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
4471 }
4472
4473 Ok(blob)
4474 }
4475
4476 #[cfg(feature = "unique-id")]
4477 pub fn unique_id(&self) -> u64 {
4481 self.unique_id
4482 }
4483
4484 pub fn is_64_bit(&self) -> bool {
4486 self.is_64_bit
4487 }
4488
4489 pub fn unique_hash(&self, include_debug: bool) -> crate::hasher::Hash {
4491 let ProgramBlob {
4492 #[cfg(feature = "unique-id")]
4493 unique_id: _,
4494 is_64_bit,
4495 ro_data_size,
4496 rw_data_size,
4497 stack_size,
4498 ro_data,
4499 rw_data,
4500 code,
4501 jump_table,
4502 jump_table_entry_size,
4503 bitmask,
4504 import_offsets,
4505 import_symbols,
4506 exports,
4507 debug_strings,
4508 debug_line_program_ranges,
4509 debug_line_programs,
4510 } = self;
4511
4512 let mut hasher = crate::hasher::Hasher::new();
4513
4514 hasher.update_u32_array([
4515 1_u32, u32::from(*is_64_bit),
4517 *ro_data_size,
4518 *rw_data_size,
4519 *stack_size,
4520 ro_data.len() as u32,
4521 rw_data.len() as u32,
4522 code.len() as u32,
4523 jump_table.len() as u32,
4524 u32::from(*jump_table_entry_size),
4525 bitmask.len() as u32,
4526 import_offsets.len() as u32,
4527 import_symbols.len() as u32,
4528 exports.len() as u32,
4529 ]);
4530
4531 hasher.update(ro_data);
4532 hasher.update(rw_data);
4533 hasher.update(code);
4534 hasher.update(jump_table);
4535 hasher.update(bitmask);
4536 hasher.update(import_offsets);
4537 hasher.update(import_symbols);
4538 hasher.update(exports);
4539
4540 if include_debug {
4541 hasher.update_u32_array([
4542 debug_strings.len() as u32,
4543 debug_line_program_ranges.len() as u32,
4544 debug_line_programs.len() as u32,
4545 ]);
4546
4547 hasher.update(debug_strings);
4548 hasher.update(debug_line_program_ranges);
4549 hasher.update(debug_line_programs);
4550 }
4551
4552 hasher.finalize()
4553 }
4554
4555 pub fn ro_data(&self) -> &[u8] {
4559 &self.ro_data
4560 }
4561
4562 pub fn ro_data_size(&self) -> u32 {
4566 self.ro_data_size
4567 }
4568
4569 pub fn rw_data(&self) -> &[u8] {
4573 &self.rw_data
4574 }
4575
4576 pub fn rw_data_size(&self) -> u32 {
4580 self.rw_data_size
4581 }
4582
4583 pub fn stack_size(&self) -> u32 {
4585 self.stack_size
4586 }
4587
4588 pub fn code(&self) -> &[u8] {
4590 &self.code
4591 }
4592
4593 #[cfg(feature = "export-internals-for-testing")]
4594 #[doc(hidden)]
4595 pub fn set_code(&mut self, code: ArcBytes) {
4596 self.code = code;
4597 }
4598
4599 pub fn bitmask(&self) -> &[u8] {
4601 &self.bitmask
4602 }
4603
4604 pub fn imports(&self) -> Imports {
4605 Imports {
4606 offsets: &self.import_offsets,
4607 symbols: &self.import_symbols,
4608 }
4609 }
4610
4611 pub fn exports(&self) -> impl Iterator<Item = ProgramExport<&[u8]>> + Clone {
4613 #[derive(Clone)]
4614 enum State {
4615 Uninitialized,
4616 Pending(u32),
4617 Finished,
4618 }
4619
4620 #[derive(Clone)]
4621 struct ExportIterator<'a> {
4622 state: State,
4623 reader: Reader<'a, [u8]>,
4624 }
4625
4626 impl<'a> Iterator for ExportIterator<'a> {
4627 type Item = ProgramExport<&'a [u8]>;
4628 fn next(&mut self) -> Option<Self::Item> {
4629 let remaining = match core::mem::replace(&mut self.state, State::Finished) {
4630 State::Uninitialized => self.reader.read_varint().ok()?,
4631 State::Pending(remaining) => remaining,
4632 State::Finished => return None,
4633 };
4634
4635 if remaining == 0 {
4636 return None;
4637 }
4638
4639 let target_code_offset = self.reader.read_varint().ok()?;
4640 let symbol = self.reader.read_bytes_with_length().ok()?;
4641 let export = ProgramExport {
4642 program_counter: ProgramCounter(target_code_offset),
4643 symbol: ProgramSymbol::new(symbol),
4644 };
4645
4646 self.state = State::Pending(remaining - 1);
4647 Some(export)
4648 }
4649 }
4650
4651 ExportIterator {
4652 state: if !self.exports.is_empty() {
4653 State::Uninitialized
4654 } else {
4655 State::Finished
4656 },
4657 reader: Reader {
4658 blob: &self.exports,
4659 position: 0,
4660 },
4661 }
4662 }
4663
4664 #[cfg_attr(not(debug_assertions), inline(always))]
4666 pub fn visit<T>(&self, dispatch_table: T, visitor: &mut T::State)
4667 where
4668 T: OpcodeVisitor<ReturnTy = ()>,
4669 {
4670 visitor_run(visitor, self, dispatch_table);
4671 }
4672
4673 #[inline]
4677 pub fn instructions<I>(&self, instruction_set: I) -> Instructions<I>
4678 where
4679 I: InstructionSet,
4680 {
4681 Instructions::new_unbounded(instruction_set, self.code(), self.bitmask(), 0)
4682 }
4683
4684 #[inline]
4688 pub fn instructions_bounded_at<I>(&self, instruction_set: I, offset: ProgramCounter) -> Instructions<I>
4689 where
4690 I: InstructionSet,
4691 {
4692 Instructions::new_bounded(instruction_set, self.code(), self.bitmask(), offset.0)
4693 }
4694
4695 pub fn is_jump_target_valid<I>(&self, instruction_set: I, target: ProgramCounter) -> bool
4697 where
4698 I: InstructionSet,
4699 {
4700 is_jump_target_valid(instruction_set, self.code(), self.bitmask(), target.0)
4701 }
4702
4703 pub fn jump_table(&self) -> JumpTable {
4705 JumpTable {
4706 blob: &self.jump_table,
4707 entry_size: u32::from(self.jump_table_entry_size),
4708 }
4709 }
4710
4711 pub fn get_debug_string(&self, offset: u32) -> Result<&str, ProgramParseError> {
4713 let mut reader = Reader {
4714 blob: &self.debug_strings,
4715 position: 0,
4716 };
4717 reader.skip(offset as usize)?;
4718 reader.read_string_with_length()
4719 }
4720
4721 pub fn get_debug_line_program_at(&self, program_counter: ProgramCounter) -> Result<Option<LineProgram>, ProgramParseError> {
4723 let program_counter = program_counter.0;
4724 if self.debug_line_program_ranges.is_empty() || self.debug_line_programs.is_empty() {
4725 return Ok(None);
4726 }
4727
4728 if self.debug_line_programs[0] != VERSION_DEBUG_LINE_PROGRAM_V1 {
4729 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4730 "the debug line programs section has an unsupported version",
4731 )));
4732 }
4733
4734 const ENTRY_SIZE: usize = 12;
4735
4736 let slice = &self.debug_line_program_ranges;
4737 if slice.len() % ENTRY_SIZE != 0 {
4738 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4739 "the debug function ranges section has an invalid size",
4740 )));
4741 }
4742
4743 let offset = binary_search(slice, ENTRY_SIZE, |xs| {
4744 let begin = u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]);
4745 if program_counter < begin {
4746 return core::cmp::Ordering::Greater;
4747 }
4748
4749 let end = u32::from_le_bytes([xs[4], xs[5], xs[6], xs[7]]);
4750 if program_counter >= end {
4751 return core::cmp::Ordering::Less;
4752 }
4753
4754 core::cmp::Ordering::Equal
4755 });
4756
4757 let Ok(offset) = offset else { return Ok(None) };
4758
4759 let xs = &slice[offset..offset + ENTRY_SIZE];
4760 let index_begin = u32::from_le_bytes([xs[0], xs[1], xs[2], xs[3]]);
4761 let index_end = u32::from_le_bytes([xs[4], xs[5], xs[6], xs[7]]);
4762 let info_offset = u32::from_le_bytes([xs[8], xs[9], xs[10], xs[11]]);
4763
4764 if program_counter < index_begin || program_counter >= index_end {
4765 return Err(ProgramParseError(ProgramParseErrorKind::Other(
4766 "binary search for function debug info failed",
4767 )));
4768 }
4769
4770 let mut reader = Reader {
4771 blob: &self.debug_line_programs,
4772 position: 0,
4773 };
4774
4775 reader.skip(info_offset as usize)?;
4776
4777 Ok(Some(LineProgram {
4778 entry_index: offset / ENTRY_SIZE,
4779 region_counter: 0,
4780 blob: self,
4781 reader,
4782 is_finished: false,
4783 program_counter: index_begin,
4784 stack: Default::default(),
4785 stack_depth: 0,
4786 mutation_depth: 0,
4787 }))
4788 }
4789
4790 #[cfg(feature = "alloc")]
4791 pub(crate) fn calculate_blob_length(&self) -> u64 {
4792 let ProgramBlob {
4793 #[cfg(feature = "unique-id")]
4794 unique_id: _,
4795 is_64_bit: _,
4796 ro_data_size: _,
4797 rw_data_size: _,
4798 stack_size: _,
4799 ro_data,
4800 rw_data,
4801 code,
4802 jump_table,
4803 jump_table_entry_size: _,
4804 bitmask,
4805 import_offsets,
4806 import_symbols,
4807 exports,
4808 debug_strings,
4809 debug_line_program_ranges,
4810 debug_line_programs,
4811 } = self;
4812
4813 let mut ranges = [
4814 ro_data.parent_address_range(),
4815 rw_data.parent_address_range(),
4816 code.parent_address_range(),
4817 jump_table.parent_address_range(),
4818 bitmask.parent_address_range(),
4819 import_offsets.parent_address_range(),
4820 import_symbols.parent_address_range(),
4821 exports.parent_address_range(),
4822 debug_strings.parent_address_range(),
4823 debug_line_program_ranges.parent_address_range(),
4824 debug_line_programs.parent_address_range(),
4825 ];
4826
4827 ranges.sort_unstable_by_key(|r| r.start);
4828
4829 let mut blob_length = 0;
4830 let mut last_range = 0..0;
4831 for range in ranges {
4832 if range == last_range {
4833 continue;
4834 }
4835 blob_length += cast(range.len()).to_u64();
4836 last_range = range;
4837 }
4838 blob_length
4839 }
4840
4841 #[cfg(feature = "alloc")]
4842 pub fn estimate_interpreter_memory_usage(&self, args: EstimateInterpreterMemoryUsageArgs) -> Result<ProgramMemoryInfo, &'static str> {
4843 let (page_size, instruction_count, basic_block_count) = match args {
4844 EstimateInterpreterMemoryUsageArgs::UnboundedCache {
4845 page_size,
4846 instruction_count,
4847 basic_block_count,
4848 ..
4849 } => (page_size, instruction_count, basic_block_count),
4850 EstimateInterpreterMemoryUsageArgs::BoundedCache {
4851 page_size,
4852 instruction_count,
4853 basic_block_count,
4854 ..
4855 } => (page_size, instruction_count, basic_block_count),
4856 };
4857
4858 let cache_entry_count_upper_bound =
4859 cast(instruction_count).to_usize() + cast(basic_block_count).to_usize() + INTERPRETER_CACHE_RESERVED_ENTRIES as usize;
4860 let cache_size_upper_bound = interpreter_calculate_cache_size(cache_entry_count_upper_bound);
4861
4862 let mut purgeable_ram_consumption = match args {
4863 EstimateInterpreterMemoryUsageArgs::UnboundedCache { .. } => cache_size_upper_bound,
4864 EstimateInterpreterMemoryUsageArgs::BoundedCache {
4865 max_cache_size_bytes,
4866 max_block_size,
4867 ..
4868 } => {
4869 let max_cache_size_bytes = cast(max_cache_size_bytes).to_usize();
4870 let cache_entry_count_hard_limit = cast(max_block_size).to_usize() + INTERPRETER_CACHE_RESERVED_ENTRIES as usize;
4871 let cache_bytes_hard_limit = interpreter_calculate_cache_size(cache_entry_count_hard_limit);
4872 if cache_bytes_hard_limit > max_cache_size_bytes {
4873 return Err("maximum cache size is too small for the given max block size");
4874 }
4875
4876 max_cache_size_bytes.min(cache_size_upper_bound)
4877 }
4878 };
4879
4880 let code_length = self.code.len();
4881 purgeable_ram_consumption = purgeable_ram_consumption.saturating_add((code_length + 1) * INTERPRETER_FLATMAP_ENTRY_SIZE as usize);
4882
4883 let Ok(purgeable_ram_consumption) = u32::try_from(purgeable_ram_consumption) else {
4884 return Err("estimated interpreter cache size is too large");
4885 };
4886
4887 let memory_map = MemoryMapBuilder::new(page_size)
4888 .ro_data_size(self.ro_data_size)
4889 .rw_data_size(self.rw_data_size)
4890 .stack_size(self.stack_size)
4891 .build()?;
4892
4893 let blob_length = self.calculate_blob_length();
4894 let Ok(baseline_ram_consumption) = u32::try_from(
4895 blob_length
4896 .saturating_add(u64::from(memory_map.ro_data_size()))
4897 .saturating_sub(self.ro_data.len() as u64)
4898 .saturating_add(u64::from(memory_map.rw_data_size()))
4899 .saturating_sub(self.rw_data.len() as u64)
4900 .saturating_add(u64::from(memory_map.stack_size())),
4901 ) else {
4902 return Err("calculated baseline RAM consumption is too large");
4903 };
4904
4905 Ok(ProgramMemoryInfo {
4906 baseline_ram_consumption,
4907 purgeable_ram_consumption,
4908 })
4909 }
4910}
4911
4912#[cfg(feature = "alloc")]
4913#[test]
4914fn test_calculate_blob_length() {
4915 let mut builder = crate::writer::ProgramBlobBuilder::new_64bit();
4916 builder.set_code(&[Instruction::trap], &[]);
4917 let blob = builder.into_vec().unwrap();
4918 let parts = ProgramParts::from_bytes(blob.into()).unwrap();
4919
4920 let big_blob = ArcBytes::from(alloc::vec![0; 1024]);
4921 let small_blob = ArcBytes::from(&parts.code_and_jump_table[..]);
4922 let parts = ProgramParts {
4923 ro_data: big_blob.subslice(10..20),
4924 ro_data_size: 24,
4925 rw_data: big_blob.subslice(24..28),
4926 rw_data_size: 4,
4927 code_and_jump_table: small_blob.clone(),
4928 debug_strings: small_blob.clone(),
4929 ..ProgramParts::default()
4930 };
4931
4932 let blob = ProgramBlob::from_parts(parts).unwrap();
4933 assert_eq!(
4934 blob.calculate_blob_length(),
4935 (big_blob.len() + small_blob.len()).try_into().unwrap()
4936 );
4937}
4938
4939#[derive(Copy, Clone, PartialEq, Eq, Debug)]
4941pub enum SourceLocation<'a> {
4942 Path { path: &'a str },
4943 PathAndLine { path: &'a str, line: u32 },
4944 Full { path: &'a str, line: u32, column: u32 },
4945}
4946
4947impl<'a> SourceLocation<'a> {
4948 pub fn path(&self) -> &'a str {
4950 match *self {
4951 Self::Path { path, .. } => path,
4952 Self::PathAndLine { path, .. } => path,
4953 Self::Full { path, .. } => path,
4954 }
4955 }
4956
4957 pub fn line(&self) -> Option<u32> {
4959 match *self {
4960 Self::Path { .. } => None,
4961 Self::PathAndLine { line, .. } => Some(line),
4962 Self::Full { line, .. } => Some(line),
4963 }
4964 }
4965
4966 pub fn column(&self) -> Option<u32> {
4968 match *self {
4969 Self::Path { .. } => None,
4970 Self::PathAndLine { .. } => None,
4971 Self::Full { column, .. } => Some(column),
4972 }
4973 }
4974}
4975
4976impl<'a> core::fmt::Display for SourceLocation<'a> {
4977 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
4978 match *self {
4979 Self::Path { path } => fmt.write_str(path),
4980 Self::PathAndLine { path, line } => write!(fmt, "{}:{}", path, line),
4981 Self::Full { path, line, column } => write!(fmt, "{}:{}:{}", path, line, column),
4982 }
4983 }
4984}
4985
4986#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
4987pub enum FrameKind {
4988 Enter,
4989 Call,
4990 Line,
4991}
4992
4993pub struct FrameInfo<'a> {
4994 blob: &'a ProgramBlob,
4995 inner: &'a LineProgramFrame,
4996}
4997
4998impl<'a> FrameInfo<'a> {
4999 pub fn namespace(&self) -> Result<Option<&str>, ProgramParseError> {
5001 let namespace = self.blob.get_debug_string(self.inner.namespace_offset)?;
5002 if namespace.is_empty() {
5003 Ok(None)
5004 } else {
5005 Ok(Some(namespace))
5006 }
5007 }
5008
5009 pub fn function_name_without_namespace(&self) -> Result<Option<&str>, ProgramParseError> {
5011 let function_name = self.blob.get_debug_string(self.inner.function_name_offset)?;
5012 if function_name.is_empty() {
5013 Ok(None)
5014 } else {
5015 Ok(Some(function_name))
5016 }
5017 }
5018
5019 pub fn path_debug_string_offset(&self) -> Option<u32> {
5021 if self.inner.path_offset == 0 {
5022 None
5023 } else {
5024 Some(self.inner.path_offset)
5025 }
5026 }
5027
5028 pub fn path(&self) -> Result<Option<&str>, ProgramParseError> {
5030 let path = self.blob.get_debug_string(self.inner.path_offset)?;
5031 if path.is_empty() {
5032 Ok(None)
5033 } else {
5034 Ok(Some(path))
5035 }
5036 }
5037
5038 pub fn line(&self) -> Option<u32> {
5040 if self.inner.line == 0 {
5041 None
5042 } else {
5043 Some(self.inner.line)
5044 }
5045 }
5046
5047 pub fn column(&self) -> Option<u32> {
5049 if self.inner.column == 0 {
5050 None
5051 } else {
5052 Some(self.inner.column)
5053 }
5054 }
5055
5056 pub fn kind(&self) -> FrameKind {
5057 self.inner.kind.unwrap_or(FrameKind::Line)
5058 }
5059
5060 pub fn full_name(&'_ self) -> Result<impl core::fmt::Display + '_, ProgramParseError> {
5062 Ok(DisplayName {
5063 prefix: self.namespace()?.unwrap_or(""),
5064 suffix: self.function_name_without_namespace()?.unwrap_or(""),
5065 })
5066 }
5067
5068 pub fn location(&self) -> Result<Option<SourceLocation>, ProgramParseError> {
5070 if let Some(path) = self.path()? {
5071 if let Some(line) = self.line() {
5072 if let Some(column) = self.column() {
5073 Ok(Some(SourceLocation::Full { path, line, column }))
5074 } else {
5075 Ok(Some(SourceLocation::PathAndLine { path, line }))
5076 }
5077 } else {
5078 Ok(Some(SourceLocation::Path { path }))
5079 }
5080 } else {
5081 Ok(None)
5082 }
5083 }
5084}
5085
5086pub struct RegionInfo<'a> {
5088 entry_index: usize,
5089 blob: &'a ProgramBlob,
5090 range: Range<ProgramCounter>,
5091 frames: &'a [LineProgramFrame],
5092}
5093
5094impl<'a> RegionInfo<'a> {
5095 pub fn entry_index(&self) -> usize {
5097 self.entry_index
5098 }
5099
5100 pub fn instruction_range(&self) -> Range<ProgramCounter> {
5102 self.range.clone()
5103 }
5104
5105 pub fn frames(&self) -> impl ExactSizeIterator<Item = FrameInfo> {
5107 self.frames.iter().map(|inner| FrameInfo { blob: self.blob, inner })
5108 }
5109}
5110
5111#[derive(Default)]
5112struct LineProgramFrame {
5113 kind: Option<FrameKind>,
5114 namespace_offset: u32,
5115 function_name_offset: u32,
5116 path_offset: u32,
5117 line: u32,
5118 column: u32,
5119}
5120
5121pub struct LineProgram<'a> {
5123 entry_index: usize,
5124 region_counter: usize,
5125 blob: &'a ProgramBlob,
5126 reader: Reader<'a, ArcBytes>,
5127 is_finished: bool,
5128 program_counter: u32,
5129 stack: [LineProgramFrame; 16],
5131 stack_depth: u32,
5132 mutation_depth: u32,
5133}
5134
5135impl<'a> LineProgram<'a> {
5136 pub fn entry_index(&self) -> usize {
5138 self.entry_index
5139 }
5140
5141 pub fn run(&mut self) -> Result<Option<RegionInfo>, ProgramParseError> {
5143 struct SetTrueOnDrop<'a>(&'a mut bool);
5144 impl<'a> Drop for SetTrueOnDrop<'a> {
5145 fn drop(&mut self) {
5146 *self.0 = true;
5147 }
5148 }
5149
5150 if self.is_finished {
5151 return Ok(None);
5152 }
5153
5154 const INSTRUCTION_LIMIT_PER_REGION: usize = 512;
5156
5157 let mark_as_finished_on_drop = SetTrueOnDrop(&mut self.is_finished);
5158 for _ in 0..INSTRUCTION_LIMIT_PER_REGION {
5159 let byte = match self.reader.read_byte() {
5160 Ok(byte) => byte,
5161 Err(error) => {
5162 return Err(error);
5163 }
5164 };
5165
5166 let Some(opcode) = LineProgramOp::from_u8(byte) else {
5167 return Err(ProgramParseError(ProgramParseErrorKind::Other(
5168 "found an unrecognized line program opcode",
5169 )));
5170 };
5171
5172 let (count, stack_depth) = match opcode {
5173 LineProgramOp::FinishProgram => {
5174 return Ok(None);
5175 }
5176 LineProgramOp::SetMutationDepth => {
5177 self.mutation_depth = self.reader.read_varint()?;
5178 continue;
5179 }
5180 LineProgramOp::SetKindEnter => {
5181 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5182 frame.kind = Some(FrameKind::Enter);
5183 }
5184 continue;
5185 }
5186 LineProgramOp::SetKindCall => {
5187 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5188 frame.kind = Some(FrameKind::Call);
5189 }
5190 continue;
5191 }
5192 LineProgramOp::SetKindLine => {
5193 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5194 frame.kind = Some(FrameKind::Line);
5195 }
5196 continue;
5197 }
5198 LineProgramOp::SetNamespace => {
5199 let value = self.reader.read_varint()?;
5200 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5201 frame.namespace_offset = value;
5202 }
5203 continue;
5204 }
5205 LineProgramOp::SetFunctionName => {
5206 let value = self.reader.read_varint()?;
5207 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5208 frame.function_name_offset = value;
5209 }
5210 continue;
5211 }
5212 LineProgramOp::SetPath => {
5213 let value = self.reader.read_varint()?;
5214 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5215 frame.path_offset = value;
5216 }
5217 continue;
5218 }
5219 LineProgramOp::SetLine => {
5220 let value = self.reader.read_varint()?;
5221 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5222 frame.line = value;
5223 }
5224 continue;
5225 }
5226 LineProgramOp::SetColumn => {
5227 let value = self.reader.read_varint()?;
5228 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5229 frame.column = value;
5230 }
5231 continue;
5232 }
5233 LineProgramOp::SetStackDepth => {
5234 self.stack_depth = self.reader.read_varint()?;
5235 continue;
5236 }
5237 LineProgramOp::IncrementLine => {
5238 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5239 frame.line += 1;
5240 }
5241 continue;
5242 }
5243 LineProgramOp::AddLine => {
5244 let value = self.reader.read_varint()?;
5245 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5246 frame.line = frame.line.wrapping_add(value);
5247 }
5248 continue;
5249 }
5250 LineProgramOp::SubLine => {
5251 let value = self.reader.read_varint()?;
5252 if let Some(frame) = self.stack.get_mut(self.mutation_depth as usize) {
5253 frame.line = frame.line.wrapping_sub(value);
5254 }
5255 continue;
5256 }
5257 LineProgramOp::FinishInstruction => (1, self.stack_depth),
5258 LineProgramOp::FinishMultipleInstructions => {
5259 let count = self.reader.read_varint()?;
5260 (count, self.stack_depth)
5261 }
5262 LineProgramOp::FinishInstructionAndIncrementStackDepth => {
5263 let depth = self.stack_depth;
5264 self.stack_depth = self.stack_depth.saturating_add(1);
5265 (1, depth)
5266 }
5267 LineProgramOp::FinishMultipleInstructionsAndIncrementStackDepth => {
5268 let count = self.reader.read_varint()?;
5269 let depth = self.stack_depth;
5270 self.stack_depth = self.stack_depth.saturating_add(1);
5271 (count, depth)
5272 }
5273 LineProgramOp::FinishInstructionAndDecrementStackDepth => {
5274 let depth = self.stack_depth;
5275 self.stack_depth = self.stack_depth.saturating_sub(1);
5276 (1, depth)
5277 }
5278 LineProgramOp::FinishMultipleInstructionsAndDecrementStackDepth => {
5279 let count = self.reader.read_varint()?;
5280 let depth = self.stack_depth;
5281 self.stack_depth = self.stack_depth.saturating_sub(1);
5282 (count, depth)
5283 }
5284 };
5285
5286 let range = ProgramCounter(self.program_counter)..ProgramCounter(self.program_counter + count);
5287 self.program_counter += count;
5288
5289 let frames = &self.stack[..core::cmp::min(stack_depth as usize, self.stack.len())];
5290 core::mem::forget(mark_as_finished_on_drop);
5291
5292 let entry_index = self.region_counter;
5293 self.region_counter += 1;
5294 return Ok(Some(RegionInfo {
5295 entry_index,
5296 blob: self.blob,
5297 range,
5298 frames,
5299 }));
5300 }
5301
5302 Err(ProgramParseError(ProgramParseErrorKind::Other(
5303 "found a line program with too many instructions",
5304 )))
5305 }
5306}
5307
5308struct DisplayName<'a> {
5309 prefix: &'a str,
5310 suffix: &'a str,
5311}
5312
5313impl<'a> core::fmt::Display for DisplayName<'a> {
5314 fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
5315 fmt.write_str(self.prefix)?;
5316 if !self.prefix.is_empty() {
5317 fmt.write_str("::")?;
5318 }
5319 fmt.write_str(self.suffix)
5320 }
5321}
5322
5323fn binary_search(slice: &[u8], chunk_size: usize, compare: impl Fn(&[u8]) -> core::cmp::Ordering) -> Result<usize, usize> {
5326 let mut size = slice.len() / chunk_size;
5327 if size == 0 {
5328 return Err(0);
5329 }
5330
5331 let mut base = 0_usize;
5332 while size > 1 {
5333 let half = size / 2;
5334 let mid = base + half;
5335 let item = &slice[mid * chunk_size..(mid + 1) * chunk_size];
5336 match compare(item) {
5337 core::cmp::Ordering::Greater => {
5338 size -= half;
5340 }
5341 core::cmp::Ordering::Less => {
5342 size -= half;
5344 base = mid;
5345 }
5346 core::cmp::Ordering::Equal => {
5347 let previous_item = &slice[(mid - 1) * chunk_size..mid * chunk_size];
5349 if compare(previous_item) != core::cmp::Ordering::Equal {
5350 return Ok(mid * chunk_size);
5352 }
5353
5354 size -= half;
5360 }
5361 }
5362 }
5363
5364 let item = &slice[base * chunk_size..(base + 1) * chunk_size];
5365 let ord = compare(item);
5366 if ord == core::cmp::Ordering::Equal {
5367 Ok(base * chunk_size)
5368 } else {
5369 Err((base + usize::from(ord == core::cmp::Ordering::Less)) * chunk_size)
5370 }
5371}
5372
5373#[cfg(test)]
5374extern crate std;
5375
5376#[cfg(test)]
5377proptest::proptest! {
5378 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(20000))]
5379 #[allow(clippy::ignored_unit_patterns)]
5380 #[test]
5381 fn test_binary_search(needle: u8, mut xs: std::vec::Vec<u8>) {
5382 xs.sort();
5383 let binary_result = binary_search(&xs, 1, |slice| slice[0].cmp(&needle));
5384 let mut linear_result = Err(0);
5385 for (index, value) in xs.iter().copied().enumerate() {
5386 #[allow(clippy::comparison_chain)]
5387 if value == needle {
5388 linear_result = Ok(index);
5389 break;
5390 } else if value < needle {
5391 linear_result = Err(index + 1);
5392 continue;
5393 } else {
5394 break;
5395 }
5396 }
5397
5398 assert_eq!(binary_result, linear_result, "linear search = {:?}, binary search = {:?}, needle = {}, xs = {:?}", linear_result, binary_result, needle, xs);
5399 }
5400}
5401
5402pub const BLOB_MAGIC: [u8; 4] = [b'P', b'V', b'M', b'\0'];
5404
5405pub type BlobLen = u64;
5410pub const BLOB_LEN_SIZE: usize = core::mem::size_of::<BlobLen>();
5411pub const BLOB_LEN_OFFSET: usize = BLOB_MAGIC.len() + 1;
5412
5413pub const SECTION_MEMORY_CONFIG: u8 = 1;
5414pub const SECTION_RO_DATA: u8 = 2;
5415pub const SECTION_RW_DATA: u8 = 3;
5416pub const SECTION_IMPORTS: u8 = 4;
5417pub const SECTION_EXPORTS: u8 = 5;
5418pub const SECTION_CODE_AND_JUMP_TABLE: u8 = 6;
5419pub const SECTION_OPT_DEBUG_STRINGS: u8 = 128;
5420pub const SECTION_OPT_DEBUG_LINE_PROGRAMS: u8 = 129;
5421pub const SECTION_OPT_DEBUG_LINE_PROGRAM_RANGES: u8 = 130;
5422pub const SECTION_END_OF_FILE: u8 = 0;
5423
5424pub const BLOB_VERSION_V1_64: u8 = 0;
5425pub const BLOB_VERSION_V1_32: u8 = 1;
5426
5427pub const VERSION_DEBUG_LINE_PROGRAM_V1: u8 = 1;
5428
5429#[derive(Copy, Clone, Debug)]
5430pub enum LineProgramOp {
5431 FinishProgram = 0,
5432 SetMutationDepth = 1,
5433 SetKindEnter = 2,
5434 SetKindCall = 3,
5435 SetKindLine = 4,
5436 SetNamespace = 5,
5437 SetFunctionName = 6,
5438 SetPath = 7,
5439 SetLine = 8,
5440 SetColumn = 9,
5441 SetStackDepth = 10,
5442 IncrementLine = 11,
5443 AddLine = 12,
5444 SubLine = 13,
5445 FinishInstruction = 14,
5446 FinishMultipleInstructions = 15,
5447 FinishInstructionAndIncrementStackDepth = 16,
5448 FinishMultipleInstructionsAndIncrementStackDepth = 17,
5449 FinishInstructionAndDecrementStackDepth = 18,
5450 FinishMultipleInstructionsAndDecrementStackDepth = 19,
5451}
5452
5453impl LineProgramOp {
5454 #[inline]
5455 pub const fn from_u8(value: u8) -> Option<Self> {
5456 match value {
5457 0 => Some(Self::FinishProgram),
5458 1 => Some(Self::SetMutationDepth),
5459 2 => Some(Self::SetKindEnter),
5460 3 => Some(Self::SetKindCall),
5461 4 => Some(Self::SetKindLine),
5462 5 => Some(Self::SetNamespace),
5463 6 => Some(Self::SetFunctionName),
5464 7 => Some(Self::SetPath),
5465 8 => Some(Self::SetLine),
5466 9 => Some(Self::SetColumn),
5467 10 => Some(Self::SetStackDepth),
5468 11 => Some(Self::IncrementLine),
5469 12 => Some(Self::AddLine),
5470 13 => Some(Self::SubLine),
5471 14 => Some(Self::FinishInstruction),
5472 15 => Some(Self::FinishMultipleInstructions),
5473 16 => Some(Self::FinishInstructionAndIncrementStackDepth),
5474 17 => Some(Self::FinishMultipleInstructionsAndIncrementStackDepth),
5475 18 => Some(Self::FinishInstructionAndDecrementStackDepth),
5476 19 => Some(Self::FinishMultipleInstructionsAndDecrementStackDepth),
5477 _ => None,
5478 }
5479 }
5480}