programs-react: fix call stack for the flat tail-call back-edge#248
Merged
Conversation
The compiler emits a tail-call-optimized back-edge as a single instruction carrying both a `return` (the iteration that is ending) and an `invoke` (the iteration that is beginning) on one context. `buildCallStack` had no case for this: it read whichever discriminant it encountered first and either pushed a second frame (invoke) or popped the frame away (return), so a tail-recursive loop's call stack grew without bound or collapsed to empty instead of staying at constant depth. Detect the combined shape structurally — an instruction whose context carries both an invoke and a return — and reuse the top frame in place, taking the next iteration's identity from the invoke leaf. Depth is unchanged, which is what a reused activation should show. Adds unit coverage for the flat back-edge (constant depth, reused identity) alongside a regression check that ordinary calls still push and pop.
Contributor
|
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.
A tail-call-optimized back-edge is emitted as a single instruction
whose context carries both a
return(the iteration that is ending)and an
invoke(the iteration that is beginning).buildCallStackhad no case for this combined shape — it acted on whichever
discriminant it read first, so a tail-recursive loop either pushed a
fresh frame every iteration (stack grows without bound) or popped the
frame away (stack collapses to empty). Neither reflects what actually
happens: the activation is reused, so depth should stay constant.
This detects the combined shape structurally — a context carrying both
an invoke and a return — and reuses the top frame in place, taking the
next iteration's identity from the invoke leaf. Recognizes both the
flat multi-discriminator context and a gather-wrapped one.
Unit coverage added for the back-edge (constant depth, reused
identity) plus a regression check that ordinary calls still push and
pop.