We will never need to create these names dynamically.
e.g. "reloc.CODE", "reloc.DATA", and "linking" will all
come from specific places in our backend code.
It's a different story for function names etc.
The original intention was to avoid creating a local when we define
and immediately return a primitive value. But now our default path
does avoids unnecessary locals anyway!
For StackMemory values we do need an optimised path but it's nicer
to just pass a flag to Storage::allocate.
SymbolStorage had some rather ad-hoc methods for extracting pieces of data.
This change makes that more intentional and organised.
Also cleaned up the API for the related function copy_memory,
as it had lots of positional arguments with the same types.
Created a struct for this just to name them and make the code clearer.
All values in stack memory can share the stack frame pointer,
with different offsets.
This avoids having to generate initialisation code for individual
pointers we used to have. It should also make things more efficient
when the runtime compiles Wasm to machine code. It can just assign
the stack frame pointer to a single CPU register and leave it there.
The original idea was to make params and local variables work the same.
(A struct param will be passed as a pointer to caller stack memory.)
But now I don't think that's worth it. Some match expressions get more
awkward this way but we might be able to refactor that to be nicer.
Result makes sense where I have something meaningful to say to the user like
"X is not implemented yet". And also for public functions that may interface
with other parts of the project like Backend.
But for private functions internal to gen_wasm, it's just unhelpful to get a stack trace
to where the Result is unwrapped! I want a stack trace to the root cause.
I always end up temporarily rewriting Err("oops") to panic!("oops") and then waiting
for it to recompile.
This feels like a more balanced approach, using each technique where it makes sense.