Skip to content

Add ABI frame lowering layers#1947

Open
Th0rgal wants to merge 5 commits into
mainfrom
codex/abi-frame-lowering
Open

Add ABI frame lowering layers#1947
Th0rgal wants to merge 5 commits into
mainfrom
codex/abi-frame-lowering

Conversation

@Th0rgal
Copy link
Copy Markdown
Member

@Th0rgal Th0rgal commented Jun 4, 2026

Summary

  • add Compiler.ABI.Frame typed ABI frame layouts for nested structs, dynamic bytes/arrays, and calldata/memory/code/storage sources
  • add Compiler.Lowering.StackSafeAbi frame-spilled lowering for events, external calls, and dynamic returns so large payloads are materialized in memory and passed as pointers
  • add Compiler.Modules.CodeData typed CREATE2/SSTORE2 code-as-data read/write facade with explicit trust surface and roundtrip shape tests
  • add Compiler.Proofs.GeneratedTransition minimal reads/writes/guards/events extraction for later Midnight RCF/totalUnits wiring

Verification

  • lake build Compiler.ABI.FrameTest Compiler.Lowering.StackSafeAbiTest Compiler.Modules.CodeDataTest Compiler.Proofs.GeneratedTransitionTest
  • lake build Compiler.Modules
  • lake build Compiler

Note

Medium Risk
New runtime return path (returnCodeData) and pointer-based ABI lowering touch codegen and EVM introspection boundaries; mostly additive with tests but incorrect frame sizing or extcodecopy semantics would break calls/returns.

Overview
Introduces typed ABI frame layouts (Compiler.ABI.Frame) that choose inline words vs memory (ptr, size) for nested structs and dynamic tails, plus StackSafeAbi helpers to spill payloads before events, external calls, and dynamic returns.

Adds Stmt.returnCodeData end-to-end: Yul via extcodesize/extcodecopy, validation/trust-surface tagging, macro returnCodeData, and a CodeDataReturnSmoke contract example. Compiler.Modules.CodeData wraps CREATE2/SSTORE2 read/write on those frames with an explicit trust surface.

Also adds GeneratedTransition (reads/writes/guards/events from stmt lists) for later proof wiring, and maps struct return types to tuple ParamType in the macro translator.

Reviewed by Cursor Bugbot for commit 25bb096. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
verity Ready Ready Preview, Comment Jun 5, 2026 7:56am

Request Review

Comment thread Compiler/ABI/Frame.lean Outdated
Comment thread Compiler/Lowering/StackSafeAbi.lean Outdated
Comment thread Compiler/Lowering/StackSafeAbi.lean Outdated
Comment thread Compiler/ABI/Frame.lean
Comment thread Compiler/ABI/Frame.lean
Comment thread Compiler/Modules/CodeData.lean
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

\n### CI Failure Hints\n\nFailed jobs: `checks`\n\nCopy-paste local triage:\n```bash\nmake check\nlake build\nFOUNDRY_PROFILE=difftest forge test -vv\n```

Comment thread Compiler/ABI/Frame.lean
| .calldata => YulExpr.call "calldataload" [sourceByteOffset field idx]
| .memory => YulExpr.call "mload" [sourceByteOffset field idx]
| .code => YulExpr.call "mload" [sourceByteOffset field idx]
| .storage => YulExpr.call "sload" [sourceStorageSlot field idx]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code frame source uses mload

High Severity

For FrameSource.code, materializeSourceWord uses mload on the field’s base identifier, same as memory. That reads contract RAM at a pointer, not runtime bytecode at an address, so spilled frames and inline args from .code fields can be wrong when the base is a contract pointer.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 54a6024. Configure here.

]),
YulStmt.expr (YulExpr.call "return" [
YulExpr.ident "__return_code_ptr",
YulExpr.ident "__return_code_size"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

returnCodeData copies whole bytecode

Medium Severity

returnCodeData uses extcodesize and extcodecopy from offset zero for the full code length. SSTORE2-style layouts and CodeData reads often skip a leading bytecode prefix via codeOffset, so returndata can include non-ABI bytes and break decoding of declared struct/tuple returns.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 54a6024. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 25bb096. Configure here.

Comment thread Compiler/ABI/Frame.lean
(List.range (fieldPayloadWords field)).map fun idx =>
YulStmt.expr (YulExpr.call "mstore"
[ YulExpr.call "add" [YulExpr.ident (ptrName base), YulExpr.lit ((offsetWords + idx) * 32)]
, materializeSourceWord field idx ])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Dynamic calldata tail copied wrongly

High Severity

spillField loads dynamic fields as consecutive words from each field’s calldata base. ABI-encoded dynamic bytes/arrays store an offset in the head and place tail data elsewhere, so sequential calldataload after the head does not reproduce the tail or valid in-memory ABI layout.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 25bb096. Configure here.

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