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