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

Pulley: tail calls #9251

Merged
merged 6 commits into from
Oct 7, 2024
Merged

Conversation

Kmeakin
Copy link
Contributor

@Kmeakin Kmeakin commented Sep 15, 2024

Sorry for the delay, I was on holiday.
The proof of concept works, but codegen for become is not actually implemented yet and so triggers an ICE:

error: internal compiler error: /rustc/9b72238eb813e9d06e9e9d270168512fbffd7ee7/compiler/rustc_codegen_ssa/src/mir/block.rs:1372:17: `TailCall` terminator is not yet supported by `rustc_codegen_ssa`
   --> pulley/src/interp/interp_loop.rs:85:25
    |
85  |                           become (next_handler.fun)($state, $pc);
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

So for now return is used instead of become and we must hope that LLVM will be smart enough to recongize the tail call

@Kmeakin Kmeakin requested a review from a team as a code owner September 15, 2024 22:00
@Kmeakin Kmeakin requested review from fitzgen and removed request for a team September 15, 2024 22:00
@github-actions github-actions bot added the pulley Issues related to the Pulley interpreter label Sep 16, 2024
Copy link

Subscribe to Label Action

cc @fitzgen

This issue or pull request has been labeled: "pulley"

Thus the following users have been cc'd because of the following labels:

  • fitzgen: pulley

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

@alexcrichton
Copy link
Member

Thanks for this! I might jump in with a small recommendation on overall structure / CI here -- with a Cargo feature controlling this it unfortunately doesn't play well with our "test with all features enabled" in CI well because it enables the feature when a stable compiler is in use. To fix this I might recommend #[cfg(pulley_tail_call)] perhaps? That would require setting RUSTFLAGS in CI but that could be added to the "Monolith Checks" perhaps as a single cargo check of pulley on nightly? That would also enable this to land despite codegen support not being in upstream rust-lang/rust yet.

@fitzgen
Copy link
Member

fitzgen commented Sep 16, 2024

Thanks! I'll take a look either later today or tomorrow.

Mildly surprised about become not working anymore, as it was working when I did a few experiments around the time of the RFC. I guess rustc is doing a new ssa thing in the midend and hasn't extended support to become yet?

@fitzgen
Copy link
Member

fitzgen commented Sep 16, 2024

FWIW, it looks like rust-lang/rust#127520 was filed about this regression, but closed as WONTFIX, so I also cross-posted the bug to the explicit_tail_calls tracking issue: rust-lang/rust#112788 (comment)

@WaffleLapkin
Copy link

@fitzgen I'm not sure how it could have worked for you -- become was never fully implemented. (maybe you tried to use it when it was just an alternative syntax for return?)

either way the reason why rust-lang/rust#127520 was WONTFIXED is that tails calls are simply not implemented yet, they are an incomplete feature. The error even says as much:

TailCall terminator is not yet supported by rustc_codegen_ssa

there is no reason to bring any more awareness to this issue -- it will be fixed naturally once the implementation is complete.

Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

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

Looks great! Really happy with how this shaped up.

Feel free to split out subcommits if you want, but it isn't necessary. Not sure they will actually land first at this point, since the final commit doesn't have as much feedback. Probably good to do for future PRs tho, just to keep things nice and easy for reviewers 👍

Big thing needed to land this is integrating with our CI infrastructure and replacing the cargo feature, as Alex mentioned.

Thanks!

pulley/src/decode.rs Outdated Show resolved Hide resolved
pulley/src/interp.rs Outdated Show resolved Hide resolved
Comment on lines 616 to 610
fn pc_rel_jump(&mut self, offset: PcRelOffset, inst_size: isize) -> Continuation {
fn pc_rel_jump(&mut self, offset: PcRelOffset, inst_size: isize) {
let offset = isize::try_from(i32::from(offset)).unwrap();
self.pc = unsafe { self.pc.offset(offset - inst_size) };
Continuation::Continue
Copy link
Member

Choose a reason for hiding this comment

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

For caller ergonomics, this could still return a ControlFlow::Continue(()). This way, we wouldn't need to update every call site to also return that value.

Comment on lines 600 to 594
enum Continuation {
Continue,
/// The reason the interpreter loop terminated.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Done {
/// A `ret` instruction was executed and the call stack was empty. This is
/// how the loop normally ends.
ReturnToHost,

/// a `trap` instruction was executed.
Trap,
Copy link
Member

Choose a reason for hiding this comment

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

FWIW, the size of ControlFlow<Done> is going to be bigger than the size of Continuation was, because there will be two enum discriminant tags instead of one. But that is fine, and we can measure performance down the line and see if this actually matters. I suspect it might matter if it crosses an ABI threshold where returning these values goes from being in a register to going through a return pointer to a caller-supplied, stack-allocated space. But, as I said, we can always measure later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4536593481e0f790a4f2d31c969b7e19

ControlFlow<Done, ()> and Continuation are both 1 byte (it seems rustc is smart enough to use the "unused discriminants" from Done as niches for ControlFlow).

ControlFlow<Done, OpcodeHandler> is 2 words. The alternative would be to return ControlFlow<Done, ()> and set the next handler via an &mut OpcodeHandler parameter, but I expect that might be harder for LLVM to optimize. In the worst case, returning a large aggregate will be lowered to returning via an out-parameter anyway, so it shouldn't be any worse performance wise

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could also squeeze ControlFlow<Done, OpcodeHandler> into a single word by using the lower 2 bits as the discriminant tag, since function pointers must be word aligned. I think there was a proposal for rustc to do this automatically but it has not yet been implemented.

pulley/src/decode.rs Outdated Show resolved Hide resolved
pulley/src/decode.rs Outdated Show resolved Hide resolved
pulley/src/interp/interp_loop.rs Show resolved Hide resolved
pulley/src/lib.rs Outdated Show resolved Hide resolved
pulley/src/interp/interp_loop.rs Outdated Show resolved Hide resolved
@Kmeakin Kmeakin force-pushed the km/pulley/tail-calls branch 6 times, most recently from 2228667 to 5c8e2f8 Compare September 18, 2024 00:16
@Kmeakin Kmeakin force-pushed the km/pulley/tail-calls branch 3 times, most recently from 51f2406 to 3d19204 Compare September 18, 2024 01:41
@fitzgen
Copy link
Member

fitzgen commented Sep 19, 2024

Once #9274 lands, is this ready to merge as well?

@fitzgen
Copy link
Member

fitzgen commented Oct 2, 2024

Once #9274 lands, is this ready to merge as well?

@Kmeakin, now that #9274 landed, is this ready to rebase and land as well, or are there still more things left to do first?

@Kmeakin
Copy link
Contributor Author

Kmeakin commented Oct 4, 2024

Once #9274 lands, is this ready to merge as well?

@Kmeakin, now that #9274 landed, is this ready to rebase and land as well, or are there still more things left to do first?

I think it is ready to land, I don't have anything else I want to add to it.

Copyright (c) 2024, Arm Limited.

Signed-off-by: Karl Meakin <[email protected]>
Copyright (c) 2024, Arm Limited.

Signed-off-by: Karl Meakin <[email protected]>
Copyright (c) 2024, Arm Limited.

Signed-off-by: Karl Meakin <[email protected]>
Copyright (c) 2024, Arm Limited.

Signed-off-by: Karl Meakin <[email protected]>
Copy link
Member

@fitzgen fitzgen left a comment

Choose a reason for hiding this comment

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

Thanks!

@fitzgen fitzgen added this pull request to the merge queue Oct 7, 2024
fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Oct 7, 2024
After bytecodealliance#9251, we will check that
Pulley builds with rustc's experimental tail calls feature enabled in the
nightly CI job. So if we touch any pulley source files in a PR, we should also
run that job in the PR's CI.
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 7, 2024
@fitzgen
Copy link
Member

fitzgen commented Oct 7, 2024

I pushed a commit moving the new CI check to the nightly tests CI job, since it requires a nightly rustc.

But it looks like there are some failures running pulley tests under miri now as well, taking a quick look.

Not the initial PC passed into `run`
@fitzgen
Copy link
Member

fitzgen commented Oct 7, 2024

Pushed a fix for the miri tests: Done::Trap needed to have the trapping PC as a payload so that we could pass that trapping PC to self.trap(pc) rather than the first PC passed to run.

@fitzgen fitzgen enabled auto-merge October 7, 2024 19:28
@fitzgen fitzgen added this pull request to the merge queue Oct 7, 2024
Merged via the queue into bytecodealliance:main with commit 623a821 Oct 7, 2024
35 checks passed
@fitzgen
Copy link
Member

fitzgen commented Oct 7, 2024

Thanks again @Kmeakin!

github-merge-queue bot pushed a commit that referenced this pull request Oct 7, 2024
* Run the nightly CI job when `pulley` files are touched in a PR

After #9251, we will check that
Pulley builds with rustc's experimental tail calls feature enabled in the
nightly CI job. So if we touch any pulley source files in a PR, we should also
run that job in the PR's CI.

* Also run MIRI tests if `pulley` files are changed

* review feedback

* grep for miri in changed files, not commit messages
/// when compiling without `#![feature(explicit_tail_calls)]` enabled (via
/// `--cfg pulley_tail_calls`).
///
/// It seems rustc first parses the the function, encounters `become` and emits
Copy link
Contributor

Choose a reason for hiding this comment

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

"the the" mistake (typo?)

Copy link
Member

Choose a reason for hiding this comment

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

@Rudxain good eye! Care to make a PR fixing it?

Copy link
Member

Choose a reason for hiding this comment

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

Nevermind, I see that you already did -- thanks!

github-merge-queue bot pushed a commit that referenced this pull request Oct 11, 2024
@Kmeakin Kmeakin deleted the km/pulley/tail-calls branch October 29, 2024 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pulley Issues related to the Pulley interpreter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants