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`