Skip to content

bugc: fix return-epilogue stack cleanup for multiple stale values#274

Open
gnidan wants to merge 1 commit into
mainfrom
compiler-return-epilogue-fix
Open

bugc: fix return-epilogue stack cleanup for multiple stale values#274
gnidan wants to merge 1 commit into
mainfrom
compiler-return-epilogue-fix

Conversation

@gnidan

@gnidan gnidan commented Jul 23, 2026

Copy link
Copy Markdown
Member

A user function's return epilogue clears any leftover stack values before jumping back to the caller, keeping only the return value on top. The cleanup rotation was wrong whenever two or more stale values sat below the return value: each iteration swapped the deepest element to the top and popped it, which on the first swap sank the return value to the bottom and then, on the next iteration, brought it back up and popped it. It only produced the right result when exactly one stale value needed removing.

In practice this surfaced whenever a function returned from a branch. loadValue duplicates an operand that is already on the stack, so a comparison operand and the branch condition are both left behind; a return in the taken branch then ran the cleanup with two stale values and returned the branch condition instead of the intended value. For example if (x == 0) { return 9; } else { return 7; } returned 1 (the condition) rather than 9 from the true branch, while the false branch happened to work.

This replaces the rotation with a repeated SWAP1; POP: with the return value on top, swap the stale value just beneath it up to the top and pop it, once per stale value. The return value stays on top throughout, so any number of leftovers are removed correctly.

A regression test covers returning a constant and a parameter from a branch, and returning a call result from a branch, at every optimization level.

The scope of this change is the return-epilogue stack cleanup, where a branch-return yielded the branch condition instead of the return value. It does not touch how the operand stack is tracked across blocks and calls, which has a separate, more serious problem: self-recursive functions are miscompiled because the pre-call stack cleanup undercounts untracked leftovers and corrupts the callee's arguments, so this fix does not yet make recursion correct. Both that active miscompilation and the latent multi-predecessor desync stem from evmgen threading its tracked stack linearly through block layout order rather than through the control-flow graph, and both are tracked in #275.

@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://ethdebug.github.io/format/pr-preview/pr-274/

Built to branch gh-pages at 2026-07-23 22:44 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

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.

1 participant