Calls
The function calls are compiled by order in zink, the first call in order (index 0) will be the main function ( to be updated ).
Functions
There are only internal functions and external functions in zink project for now.
Internal Functions
The parameters of the internal functions will be queued to the
locals of the them and taking the first n
stack
for storing them.
External Functions
Same as internal functions, will be updated once have the design of selector in v0.2.0
Extended Functions
We have also introduces extended functions inside the compiler for complete the difference between EVM bytecode and WASM, see the implementation select as example.
Main Function
You may never meet this because it is embedded in the compiled bytecode, but it is the entry of zink programs.
It takes parameters from the calldata
by order, for loading
32-byte parameters, it will process
// parameter 1
PUSH1 0x00
calldataload
// parameter 2
PUSH1 0x20
calldataload
Layout
Each function in zink is started with JUMPDEST
in the layout
of the bytecode for the insane jumping…
Each function call’s stack starts with PC
which stores the last active
program counter for the program for jumping back to the main process since
the callee functions could be called by any functions but not only one.
There is a tricky problem that how to detect the last pc before jumping,
for solving this, zinkc
registers the original PC
to the jump table when
meeting jumps and relocates them after compiling all functions.
Example Addition
(module
(func (export "main") (param i32) (param i32) (result i32)
(call $add (local.get 0) (local.get 1))
)
(func $add (param i32 i32) (result i32)
(local.get 0)
(local.get 1)
(i32.add)
)
)
Let’s assume we are calling an add
function with parameters 1, 1
and
now we are at the first byte right before it:
/* 0x00 */ PUSH1 0x01 // push the first parameter on the stack
/* 0x02 */ PUSH1 0x01 // push the second parameter on the stack
/* */ //
/* */ //
/* 0x04 */ pc // the first byte before calling the callee function
/* */ //
/* */ //
/* 0x05 */ PUSH1 0x42 // This 0x42 will be reloacted by `zinkc`
/* */ //
/* */ //
/* 0x07 */ jump // jump to the callee function
/* */ //
/* */ //
/* 0x08 */ jumpdest // the pc for jumping back from the callee function.
/* */ //
/* */ // the rest logic of the main process.
/* */ //
/* */ //
/* 0x42 */ jumpdest // the first byte of the callee function
/* */ //
/* */ //
/* 0x43 */ add // for the current stack: [PC, 0x02]
/* */ //
/* */ //
/* 0x44 */ SWAP1 // shift the stored PC to the top of the stack
/* */ //
/* */ //
/* 0x45 */ PUSH1 0x04 // the jumpdest is the original pc + 4 bcz we have
/* 0x47 */ add // `push1`, `0x42`, `jump`, `jumpdest` queued after
/* */ // `pc`.
/* */ //
/* */ //
/* 0x48 */ jump // This 0x07 will be reloacted by `zinkc`