ewasm / design

Ewasm Design Overview and Specification
Apache License 2.0
1.02k stars 125 forks source link

[Rough] ewasm calling convention #125

Open chfast opened 6 years ago

chfast commented 6 years ago

I lack proper knowledge of wasm, Pramea and pwasm. This is just to dump the idea out of my head.

Let's imagine we have 2 ewasm contracts 0xA and 0xB. The 0xB has exported function "play" and 0xA whats to call it. That requires wasm host assistance because execution contexts of 0xA and 0xB do no share memory.

First of all, 0xA have to address 0xB, "play". Let's assume this is possible by providing the 0xB address as bytes and "play" name as a string.

Then host have to take the arguments provided by 0xA to the function and copy them to 0xB context.

For scalar parameter types i32 and i64 we can put them into a single memory buffer following the order in declaration and simple C ABI rules for aligning struct fields, i.e. i64 must be aligned to positions divisible by 8, i32 must be aligned to positions divisible by 4. Some additional padding can happen. The memory buffer is only needed to calculate amount of memory the hast has to transfer.

But scalars are not enough. The killer feature would be to pass memory buffers. The 0xA references a memory buffer by providing the memory index and the buffer length. The host has to copy the buffer from 0xA memory to 0xB memory and pass the reference to this buffer to the "play" function.

Quick hack: Because in ewasm we use i32 to index memory we can fit i32, i32 pair in the i64 and pass this as a memory buffer.

Quick hack²: What if we want to return an error code and a memory buffer from a function? We can use 24 bits for memory index, 24 bits for memory size and 16 bits for error code. All packed into i64.

Why this is important now? Because we could use this calling convention to implement EVM1 compatible main function (pseudo code):

(func $main (param memory_buffer) (result memory_buffer_ref_with_error_code))

How to pass the buffers to the callee (0xB)? The obvious solution is to copy them to the 0xB's memory in deterministic manner (memory index can leak to the Ethereum state) before execution of 0xB and buffer references passed to the called function.

The problem here is the memory cost. If the formula for cost is how much allocated × how long allocated the input size of the function is a linear cost factor of the function (double the input can double the total function cost). And the callee cannot deallocate the memory.