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

Enable the tail calling convention by default #8540

Merged

Conversation

elliottt
Copy link
Member

@elliottt elliottt commented May 3, 2024

Switch to enabling the wasm tail call feature by default when the target is one of x86_64, aarch64, or riscv64, and the compilation strategy is cranelift. We also added tests checking that the feature is not enabled for either winch or the s390x backend.

Co-authored-by: Nick Fitzgerald [email protected]

@elliottt elliottt requested a review from a team as a code owner May 3, 2024 17:27
@elliottt elliottt requested review from fitzgen and removed request for a team May 3, 2024 17:27
@elliottt elliottt force-pushed the trevor/tail-calls-default branch from 3522c5c to beb7738 Compare May 3, 2024 17:27
@elliottt elliottt requested a review from alexcrichton May 3, 2024 17:28
@elliottt elliottt force-pushed the trevor/tail-calls-default branch from beb7738 to 68bc800 Compare May 3, 2024 17:29
Copy link
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

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

Nice! Out of curiosity do you know if a sightglass run has been done to confirm there's no perf regression?

Otherwise though I see how this is a little tricky to handle this. I'm wary of how this is currently implemented. We've tried to avoid interactions where one feature silently enables another or interacts with another and this is adding a bit of that with relation to the tail call handling.

Could the validation/etc be done without needing mut self on Config::validate? Instead could this preserve the &self-ness and perhaps add other validations to ensure the stars align right?

@elliottt
Copy link
Member Author

elliottt commented May 3, 2024

I did some perf testing during the initial tail-call refactoring PRs and didn't notice any regression in perf. I'll do another sightglass run with this branch to verify that.

We initially tried to implement this in Config::new, but as we still have some cases that don't permit tail calls, it ended up being easiest to interpret the default in validate. I agree that the conditions are a little unfortunate, but I think this is the best option currently.

@alexcrichton
Copy link
Member

Do you think it would be workable to enable tail calls by default except for s390x and then have a validation check that tail calls aren't enabled for winch? That would require -W tail-call=n for running with Winch but that I think would compose a bit better

@elliottt
Copy link
Member Author

elliottt commented May 3, 2024

Where would we set the default for tail-call that isn't Config::new? The problem with setting it there is that we don't yet know the compilation target. That was part of what motivated delaying the interpretation of the default to Config::validate, as we would definitely know what target we were compiling for at that point. Otherwise, I do like the approach of explicitly disabling tail calls when also targeting winch.

@fitzgen
Copy link
Member

fitzgen commented May 3, 2024

Nice! Out of curiosity do you know if a sightglass run has been done to confirm there's no perf regression?

Yep, there were multiple runs at various points here, and there is no longer any gap between tail and wasmtime-systemv anymore. In fact, I'm 99% sure the two calling conventions are identical when there aren't any actual tail calls.

Could the validation/etc be done without needing mut self on Config::validate? Instead could this preserve the &self-ness and perhaps add other validations to ensure the stars align right?

We tried to do this in Config::new initially, but because we don't know the compilation strategy (cranelift vs winch) or target (s309x or not) yet, we therefore don't know what default value to choose.

The only alternatives I see are

  1. Checking these conditions when setting every related knob (i.e. check that the target isn't set to s390x when enabling tail calls, check that tail calls aren't set when configuring s390x as a target, etc...). This is annoying because there are now many checks to keep in sync with each other.

  2. Enable tail calls by default, don't change any other logic. This effectively will require that s390x and winch configs explicitly disable tail calls, which is kind of annoying.

  3. Move the choose-the-default-if-it-wasn't-explicitly-configured logic we added here out of validate and into a finalize_defaults method or something that is called just before validate. This way validate would still be &self and we would have a clear division between validation and default computation. But this is sort of just rearranging deck chairs.

  4. Don't enable tail calls by default until all configs can support them. This is pretty suboptimal. Do we just wait for s390x? Or winch too? This could be a long time and I really want to start leveraging all the good work that has gone into this feature, deleting the old calling conventions, and all that. FWIW, we have prior art of enabling wasm features by default on some targets but not others when they aren't ready there yet (reference types, for example).

@alexcrichton
Copy link
Member

Ah sorry yeah I was thinking Config::new would be the place, but I always forget about cross-compilation which is why a cfg! for s390x wouldn't work. I also agree that all the other alternatives you outlined Nick aren't great, so I'm sold.

As a bikeshed, though, how about restructuring this slightly? Something like:

  • Leave validate as-is with no changes
  • Add a fn conditionally_enable_features(&mut self) method which is called just before validate
  • Move this logic to conditionally_enable_features

Also, in the case that self.tunables.tail_callable is None I think that conditionally_enable_features would be able to do nothing in this case, right? It's only in the None case where we infer a proper default from it given other configuration settings?

@alexcrichton
Copy link
Member

Oh also no need to worry about perf, just wanted to double check, sounds like plenty of due diligence

@elliottt elliottt requested a review from alexcrichton May 3, 2024 18:28
@elliottt elliottt force-pushed the trevor/tail-calls-default branch from 15838b4 to d5f95a2 Compare May 3, 2024 18:30
Comment on lines 1732 to 1733
// Enable tail calls by default when cranelift is going to be used as the compilation
// strategy, and we're not targeting s390x, which currently lacks tail-call support.
Copy link
Member

Choose a reason for hiding this comment

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

Mind also editing this to cover that this logic is only applicable if tail calls were not otherwise explicitly disabled or enabled? (aka explaining the .is_none())

@alexcrichton alexcrichton added this pull request to the merge queue May 3, 2024
Merged via the queue into bytecodealliance:main with commit 0264ec3 May 3, 2024
21 checks passed
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