Bytecode & VM: Making It Run
- Connect each source statement to stack and scope effects.
- Patch absolute jumps and preserve caller operands across function execution.
- Distinguish a VM stack result from discarded source expression statements.
1 / 6
Compile let x=3; while (x>1) { x=x-1; }:
0 PUSH_NUM 3 1 DEFINE_VAR x
2 LOAD x 3 PUSH_NUM 1 4 GT
5 JUMP_IF_FALSE 13
6 ENTER_SCOPE 7 LOAD x 8 PUSH_NUM 1
9 SUB 10 STORE x 11 EXIT_SCOPE
12 JUMP 2
A target of 13 is the legal one-past-end exit in this code slice. At x=3 and
x=2 the test consumes two numeric operands and produces true; the conditional
jump consumes that boolean. Each body returns to the same stack height and
exits its scope. At x=1 the jump exits. The final operand stack is empty, so
Run returns nil. Changing the back edge from 2 to 0 would reset x forever.
A while loop leaves one condition value on the stack after every test, and a program ending in f(); unexpectedly returns f’s result. Identify the two missing stack effects.
Replace the worked loop body with a nested block containing a shadowing declaration. Trace which x each LOAD/STORE sees and verify the outer loop still progresses. Independently hand-author bytecode computing 17-5 to test right-then-left pops.
Carry an executable Bytecode object into BC1 serialization. Preserve the instruction-index trace as a comparison point after decoding.