Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calls optimization plan #563

Open
chfast opened this issue Oct 1, 2020 · 0 comments
Open

Calls optimization plan #563

chfast opened this issue Oct 1, 2020 · 0 comments

Comments

@chfast
Copy link
Collaborator

chfast commented Oct 1, 2020

Detailed calls optimization plan

Current status

In Fizzy 0.5, the internal calls work like this:

  1. The call instruction implementation needs to know the number or arguments in the callee. This is taken from module's function and type sections.
  2. The required number of argument is "packed" into span<const Value> (remember this is a reference type).
  3. The span of arguments is passed to the callee.
  4. Callee copies the argument to the locals storage. Also allocates space for local variables and stack in single go.
  5. In the end of callee execution, the execute() function returns the top stack item, if any.
  6. The caller must drop the call arguments from the stack (i.e. move the stack pointer).
  7. And copy the callee return value (if any) to the top of the stack. This is currently done by inspecting the function type, but can be done by inspecting ExecutionResult. Call optimization #554

Plan

  1. Both caller and callee needs information about number of arguments. Caller takes this from the module, callee from the span. The callee can take it from the module as well, so execute() can skip this information by only passing const Value* without size. Although, in theory this may not be faster. Pass arguments as const Value* #552
  2. Instead of allocating "stack space" in each call, we can pre-allocate shared execution-thread stack space. It can be fixed size (this can work as a valid wasm exhaustion limit) or infinite by allocate more segments when needed. Thread stack space #529
  3. When 2 is implemented, arguments don't need to be copied because there are already in the right place (unless new segment must be allocated).
  4. At the call end, locals must be dropped and the top stack item (if any) must be beginning of the "stack space" (where the first argument was). After returning, the caller will have the result on the top of the stack. Caller does not even need to know if there is any result.
  5. Therefore, we don't need to return anything from the execute() except for the "trapped" flag.

Bonus (out-of-order items)

  1. The call instruction always calls the same function, so the number or arguments can be encoded in immediates. No need to reach module each time.
  2. Similarly, all execution required information (max stack height, number of arguments) may be added to the Code type. Then accessing function and type sections is not needed during execution.

TODO

  1. It seems easy to wrap this "unsafe" execution API with a "safe" API which will check if passed argument are of the correct number and type. But also reverse-wrapping is needed for host function - unsafe host function entry point will only receive Value*. Some mechanism is needed to check and extract the type information about received arguments.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant