perf(vm): move the interpreter loop onto Machine.loadAt and Machine.storeAt #250
Loading…
Reference in a new issue
No description provided.
Delete branch "loop/163-interpreter-natives"
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?
Closes #163
Part 2 of 2: the first commit (PR #221) taught the runtime
Machine.loadAtandMachine.storeAt. The published seeds already know them, so this moves the interpreter loop onto them.What
perf(ir): the range analysis now bounds a bitwise and with an operand that cannot be negative:x & mwithm >= 0is in0..=m. Before this,head >> 8 & 65535was unbounded, soa + offsetin the loop's copy loops kept its overflow check.perf(vm):executeLoopand the helpers it calls (header,parametersOf,parameterWord,ensureFrame,placeAt,resolvePlace,enter,noteFrames,loadRegister/storeRegister) read and write words withMachine.loadAt(base, index)/Machine.storeAt(base, index, value). The runtime computesbase + 8 * indexunchecked and inline, so the loop no longer computes addresses withMachine.load(frame + 8 * a).& 65535. The emitter reports "a frame of more thanlargestOperandwords" and refuses to emit it, so on a valid image the mask is a no-op. It lets the range analysis drop the check ofa + offset/target + offsetin Move, Constant, Zero, Load, Store, Call, CallClosure, CallWitness and Return.perf(vm): the pool offset a Constant instruction carries is masked to 32 bits (a pool holds fewer than 2^32 words), so the arm reads the pool withMachine.loadAt(pool, start + index)and no checked arithmetic.Returnreads this word; the debugger reads the other three, which are unchanged.What stays checked (on purpose)
pc + 1,pc + 2and the pc advances.pcis an unbounded code offset, and the analysis cannot bound it without a mask that would change meaning.first + 8 * base), of its header record and of its parameter table. Each is one checked computation per call or return, where before there was one per word.AddIntegerfamily keeps its overflow panics. Only the interpreter's own address arithmetic changed.Test
compiler/tests/ranges.test.trb: a mask with a nonnegative constant bounds the field it takes out of a word (add.i64.uncheckedfor both the counter anda + offset); a mask that may be negative bounds nothing.--vmand tier A/B in CI cover "nothing observable changes", includingoverflow.trb.executeLoopfrom this branch'scompiler-linux-x64artifact will be checked for overflow builtins and the evidence posted below once CI is green.fibonacci(30)before/after is measured by #165, not here.Evidence: checked arithmetic in the generated C of
executeLoopI compared the C of
executeLoopin thecompiler-linux-x64artifact of this PR's CI run (index 504, head9889e8f9) with main's (cf14b601). I counted every call oftorb_add_i64,torb_subtract_i64andtorb_multiply_i64and attributed it to its opcode arm through theTORB_LOCATIONline.executeLooptorb_machine_load_at/torb_machine_store_atPer arm (add/multiply/subtract):
pc = pc + 1pc + 1(pool offset word),pc = pc + 2pc + 1,pc = pc + 2pc = pc + 1pc = pc + 1pc = pc + 1pc + 1,pc = pc + 2, andleft + right: the overflow panicleft - rightleft * rightpc + k,calleeBase,first + 8 * calleeBase,code + 8 * (pc + 2),depth + 1,top: once per call, not per wordfirst + 8 * base,top,depth - 1: once per returnNo register, pool or header word is addressed with checked arithmetic in these arms. What is left is the program counter, which is unbounded and stays checked, and one frame address per call and return.
Approved. Checklist for head
9889e8f:ranges.test.trbcovers both sides of the new rule (nonnegative mask bounds the field anda + offset; a possibly-negative mask bounds nothing). The loop change is covered by conformance--vm, tier A/B and the same-C check, all green on this head.bitwiseAndIntervalis sound (x & mwithm >= 0is in0..=mfor anyx; two nonnegative operands give0..=min), and only applies to kinds the analysis tracks. The return record change is consistent: all threeentersites now store the caller-relative register,Returnreads the record before copying and recomputes the caller frame, andnoteFrames/ the debugger only read the other three words. The& 65535and& 4294967295masks are no-ops on a valid image (largestOperand = 65535, the emitter refuses larger frames).Non-blocking:
executeLoopfor overflow builtins; I did not see the evidence posted yet. Worth adding as a comment (or in #165) for the record.