Skip to content

Draft: Optimize wasm with return_call and local variables

Camil Staps requested to merge optimize-wasm into main

What does this MR do?

Optimizes the Wasm interpreter with return_call.

This is inspired by, but different from, https://blog.reverberate.org/2021/04/21/musttail-efficient-interpreters.html. The suggestion there is to replace a giant interpreter switch by a simple switch that only does a tail call to a (small) function implementing the instruction. Those small functions for each instruction then do a tail call back to the switch. The idea is that this helps register allocation, as local variables have a much smaller scope (only the small instruction function, instead of the entire switch). Register allocation is bad at least in SpiderMonkey, but generally difficult for Wasm compilers on large functions, and for this reason the current implementation uses globals for ABC machine registers (pc, hp, hp-free, asp, bsp, csp) as well as temporary values.

Unfortunately, we cannot use the tail call approach, because for safety Wasm has to check stack space at every function entry. This makes it more efficient to remain in a single loop. But we can do this for external calls from the giant switch, most notably to the garbage collector. Using a return_call to a separate garbage collection function, and a return_call back to the interpreter loop, makes it feasible to use locals for ABC machine registers and temporaries. (It turns out that it's better to still use a global for csp, as it is not used often except in synthetic benchmarks and the compiler can then use registers for other values.)

Speedup:

Test Current main (relative to native) This MR (relative to native) Speed-up (relative to main)
fsieve 15.6 12.8 1.2
lqueen 16.0 12.6 1.3
nfib 15.6 14.5 1.1
reverse 3.1 2.5 1.2
squeen 13.6 14.4 0.9
symsieve 6.1 9.2 0.7
tak 21.4 16.3 1.3

To do

  • It's a little scary that in some cases performance is degraded. If this remains the case it would be good to also check larger programs.
  • Clean up the code.
  • As long as tail calls are not widely supported, there should be two wasm files (one with and one without tail calls), and the supporting JavaScript should do a feature check to select the right one.

Changes to public APIs

None.

Author's checklist (required)

  • If bugs have been solved, tests have been added (guidelines)
  • A changelog entry has been added (guidelines)
  • Newly added code is documented (guidelines)
  • Newly added code has a style consistent with the rest of the repository
  • Intermediate commits compile (use git rebase -i main if not)

Related issues

Merge request reports