Skip to main content

sbpf_vm/
vm.rs

1use {
2    crate::{
3        compute::ComputeMeter,
4        errors::{SbpfVmError, SbpfVmResult},
5        memory::Memory,
6        syscalls::SyscallHandler,
7    },
8    sbpf_common::{
9        errors::ExecutionError, execute::Vm, inst_handler::OPCODE_TO_HANDLER,
10        instruction::Instruction,
11    },
12    serde::{Deserialize, Serialize},
13};
14
15/// VM configuration
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct SbpfVmConfig {
18    pub max_call_depth: usize,
19    pub compute_unit_limit: u64,
20    pub heap_size: usize,
21}
22
23impl Default for SbpfVmConfig {
24    fn default() -> Self {
25        Self {
26            max_call_depth: 64,
27            compute_unit_limit: 1_400_000,
28            heap_size: Memory::DEFAULT_HEAP_SIZE,
29        }
30    }
31}
32
33/// Call frame for internal function calls
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct CallFrame {
36    pub return_pc: usize,
37    pub saved_registers: [u64; 4], // callee-saved registers (r6-r9)
38    pub saved_frame_pointer: u64,
39}
40
41/// sBPF Virtual Machine
42pub struct SbpfVm<H: SyscallHandler> {
43    pub config: SbpfVmConfig,
44    pub registers: [u64; 11],
45    pub pc: usize,
46    pub call_stack: Vec<CallFrame>,
47    pub memory: Memory,
48    pub program: Vec<Instruction>,
49    pub halted: bool,
50    pub exit_code: Option<u64>,
51    pub compute_meter: ComputeMeter,
52    pub syscall_handler: H,
53}
54
55impl<H: SyscallHandler> SbpfVm<H> {
56    pub fn new(
57        program: Vec<Instruction>,
58        input: Vec<u8>,
59        rodata: Vec<u8>,
60        syscall_handler: H,
61    ) -> Self {
62        Self::new_with_config(
63            program,
64            input,
65            rodata,
66            syscall_handler,
67            SbpfVmConfig::default(),
68        )
69    }
70
71    pub fn new_with_config(
72        program: Vec<Instruction>,
73        input: Vec<u8>,
74        rodata: Vec<u8>,
75        syscall_handler: H,
76        config: SbpfVmConfig,
77    ) -> Self {
78        let memory = Memory::new(
79            input,
80            rodata,
81            Memory::stack_size(config.max_call_depth),
82            config.heap_size,
83        );
84
85        let mut registers = [0u64; 11];
86        registers[1] = Memory::INPUT_START;
87        registers[10] = memory.initial_frame_pointer();
88
89        Self {
90            registers,
91            pc: 0,
92            call_stack: Vec::new(),
93            memory,
94            program,
95            halted: false,
96            exit_code: None,
97            compute_meter: ComputeMeter::new(config.compute_unit_limit),
98            syscall_handler,
99            config,
100        }
101    }
102
103    pub fn reset(&mut self) {
104        self.registers = [0u64; 11];
105        self.registers[1] = Memory::INPUT_START;
106        self.registers[10] = self.memory.initial_frame_pointer();
107        self.pc = 0;
108        self.call_stack.clear();
109        self.halted = false;
110        self.exit_code = None;
111        self.compute_meter.reset();
112        self.memory.reset_heap();
113    }
114
115    pub fn current_instruction(&self) -> SbpfVmResult<&Instruction> {
116        self.program
117            .get(self.pc)
118            .ok_or(SbpfVmError::PcOutOfBounds(self.pc))
119    }
120
121    pub fn set_entrypoint(&mut self, pc: usize) {
122        self.pc = pc;
123    }
124
125    pub fn is_pc_valid(&self) -> bool {
126        self.pc < self.program.len()
127    }
128
129    pub fn get_remaining(&self) -> u64 {
130        self.compute_meter.get_remaining()
131    }
132
133    pub fn step(&mut self) -> SbpfVmResult<()> {
134        if self.halted {
135            return Ok(());
136        }
137
138        if !self.is_pc_valid() {
139            return Err(SbpfVmError::PcOutOfBounds(self.pc));
140        }
141
142        self.compute_meter.consume(1)?;
143
144        let inst = self.current_instruction()?.clone();
145        self.execute_instruction(&inst)?;
146
147        Ok(())
148    }
149
150    fn execute_instruction(&mut self, inst: &Instruction) -> SbpfVmResult<()> {
151        if let Some(handler) = OPCODE_TO_HANDLER.get(&inst.opcode) {
152            (handler.execute)(self, inst)?;
153            Ok(())
154        } else {
155            Err(SbpfVmError::InvalidInstruction)
156        }
157    }
158
159    pub fn run(&mut self) -> SbpfVmResult<()> {
160        let mut steps = 0;
161
162        while !self.halted && steps < self.config.compute_unit_limit {
163            self.step()?;
164            steps += 1;
165        }
166
167        if !self.halted && steps >= self.config.compute_unit_limit {
168            return Err(SbpfVmError::ExecutionLimitReached(
169                self.config.compute_unit_limit,
170            ));
171        }
172
173        Ok(())
174    }
175}
176
177impl<H: SyscallHandler> Vm for SbpfVm<H> {
178    fn get_register(&self, reg: usize) -> u64 {
179        self.registers[reg]
180    }
181
182    fn set_register(&mut self, reg: usize, value: u64) {
183        self.registers[reg] = value;
184    }
185
186    fn get_pc(&self) -> usize {
187        self.pc
188    }
189
190    fn set_pc(&mut self, pc: usize) {
191        self.pc = pc;
192    }
193
194    fn read_u8(&self, addr: u64) -> Result<u8, ExecutionError> {
195        self.memory
196            .read_u8(addr)
197            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
198    }
199
200    fn read_u16(&self, addr: u64) -> Result<u16, ExecutionError> {
201        self.memory
202            .read_u16(addr)
203            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
204    }
205
206    fn read_u32(&self, addr: u64) -> Result<u32, ExecutionError> {
207        self.memory
208            .read_u32(addr)
209            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
210    }
211
212    fn read_u64(&self, addr: u64) -> Result<u64, ExecutionError> {
213        self.memory
214            .read_u64(addr)
215            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
216    }
217
218    fn write_u8(&mut self, addr: u64, value: u8) -> Result<(), ExecutionError> {
219        self.memory
220            .write_u8(addr, value)
221            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
222    }
223
224    fn write_u16(&mut self, addr: u64, value: u16) -> Result<(), ExecutionError> {
225        self.memory
226            .write_u16(addr, value)
227            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
228    }
229
230    fn write_u32(&mut self, addr: u64, value: u32) -> Result<(), ExecutionError> {
231        self.memory
232            .write_u32(addr, value)
233            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
234    }
235
236    fn write_u64(&mut self, addr: u64, value: u64) -> Result<(), ExecutionError> {
237        self.memory
238            .write_u64(addr, value)
239            .map_err(|_| ExecutionError::InvalidMemoryAccess(addr))
240    }
241
242    fn get_call_depth(&self) -> usize {
243        self.call_stack.len()
244    }
245
246    fn max_call_depth(&self) -> usize {
247        self.config.max_call_depth
248    }
249
250    fn push_frame(
251        &mut self,
252        return_pc: usize,
253        saved_registers: [u64; 4],
254        saved_frame_pointer: u64,
255    ) -> Result<(), ExecutionError> {
256        self.call_stack.push(CallFrame {
257            return_pc,
258            saved_registers,
259            saved_frame_pointer,
260        });
261        Ok(())
262    }
263
264    fn pop_frame(&mut self) -> Option<(usize, [u64; 4], u64)> {
265        self.call_stack.pop().map(|frame| {
266            (
267                frame.return_pc,
268                frame.saved_registers,
269                frame.saved_frame_pointer,
270            )
271        })
272    }
273
274    fn halt(&mut self, exit_code: u64) {
275        self.halted = true;
276        self.exit_code = Some(exit_code);
277    }
278
279    fn get_stack_frame_size(&self) -> u64 {
280        Memory::STACK_FRAME_SIZE
281    }
282
283    fn handle_syscall(&mut self, name: &str) -> Result<u64, ExecutionError> {
284        let registers = [
285            self.registers[1],
286            self.registers[2],
287            self.registers[3],
288            self.registers[4],
289            self.registers[5],
290        ];
291        self.syscall_handler
292            .handle(
293                name,
294                registers,
295                &mut self.memory,
296                self.compute_meter.clone(),
297            )
298            .map_err(|e| ExecutionError::SyscallError(e.to_string()))
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use {
305        super::*,
306        crate::syscalls::MockSyscallHandler,
307        either::Either,
308        sbpf_common::{
309            inst_param::{Number, Register},
310            opcode::Opcode,
311        },
312    };
313
314    fn make_test_instruction(
315        opcode: sbpf_common::opcode::Opcode,
316        dst: Option<sbpf_common::inst_param::Register>,
317        src: Option<sbpf_common::inst_param::Register>,
318        off: Option<Either<String, i16>>,
319        imm: Option<Either<String, Number>>,
320    ) -> Instruction {
321        Instruction {
322            opcode,
323            dst,
324            src,
325            off,
326            imm,
327            span: 0..0,
328        }
329    }
330
331    #[test]
332    fn test_vm_initialization() {
333        let program = vec![make_test_instruction(Opcode::Exit, None, None, None, None)];
334        let vm = SbpfVm::new(
335            program,
336            vec![1, 2, 3, 4],
337            vec![],
338            MockSyscallHandler::default(),
339        );
340
341        assert_eq!(vm.pc, 0);
342        assert_eq!(vm.registers[1], Memory::INPUT_START);
343        assert_eq!(
344            vm.registers[10],
345            Memory::STACK_START + Memory::STACK_FRAME_SIZE
346        );
347        assert!(!vm.halted);
348        assert_eq!(vm.exit_code, None);
349    }
350
351    #[test]
352    fn test_vm_reset() {
353        let program = vec![make_test_instruction(Opcode::Exit, None, None, None, None)];
354        let mut vm = SbpfVm::new(
355            program,
356            vec![1, 2, 3, 4],
357            vec![],
358            MockSyscallHandler::default(),
359        );
360
361        // modify vm
362        vm.registers[0] = 11;
363        vm.pc = 10;
364        vm.halted = true;
365        vm.exit_code = Some(1);
366
367        // reset
368        vm.reset();
369
370        assert_eq!(vm.pc, 0);
371        assert_eq!(vm.registers[0], 0);
372        assert_eq!(vm.registers[1], Memory::INPUT_START);
373        assert!(!vm.halted);
374        assert_eq!(vm.exit_code, None);
375    }
376
377    #[test]
378    fn test_current_instruction() {
379        let program = vec![
380            make_test_instruction(
381                Opcode::Mov64Imm,
382                Some(Register { n: 0 }),
383                None,
384                None,
385                Some(Either::Right(Number::Int(123))),
386            ),
387            make_test_instruction(Opcode::Exit, None, None, None, None),
388        ];
389        let vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
390
391        let inst = vm.current_instruction().unwrap();
392        assert_eq!(inst.opcode, Opcode::Mov64Imm);
393    }
394
395    #[test]
396    fn test_load_store() {
397        // lddw r1, 0x12345
398        // mov64 r2, r10
399        // sub r2, 8
400        // stxdw [r2 + 0], r1
401        // ldxdw r3, [r2 + 0]
402        let program = vec![
403            make_test_instruction(
404                Opcode::Lddw,
405                Some(Register { n: 1 }),
406                None,
407                None,
408                Some(Either::Right(Number::Int(0x12345u64 as i64))),
409            ),
410            make_test_instruction(
411                Opcode::Mov64Reg,
412                Some(Register { n: 2 }),
413                Some(Register { n: 10 }),
414                None,
415                None,
416            ),
417            make_test_instruction(
418                Opcode::Sub64Imm,
419                Some(Register { n: 2 }),
420                None,
421                None,
422                Some(Either::Right(Number::Int(8))),
423            ),
424            make_test_instruction(
425                Opcode::Stxdw,
426                Some(Register { n: 2 }),
427                Some(Register { n: 1 }),
428                Some(Either::Right(0)),
429                None,
430            ),
431            make_test_instruction(
432                Opcode::Ldxdw,
433                Some(Register { n: 3 }),
434                Some(Register { n: 2 }),
435                Some(Either::Right(0)),
436                None,
437            ),
438        ];
439
440        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
441
442        for _ in 0..5 {
443            vm.step().unwrap();
444        }
445
446        assert_eq!(vm.registers[3], 0x12345);
447    }
448
449    #[test]
450    fn test_alu64_operations() {
451        // mov64 r1, 10
452        // add64 r1, 5
453        // mul r1, 2
454        let program = vec![
455            make_test_instruction(
456                Opcode::Mov64Imm,
457                Some(Register { n: 1 }),
458                None,
459                None,
460                Some(Either::Right(Number::Int(10))),
461            ),
462            make_test_instruction(
463                Opcode::Add64Imm,
464                Some(Register { n: 1 }),
465                None,
466                None,
467                Some(Either::Right(Number::Int(5))),
468            ),
469            make_test_instruction(
470                Opcode::Mul64Imm,
471                Some(Register { n: 1 }),
472                None,
473                None,
474                Some(Either::Right(Number::Int(2))),
475            ),
476        ];
477
478        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
479
480        vm.step().unwrap();
481        assert_eq!(vm.registers[1], 10);
482
483        vm.step().unwrap();
484        assert_eq!(vm.registers[1], 15);
485
486        vm.step().unwrap();
487        assert_eq!(vm.registers[1], 30);
488    }
489
490    #[test]
491    fn test_memory_regions() {
492        // Check input region
493        let input = vec![1, 2, 3, 4, 5, 6, 7, 8];
494        let rodata = vec![10, 20, 30, 40];
495
496        let program = vec![make_test_instruction(Opcode::Exit, None, None, None, None)];
497        let vm = SbpfVm::new(program, input, rodata, MockSyscallHandler::default());
498
499        assert_eq!(vm.memory.read_u8(Memory::INPUT_START).unwrap(), 1);
500        assert_eq!(
501            vm.memory.read_u64(Memory::INPUT_START).unwrap(),
502            0x0807060504030201u64
503        );
504
505        // Check rodata region
506        assert_eq!(vm.memory.read_u8(Memory::RODATA_START).unwrap(), 10);
507    }
508
509    #[test]
510    fn test_program_without_exit() {
511        let program = vec![
512            make_test_instruction(
513                Opcode::Mov64Imm,
514                Some(Register { n: 0 }),
515                None,
516                None,
517                Some(Either::Right(Number::Int(10))),
518            ),
519            make_test_instruction(
520                Opcode::Add64Imm,
521                Some(Register { n: 0 }),
522                None,
523                None,
524                Some(Either::Right(Number::Int(8))),
525            ),
526            // no exit instruction
527        ];
528
529        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
530
531        vm.step().unwrap();
532        assert_eq!(vm.pc, 1);
533
534        vm.step().unwrap();
535        assert_eq!(vm.pc, 2);
536
537        let result = vm.step();
538        assert!(result.is_err());
539        assert!(matches!(result, Err(SbpfVmError::PcOutOfBounds(2))));
540    }
541
542    #[test]
543    fn test_step_complete_program() {
544        // mov64 r1, 10
545        // add64 r1, 5
546        // mul r1, 3
547        // sub r1, 7
548        // exit
549        let program = vec![
550            make_test_instruction(
551                Opcode::Mov64Imm,
552                Some(Register { n: 1 }),
553                None,
554                None,
555                Some(Either::Right(Number::Int(10))),
556            ),
557            make_test_instruction(
558                Opcode::Add64Imm,
559                Some(Register { n: 1 }),
560                None,
561                None,
562                Some(Either::Right(Number::Int(5))),
563            ),
564            make_test_instruction(
565                Opcode::Mul64Imm,
566                Some(Register { n: 1 }),
567                None,
568                None,
569                Some(Either::Right(Number::Int(3))),
570            ),
571            make_test_instruction(
572                Opcode::Sub64Imm,
573                Some(Register { n: 1 }),
574                None,
575                None,
576                Some(Either::Right(Number::Int(7))),
577            ),
578            make_test_instruction(Opcode::Exit, None, None, None, None),
579        ];
580
581        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
582
583        vm.step().unwrap();
584        assert_eq!(vm.pc, 1);
585        assert_eq!(vm.registers[1], 10);
586        assert_eq!(vm.compute_meter.get_consumed(), 1);
587        assert!(!vm.halted);
588
589        vm.step().unwrap();
590        assert_eq!(vm.pc, 2);
591        assert_eq!(vm.registers[1], 15);
592        assert_eq!(vm.compute_meter.get_consumed(), 2);
593        assert!(!vm.halted);
594
595        vm.step().unwrap();
596        assert_eq!(vm.pc, 3);
597        assert_eq!(vm.registers[1], 45);
598        assert_eq!(vm.compute_meter.get_consumed(), 3);
599        assert!(!vm.halted);
600
601        vm.step().unwrap();
602        assert_eq!(vm.pc, 4);
603        assert_eq!(vm.registers[1], 38);
604        assert_eq!(vm.compute_meter.get_consumed(), 4);
605        assert!(!vm.halted);
606
607        vm.step().unwrap();
608        assert_eq!(vm.pc, 4);
609        assert_eq!(vm.registers[1], 38);
610        assert_eq!(vm.compute_meter.get_consumed(), 5);
611        assert!(vm.halted);
612    }
613
614    #[test]
615    fn test_run_complete_program() {
616        // mov64 r1, 10
617        // add64 r1, 5
618        // mul r1, 3
619        // sub r1, 7
620        // exit
621        let program = vec![
622            make_test_instruction(
623                Opcode::Mov64Imm,
624                Some(Register { n: 1 }),
625                None,
626                None,
627                Some(Either::Right(Number::Int(10))),
628            ),
629            make_test_instruction(
630                Opcode::Add64Imm,
631                Some(Register { n: 1 }),
632                None,
633                None,
634                Some(Either::Right(Number::Int(5))),
635            ),
636            make_test_instruction(
637                Opcode::Mul64Imm,
638                Some(Register { n: 1 }),
639                None,
640                None,
641                Some(Either::Right(Number::Int(3))),
642            ),
643            make_test_instruction(
644                Opcode::Sub64Imm,
645                Some(Register { n: 1 }),
646                None,
647                None,
648                Some(Either::Right(Number::Int(7))),
649            ),
650            make_test_instruction(Opcode::Exit, None, None, None, None),
651        ];
652
653        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
654
655        vm.run().unwrap();
656
657        assert!(vm.halted);
658        assert_eq!(vm.registers[1], 38);
659        assert_eq!(vm.pc, 4);
660        assert_eq!(vm.compute_meter.get_consumed(), 5);
661    }
662
663    #[test]
664    fn test_program_with_input() {
665        // ldxdw r2, [r1 + 0]
666        // ldxdw r3, [r1 + 8]
667        // mov64 r4, r2
668        // add64 r4, r3
669        // exit
670
671        let mut input = Vec::new();
672        input.extend_from_slice(&10u64.to_le_bytes());
673        input.extend_from_slice(&20u64.to_le_bytes());
674
675        let program = vec![
676            make_test_instruction(
677                Opcode::Ldxdw,
678                Some(Register { n: 2 }),
679                Some(Register { n: 1 }),
680                Some(Either::Right(0)),
681                None,
682            ),
683            make_test_instruction(
684                Opcode::Ldxdw,
685                Some(Register { n: 3 }),
686                Some(Register { n: 1 }),
687                Some(Either::Right(8)),
688                None,
689            ),
690            make_test_instruction(
691                Opcode::Mov64Reg,
692                Some(Register { n: 4 }),
693                Some(Register { n: 2 }),
694                None,
695                None,
696            ),
697            make_test_instruction(
698                Opcode::Add64Reg,
699                Some(Register { n: 4 }),
700                Some(Register { n: 3 }),
701                None,
702                None,
703            ),
704            make_test_instruction(Opcode::Exit, None, None, None, None),
705        ];
706
707        let mut vm = SbpfVm::new(program, input, vec![], MockSyscallHandler::default());
708
709        vm.run().unwrap();
710
711        assert!(vm.halted);
712        assert_eq!(vm.registers[2], 10);
713        assert_eq!(vm.registers[3], 20);
714        assert_eq!(vm.registers[4], 30);
715        assert_eq!(vm.compute_meter.get_consumed(), 5);
716    }
717
718    #[test]
719    fn test_program_with_internal_function_call() {
720        // call test
721        // lddw r2, 0x2
722        // exit
723        //
724        // test:
725        //   lddw r1, 0x1
726        //   exit
727        let program = vec![
728            make_test_instruction(
729                Opcode::Call,
730                None,
731                None,
732                None,
733                Some(Either::Right(Number::Int(2))),
734            ),
735            make_test_instruction(
736                Opcode::Lddw,
737                Some(Register { n: 2 }),
738                None,
739                None,
740                Some(Either::Right(Number::Int(0x2))),
741            ),
742            make_test_instruction(Opcode::Exit, None, None, None, None),
743            make_test_instruction(
744                Opcode::Lddw,
745                Some(Register { n: 1 }),
746                None,
747                None,
748                Some(Either::Right(Number::Int(0x1))),
749            ),
750            make_test_instruction(Opcode::Exit, None, None, None, None),
751        ];
752
753        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
754
755        vm.run().unwrap();
756
757        assert!(vm.halted);
758        assert_eq!(vm.registers[1], 0x1);
759        assert_eq!(vm.registers[2], 0x2);
760    }
761
762    #[test]
763    fn test_vm_jmp32_imm() {
764        // lddw r1, 0x00000001_00000005
765        // jeq32 r1, 5, +2
766        // mov64 r2, 1
767        // exit
768        // mov64 r2, 2
769        // exit
770        let program = vec![
771            make_test_instruction(
772                Opcode::Lddw,
773                Some(Register { n: 1 }),
774                None,
775                None,
776                Some(Either::Right(Number::Int(0x0000_0001_0000_0005))),
777            ),
778            make_test_instruction(
779                Opcode::Jeq32Imm,
780                Some(Register { n: 1 }),
781                None,
782                Some(Either::Right(2)),
783                Some(Either::Right(Number::Int(5))),
784            ),
785            make_test_instruction(
786                Opcode::Mov64Imm,
787                Some(Register { n: 2 }),
788                None,
789                None,
790                Some(Either::Right(Number::Int(1))),
791            ),
792            make_test_instruction(Opcode::Exit, None, None, None, None),
793            make_test_instruction(
794                Opcode::Mov64Imm,
795                Some(Register { n: 2 }),
796                None,
797                None,
798                Some(Either::Right(Number::Int(2))),
799            ),
800            make_test_instruction(Opcode::Exit, None, None, None, None),
801        ];
802
803        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
804        vm.run().unwrap();
805
806        assert!(vm.halted);
807        assert_eq!(vm.registers[2], 2);
808    }
809
810    #[test]
811    fn test_vm_jmp32_reg() {
812        // mov64 r1, -1
813        // mov64 r2, 0
814        // jslt32 r1, r2, +2
815        // mov64 r3, 1
816        // exit
817        // mov64 r3, 2
818        // exit
819        let program = vec![
820            make_test_instruction(
821                Opcode::Mov64Imm,
822                Some(Register { n: 1 }),
823                None,
824                None,
825                Some(Either::Right(Number::Int(-1))),
826            ),
827            make_test_instruction(
828                Opcode::Mov64Imm,
829                Some(Register { n: 2 }),
830                None,
831                None,
832                Some(Either::Right(Number::Int(0))),
833            ),
834            make_test_instruction(
835                Opcode::Jslt32Reg,
836                Some(Register { n: 1 }),
837                Some(Register { n: 2 }),
838                Some(Either::Right(2)),
839                None,
840            ),
841            make_test_instruction(
842                Opcode::Mov64Imm,
843                Some(Register { n: 3 }),
844                None,
845                None,
846                Some(Either::Right(Number::Int(1))),
847            ),
848            make_test_instruction(Opcode::Exit, None, None, None, None),
849            make_test_instruction(
850                Opcode::Mov64Imm,
851                Some(Register { n: 3 }),
852                None,
853                None,
854                Some(Either::Right(Number::Int(2))),
855            ),
856            make_test_instruction(Opcode::Exit, None, None, None, None),
857        ];
858
859        let mut vm = SbpfVm::new(program, vec![], vec![], MockSyscallHandler::default());
860        vm.run().unwrap();
861
862        assert!(vm.halted);
863        assert_eq!(vm.registers[3], 2);
864    }
865}