Skip to content

bugc: fix internal function calls#184

Merged
gnidan merged 1 commit intomainfrom
compiler-fix-function-calls
Mar 11, 2026
Merged

bugc: fix internal function calls#184
gnidan merged 1 commit intomainfrom
compiler-fix-function-calls

Conversation

@gnidan
Copy link
Member

@gnidan gnidan commented Mar 11, 2026

Summary

  • Fixes multiple interacting bugs that caused stack underflow when calling user-defined functions
  • Internal function calls now work for simple calls, nested calls (function calling function), multiple function calls per block, and function calls inside loops
  • Unskips the behavioral test from bugc: Add behavioral test helper for end-to-end bytecode validation #183 and adds new test coverage

Changes

Fixes addressed 8 separate issues in the EVMGen pipeline:

  1. Scratch memory collision: copy-based reads (RETURNDATACOPY/CODECOPY) used 0x60 as scratch, same slot as function call return PC. Moved to 0x00 (Solidity scratch space 1).

  2. Liveness analysis gaps: call terminator arguments and continuation blocks weren't tracked, so values needed for calls didn't get memory allocations.

  3. Stack cleanup before calls: caller stack had leftover values from DUP-based loadValue. Added POP-all cleanup before loading call arguments from memory.

  4. State stack tracking: after call terminator, state stack still tracked loaded arguments instead of reflecting runtime (function consumes args, returns value).

  5. Fall-through into user functions: the isLastBlock optimization skipped STOP for void returns, but user functions are appended immediately after. Added STOP guard byte.

  6. Return value spilling: call return values weren't stored to memory, so subsequent call terminators lost them during stack cleanup. Added DUP+MSTORE at continuation blocks.

  7. Relative jump targets: block/continuation patches within user functions used offsets relative to function start, but EVM JUMP needs absolute PC values. Deferred patching to module level where base offsets are known.

  8. Return PC clobbering: nested function calls overwrote memory[0x60] with their own return PC. Added per-function savedReturnPcOffset in prologue and staggered memory allocations across functions.

Fix multiple interacting bugs that caused stack underflow
when calling user-defined functions:

- Move copy-based read scratch from 0x60 to 0x00 to avoid
  collision with function call return PC storage
- Track call terminator arguments and continuations in
  liveness analysis so values get memory allocations
- Clean caller stack before setting up call arguments
- Reset state stack tracking after call to match runtime
- Insert STOP guard between main function and user functions
  to prevent fall-through
- Spill call return values to memory at continuation blocks
- Defer block/continuation jump patching for user functions
  to module level where absolute offsets are known
- Save return PC to per-function memory slot in prologue
  so nested calls don't clobber it via 0x60
- Stagger memory allocations across functions so callee
  frames don't overlap with caller frames
@github-actions
Copy link
Contributor

github-actions bot commented Mar 11, 2026

PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-03-11 06:47 UTC

@gnidan gnidan merged commit 1c39cd6 into main Mar 11, 2026
4 checks passed
@gnidan gnidan deleted the compiler-fix-function-calls branch March 11, 2026 06:43
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