vm: the loop's address arithmetic without overflow checks #163
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Take the overflow checks off the address arithmetic of the VM's loop (
compiler/src/vm/interpret.trb), the first of the open speed steps of VM.md "Speed, the next steps", measured againstfibonacci(30).Source
compiler/src/ir/ranges.trb: the range analysis is a forward, per-function interval analysis whose facts come from constants, the operation that computed a slot, and the comparison aBranchstands on - nothing else.Split out of #8. Written down by the refine step; the mechanism is not settled by any design record, see the owner decision below.
Why this is not simply "teach the analysis"
Every
Machine.load(frame + 8 * a)of the loop adds toframe, a raw address (anInt64nothing bounds), so the sum can overflow as far as the analysis can tell, whateverais (head >> 8 & 65535is bounded,frameis not). A per-function interval analysis cannot bound an address that is loop-carried and set in other functions. So the choice is between a new fact source for the analysis, a new kernel native that takes the base and the offset, or a library-level unchecked/wrapping operation.Acceptance criteria (once the mechanism is decided)
executeLoopforMove,Constant,Word,Zero,Load,Store, the arithmetic and comparison opcodes,CallandReturncompute their addresses without a checked add/multiply in the generated C (shown withtorb build --emit-cor the equivalent on the touched function, or by a test incompiler/tests/on the IR).tools/conformance.sh --vmand tier A/B are green, including the overflow panics of program arithmetic (theAddIntegerfamily keeps its checks; only the interpreter's own address arithmetic loses them).fibonacci(30)measured before and after on one machine, best of five, by the method of VM.md section 8 (in the measuring issue, not in the worker's exec).Seed
Depends on the mechanism: a new
Machinenative or a new std name is a seed-refresh change (CLAUDE.md "Seed and breaking changes"); an extension ofir/ranges.trbalone is not.Dependencies
Owner decision (comment below). Measured by #165.
[loop:torbscript] owner decision: how does the VM loop drop the overflow checks of its own address arithmetic?
What.
executeLoopcomputes every register, code and header address asframe + 8 * offsetonInt64; each add and multiply keeps its overflow check in the generated C becauseframeis an unbounded address. VM.md says the range analysis "could prove safe once a frame's size bounds its registers", but names no mechanism, and the analysis (ir/ranges.trb) is per function with facts only from constants, operations and branch comparisons, so it cannot bound a loop-carried address.Options.
Machine.loadAt(base, index)/storeAt(base, index, value),static inlineintorb.hlikeMachine.load, computingbase + 8 * indexunchecked. Internal tostd/machine("not for programs"). Needs two commits and a seed refresh (new native, CLAUDE.md "Seed and breaking changes"), which the loop may not do (theseedworkflow is the merge gate's or Torben's).x & 65535bounded, plus a trusted-base fact for a slot loaded from the registers). No seed, but it widens the analysis's trust model (a fact it cannot derive), the risk the file's header calls "the one mistake this file may not make".std(public API): general, but a library and language design choice and a much larger surface than this speed step.Recommendation. Option 1: smallest, local to the kernel, mirrors how
Machine.loadandstorewere added, and keeps the analysis sound. Cost: one seed-refresh cycle (first commit teaches the natives, theseedworkflow runs, the second migrates the loop). Say "go option 1/2/3", or "measure first" to hold this until #8 has landed and #165 has put a number on how much of the remaining per-call cost the checks are.Question q1 for torben (decision, blocking): How does the VM loop drop the overflow checks of its own address arithmetic?
[loop:torbscript] refine: owner decision filed (q1), next: waiting (owner decision q1)
[loop:torbscript] owner decision recorded (torben via WhatsApp, 2026-10-08 16:35 Europe/Berlin): "Ja" (recommendation accepted).
Resolution: option
kernel-natives: internalloadAt/storeAtin std/machine, static inline, uncheckedbase + 8 * index, seed refresh with the two-commit rule. Labelledready.Standing guidance from the same answer: "Das meiste davon konntest du selbst beantworten" (most of this you could have answered yourself). Decide performance steps, seed refreshes that follow an existing plan, naming that follows the conventions, and anything measurable on your own; measure before asking; large refactors are fine when they make sense, size is no argument against an option.
[question-closed] q=q1 reason=answered-elsewhere
[loop:torbscript] claim implement by session:loop-torbscript-20261008 until 2026-10-09T02:29Z
[loop:torbscript] release implement: no worker could be started (sessions_spawn: the model account is unavailable, usage limit). Nothing was changed. next: implement (ready)
[loop:torbscript] claim implement by session:loop-torbscript-20261008 until 2026-10-09T03:04Z
[loop:torbscript] implement: PR #221, CI success (run 393,
91c009ff), next: review (waiting on the merge gate argus)This is part 1 of 2 under the two-commit rule. It adds
Machine.loadAt/Machine.storeAt(static inline, wrappingbase + 8 * index), the manifest rows, the regenerated natives files, the runtime tests and VM.md. The interpreter does not use the natives yet, because the published seed cannot lower them. After the merge, theseedworkflow (or the nightly) has to publish a seed that knows them. Then part 2 migratescompiler/src/vm/interpret.trband brings thefibonacci(30)numbers, so this issue stays open until then (the PR saysRefs, notCloses).Worker usage: one implement subagent, no further workers.
[loop:torbscript] implement: PR 221 merged by argus (
91c009ff), part 1 of 2 landed. Part 2 (interpreter migration) needs a published seed that knowsMachine.loadAt/Machine.storeAt; the seed refresh is Torben's, forbidden for the loop. next: waiting (seed refresh publishing the new natives)[post-merge] PR #221 (part 1 of 2) merged by argus as
91c009ff. After-merge step 1 done: dispatched theseedworkflow on main (run 439, queued behind CI runs on the busy runner). Not yet verified: the seed is published once run 439 is green. Next: when the published seed knowsMachine.loadAt/Machine.storeAt(or the nightly has published it), the loop opens part 2 (interpret.trb onto the natives, fibonacci(30) before/after numbers). #163 stays open until then.Buffer<Item>as the heap storage primitive #54[loop:torbscript] claim implement by session:loop-torbscript-20261009 until 2026-10-09T10:45Z
torb#21login,logout,ownerandyankcommands #22torb audit#23torb vendor#24std/http#30torb doc --checkfail #31Decimalthat computes #34Float32arithmetic, comparison, text and narrowing fromFloat64(slice 1) #35setVariable#38Flags#47ArrayList, the hash tables and the tries onBuffer#55TakeOut/PutBackon the map #56forover a concrete list is a counted loop #58torb build --explain-copies#61Into<Path>signatures (PATH slice 2) #68Storage(URI gap 13) #69101 Switching Protocolsand a duplex connection (NETWORK slice 14) #74std/toml#77std/xml#78std/htmldocuments (epic) #79Dualnumbers (forward mode) #83std/ecswith oneStoreper group type, andstd/transform(ECS slice 1) #89std/htmlmarkup (WEB slice 1) #93[loop:torbscript] close: part 2 landed. PR #250 approved and merged by argus at 2026-10-09T06:13Z as
9889e8f9(three commits:perf(ir)bound a bitwise and with a nonnegative operand,perf(vm)the loop's words throughMachine.loadAt/Machine.storeAt,perf(vm)a constant through a masked pool offset). PR CI green (tier A, tier B, bootstrap, gates). Criterion on generated C: checked operations inexecuteLoop440 on main vs 134 with the PR, 121loadAt/ 38storeAtcalls (evidence comment on the PR); the remaining checks arepcsteps and the integer overflow panics, as the criteria require. The fibonacci(30) before/after numbers are taken by #165 from a nightly built after this. Main CI on9889e8f9is running at the time of this comment. Worker usage: not reported (the worker's run ended with an oversized output after the PR was merged). Claim released.