Skip to content

feat(LLVM): enable pass-params by default#6156

Closed
marxin wants to merge 3 commits intomainfrom
pass-params-fixes-v2
Closed

feat(LLVM): enable pass-params by default#6156
marxin wants to merge 3 commits intomainfrom
pass-params-fixes-v2

Conversation

@marxin
Copy link
Contributor

@marxin marxin commented Feb 2, 2026

Based on the recent benchmark effort, I measured a significant speed-up with pass-params (aka. g0m0 aka. stack-pointer and heap arguments) across the board:
rustc_perf_runtime2

The PR preserves the suggested_compiler_opts logic for the package files - currently only:

[module.annotations.suggested_compiler_optimizations]
pass_params = false

is taken into account (pass_params = true is on by default). Similarly for the Wasmer CLI, I ignore --enable-pass-params-opt option and a user can disable the optimization with --disable-pass-params-opt.

Apart from that, the PR addresses one bug where we wrongly included the 2 extra arguments for host-fn call.

@marxin marxin requested a review from syrusakbary as a code owner February 2, 2026 15:02
Copilot AI review requested due to automatic review settings February 2, 2026 15:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enables the "pass-params" optimization (internally known as g0m0) by default in the LLVM compiler backend based on benchmark results showing significant performance improvements. The optimization passes the first global (#0) and first memory (#0) as explicit parameters between guest functions.

Changes:

  • Changed default value of enable_g0m0_opt from false to true in the LLVM config
  • Renamed the configuration method from enable_pass_params_opt to disable_pass_params_opt to reflect the new default-on behavior
  • Added g0m0_enabled_for_module helper function that checks both the config flag and module prerequisites (memory #0 exists, global #0 exists and is i32)
  • Fixed a bug where g0m0 parameters were incorrectly passed to imported (host) functions
  • Updated CLI to deprecate --enable-pass-params-opt flag and add --disable-pass-params-opt flag

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
lib/compiler-llvm/src/config.rs Changed default value of enable_g0m0_opt to true and renamed method from enable_pass_params_opt to disable_pass_params_opt
lib/compiler-llvm/src/compiler.rs Updated with_opts to only disable the optimization when explicitly requested; added g0m0_enabled_for_module helper function with module validation
lib/cli/src/backend.rs Deprecated --enable-pass-params-opt flag (renamed to _enable_pass_params_opt and ignored), added --disable-pass-params-opt flag
lib/compiler-llvm/src/translator/code.rs Updated to use g0m0_enabled_for_module helper; fixed bug by passing None instead of g0m0 params for imported function calls
lib/compiler-llvm/src/translator/intrinsics.rs Updated to use g0m0_enabled_for_module helper function
lib/compiler-llvm/src/trampoline/wasm.rs Updated to use g0m0_enabled_for_module helper function in multiple locations

Comment on lines +203 to 204
/// Deprecated, "pass-params" optimization enabled by default, where the first (#0)
/// global and the first (#0) memory passed between guest functions as explicit parameters.
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sentence is grammatically incorrect. It should read "global and the first (#0) memory are passed" (with "are" instead of "passed").

Copilot uses AI. Check for mistakes.
Comment on lines +210 to +211
/// Disable the "pass-params" optimization, where the first (#0)
/// global and the first (#0) memory passed between guest functions as explicit parameters.
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sentence is grammatically incorrect. It should read "global and the first (#0) memory are passed" (with "are" instead of "passed").

Copilot uses AI. Check for mistakes.

/// (warning: experimental) Pass the value of the first (#0) global and the base pointer of the
/// Disable the passing of the value of the first (#0) global and the base pointer of the
/// first (#0) memory as parameter between guest functions.
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The phrase "as parameter" should be "as parameters" (plural) to match the grammatical structure of passing two items (global and memory).

Suggested change
/// first (#0) memory as parameter between guest functions.
/// first (#0) memory as parameters between guest functions.

Copilot uses AI. Check for mistakes.
@syrusakbary
Copy link
Member

I'm not sure about this. RIght now pass params embeds the stack pointer so it's not actually a variable from the outside. This has wide implications, for example when using dynamic linked modules.

However, if we move the strategy of the stack pointer being in one register (rather than in a local function variable that is "passed" as a copy), then we should be good to merge

@marxin
Copy link
Contributor Author

marxin commented Feb 3, 2026

All right, I am getting into the nitty-gritty.

First, now I understand the __stack_pointer is a Clang (name your other favorite compiler) ABI concept, where the compiler overcomes the limitation of the WebAssembly as one can't take addresses of a stack variables. That's where __stack_pointer comes into play to serve languages like C and C++. Apart from that, our LLVM codegen depends on a classical stack-pointer register (and frame-pointer) that is used to access the alloca-based local stack slots.

That being said, I tried an experimental branch where I replaced the g0m0 just with m0. So only the memory(0) (aka start of the heap) pointer is being added to the calling conventions. By doing that, I get vast majority of the speed-up provided by the original g0m0!

I'm not sure about this. RIght now pass params embeds the stack pointer so it's not actually a variable from the outside. This has wide implications, for example when using dynamic linked modules.

Can you depict the exact scenario where that would break? Still, I am getting into all the WA aspects.
By still, I think we can get the biggest value from the heap pointer optimization (m0). Plus, I noticed we're currently using rbp register for non-leaf functions - we might adjust it as our unwinding mechanism depends on the DWARF info.

However, if we move the strategy of the stack pointer being in one register (rather than in a local function variable that is "passed" as a copy), then we should be good to merge

There appears to be recent activity on this front: llvm/llvm-project#179036. At the moment, support for x86_64 is still missing for a critical LLVM option. It’s worth emphasizing that in this case we must rely entirely on the compiler—otherwise subtle but serious issues can arise, for example with exception handling and correct stack unwinding.

@syrusakbary
Copy link
Member

I get vast majority of the speed-up provided by the original g0m0

I guess depends on what benchmark you run. If you run the interpreter I attached in slack a few days ago, the major speedup came from g0 (if I recall correctly).

Can you depict the exact scenario where that would break? Still, I am getting into all the WA aspects.

In the scenario that you run multiple threads (or you fork). Right now, things are cloned as you call it (namely, the globals, in which the stack pointer is included). When you move the stack pointer out of the global to be a function variable (that gets passed as value, not reference), then cloning will do nothing, and might break the Wasm behavior.
One way to alleviate that is to put the global value up to date by the end of Wasm function (when it goes outside of the module), but feels a bit hacky.

@marxin marxin marked this pull request as draft February 5, 2026 08:25
@marxin
Copy link
Contributor Author

marxin commented Feb 12, 2026

Once #6187 gets in, we can basically enable the g0 optimization by default (effectively removing the --enable-pass-params-opt option).

@marxin marxin closed this Feb 12, 2026
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

Successfully merging this pull request may close these issues.

3 participants