Skip to content

refactor: move runtime local access to slot-backed frames#1059

Draft
Stevengre wants to merge 19 commits intomasterfrom
codex/20260411-locals-refactor
Draft

refactor: move runtime local access to slot-backed frames#1059
Stevengre wants to merge 19 commits intomasterfrom
codex/20260411-locals-refactor

Conversation

@Stevengre
Copy link
Copy Markdown
Contributor

@Stevengre Stevengre commented Apr 11, 2026

Context

This PR refactors KMIR runtime locals from per-frame value lists into slot-backed storage. The <locals> cell still represents the current frame's MIR local index order, but it now stores runtime slot ids. The actual TypedLocal data lives in the global <slotStore>, and <nextSlot> allocates fresh slots for new frames.

The motivation is not just renaming storage:

  • reduce the complexity caused by stack-depth / OFFSET-style reference and pointer bookkeeping by giving runtime locals stable slot identities
  • model MIR stack locals more accurately as addressable runtime storage, separate from MIR's relative local(i) indexes
  • leave a cleaner target for future address support
  • make generated function-summary rules easier to express against stable slot-store entries instead of frame-local value-list positions

Summary

  • Adds slot-backed runtime local storage with <slotStore>, <nextSlot>, SlotPlace, and #frameSlotId.
  • Reworks local reads, moves, writes, references, pointers, projections, returns, and frame unwind/restore paths to resolve MIR local(i) through the current frame's slot list before reading or updating <slotStore>.
  • Refactors function-call setup so non-intrinsic calls materialize operands while the caller frame is still current, normalize the resulting values for rust-call / closure calls, then reserve and initialize the callee's slots from that value list.
  • Keeps intrinsic dispatch separate from body-backed call setup and updates intrinsic local/type handling, including raw_eq, to work with slot-backed locals.
  • Updates Python-side config/value construction so concrete, symbolic, and random test inputs initialize <locals> as slot ids plus <slotStore> entries.
  • Adds slotstore-symbolic-branch proof coverage and updates affected exec/prove/show snapshots for the new runtime configuration shape.

Comment thread kmir/src/kmir/kdist/mir-semantics/rt/data.md Outdated
@Stevengre Stevengre force-pushed the codex/20260411-locals-refactor branch from 39b2264 to 9710753 Compare April 13, 2026 03:33
Stevengre added a commit that referenced this pull request Apr 14, 2026
Initial implementation of CSE on the slotStore branch (PR #1059):
- _cse.py: clean CSE pipeline (callee proof, cover path extraction,
  summary rule generation, add-module reuse)
- cse-simple-callee.rs: simple test program (double(x) = x + x)
- test_cse_callee_proof: verifies callee proof completes with covers
- cse-spec.md: full specification with slotStore-based design

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stevengre added a commit that referenced this pull request Apr 14, 2026
Migrate all cheatcode rules from old locals/stack-based Reference
format to the new slotStore-based format (PR #1059):

p-token.md:
- PAccByteRef: (Int, Place, Mutability, Int) → (SlotPlace, Mutability, Int)
- Remove #adjustRef for PAccByteRef (no stack offsets needed)
- #mkPAccByteRef: resolve PtrLocal via slotStore instead of stack
- #mkPAccByteRefLen: use SlotPlace instead of (Int, Place)
- #mkPAccountRef: Reference/PtrLocal use slotPlace(SLOT, proj)

spl-token.md:
- #forceSetPlaceValue: <locals> → <ownedSlots> + <slotStore>
- #traverseProjection write rules: toLocal → toSlot, toStack → removed
- #setSPLBorrowData: Reference/PtrLocal use slotPlace(SLOT, proj)
- #execSPLSolMemset: <locals> → <ownedSlots> in StackFrame

Build passes (make build).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Stevengre Stevengre changed the title [codex] externalize runtime locals into slot store [codex] route runtime locals through the slot store Apr 18, 2026
@Stevengre Stevengre changed the title [codex] route runtime locals through the slot store [codex] move runtime local access to slot-backed frames Apr 18, 2026
@Stevengre Stevengre changed the title [codex] move runtime local access to slot-backed frames refactor: move runtime local access to slot-backed frames Apr 18, 2026
@Stevengre Stevengre changed the base branch from master to _update-deps/runtimeverification/k April 18, 2026 03:14
@Stevengre Stevengre force-pushed the codex/20260411-locals-refactor branch 4 times, most recently from 2021234 to ecc115d Compare April 18, 2026 03:45
Base automatically changed from _update-deps/runtimeverification/k to master April 18, 2026 04:33
@Stevengre Stevengre force-pushed the codex/20260411-locals-refactor branch 2 times, most recently from 6b39123 to ce91d6a Compare April 18, 2026 08:28
@Stevengre Stevengre force-pushed the codex/20260411-locals-refactor branch from ce91d6a to 4bb4318 Compare April 18, 2026 08:51
@Stevengre Stevengre self-assigned this Apr 20, 2026
@Stevengre
Copy link
Copy Markdown
Contributor Author

Stevengre commented Apr 20, 2026

Some ideas for better performance:

  1. transform the locals into slots when call a function to reduce the term size
  2. investigate why it makes proof slower and optimize the haskell backend. Maybe map access functions should be optimized in the haskell backend.
  3. We may remove the newlocal value, because if the map item is not exist, it should be newlocal.

Comment on lines +47 to +62
ListItem ( 0 )
ListItem ( 1 )
ListItem ( 2 )
ListItem ( 3 )
ListItem ( 4 )
ListItem ( 5 )
ListItem ( 6 )
ListItem ( 7 )
ListItem ( 8 )
ListItem ( 9 )
ListItem ( 10 )
ListItem ( 11 )
ListItem ( 12 )
ListItem ( 13 )
ListItem ( 14 )
ListItem ( 15 )
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are these lists always just counting up from 0? THen do we need the list at all?

Comment on lines +32 to +48
ListItem ( 22 )
ListItem ( 23 )
ListItem ( 24 )
ListItem ( 25 )
ListItem ( 26 )
ListItem ( 27 )
ListItem ( 28 )
ListItem ( 29 )
ListItem ( 30 )
ListItem ( 31 )
ListItem ( 32 )
ListItem ( 33 )
ListItem ( 34 )
ListItem ( 35 )
ListItem ( 36 )
ListItem ( 37 )
ListItem ( 38 )
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see here that we have an example where there are multiple call frames, and so it now starts at 22 and goes to 38. But it still just counts by 1. So I tihnk we really only need to store, for each frame, the start index (here 22), and the width (here 16), just two integers instead of a list of integers. Then we can use basic arithmetic to figure out the slot index. Would that work?

@ehildenb
Copy link
Copy Markdown
Member

I think in general, doing map accesses for both CSE and symbolic execution may not perform as well as doing list access. So I'm not sure this will actually do better for our proof performance than how the semantics is currently arranged. What about this change makes you think CSE will work better?

@Stevengre
Copy link
Copy Markdown
Contributor Author

Stevengre commented Apr 20, 2026

@ehildenb After discussing with Daniel and Maria, we found an easy way to specify the references and pointers in arguments. This is to implement existing traverse-projection as a function so that we can write something like

ARG: Referece(OFFSET, LOCAL_ID, ...)
requires traverseprojection(ARG, LOCAL_CELL, FRAME_CELL) == Value

Another solution for this using List maybe

<local> Start Index in the slotStore </local>
<slot> List of locals from caller to callee <slot>

For the specification:

ARG: Referece(SlotIDX, ...)
requires STORE[ID] == Reference (slotid) & traverseProjection(SLOT_CELL, ID) = Value

okay, we always need this traverseProjection function...

BTW, this structure may also cause performance issue, because I remember that the haskell backend access the List through a front -> end way.


I think we still need to provide better support for map, especially the map with concrete key or int/string/id key (this should cover most of cases). Otherwise, it's hard to reason programs like C and Java directly. Additionally, it may introduce more complexity to the semantics and make the semantics harder to understand. And I think the change should be relatively small.

For this reason, even though map may not be the best way to model, I'd like to keep this PR as a performance test for the haskell backend.

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.

2 participants