Skip to main content

revmc_codegen/tests/
fibonacci.rs

1use super::{DEF_SPEC, with_evm_context};
2use crate::{Backend, EvmCompiler};
3use paste::paste;
4use revm_bytecode::opcode as op;
5use revm_interpreter::InstructionResult;
6use revm_primitives::U256;
7
8macro_rules! fibonacci_tests {
9    ($($i:expr),* $(,)?) => {paste! {
10        $(
11            matrix_tests!([<native_ $i>] = |jit| run_fibonacci_test(jit, $i, false));
12            matrix_tests!([<dynamic_ $i>] = |jit| run_fibonacci_test(jit, $i, true));
13        )*
14    }};
15}
16
17fibonacci_tests!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100, 1000);
18
19fn run_fibonacci_test<B: Backend>(compiler: &mut EvmCompiler<B>, input: u16, dynamic: bool) {
20    let code = mk_fibonacci_code(input, dynamic);
21
22    unsafe { compiler.clear() }.unwrap();
23    compiler.inspect_stack(true);
24    let f = unsafe { compiler.jit("fib", &code, DEF_SPEC) }.unwrap();
25
26    with_evm_context(&code, DEF_SPEC, |ecx, stack, stack_len| {
27        if dynamic {
28            stack.set(0, U256::from(input).into());
29            *stack_len = 1;
30        }
31        let r = unsafe { f.call(stack, stack_len, ecx) };
32        assert_eq!(r, InstructionResult::Stop);
33        // Apparently the code does `fibonacci(input + 1)`.
34        assert_eq!(*stack_len, 1);
35        assert_eq!(unsafe { stack.as_slice(*stack_len) }[0].to_u256(), fibonacci_rust(input + 1));
36    });
37}
38
39fn mk_fibonacci_code(input: u16, dynamic: bool) -> Vec<u8> {
40    if dynamic {
41        [&[op::JUMPDEST; 3][..], FIBONACCI_CODE].concat()
42    } else {
43        let input = input.to_be_bytes();
44        [[op::PUSH2].as_slice(), input.as_slice(), FIBONACCI_CODE].concat()
45    }
46}
47
48// Modified from jitevm: https://github.com/paradigmxyz/jitevm/blob/f82261fc8a1a6c1a3d40025a910ba0ce3fcaed71/src/test_data.rs#L3
49#[rustfmt::skip]
50const FIBONACCI_CODE: &[u8] = &[
51    // Expects the code to be offset 3 bytes.
52    // JUMPDEST, JUMPDEST, JUMPDEST,
53
54    // 1st/2nd fib number
55    op::PUSH1, 0,
56    op::PUSH1, 1,
57    // 7
58
59    // MAINLOOP:
60    op::JUMPDEST,
61    op::DUP3,
62    op::ISZERO,
63    op::PUSH1, 28, // cleanup
64    op::JUMPI,
65
66    // fib step
67    op::DUP2,
68    op::DUP2,
69    op::ADD,
70    op::SWAP2,
71    op::POP,
72    op::SWAP1,
73    // 19
74
75    // decrement fib step counter
76    op::SWAP2,
77    op::PUSH1, 1,
78    op::SWAP1,
79    op::SUB,
80    op::SWAP2,
81    op::PUSH1, 7, // goto MAINLOOP
82    op::JUMP,
83    // 28
84
85    // CLEANUP:
86    op::JUMPDEST,
87    op::SWAP2,
88    op::POP,
89    op::POP,
90    // done: requested fib number is the only element on the stack!
91    op::STOP,
92];
93
94fn fibonacci_rust(n: u16) -> U256 {
95    let mut a = U256::from(0);
96    let mut b = U256::from(1);
97    for _ in 0..n {
98        let tmp = a;
99        a = b;
100        b = b.wrapping_add(tmp);
101    }
102    a
103}
104
105#[test]
106fn test_fibonacci_rust() {
107    revm_primitives::uint! {
108        assert_eq!(fibonacci_rust(0), 0_U256);
109        assert_eq!(fibonacci_rust(1), 1_U256);
110        assert_eq!(fibonacci_rust(2), 1_U256);
111        assert_eq!(fibonacci_rust(3), 2_U256);
112        assert_eq!(fibonacci_rust(4), 3_U256);
113        assert_eq!(fibonacci_rust(5), 5_U256);
114        assert_eq!(fibonacci_rust(6), 8_U256);
115        assert_eq!(fibonacci_rust(7), 13_U256);
116        assert_eq!(fibonacci_rust(8), 21_U256);
117        assert_eq!(fibonacci_rust(9), 34_U256);
118        assert_eq!(fibonacci_rust(10), 55_U256);
119        assert_eq!(fibonacci_rust(100), 354224848179261915075_U256);
120        assert_eq!(fibonacci_rust(1000), 0x2e3510283c1d60b00930b7e8803c312b4c8e6d5286805fc70b594dc75cc0604b_U256);
121    }
122}