Skip to content

GC Optimization Guidebook

Alon Zakai edited this page Nov 22, 2022 · 37 revisions

As mentioned in the wasm-opt prompt engineering page, Binaryen's primary optimization pipeline has been optimized on LLVM output. That is, when you do wasm-opt -O3 then you get a useful set of optimizations to run on something clang/rustc/etc. emitted at -O3. Wasm GC languages may benefit from a different set of optimizations, primarily because such languages generally do use LLVM (e.g., Java, Dart, etc.), so the "shape" of the Wasm they emit is different, and different Binaryen optimizations may work better.

In addition to the fact that LLVM is often not used, Wasm GC is fundamentally different from Wasm MVP in the sense that it much closer to a true IR for optimization. In particular, in C/C++/Rust etc. output in Wasm MVP we don't have an explicit user stack (just a region of linear memory that is managed manually), nor do we have explicit allocations (malloc/free are just functions that manage another region of linear memory), and we don't have explicit function pointers (just integers, that are used as offsets into a table). All that greatly limits what optimizations can be done on Wasm MVP, compared to say LLVM IR. As a result, LLVM -O3 can do a lot of things you can't do on Wasm, like move an allocation from the user stack to a local. But that changes with Wasm GC, in particular:

  • Allocations are explicit, using struct.new and array.new.
  • Function pointers are explicit, using ref.func and call_ref.

This makes Wasm GC much more optimizable, as now we can do things like move an allocation into locals (and Binaryen does that in the Heap2Local pass). So Binaryen cannot be a general-purpose optimizer for Wasm MVP content - MVP is not expressive enough - but it can be for Wasm GC. And that is good, since those languages often do not use LLVM as mentioned above, and so Binaryen can help out by doing general-purpose optimizations.

To summarize all that, wasm-opt -O3 on LLVM output does some useful refining of LLVM's optimizations for Wasm MVP, but on Wasm GC Binaryen can do a lot more and be used as the primary toolchain optimizer. Doing so requires more than just passing -O3 or such, and that is the topic of the rest of this page.

GUFA

The Grand Unified Flow Analysis is an optimization that scans the entire program and makes inferences about what content can appear where. This can help MVP content, but really shines on GC because it infers a lot about types, in particular, it can find when a location must contain a constant, which can lead to devirtualization, a crucial optimization.

GUFA is a heavyweight optimization and not run by default. You run it manually with -O3. When to run it, and how many times, is worth experimenting with, but you can try things like this:

  • -O3 --gufa -O3: One run of the main optimization pipeline, then GUFA, then another run of the pipeline to take advantage of GUFA's findings.
  • -O3 --gufa -O3 -O3
  • -O3 --gufa -O3 --gufa -O3

etc. Note that wasm-opt -O3 -O3 not only sets the opt level to 3 but also asks the tool to run the optimization pipeline, and so it can be specified more than once (this is different than the UI for gcc and clang).

It can be useful to run --metrics in the middle, to see the impact of a pass. For example, wasm-opt --metrics -O3 --metrics will dump metrics once, then optimize, then dump metrics again. The second dump will contain a diff compared to the last metrics, so you can see stats on the change in the number of each type of instruction.

Traps Never Happen

wasm-opt -tnh is a flag that tells the compiler that your program can be assumed to not trap when it runs. That assumption lets the optimizer remove more code and optimize more efficiently.

You may not want this if you have something like "crash reporting", that is, your program has code paths in which it writes an error and then traps. In TNH mode, Binaryen will assume the trap is not reached, and remove that error logging code.

TODO call.without.effects

TODO TypeSSA, TypeCoalescing

Other tips

See the wasm-opt prompt engineering for general advice on optimizer options.

Clone this wiki locally