1use crate::decode::predecode;
32use crate::ecall::{EcallHandler, EcallKind, EcallResult};
33use crate::exit::ExitReason;
34use crate::gas::GasCounter;
35use crate::instruction::Opcode;
36use crate::mem::Memory;
37use crate::program::PvmProgram;
38use crate::regs::Regs;
39
40pub struct Interpreter;
42
43impl Interpreter {
44 pub fn run<M: Memory>(
49 program: &PvmProgram,
50 regs: &mut Regs,
51 mem: &mut M,
52 gas: &mut GasCounter,
53 handler: &mut dyn EcallHandler,
54 ) -> ExitReason {
55 macro_rules! do_store {
59 ($mem:expr, $exit:ident, $addr:expr, $write_fn:ident, $val:expr) => {{
60 let a = $addr;
61 if !$mem.$write_fn(a, $val) {
62 $exit = Some(ExitReason::PageFault(a & !0xFFF));
63 }
64 }};
65 }
66 macro_rules! do_load {
67 ($mem:expr, $regs:expr, $exit:ident, $dst:expr, $addr:expr, $read_fn:ident, |$v:ident| $conv:expr) => {{
68 let a = $addr;
69 match $mem.$read_fn(a) {
70 Some($v) => {
71 $regs.gpr[$dst] = $conv;
72 }
73 None => {
74 $exit = Some(ExitReason::PageFault(a & !0xFFF));
75 }
76 }
77 }};
78 }
79
80 let predecoded = predecode(program);
81 let insts = &predecoded.decoded_insts;
82 let pc_to_idx = &predecoded.pc_to_idx;
83 let basic_block_starts = &predecoded.basic_block_starts;
84 let jump_table = &program.jump_table;
85
86 let mut idx = if (regs.pc as usize) < pc_to_idx.len() {
88 pc_to_idx[regs.pc as usize]
89 } else {
90 u32::MAX
91 };
92 if idx == u32::MAX {
93 return ExitReason::Panic;
94 }
95
96 loop {
97 let inst = *unsafe { insts.get_unchecked(idx as usize) };
101
102 if inst.bb_gas_cost > 0 && gas.charge(inst.bb_gas_cost as u64).is_err() {
105 regs.pc = inst.pc as u64;
106 return ExitReason::OutOfGas;
107 }
108
109 let ra = inst.ra as usize;
110 let rb = inst.rb as usize;
111 let rd = inst.rd as usize;
112 let imm1 = inst.imm1;
113
114 let mut branch_idx: u32 = u32::MAX;
117 let mut exit: Option<ExitReason> = None;
118
119 match inst.opcode {
120 Opcode::Trap => {
122 exit = Some(ExitReason::Trap);
123 }
124 Opcode::Fallthrough | Opcode::Unlikely => {}
125 Opcode::Ecall => {
126 regs.pc = inst.next_pc as u64;
127 match handler.handle(EcallKind::Ecall, regs, mem) {
128 EcallResult::Continue => {
129 idx = inst.next_idx;
130 continue;
131 }
132 EcallResult::Exit(reason) => return reason,
133 }
134 }
135
136 Opcode::Ecalli => {
138 regs.pc = inst.next_pc as u64;
139 match handler.handle(EcallKind::Ecalli(imm1 as u32), regs, mem) {
140 EcallResult::Continue => {
141 idx = inst.next_idx;
142 continue;
143 }
144 EcallResult::Exit(reason) => return reason,
145 }
146 }
147
148 Opcode::LoadImm64 => {
150 regs.gpr[ra] = imm1;
151 }
152
153 Opcode::Jump => {
155 if inst.target_idx != u32::MAX {
156 branch_idx = inst.target_idx;
157 } else {
158 exit = Some(ExitReason::Panic);
159 }
160 }
161
162 Opcode::JumpInd => {
164 let addr = regs.gpr[ra].wrapping_add(imm1) % (1u64 << 32);
165 match djump(addr, jump_table, basic_block_starts) {
166 Ok(target_pc) => {
167 let t = target_pc as usize;
168 if t < pc_to_idx.len() {
169 let tidx = pc_to_idx[t];
170 if tidx != u32::MAX {
171 branch_idx = tidx;
172 } else {
173 exit = Some(ExitReason::Panic);
174 }
175 } else {
176 exit = Some(ExitReason::Panic);
177 }
178 }
179 Err(reason) => exit = Some(reason),
180 }
181 }
182 Opcode::LoadImm => {
183 regs.gpr[ra] = imm1;
184 }
185
186 Opcode::MoveReg => {
188 regs.gpr[rd] = regs.gpr[ra];
189 }
190 Opcode::Sbrk => {
191 exit = Some(ExitReason::Panic);
193 }
194 Opcode::CountSetBits64 => {
195 regs.gpr[rd] = regs.gpr[ra].count_ones() as u64;
196 }
197 Opcode::CountSetBits32 => {
198 regs.gpr[rd] = (regs.gpr[ra] as u32).count_ones() as u64;
199 }
200 Opcode::LeadingZeroBits64 => {
201 regs.gpr[rd] = regs.gpr[ra].leading_zeros() as u64;
202 }
203 Opcode::LeadingZeroBits32 => {
204 regs.gpr[rd] = (regs.gpr[ra] as u32).leading_zeros() as u64;
205 }
206 Opcode::TrailingZeroBits64 => {
207 regs.gpr[rd] = regs.gpr[ra].trailing_zeros() as u64;
208 }
209 Opcode::TrailingZeroBits32 => {
210 regs.gpr[rd] = (regs.gpr[ra] as u32).trailing_zeros() as u64;
211 }
212 Opcode::SignExtend8 => {
213 regs.gpr[rd] = regs.gpr[ra] as u8 as i8 as i64 as u64;
214 }
215 Opcode::SignExtend16 => {
216 regs.gpr[rd] = regs.gpr[ra] as u16 as i16 as i64 as u64;
217 }
218 Opcode::ZeroExtend16 => {
219 regs.gpr[rd] = regs.gpr[ra] as u16 as u64;
220 }
221 Opcode::ReverseBytes => {
222 regs.gpr[rd] = regs.gpr[ra].swap_bytes();
223 }
224
225 Opcode::AddImm32 => {
227 regs.gpr[ra] = crate::args::sign_extend_32(regs.gpr[rb].wrapping_add(imm1));
228 }
229 Opcode::AddImm64 => {
230 regs.gpr[ra] = regs.gpr[rb].wrapping_add(imm1);
231 }
232 Opcode::MulImm32 => {
233 regs.gpr[ra] = crate::args::sign_extend_32(
234 (regs.gpr[rb] as u32).wrapping_mul(imm1 as u32) as u64,
235 );
236 }
237 Opcode::MulImm64 => {
238 regs.gpr[ra] = regs.gpr[rb].wrapping_mul(imm1);
239 }
240 Opcode::AndImm => {
241 regs.gpr[ra] = regs.gpr[rb] & imm1;
242 }
243 Opcode::XorImm => {
244 regs.gpr[ra] = regs.gpr[rb] ^ imm1;
245 }
246 Opcode::OrImm => {
247 regs.gpr[ra] = regs.gpr[rb] | imm1;
248 }
249 Opcode::SetLtUImm => {
250 regs.gpr[ra] = if regs.gpr[rb] < imm1 { 1 } else { 0 };
251 }
252 Opcode::SetLtSImm => {
253 regs.gpr[ra] = if (regs.gpr[rb] as i64) < (imm1 as i64) {
254 1
255 } else {
256 0
257 };
258 }
259 Opcode::SetGtUImm => {
260 regs.gpr[ra] = if regs.gpr[rb] > imm1 { 1 } else { 0 };
261 }
262 Opcode::SetGtSImm => {
263 regs.gpr[ra] = if (regs.gpr[rb] as i64) > (imm1 as i64) {
264 1
265 } else {
266 0
267 };
268 }
269 Opcode::ShloLImm32 => {
270 regs.gpr[ra] = crate::args::sign_extend_32(
271 (regs.gpr[rb] as u32).wrapping_shl((imm1 % 32) as u32) as u64,
272 );
273 }
274 Opcode::ShloRImm32 => {
275 regs.gpr[ra] = crate::args::sign_extend_32(
276 (regs.gpr[rb] as u32).wrapping_shr((imm1 % 32) as u32) as u64,
277 );
278 }
279 Opcode::SharRImm32 => {
280 regs.gpr[ra] =
281 (regs.gpr[rb] as u32 as i32).wrapping_shr((imm1 % 32) as u32) as i64 as u64;
282 }
283 Opcode::ShloLImm64 => {
284 regs.gpr[ra] = regs.gpr[rb].wrapping_shl((imm1 % 64) as u32);
285 }
286 Opcode::ShloRImm64 => {
287 regs.gpr[ra] = regs.gpr[rb].wrapping_shr((imm1 % 64) as u32);
288 }
289 Opcode::SharRImm64 => {
290 regs.gpr[ra] = (regs.gpr[rb] as i64).wrapping_shr((imm1 % 64) as u32) as u64;
291 }
292 Opcode::NegAddImm32 => {
293 regs.gpr[ra] =
294 crate::args::sign_extend_32(imm1.wrapping_sub(regs.gpr[rb]) as u32 as u64);
295 }
296 Opcode::NegAddImm64 => {
297 regs.gpr[ra] = imm1.wrapping_sub(regs.gpr[rb]);
298 }
299 Opcode::CmovIzImm => {
300 if regs.gpr[rb] == 0 {
301 regs.gpr[ra] = imm1;
302 }
303 }
304 Opcode::CmovNzImm => {
305 if regs.gpr[rb] != 0 {
306 regs.gpr[ra] = imm1;
307 }
308 }
309 Opcode::RotR64Imm => {
310 regs.gpr[ra] = regs.gpr[rb].rotate_right((imm1 % 64) as u32);
311 }
312 Opcode::RotR32Imm => {
313 regs.gpr[ra] = crate::args::sign_extend_32(
314 (regs.gpr[rb] as u32).rotate_right((imm1 % 32) as u32) as u64,
315 );
316 }
317
318 Opcode::ShloLImmAlt32 => {
320 regs.gpr[ra] = crate::args::sign_extend_32(
321 (imm1 as u32).wrapping_shl((regs.gpr[rb] % 32) as u32) as u64,
322 );
323 }
324 Opcode::ShloRImmAlt32 => {
325 regs.gpr[ra] = crate::args::sign_extend_32(
326 (imm1 as u32).wrapping_shr((regs.gpr[rb] % 32) as u32) as u64,
327 );
328 }
329 Opcode::SharRImmAlt32 => {
330 regs.gpr[ra] = ((imm1 as u32) as i32).wrapping_shr((regs.gpr[rb] % 32) as u32)
331 as i64 as u64;
332 }
333 Opcode::ShloLImmAlt64 => {
334 regs.gpr[ra] = imm1.wrapping_shl((regs.gpr[rb] % 64) as u32);
335 }
336 Opcode::ShloRImmAlt64 => {
337 regs.gpr[ra] = imm1.wrapping_shr((regs.gpr[rb] % 64) as u32);
338 }
339 Opcode::SharRImmAlt64 => {
340 regs.gpr[ra] = (imm1 as i64).wrapping_shr((regs.gpr[rb] % 64) as u32) as u64;
341 }
342 Opcode::RotR64ImmAlt => {
343 regs.gpr[ra] = imm1.rotate_right((regs.gpr[rb] % 64) as u32);
344 }
345 Opcode::RotR32ImmAlt => {
346 regs.gpr[ra] = crate::args::sign_extend_32(
347 (imm1 as u32).rotate_right((regs.gpr[rb] % 32) as u32) as u64,
348 );
349 }
350
351 Opcode::BranchEq
353 | Opcode::BranchNe
354 | Opcode::BranchLtU
355 | Opcode::BranchGeU
356 | Opcode::BranchLtS
357 | Opcode::BranchGeS => {
358 let (a, b) = (regs.gpr[ra], regs.gpr[rb]);
359 let cond = match inst.opcode {
360 Opcode::BranchEq => a == b,
361 Opcode::BranchNe => a != b,
362 Opcode::BranchLtU => a < b,
363 Opcode::BranchGeU => a >= b,
364 Opcode::BranchLtS => (a as i64) < (b as i64),
365 Opcode::BranchGeS => (a as i64) >= (b as i64),
366 _ => unreachable!(),
367 };
368 if cond {
369 if inst.target_idx != u32::MAX {
370 branch_idx = inst.target_idx;
371 } else {
372 exit = Some(ExitReason::Panic);
373 }
374 }
375 }
376
377 Opcode::Add32 => {
379 regs.gpr[rd] =
380 crate::args::sign_extend_32(regs.gpr[ra].wrapping_add(regs.gpr[rb]));
381 }
382 Opcode::Sub32 => {
383 regs.gpr[rd] =
384 crate::args::sign_extend_32(regs.gpr[ra].wrapping_sub(regs.gpr[rb]));
385 }
386 Opcode::Add64 => {
387 regs.gpr[rd] = regs.gpr[ra].wrapping_add(regs.gpr[rb]);
388 }
389 Opcode::Sub64 => {
390 regs.gpr[rd] = regs.gpr[ra].wrapping_sub(regs.gpr[rb]);
391 }
392 Opcode::Mul32 => {
393 regs.gpr[rd] = crate::args::sign_extend_32(
394 (regs.gpr[ra] as u32).wrapping_mul(regs.gpr[rb] as u32) as u64,
395 );
396 }
397 Opcode::Mul64 => {
398 regs.gpr[rd] = regs.gpr[ra].wrapping_mul(regs.gpr[rb]);
399 }
400 Opcode::And => {
401 regs.gpr[rd] = regs.gpr[ra] & regs.gpr[rb];
402 }
403 Opcode::Or => {
404 regs.gpr[rd] = regs.gpr[ra] | regs.gpr[rb];
405 }
406 Opcode::Xor => {
407 regs.gpr[rd] = regs.gpr[ra] ^ regs.gpr[rb];
408 }
409 Opcode::SetLtU => {
410 regs.gpr[rd] = if regs.gpr[ra] < regs.gpr[rb] { 1 } else { 0 };
411 }
412 Opcode::SetLtS => {
413 regs.gpr[rd] = if (regs.gpr[ra] as i64) < (regs.gpr[rb] as i64) {
414 1
415 } else {
416 0
417 };
418 }
419 Opcode::CmovIz => {
420 if regs.gpr[rb] == 0 {
421 regs.gpr[rd] = regs.gpr[ra];
422 }
423 }
424 Opcode::CmovNz => {
425 if regs.gpr[rb] != 0 {
426 regs.gpr[rd] = regs.gpr[ra];
427 }
428 }
429 Opcode::ShloL32 => {
430 regs.gpr[rd] = crate::args::sign_extend_32(
431 (regs.gpr[ra] as u32).wrapping_shl((regs.gpr[rb] % 32) as u32) as u64,
432 );
433 }
434 Opcode::ShloR32 => {
435 regs.gpr[rd] = crate::args::sign_extend_32(
436 (regs.gpr[ra] as u32).wrapping_shr((regs.gpr[rb] % 32) as u32) as u64,
437 );
438 }
439 Opcode::SharR32 => {
440 regs.gpr[rd] = (regs.gpr[ra] as u32 as i32)
441 .wrapping_shr((regs.gpr[rb] % 32) as u32)
442 as i64 as u64;
443 }
444 Opcode::ShloL64 => {
445 regs.gpr[rd] = regs.gpr[ra].wrapping_shl((regs.gpr[rb] % 64) as u32);
446 }
447 Opcode::ShloR64 => {
448 regs.gpr[rd] = regs.gpr[ra].wrapping_shr((regs.gpr[rb] % 64) as u32);
449 }
450 Opcode::SharR64 => {
451 regs.gpr[rd] =
452 (regs.gpr[ra] as i64).wrapping_shr((regs.gpr[rb] % 64) as u32) as u64;
453 }
454 Opcode::RotL64 => {
455 regs.gpr[rd] = regs.gpr[ra].rotate_left((regs.gpr[rb] % 64) as u32);
456 }
457 Opcode::RotR64 => {
458 regs.gpr[rd] = regs.gpr[ra].rotate_right((regs.gpr[rb] % 64) as u32);
459 }
460 Opcode::RotL32 => {
461 regs.gpr[rd] = crate::args::sign_extend_32(
462 (regs.gpr[ra] as u32).rotate_left((regs.gpr[rb] % 32) as u32) as u64,
463 );
464 }
465 Opcode::RotR32 => {
466 regs.gpr[rd] = crate::args::sign_extend_32(
467 (regs.gpr[ra] as u32).rotate_right((regs.gpr[rb] % 32) as u32) as u64,
468 );
469 }
470 Opcode::AndInv => {
471 regs.gpr[rd] = regs.gpr[ra] & !regs.gpr[rb];
472 }
473 Opcode::OrInv => {
474 regs.gpr[rd] = regs.gpr[ra] | !regs.gpr[rb];
475 }
476 Opcode::Xnor => {
477 regs.gpr[rd] = !(regs.gpr[ra] ^ regs.gpr[rb]);
478 }
479 Opcode::Max => {
480 regs.gpr[rd] = core::cmp::max(regs.gpr[ra] as i64, regs.gpr[rb] as i64) as u64;
481 }
482 Opcode::MaxU => {
483 regs.gpr[rd] = core::cmp::max(regs.gpr[ra], regs.gpr[rb]);
484 }
485 Opcode::Min => {
486 regs.gpr[rd] = core::cmp::min(regs.gpr[ra] as i64, regs.gpr[rb] as i64) as u64;
487 }
488 Opcode::MinU => {
489 regs.gpr[rd] = core::cmp::min(regs.gpr[ra], regs.gpr[rb]);
490 }
491
492 Opcode::LoadIndU8 => do_load!(
494 mem,
495 regs,
496 exit,
497 ra,
498 regs.gpr[rb].wrapping_add(imm1) as u32,
499 read_u8,
500 |v| v as u64
501 ),
502 Opcode::LoadIndI8 => do_load!(
503 mem,
504 regs,
505 exit,
506 ra,
507 regs.gpr[rb].wrapping_add(imm1) as u32,
508 read_u8,
509 |v| v as i8 as i64 as u64
510 ),
511 Opcode::LoadIndU16 => do_load!(
512 mem,
513 regs,
514 exit,
515 ra,
516 regs.gpr[rb].wrapping_add(imm1) as u32,
517 read_u16_le,
518 |v| v as u64
519 ),
520 Opcode::LoadIndI16 => do_load!(
521 mem,
522 regs,
523 exit,
524 ra,
525 regs.gpr[rb].wrapping_add(imm1) as u32,
526 read_u16_le,
527 |v| v as i16 as i64 as u64
528 ),
529 Opcode::LoadIndU32 => do_load!(
530 mem,
531 regs,
532 exit,
533 ra,
534 regs.gpr[rb].wrapping_add(imm1) as u32,
535 read_u32_le,
536 |v| v as u64
537 ),
538 Opcode::LoadIndI32 => do_load!(
539 mem,
540 regs,
541 exit,
542 ra,
543 regs.gpr[rb].wrapping_add(imm1) as u32,
544 read_u32_le,
545 |v| v as i32 as i64 as u64
546 ),
547 Opcode::LoadIndU64 => do_load!(
548 mem,
549 regs,
550 exit,
551 ra,
552 regs.gpr[rb].wrapping_add(imm1) as u32,
553 read_u64_le,
554 |v| v
555 ),
556
557 Opcode::StoreIndU8 => do_store!(
559 mem,
560 exit,
561 regs.gpr[rb].wrapping_add(imm1) as u32,
562 write_u8,
563 regs.gpr[ra] as u8
564 ),
565 Opcode::StoreIndU16 => do_store!(
566 mem,
567 exit,
568 regs.gpr[rb].wrapping_add(imm1) as u32,
569 write_u16_le,
570 regs.gpr[ra] as u16
571 ),
572 Opcode::StoreIndU32 => do_store!(
573 mem,
574 exit,
575 regs.gpr[rb].wrapping_add(imm1) as u32,
576 write_u32_le,
577 regs.gpr[ra] as u32
578 ),
579 Opcode::StoreIndU64 => do_store!(
580 mem,
581 exit,
582 regs.gpr[rb].wrapping_add(imm1) as u32,
583 write_u64_le,
584 regs.gpr[ra]
585 ),
586
587 Opcode::DivU32 => {
589 let b = regs.gpr[rb] as u32;
590 regs.gpr[rd] = (regs.gpr[ra] as u32)
591 .checked_div(b)
592 .map(|q| crate::args::sign_extend_32(q as u64))
593 .unwrap_or(u64::MAX);
594 }
595 Opcode::DivU64 => {
596 let b = regs.gpr[rb];
597 regs.gpr[rd] = regs.gpr[ra].checked_div(b).unwrap_or(u64::MAX);
598 }
599 Opcode::DivS32 => {
600 let a = regs.gpr[ra] as i32;
601 let b = regs.gpr[rb] as i32;
602 regs.gpr[rd] = if b == 0 {
603 u64::MAX
604 } else if a == i32::MIN && b == -1 {
605 a as u64
606 } else {
607 crate::args::sign_extend_32((a / b) as i64 as u64)
608 };
609 }
610 Opcode::DivS64 => {
611 let a = regs.gpr[ra] as i64;
612 let b = regs.gpr[rb] as i64;
613 regs.gpr[rd] = if b == 0 {
614 u64::MAX
615 } else if a == i64::MIN && b == -1 {
616 a as u64
617 } else {
618 (a / b) as u64
619 };
620 }
621 Opcode::RemU32 => {
622 let b = regs.gpr[rb] as u32;
623 regs.gpr[rd] = if b == 0 {
624 crate::args::sign_extend_32(regs.gpr[ra] as u32 as u64)
625 } else {
626 crate::args::sign_extend_32((regs.gpr[ra] as u32 % b) as u64)
627 };
628 }
629 Opcode::RemU64 => {
630 let b = regs.gpr[rb];
631 regs.gpr[rd] = if b == 0 {
632 regs.gpr[ra]
633 } else {
634 regs.gpr[ra] % b
635 };
636 }
637 Opcode::RemS32 => {
638 let a = regs.gpr[ra] as i32;
639 let b = regs.gpr[rb] as i32;
640 regs.gpr[rd] = if b == 0 {
641 a as u64
642 } else if a == i32::MIN && b == -1 {
643 0
644 } else {
645 crate::args::sign_extend_32((a % b) as i64 as u64)
646 };
647 }
648 Opcode::RemS64 => {
649 let a = regs.gpr[ra] as i64;
650 let b = regs.gpr[rb] as i64;
651 regs.gpr[rd] = if b == 0 {
652 a as u64
653 } else if a == i64::MIN && b == -1 {
654 0
655 } else {
656 (a % b) as u64
657 };
658 }
659 Opcode::MulUpperSS => {
660 regs.gpr[rd] = ((regs.gpr[ra] as i64 as i128)
661 .wrapping_mul(regs.gpr[rb] as i64 as i128)
662 >> 64) as u64;
663 }
664 Opcode::MulUpperUU => {
665 regs.gpr[rd] =
666 ((regs.gpr[ra] as u128).wrapping_mul(regs.gpr[rb] as u128) >> 64) as u64;
667 }
668 Opcode::MulUpperSU => {
669 regs.gpr[rd] = ((regs.gpr[ra] as i64 as i128)
670 .wrapping_mul(regs.gpr[rb] as u128 as i128)
671 >> 64) as u64;
672 }
673
674 Opcode::StoreImmU8 => {
676 do_store!(mem, exit, imm1 as u32, write_u8, inst.imm2 as u8)
677 }
678 Opcode::StoreImmU16 => {
679 do_store!(mem, exit, imm1 as u32, write_u16_le, inst.imm2 as u16)
680 }
681 Opcode::StoreImmU32 => {
682 do_store!(mem, exit, imm1 as u32, write_u32_le, inst.imm2 as u32)
683 }
684 Opcode::StoreImmU64 => {
685 do_store!(mem, exit, imm1 as u32, write_u64_le, inst.imm2)
686 }
687
688 Opcode::LoadU8 => {
690 do_load!(mem, regs, exit, ra, imm1 as u32, read_u8, |v| v as u64)
691 }
692 Opcode::LoadI8 => {
693 do_load!(
694 mem,
695 regs,
696 exit,
697 ra,
698 imm1 as u32,
699 read_u8,
700 |v| v as i8 as i64 as u64
701 )
702 }
703 Opcode::LoadU16 => {
704 do_load!(mem, regs, exit, ra, imm1 as u32, read_u16_le, |v| v as u64)
705 }
706 Opcode::LoadI16 => do_load!(mem, regs, exit, ra, imm1 as u32, read_u16_le, |v| v
707 as i16
708 as i64
709 as u64),
710 Opcode::LoadU32 => {
711 do_load!(mem, regs, exit, ra, imm1 as u32, read_u32_le, |v| v as u64)
712 }
713 Opcode::LoadI32 => do_load!(mem, regs, exit, ra, imm1 as u32, read_u32_le, |v| v
714 as i32
715 as i64
716 as u64),
717 Opcode::LoadU64 => {
718 do_load!(mem, regs, exit, ra, imm1 as u32, read_u64_le, |v| v)
719 }
720
721 Opcode::StoreU8 => {
723 do_store!(mem, exit, imm1 as u32, write_u8, regs.gpr[ra] as u8)
724 }
725 Opcode::StoreU16 => {
726 do_store!(mem, exit, imm1 as u32, write_u16_le, regs.gpr[ra] as u16)
727 }
728 Opcode::StoreU32 => {
729 do_store!(mem, exit, imm1 as u32, write_u32_le, regs.gpr[ra] as u32)
730 }
731 Opcode::StoreU64 => {
732 do_store!(mem, exit, imm1 as u32, write_u64_le, regs.gpr[ra])
733 }
734
735 Opcode::StoreImmIndU8 => do_store!(
737 mem,
738 exit,
739 regs.gpr[ra].wrapping_add(imm1) as u32,
740 write_u8,
741 inst.imm2 as u8
742 ),
743 Opcode::StoreImmIndU16 => do_store!(
744 mem,
745 exit,
746 regs.gpr[ra].wrapping_add(imm1) as u32,
747 write_u16_le,
748 inst.imm2 as u16
749 ),
750 Opcode::StoreImmIndU32 => do_store!(
751 mem,
752 exit,
753 regs.gpr[ra].wrapping_add(imm1) as u32,
754 write_u32_le,
755 inst.imm2 as u32
756 ),
757 Opcode::StoreImmIndU64 => do_store!(
758 mem,
759 exit,
760 regs.gpr[ra].wrapping_add(imm1) as u32,
761 write_u64_le,
762 inst.imm2
763 ),
764
765 Opcode::LoadImmJump => {
767 regs.gpr[ra] = imm1;
768 if inst.target_idx != u32::MAX {
769 branch_idx = inst.target_idx;
770 } else {
771 exit = Some(ExitReason::Panic);
772 }
773 }
774
775 Opcode::BranchEqImm
777 | Opcode::BranchNeImm
778 | Opcode::BranchLtUImm
779 | Opcode::BranchLeUImm
780 | Opcode::BranchGeUImm
781 | Opcode::BranchGtUImm
782 | Opcode::BranchLtSImm
783 | Opcode::BranchLeSImm
784 | Opcode::BranchGeSImm
785 | Opcode::BranchGtSImm => {
786 let (a, b) = (regs.gpr[ra], imm1);
787 let cond = match inst.opcode {
788 Opcode::BranchEqImm => a == b,
789 Opcode::BranchNeImm => a != b,
790 Opcode::BranchLtUImm => a < b,
791 Opcode::BranchLeUImm => a <= b,
792 Opcode::BranchGeUImm => a >= b,
793 Opcode::BranchGtUImm => a > b,
794 Opcode::BranchLtSImm => (a as i64) < (b as i64),
795 Opcode::BranchLeSImm => (a as i64) <= (b as i64),
796 Opcode::BranchGeSImm => (a as i64) >= (b as i64),
797 Opcode::BranchGtSImm => (a as i64) > (b as i64),
798 _ => unreachable!(),
799 };
800 if cond {
801 if inst.target_idx != u32::MAX {
802 branch_idx = inst.target_idx;
803 } else {
804 exit = Some(ExitReason::Panic);
805 }
806 }
807 }
808
809 Opcode::LoadImmJumpInd => {
811 regs.gpr[ra] = imm1;
812 let addr = regs.gpr[rb].wrapping_add(inst.imm2) % (1u64 << 32);
813 match djump(addr, jump_table, basic_block_starts) {
814 Ok(target_pc) => {
815 let t = target_pc as usize;
816 if t < pc_to_idx.len() {
817 let tidx = pc_to_idx[t];
818 if tidx != u32::MAX {
819 branch_idx = tidx;
820 } else {
821 exit = Some(ExitReason::Panic);
822 }
823 } else {
824 exit = Some(ExitReason::Panic);
825 }
826 }
827 Err(reason) => exit = Some(reason),
828 }
829 }
830 }
831
832 if let Some(reason) = exit {
833 regs.pc = inst.pc as u64;
834 return reason;
835 }
836
837 idx = if branch_idx == u32::MAX {
838 inst.next_idx
839 } else {
840 branch_idx
841 };
842 }
843 }
844}
845
846fn djump(a: u64, jump_table: &[u32], basic_block_starts: &[bool]) -> Result<u32, ExitReason> {
852 const ZA: u64 = 2;
853 if a == 0 || a > (jump_table.len() as u64) * ZA || !a.is_multiple_of(ZA) {
854 return Err(ExitReason::Panic);
855 }
856 let idx = (a / ZA) as usize - 1;
857 let target = jump_table[idx];
858 let t = target as usize;
859 if t >= basic_block_starts.len() || !basic_block_starts[t] {
860 return Err(ExitReason::Panic);
861 }
862 Ok(target)
863}
864
865#[cfg(test)]
866mod tests {
867 use super::*;
868 use crate::ecall::PanickingHandler;
869 use crate::mem::Mem;
870 use crate::regs::REG_COUNT;
871
872 fn single_byte_prog(opcode_byte: u8) -> PvmProgram {
874 PvmProgram::new(vec![opcode_byte], vec![1u8], vec![], 25).unwrap()
875 }
876
877 fn run_with_panic_handler(prog: &PvmProgram, gas: u64) -> (ExitReason, Regs) {
878 let mut regs = Regs::new();
879 let mut mem = Mem::new();
880 let mut g = GasCounter::new(gas);
881 let mut h = PanickingHandler;
882 let r = Interpreter::run(prog, &mut regs, &mut mem, &mut g, &mut h);
883 (r, regs)
884 }
885
886 #[test]
887 fn trap_returns_trap() {
888 let (r, _) = run_with_panic_handler(&single_byte_prog(0), 1000);
889 assert_eq!(r, ExitReason::Trap);
890 }
891
892 #[test]
893 fn fallthrough_falls_into_sentinel_trap() {
894 let (r, _) = run_with_panic_handler(&single_byte_prog(1), 1000);
895 assert_eq!(r, ExitReason::Trap);
896 }
897
898 #[test]
899 fn unlikely_falls_into_sentinel_trap() {
900 let (r, _) = run_with_panic_handler(&single_byte_prog(2), 1000);
901 assert_eq!(r, ExitReason::Trap);
902 }
903
904 #[test]
906 fn ecalli_routes_through_handler() {
907 let prog = PvmProgram::new(vec![10u8, 42, 0], vec![1, 0, 1], vec![], 25).unwrap();
909
910 struct Capture {
911 seen: Option<EcallKind>,
912 }
913 impl EcallHandler for Capture {
914 fn handle(
915 &mut self,
916 kind: EcallKind,
917 _r: &mut Regs,
918 _m: &mut dyn Memory,
919 ) -> EcallResult {
920 self.seen = Some(kind);
921 EcallResult::Exit(ExitReason::HostCall(match kind {
922 EcallKind::Ecalli(op) => op,
923 EcallKind::Ecall => 0,
924 }))
925 }
926 }
927
928 let mut regs = Regs::new();
929 let mut mem = Mem::new();
930 let mut gas = GasCounter::new(1000);
931 let mut h = Capture { seen: None };
932 let r = Interpreter::run(&prog, &mut regs, &mut mem, &mut gas, &mut h);
933 assert_eq!(r, ExitReason::HostCall(42));
934 assert_eq!(h.seen, Some(EcallKind::Ecalli(42)));
935 }
936
937 fn run_with_regs(
948 code: Vec<u8>,
949 bitmask: Vec<u8>,
950 initial_regs: [u64; REG_COUNT],
951 gas_budget: u64,
952 ) -> (ExitReason, Regs, u64) {
953 let prog = PvmProgram::new(code, bitmask, vec![], 25).unwrap();
954 let mut regs = Regs::new();
955 regs.gpr = initial_regs;
956 let mut mem = Mem::new();
957 let mut g = GasCounter::new(gas_budget);
958 let mut h = PanickingHandler;
959 let r = Interpreter::run(&prog, &mut regs, &mut mem, &mut g, &mut h);
960 (r, regs, gas_budget - g.remaining())
961 }
962
963 #[test]
964 fn out_of_gas_in_long_fallthrough() {
965 let (r, _, _) = run_with_regs(vec![1u8; 100], vec![1u8; 100], [0; REG_COUNT], 5);
967 assert_eq!(r, ExitReason::OutOfGas);
968 }
969
970 #[test]
971 fn empty_program_panics() {
972 let prog = PvmProgram::new(vec![], vec![], vec![], 25).unwrap();
974 let mut regs = Regs::new();
975 let mut mem = Mem::new();
976 let mut g = GasCounter::new(100);
977 let mut h = PanickingHandler;
978 assert_eq!(
979 Interpreter::run(&prog, &mut regs, &mut mem, &mut g, &mut h),
980 ExitReason::Panic
981 );
982 }
983
984 #[test]
985 fn load_imm_sets_register() {
986 let code = vec![51, 0x00, 42, 0, 0, 0, 0];
990 let bitmask = vec![1, 0, 0, 0, 0, 0, 1];
991 let (r, regs, _) = run_with_regs(code, bitmask, [0; REG_COUNT], 100);
992 assert_eq!(r, ExitReason::Trap);
993 assert_eq!(regs.gpr[0], 42);
994 }
995
996 #[test]
997 fn add_imm_64_two_reg_one_imm() {
998 let code = vec![149, 0x10, 10, 0, 0, 0, 0];
1001 let bitmask = vec![1, 0, 0, 0, 0, 0, 1];
1002 let mut regs = [0u64; REG_COUNT];
1003 regs[1] = 32;
1004 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1005 assert_eq!(r, ExitReason::Trap);
1006 assert_eq!(regs_out.gpr[0], 42);
1007 }
1008
1009 #[test]
1010 fn add64_three_reg() {
1011 let code = vec![200, 0x10, 2, 0];
1014 let bitmask = vec![1, 0, 0, 1];
1015 let mut regs = [0u64; REG_COUNT];
1016 regs[0] = 100;
1017 regs[1] = 200;
1018 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1019 assert_eq!(r, ExitReason::Trap);
1020 assert_eq!(regs_out.gpr[2], 300);
1021 }
1022
1023 #[test]
1024 fn sub64_three_reg() {
1025 let code = vec![201, 0x10, 2, 0];
1027 let bitmask = vec![1, 0, 0, 1];
1028 let mut regs = [0u64; REG_COUNT];
1029 regs[0] = 300;
1030 regs[1] = 100;
1031 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1032 assert_eq!(r, ExitReason::Trap);
1033 assert_eq!(regs_out.gpr[2], 200);
1034 }
1035
1036 #[test]
1037 fn and_three_reg() {
1038 let code = vec![210, 0x10, 2, 0];
1040 let bitmask = vec![1, 0, 0, 1];
1041 let mut regs = [0u64; REG_COUNT];
1042 regs[0] = 0xFF00;
1043 regs[1] = 0x0FF0;
1044 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1045 assert_eq!(r, ExitReason::Trap);
1046 assert_eq!(regs_out.gpr[2], 0x0F00);
1047 }
1048
1049 #[test]
1050 fn set_lt_u_three_reg() {
1051 let code = vec![216, 0x10, 2, 0];
1053 let bitmask = vec![1, 0, 0, 1];
1054 let mut regs = [0u64; REG_COUNT];
1055 regs[0] = 5;
1056 regs[1] = 10;
1057 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1058 assert_eq!(r, ExitReason::Trap);
1059 assert_eq!(regs_out.gpr[2], 1);
1060 }
1061
1062 #[test]
1063 fn move_reg_two_reg() {
1064 let code = vec![100, 0x10, 0];
1066 let bitmask = vec![1, 0, 1];
1067 let mut regs = [0u64; REG_COUNT];
1068 regs[1] = 42;
1069 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1070 assert_eq!(r, ExitReason::Trap);
1071 assert_eq!(regs_out.gpr[0], 42);
1072 }
1073
1074 #[test]
1075 fn count_set_bits_64() {
1076 let code = vec![102, 0x10, 0];
1078 let bitmask = vec![1, 0, 1];
1079 let mut regs = [0u64; REG_COUNT];
1080 regs[1] = 0xFF;
1081 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1082 assert_eq!(r, ExitReason::Trap);
1083 assert_eq!(regs_out.gpr[0], 8);
1084 }
1085
1086 #[test]
1087 fn div_u64_by_zero_returns_max() {
1088 let code = vec![203, 0x10, 2, 0];
1090 let bitmask = vec![1, 0, 0, 1];
1091 let mut regs = [0u64; REG_COUNT];
1092 regs[0] = 100;
1093 regs[1] = 0;
1094 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1095 assert_eq!(r, ExitReason::Trap);
1096 assert_eq!(regs_out.gpr[2], u64::MAX);
1097 }
1098
1099 #[test]
1100 fn sign_extend_8() {
1101 let code = vec![108, 0x10, 0];
1103 let bitmask = vec![1, 0, 1];
1104 let mut regs = [0u64; REG_COUNT];
1105 regs[1] = 0x80;
1106 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1107 assert_eq!(r, ExitReason::Trap);
1108 assert_eq!(regs_out.gpr[0] as i64, -128);
1109 }
1110
1111 #[test]
1112 fn reverse_bytes_u64() {
1113 let code = vec![111, 0x10, 0];
1115 let bitmask = vec![1, 0, 1];
1116 let mut regs = [0u64; REG_COUNT];
1117 regs[1] = 0x0123456789ABCDEF;
1118 let (r, regs_out, _) = run_with_regs(code, bitmask, regs, 100);
1119 assert_eq!(r, ExitReason::Trap);
1120 assert_eq!(regs_out.gpr[0], 0xEFCDAB8967452301);
1121 }
1122
1123 #[test]
1124 fn sbrk_panics() {
1125 let code = vec![101, 0x00];
1128 let bitmask = vec![1, 0];
1129 let (r, _, _) = run_with_regs(code, bitmask, [0; REG_COUNT], 100);
1130 assert_eq!(r, ExitReason::Panic);
1131 }
1132
1133 #[test]
1134 fn page_fault_on_unmapped_load() {
1135 let code = vec![52, 0x00, 0x00, 0x10, 0x00, 0x00, 0];
1139 let bitmask = vec![1, 0, 0, 0, 0, 0, 1];
1140 let (r, _, _) = run_with_regs(code, bitmask, [0; REG_COUNT], 100);
1141 assert_eq!(r, ExitReason::PageFault(0x1000));
1142 }
1143
1144 #[test]
1148 fn gas_blocks_exclude_branch_targets() {
1149 use crate::decode::{
1150 compute_basic_block_starts, compute_block_gas_costs, compute_gas_block_starts,
1151 };
1152
1153 let code = vec![1, 100, 0x10, 100, 0x10, 40, 0xFE, 0xFF, 0xFF, 0xFF, 0];
1160 let bitmask = vec![1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1];
1161
1162 let bb = compute_basic_block_starts(&code, &bitmask);
1163 let gas = compute_gas_block_starts(&code, &bitmask);
1164 let costs = compute_block_gas_costs(&code, &bitmask, &gas, 25);
1165
1166 assert!(bb[3], "PC 3 is a branch target");
1168 assert!(!gas[3], "PC 3 is NOT a gas block start");
1169 assert_eq!(costs[3], 0, "PC 3 carries no gas cost");
1170 assert!(gas[1] && gas[10]);
1172 assert!(costs[1] > 0 && costs[10] > 0);
1173 }
1174}