Repository navigation
fix(cranelift): publish the caller's rwasm counters before a tail call - #15
Merged
Merged
Conversation
A tail call left the store's rwasm stack counters untouched, on the assumption that every reader runs right after the call site that wrote them. That fails for a frame that makes a plain call first: the call publishes the callee's depth and base, nothing restores them, and the tail callee's prologue then reads them as its own. A tail-recursive loop with a call in its body therefore counted one frame per iteration on Wasmtime and trapped StackOverflow after N_MAX_RECURSION_DEPTH iterations, while the rwasm VM ran it. return_call, return_call_indirect and return_call_ref now store the caller's own counters before the call, like ReturnCallInternal. Modules without tail calls compile to the same code as before. Version 45.0.0-rwasm.4. Pinned by rwasm's tests/stack-overflow.rs.
dmitry123
marked this pull request as ready for review
September 26, 2026 03:59
Member
Author
|
Local run of rwasm
Ready for review. After merge: tag |
tests/all/rwasm_stack.rs drives Config::rwasm_stack_limits directly: plain recursion traps StackOverflow at max_call_depth (the control), and a return_call or return_call_indirect made after a plain call in the same frame runs far past that depth, because the tail call publishes the caller's own counters. The second case fails on the lowering of 45.0.0-rwasm.3 and passes with the fix.
Member
Author
|
Added a fork-local regression test,
|
hedwig0x
approved these changes
Sep 28, 2026
hedwig0x
left a comment
There was a problem hiding this comment.
Reviewed the compiler lowering, regression coverage, and crate version updates. The focused rwasm stack tests pass locally; no blocking issues found.
dmitry123
added a commit
to fluentlabs-xyz/rwasm
that referenced
this pull request
Sep 28, 2026
The fork release carries the tail-call fix (fluentlabs-xyz/wasmtime#15): a return_call after a plain call now publishes the caller's rwasm counters, so the new stack-overflow case passes on both strategies.
dmitry123
added a commit
to fluentlabs-xyz/rwasm
that referenced
this pull request
Sep 28, 2026
…all (#224) * test: pin that a tail call after a call keeps the caller's frame on both backends A frame that makes a plain call and then a return_call or return_call_indirect must run at its own depth and base on both strategies. On wasmtime-rwasm 45.0.0-rwasm.3 the tail callee read the counters the plain call had published for its callee, so a tail-recursive loop with a call in its body trapped StackOverflow after N_MAX_RECURSION_DEPTH iterations on Wasmtime while the rwasm VM ran it. The case fails on rwasm.3 and passes with the fork fix (rwasm.4). * docs: correct the tail-call rule of the Wasmtime stack emulation The pipeline notes claimed a call site restores its own counters after the call; it does not, the caller keeps them in registers, which is why a tail call has to publish the caller's counters itself. Record the fix under Unreleased. * build(deps): pin wasmtime-rwasm 45.0.0-rwasm.4 The fork release carries the tail-call fix (fluentlabs-xyz/wasmtime#15): a return_call after a plain call now publishes the caller's rwasm counters, so the new stack-overflow case passes on both strategies. * build(deps): pin the fuzz oracle to wasmtime-rwasm 45.0.0-rwasm.4 tests/fuzz_versions.rs requires the differential-fuzz oracle to run the same wasmtime-rwasm as the root crate.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
return_callhanded the tail callee the callee's counters, and every iteration of a tail-recursive loop with a call in its body counted as one more frame untilStackOverflowatN_MAX_RECURSION_DEPTH. The rwasm VM runs the same module, so the two backends diverged.return_call,return_call_indirectandreturn_call_refnow store the caller's own counters (the SSA value its prologue loaded) before the call, likeReturnCallInternal. One extra store per tail-call site; modules without tail calls compile to the same code.Reproduction
rwasm
tests/stack-overflow.rs, new casetail_calls_after_a_call_do_not_count_towards_the_recursion_limit(return_callandreturn_call_indirectafter acall, 100 ×N_MAX_RECURSION_DEPTHiterations):[("rwasm", Ok(I32(0))), ("wasmtime", Err(StackOverflow))]Impact
Reachable only by modules with tail calls after a non-tail call in the same function. None of the Fluent networks' historical system-runtime hints use
return_call, so nothing on chain is affected; the fix is needed before any tail-call payload lands.Companion: rwasm
fix: pin wasmtime-rwasm 45.0.0-rwasm.4 and cover tail calls after a call(test, docs, changelog).