Skip to content

move a value into its last call - #130

Merged
alii merged 1 commit into
masterfrom
vm-moves
Sep 24, 2026
Merged

alii merged 1 commit into
masterfrom
vm-moves

Conversation

@alii

@alii alii commented Sep 24, 2026

Copy link
Copy Markdown
Member

The last piece Perceus reuse was waiting on. A call whose argument the caller is done with hands over the reference its register holds, rather than adding one and giving it up after the call returns.

Why it matters. The reuse #124 added fires only when the cell's count is 1. Passing a value to a function made it 2, and the caller's own release came after the call, so the callee always saw 2 and never reused:

let %5 = call self(%2)
drop %2                 <- too late: the callee has already looked

With the move, the callee is the only holder and the cell it drops is one it may overwrite.

How it works. moves_on_last_use runs over each function's instructions once the loader has built them: a call followed by the Drop of one of its own argument registers marks that argument moved, and a moved argument is taken out of its register instead of shared.

  • The Drop stays where it is. The register is empty by then, so it does nothing — and a jump that lands on it without the call having run still finds the value and releases it. Nothing is deleted, so no jump target moves.
  • Only the last position holding a register is marked, since an earlier one still reads it: f(x, x) shares once and moves once, which is exactly the two references the callee needs.
  • A DropReuse is left alone, so a cell this frame has already paired with a constructor of its own stays here.
  • A call in tail position needs nothing: the frame releases its registers after reading the arguments, so it was already a move.

Numbers.

allocations reused
add_one over a 1000-cell list, before 1000 0
after 0 1000

Every Cons of the new list is the matching one of the old, written over. Three hundred passes over a 3,000-element list: 0.09 s → 0.05 s, best of three, release build. The loop from #124 also improves, 2 allocations to 1.

Tests. A new one in crates/scarlet_vm/tests/run.rs pins the zero: the walk allocates nothing and reuses 1000, and still prints the right sum. #124's loop test moves to its new counts. Every .scrl in examples/ and tests/programs/ prints exactly what it printed before.

All seven CI gates pass locally. hawk ran for the host target, and nothing here is gated on a cfg.

@alii
alii merged commit 6b66992 into master Sep 24, 2026
4 checks passed
@alii
alii deleted the vm-moves branch September 24, 2026 01:51
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