◄ WORLD V · SONNY 5DART 162 · a helldive at the net

TWO-PASS ASSEMBLER pass one places the labels; pass two patches the jumps

An assembler hits a forward reference — a jump to a label defined later — before it knows the label’s address. The fix is two passes: pass one walks every instruction accumulating addresses and records each label in a symbol table; pass two walks again, now emitting bytes and patching every operand that referenced a label. Two linear passes turn forward references from impossible into trivial.

THE TECHNIQUE symbol table, then backpatch

A program branches forward to L2, defined three instructions later. Pass one sums instruction sizes to place L2; pass two patches the branch operand. Watch the symbol table fill and the operand resolve: live demo


HISTORY & CREDIT forward references; assembler folklore

“You need two passes for an assembler.” — not strictly. A one-pass assembler works by backpatching: emit the branch with a blank operand, keep a fix-up list, and fill it when the label appears. Two passes is the simplest correct design, not the only one. cited

1940s–50s · Wilkes, Wheeler & Gill (EDSAC) — symbolic assembly and subroutine libraries; the forward-reference problem appears with the first symbolic assemblers.
classical · the two-pass / symbol-table structure is textbook folklore (no single inventor) — canonised in every systems-programming text (Beck, “System Software”).
alternative · one-pass backpatching keeps a per-symbol fix-up list — the same idea the linker uses for relocations (dart 163).

The symbol table is the whole trick: a name → address map built in one walk, consulted in the next. It is the assembler’s half of the same job the linker finishes across files. classical / folklore

RECOMMEND FOR I-13 the label address, computed

Pass one’s address arithmetic and pass two’s patch run on the canonical compiler:

$ i13 run asm.i13 # pass 1: L2 address = sum of instruction sizes before it L2_addr = 6 -- sizes [2,1,3] before L2 -> 2+1+3 = 6 jmp_operand_before = 0 -- pass 1: forward ref, operand unknown jmp_operand_after = 6 -- pass 2: patched with L2's address
Recommend: the two-pass structure is LIT and the exact shape an I-13 assembler/loader wants — the canonical compiler computes L2’s address as the running sum of instruction sizes (6 for sizes 2,1,3) and patches the forward branch operand from 0 to 6, all in array arithmetic. A symbol table is a name/address parallel array; pass one fills it, pass two reads it. If I-13 ever emits its own IVM object form, this is the model — and it is the assembler’s half of the linker’s job (dart 163). Backpatching is the one-pass variant.