v5 Linker: merging module bytecode

Work through examples, try the reasoning yourself, then put it into practice.

View Slides

Before you start

  • Derive module initializer artifacts and absolute instruction indexes.

What you will practice

  • Relocate function IDs and Main jump addresses in separate coordinate systems.
  • Preserve function-local jumps and input artifacts.
  • Validate dependency order before deterministic concatenation.
In this lesson

Lesson

3 min read

v5 Linker: merging module bytecode

Link combines ModuleBytecode objects into one Bytecode. First validate unique module names, main-last, and every import referring to an earlier module. Then relocate code. Inputs from CompileModules use dense local function IDs 0..N-1 and valid local jump coordinates; this linker is not a general verifier for arbitrary corrupt artifacts.

Worked reconstruction

Suppose util has two functions and seven Main instructions. main has local function ID 0 and a Main JUMP_IF_FALSE target 5:

Operand in main artifact Offset Linked value
Function ID 0 previous function count 2 2
MAKE_CLOSURE 0 in Main previous function count 2 2
MAKE_CLOSURE 0 inside a function previous function count 2 2
Main JUMP_IF_FALSE 5 previous Main length 7 12
Function-local JUMP 5 no instruction relocation 5
CALL 2 arity, not an address 2

Function IDs and instruction addresses are different number spaces. A module's Main moves inside the concatenated Main, while each function keeps its own separate code slice. Only its closure-ID operands need the function offset; its local jumps stay unchanged. Jump targets equal to the old Main length also shift: they represent that initializer's end, now the next initializer's start in the merged stream.

Relocation algorithm from the invariant

Maintain functionOffset and mainOffset. Copy each function and instruction slice before patching. Add functionOffset to Function.ID and MAKE_CLOSURE operands in both function and Main code. Add mainOffset only to JUMP and JUMP_IF_FALSE operands in Main. Append the relocated copies, then advance each offset by its own count.

Calling Link twice on the same input must produce equivalent output. A shallow copy of Bytecode still shares its slice backing arrays; modifying its Functions or Code in place can permanently rebase the input and double-shift the second link. Preserve the artifacts so they can be cached, linked again, or compared.

Dependency validation is not scheduling

Source order is the required initialization order. The linker rejects a future import rather than sorting modules to repair it. main being last does not mean it is the only initializer executed: all Main slices run in dependency order. Link errors stop the capstone before encoding or runtime execution.

Debugging lab

A linker correctly shifts every closure ID but leaves Main jumps unchanged. With util length 7, main’s local back edge targets instruction 2. Where does it jump incorrectly, and what should the target be?

Compare your diagnosis after attempting the lab

It jumps into util’s initializer at global index 2 instead of main’s local index 2 now located at global 9. Add the accumulated Main length to Main jump operands; do not apply that offset to function-local jumps or call arities.

Transfer task

Work a three-module layout with function counts 2,1,3 and Main lengths 7,4,9. Derive the third module’s function and Main offsets, including a jump to its one-past-end label. Then explain how to test linking twice without relying only on output length.

Carry forward

Carry linked Bytecode with stable unique IDs and relocated Main targets into the v5 capstone. Save a multi-module program with a loop in a later initializer; straight-line examples cannot expose missing jump relocation.

Build bytecode-linker-v5. Keep a before/after trace of one failing test. Use the checkpoint to diagnose specific skills; passing it does not replace implementing and testing the artifact.


Practice sequence

Use the order below for a guided path, or choose an item to revisit. A pass records a successful attempt; try a fresh example later to check what you can reconstruct.

Join Discord