-
Notifications
You must be signed in to change notification settings - Fork 332
Description
Let's consider memory "flow" for a caller running at depth d+0 calling a callee at depth d+1.
Present
This is memory buffers workflow with the maximum number of copies. But this is actually current practice for EVMC/evmone.
- The caller empties return data buffer
R0(its lifetime ends here). - The caller calls the callee.
- The callee allocates EVM memory for itself
M1. - The callee returns output
O1(a copy of a slice ofM1). - The callee deallocates
M1. - The caller copies
O1toR0. - The caller deallocates
O1.
Additionally, if CALL output arguments are used the caller must copy a part of R0 to the dedicated place in its memory. There is not much we can do about this copy.
The current EVMC may eliminate M1 → O1 copy or O1 → R0 copy.
Future
What we want is:
struct memory_buffer
{
uint8_t* ptr;
size_t size;
size_t capacity;
uint8_t* return_data;
size_t return_data_size;
void expand(size_t new_size);
};It can represent EVM memory and return data at the same time. And it can work like this:
- The caller "empties" return data buffer
R0. - The callee calls the callee and passes it the
R0. - The callee uses
R0as its EVM memory. Expands its capacity if needed. - The callee returns
R0with the reference to the return data in it. - The caller uses
R0as return data buffer.
The same buffer R0 is passed to every call or create the caller makes. It can grow to the value limited by the quadratic memory cost (see below).
Additionally, we can create intrusive list of memory buffers to preserve more than single buffer for "deeper" calls.
Memory size analysis
- With 30M gas limit you can allocate ~4M of memory at depth 0.
- But for higher depths the 63/64 rules applies so the memory size limit will be smaller.
- Someone should model this, but keeping the chain of all buffers seems risky.
Notes
- The output from successful
CREATEdoes not go to the return data buffer. The caller must remember to "empty" the buffer got from the callee in this case. - The callee cannot use caller's memory spare capacity (shared buffer). The proof is trivial therefore left for the reader.
- This should play nicely with precompiled contracts too. E.g.
identityis optional expand and single copy.