zingen/visitor/
call.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! call instructions

use crate::{
    wasm::{HostFunc, ToLSBytes},
    Error, Function, Result,
};
use anyhow::anyhow;
use opcodes::ShangHai as OpCode;

impl Function {
    /// The call indirect instruction calls a function indirectly
    /// through an operand indexing into a table.
    pub fn _call_indirect(
        &mut self,
        _type_index: u32,
        _table_index: u32,
        _table_byte: u8,
    ) -> Result<()> {
        todo!()
    }

    /// The call instruction calls a function specified by its index.
    pub fn _call(&mut self, index: u32) -> Result<()> {
        if self.env.is_external(index) {
            // TODO: throw with error
            panic!("External functions could not be called internally");
        }

        if self.env.imports.len() as u32 > index {
            self.call_imported(index)
        } else {
            self.call_internal(index)
        }
    }

    /// Call internal functions
    fn call_internal(&mut self, index: u32) -> Result<()> {
        if self.env.index == Some(index) {
            return Err(anyhow!(
                "Recursion is no more supported in this version, see https://github.com/zink-lang/zink/issues/248"
            )
            .into());
        }

        tracing::debug!("call internal function: index={index}");
        let (params, results) = self.env.funcs.get(&index).unwrap_or(&(0, 0));

        // TODO: adapat the case that the params is larger than 0xff (#247)
        //
        // 1. record the program counter of the end of this expression
        // call and pass it to the callee function.
        //
        // [ ..,
        //   <PC>,
        //   params[SWAP], params[PUSH, SLOT, MSTORE],
        //   {(PUSH, PC), JUMP, JUMPDEST}
        // ]
        // <- selfparams[PUSH, OFFSET, CALLDATALOAD]
        //
        // 2. move PC before the params in stack
        self.table
            .offset(self.masm.pc_offset(), 5 + 4 * (*params as u16));
        self.masm.increment_sp(1)?;

        // Stack
        // =====
        //
        // from  [ <PARAMS>, PC ]
        // to    [ PC, <PARAMS> ]
        self.masm.shift_stack(*params as u8, true)?;

        // Call an internal function.
        //
        // 1. store params in memory
        // 2. register the call index to the jump table.
        let reserved = self.env.slots.get(&index).unwrap_or(&0);
        for i in (0..*params).rev() {
            tracing::trace!("storing local at {} for function {index}", i + reserved);
            self.masm.push(&((i + reserved) * 0x20).to_ls_bytes())?;
            self.masm._mstore()?;
        }

        // TODO: support same pc different jumps. (#160)
        self.table.call(self.masm.pc_offset(), index);

        // jump to the callee function
        self.masm._jump()?;
        self.masm._jumpdest()?;

        // Stack: [ , ..results ]
        self.masm.increment_sp(*results as u8)?;
        Ok(())
    }

    /// Call imported functions
    fn call_imported(&mut self, index: u32) -> Result<()> {
        // call an imported function.
        //
        // register the imported function index to the jump table.
        let func = *self
            .env
            .imports
            .get(&index)
            .ok_or(Error::ImportedFuncNotFound(index))?;

        tracing::trace!("call imported function, index={index}, func={func:?}");
        match func {
            HostFunc::Evm(OpCode::LOG0) => self.log(0),
            HostFunc::Evm(OpCode::LOG1) => self.log(1),
            HostFunc::Evm(OpCode::LOG2) => self.log(2),
            HostFunc::Evm(OpCode::LOG3) => self.log(3),
            HostFunc::Evm(OpCode::LOG4) => self.log(4),
            HostFunc::Evm(op) => self.masm.emit_op(op),
            HostFunc::U256MAX => self.masm.push(&[255; 32]),
            HostFunc::Revert(count) => self.revert(count),
            HostFunc::NoOp | HostFunc::Label(_) => Ok(()),
            _ => {
                tracing::error!("unsupported host function {func:?}");
                Err(Error::UnsupportedHostFunc(func))
            }
        }
    }
}