Skip to main content

1716. Full compile-pipeline port roadmap (flowgraph + assemble + driver)

Ground rule

Same rule as 1704 / 1705 / 1708 / 1712 / 1713 / 1715. Port whole CPython files, function by function, every // CPython: citation carries filename plus line number. No keeping flat-sequence shims alongside the graph "for now". The shim file gets deleted in the same change that ports its last caller.

This spec subsumes the still-pending parts of 1715 (the original graph-substrate port). 1715 landed phases 1 through 5; this spec finishes phase 6 and extends the scope across Python/assemble.c and the optimize_and_assemble_code_unit driver in Python/compile.c, so the whole CPython compile back-end lands as one coherent port instead of three half-overlapping ones.

Why this exists

Spec 1713 has been driving disassembly-stream parity, and every divergence so far has been fixed by patching compile/flowgraph_passes.go: the flat-sequence pipeline that predates the CFG substrate. Recent examples that all needed custom flat-sequence logic with no analogue in CPython:

  • propagateLineNumbers had to invent a block-boundary detector (computeBlockStartsResolved) because the flat sequence does not carry b_next / b_predecessors.
  • removeUnusedConsts shipped a "are we inside a tuple body" heuristic that has no analogue in CPython.
  • foldConstSequences had to special-case the LABEL pseudo-op that does not exist in CPython's graph.
  • optimizeLoadFastOnSequence builds a temporary cfg, runs the real CPython pass on it, then copies opcode rewrites back into the sequence. It already exists 1:1 on the graph; the flat-sequence wrapper is a roundtrip.

Each patch reads the CPython source, then translates it onto the flat sequence. Every translation is a fresh opportunity for the substrate mismatch to produce a divergent edge case. The cost compounds because spec 1713's gate is byte-equality: divergences surface as opaque marshal-output differences, then need to be traced back through the flat-sequence translation before they can be compared against the CPython source the translation was supposed to mirror.

A second pipeline already exists in compile/flowgraph_cfg*.go. It is a 1:1 port of CPython's CFG passes, complete enough to drive _PyCfg_OptimizeCodeUnit end to end. It is not wired into the compile entry point. Every flat-sequence patch shipped under 1713 is therefore re-implementing logic that gopy already has, on a substrate that does not match CPython.

This spec retires the flat-sequence track. The CFG track becomes the only track.

Phase summary

Top-level progress at a glance. Each phase has its own detail table below; update both this table and the detail table in the same commit that flips a row.

PhaseGoalStatusCommit
AFunction-by-function audit: Python/flowgraph.c, Python/assemble.c, optimize_and_assemble_code_unitdone95b53a4
B.0gopy substrate (DumpCfg + CfgPhaseHook)donee94fcfa
B.1CPython patch (per-phase dump + hook export)donec9d380f
B.2CPython oracle wrappers (L1 / L3 / L4)donef960caf
B.3Diff harness (cfg_phase_parity_test.go + corpus)donefbb22f7
C.1Finish Python/flowgraph.c port (convert_pseudo_conditional_jumps, prepare_localsplus, _PyCfg_OptimizedCfgToInstructionSequence, helpers)donee4c6827
C.2Finish Python/assemble.c port (split into assemble_makecode.go / assemble_jumps.go / assemble.go)done686fd64
C.3Port optimize_and_assemble_code_unit driver into compile/compiler.godone37563f5
D.1Flip assembleUnit to the cfg driverdonec124587
D.2Delete compile/flowgraph_passes.go, flowgraph_jumps.go, flowgraph_except.go, flowgraph_locals.go, flowgraph_stackdepth.godone8d12ecf
D.3Rename cfg files so the filename map in Goal holdsdone8d12ecf
EAll gates green; 1713 byte-equality work resumes on the new substratedone6004c1c

Checklist

  • Phase A: function-by-function audit of Python/flowgraph.c, Python/assemble.c, and the optimize_and_assemble_code_unit driver.
  • Phase B.0: gopy substrate (DumpCfg, CfgPhaseHook).
  • Phase B.1: CPython patch adding _testinternalcapi.set_cfg_phase_hook and the per-phase dump format.
  • Phase B.2: CPython oracle wrappers (codegen / optimize / assemble).
  • Phase B.3: diff harness test/gate/cfg_phase_parity_test.go plus cfg_phase_corpus.txt + cfg_phase_skip.txt.
  • Phase C.1: finish Python/flowgraph.c port (convert_pseudo_conditional_jumps, prepare_localsplus, _PyCfg_OptimizedCfgToInstructionSequence, helpers).
  • Phase C.2: finish Python/assemble.c port, split across compile/assemble.go + assemble_makecode.go + assemble_jumps.go.
  • Phase C.3: port optimize_and_assemble_code_unit driver into compile/compiler_assemble.go.
  • Phase D.1: flip assembleUnit to the cfg driver.
  • Phase D.2: delete the flat-sequence pass files.
  • Phase D.3: rename cfg files so the filename map in Goal holds.
  • Phase E: add the L1 / L3 / L4 gate suite (compile/parity_dump.go, test/gate/codegen_parity_test.go, test/gate/assemble_parity_test.go, corpus + skip files).

Goal

The compile back-end maps file for file onto CPython 3.14:

Python/flowgraph.c -> compile/flowgraph.go (public surface)
compile/flowgraph_cfg.go (basicblock, cfgBuilder, cfgInstr)
compile/flowgraph_cfg_passes.go (every optimization pass)
compile/flowgraph_cfg_locals.go (optimize_load_fast + ref_stack)
compile/flowgraph_cfg_stackdepth.go
compile/flowgraph_cfg_bridge.go (from/to instruction sequence)

Python/assemble.c -> compile/assemble.go (public surface, _PyAssemble_MakeCodeObject)
compile/assemble_locations.go (PEP 657 location table writers)
compile/assemble_exceptions.go (exception table writers)
compile/assemble_varint.go (varint helpers)
compile/assemble_jumps.go (resolve_jump_offsets, resolve_unconditional_jumps)
compile/assemble_makecode.go (makecode, compute_localsplus_info)

Python/compile.c -> compile/compiler.go
optimize_and_assemble_code_unit compile/compiler_assemble.go (assembleUnit ported 1:1)

Spec done means: assembleUnit calls _PyCfg_FromInstructionSequence -> _PyCfg_OptimizeCodeUnit -> _PyCfg_OptimizedCfgToInstructionSequence -> _PyAssemble_MakeCodeObject in the same order, with the same arguments, as optimize_and_assemble_code_unit in Python/compile.c:1411. compile/flowgraph_passes.go, compile/flowgraph_jumps.go, compile/flowgraph_except.go, compile/flowgraph_locals.go, and compile/flowgraph_stackdepth.go no longer exist as flat-sequence files.

Phase A: audit

Function-by-function map of the three CPython files against the current gopy state. The audit is fixed: rows only change as C.1 / C.2 / C.3 land the missing functions.

A.1. Python/flowgraph.c (4165 lines)

CPython functionCPython linegopy stateStatusCommit
_PyCfg_OptimizeCodeUnit3659ported 1:1 onto cfgBuilderdone6f49fd5
optimize_cfg3273ported 1:1done6f49fd5
optimize_load_const2552ported 1:1done1265050
remove_unused_consts2588ported 1:1done1265050
mark_warm / mark_cold / push_cold_blocks_to_end3174 / 3273 / 3323ported 1:1done3b1fb50
insert_superinstructions3404ported 1:1donecaf4e13
add_checks_for_loads_of_uninitialized_variables2843ported 1:1 (lives in flowgraph_cfg_locals.go)donee94fcfa
convert_pseudo_conditional_jumps3478ported 1:1 onto cfgBuilderdonea62563b
calculate_stackdepth (graph version)1352ported 1:1 (flowgraph_cfg_stackdepth.go)done3dfe874
prepare_localsplus3768ported 1:1doneea74ea5
build_cellfixedoffsets3711ported 1:1doneea74ea5
insert_prefix_instructions (graph version)3760ported 1:1doneea74ea5
fix_cell_offsets3729ported 1:1doneea74ea5
_PyCfg_OptimizedCfgToInstructionSequence4026ported 1:1 (flowgraph_cfg_bridge.go)done9fb2206

A.2. Python/assemble.c (802 lines)

CPython functionCPython linegopy stateStatusCommit
same_location30inline in assembleLocationInfodonespec 1708
instr_size39instrSize in assemble_locations.godonespec 1708
assemble_init / assemble_free62 / 90replaced by Assembler struct lifecycledonespec 1708
write_except_byte98donedonespec 1708
assemble_emit_exception_table_item106donedonespec 1708
assemble_emit_exception_table_entry133donedonespec 1708
assemble_exception_table158done (assembleExceptionTable)donespec 1708
write_location_byte196donedonespec 1708
write_location_first_byte211donedonespec 1708
write_location_varint218donedonespec 1708
write_location_signed_varint226donedonespec 1708
write_location_info_short_form233donedonespec 1708
write_location_info_oneline_form246donedonespec 1708
write_location_info_long_form258donedonespec 1708
write_location_info_none270donedonespec 1708
write_location_info_no_column276donedonespec 1708
write_location_info_entry286donedonespec 1708
assemble_emit_location324donedonespec 1708
assemble_location_info337donedonespec 1708
write_instr369done (inline in assembleEmitInstr)donespec 1708
assemble_emit_instr413done (assembleEmitInstr in assemble.go)done686fd64
assemble_emit432done (assembleEmit in assemble.go)done686fd64
dict_keys_inorder458done (dictKeysInorder in assemble_makecode.go)done686fd64
compute_localsplus_info484done (computeLocalsplusInfo in assemble_makecode.go)done686fd64
makecode575done (makecode in assemble_makecode.go)done686fd64
resolve_jump_offsets675done (resolveJumpOffsets in assemble_jumps.go)done12ee36f
resolve_unconditional_jumps749done (resolveUnconditionalJumps in assemble_jumps.go)done12ee36f
_PyAssemble_MakeCodeObject779done (assembleMakeCodeObject in assemble_makecode.go)done686fd64

The location and exception writers were already clean from 1708. C.2 split the remaining inline pipeline. _PyAssemble_MakeCodeObject -> assemble_emit -> assemble_emit_instr are now three separate functions (assembleMakeCodeObject in assemble_makecode.go, assembleEmit and assembleEmitInstr in assemble.go). The jump-resolution helpers moved off the flat-sequence file into assemble_jumps.go, and makecode / compute_localsplus_info / dict_keys_inorder are factored out in assemble_makecode.go.

A.3. Python/compile.c driver

g = _PyCfg_FromInstructionSequence(u->u_instr_sequence);
_PyCfg_OptimizeCodeUnit(g, consts, ...);
_PyCfg_OptimizedCfgToInstructionSequence(g, &u->u_metadata, code_flags,
&stackdepth, &nlocalsplus,
&optimized_instrs);
co = _PyAssemble_MakeCodeObject(&u->u_metadata, const_cache, consts,
stackdepth, &optimized_instrs, nlocalsplus,
code_flags, filename);

compile/compiler.go:assembleUnit today calls OptimizeWithFlags (the flat-sequence pipeline) then Assemble. After C.3 lands, assembleUnit matches the four-call structure above.

Phase B: state capture

Per-phase compat tests need a way to dump the cfg between every pipeline stage and diff against a CPython-side dump. Phase B ships the format, the gopy hook, the matching CPython patch, and the diff harness that consumes both sides.

B.0. gopy substrate (DumpCfg + CfgPhaseHook)

ItemStatusCommit
compile.DumpCfg reproducible dump format (compile/flowgraph_cfg_dump.go)donee94fcfa
CfgPhaseHook callback type + cfgOptimizeCodeUnitWithHook driverdonee94fcfa
Unit tests covering the dump format and hook firing orderdonee94fcfa

B.1. CPython patch (per-phase dump + hook export)

The CPython side does not expose intermediate CFG state. We ship a small, additive patch to the bundled CPython checkout that re-enables the #if 0 dumpers, routes them through a PyUnicodeWriter, and fires a Python-level hook between each pass. Modules/_testinternalcapi.c grows a set_cfg_phase_hook(callback) thunk so the harness can install a Python callable.

ItemStatusCommit
test/cpython/patches/0001-cfg-phase-dump.patch (re-enables dumpers, adds hook registry + FIRE_PHASE calls, exposes thunk)donec9d380f
test/cpython/patches/dump_phases.py harness driving the patched interpreter into a <unit>.<phase>.cfg treedonec9d380f
test/cpython/patches/README.md (how to apply + rebuild + run the harness)donec9d380f
Patched build smoke-tested: python.exe dump_phases.py --src foo.py --out /tmp/cfg produces one file per (unit, phase)donec9d380f

Phase boundaries fired (each emits one dump per code unit):

#Phase nameWhere it fires (Python/flowgraph.c)
0entrystart of _PyCfg_OptimizeCodeUnit (line 3662)
1translate_jump_labels_to_targetsline 3665
2mark_except_handlersline 3666
3label_exception_targetsline 3667
4optimize_cfgline 3691
5remove_unused_constsline 3697
6add_checks_for_loads_of_uninitialized_variablesline 3700
7insert_superinstructionsline 3701
8push_cold_blocks_to_endline 3703
9resolve_line_numbersline 3704

The harness writes unit<idx>.<phase>.cfg per fire. Two entry fires for the same idx means a bug: each unit fires entry exactly once.

B.2. CPython oracle wrappers (L1 / L3 / L4)

Thin Python wrappers around the three _testinternalcapi entry points so the Go-side gate can compare instruction sequences and code objects without re-implementing CPython's introspection. assemble_oracle.py drives the regular compile() builtin instead of _testinternalcapi.assemble_code_object because that entry needs a full metadata dict (names / varnames / cellvars / freevars / fasthidden as dicts) that compiler_codegen does not expose, so re-building it in Python would defeat the point of a comparison oracle. compile() runs the same _PyAssemble_MakeCodeObject internally.

ItemStatusCommit
test/cpython/patches/oracle_common.py: shared opname, format_const, dump_instruction_sequence, parse_source helpersdonef960caf
test/cpython/patches/codegen_oracle.py: drives _testinternalcapi.compiler_codegen(ast, filename, optimize) -> unit000.l1.txtdonef960caf
test/cpython/patches/optimize_oracle.py: drives compiler_codegen then _testinternalcapi.optimize_cfg(seq, consts, nlocals) -> unit000.l3.txtdonef960caf
test/cpython/patches/assemble_oracle.py: walks every nested code object from compile(src, filename, "exec") -> unit<idx>.l4.txtdonef960caf
Each oracle prints stable InstructionSequence.get_instructions() tuples (L1/L3) or code-object fields (L4) for diffdonef960caf
Smoke-tested: def f(x): return x+1; print(f(2)) shows the LOAD_CONST 2 -> LOAD_SMALL_INT 2 rewrite between L1 and L3donef960caf

B.3. Diff harness (cfg_phase_parity_test.go)

ItemStatusCommit
Align gopy JumpTargetLabel numbering with CPython: 0-based NewLabel, -1 for no-label (today gopy starts at 1, uses 0 for none)donee11370a
compile.CompileWithCfgPhaseHook public entry that drives the cfg pipeline with a hookdone266f73e
test/gate/cfg_phase_parity_test.go (subprocess to patched CPython, run gopy with CompileWithCfgPhaseHook, diff per (unit, phase))donefbb22f7
test/gate/cfg_phase_corpus.txt (file list, grows from __future__.py outward)donefbb22f7
test/gate/cfg_phase_skip.txt (per-file reasons for L0 failures, linked to bug tracker)donefbb22f7
Workflow wiring: gate workflow builds the patched CPython once, caches it, then runs the testdonefbb22f7

B Reference: what CPython exposes vs what we add

HookCPython sourceWhat it returnsLevel
_testinternalcapi.new_instruction_sequence()Modules/_testinternalcapi.c:715empty _PyInstructionSequence-
_testinternalcapi.compiler_codegen(ast, filename, optimize, mode=0)Modules/_testinternalcapi.c:728(InstructionSequence, metadata_dict) from codegen.cL1
_testinternalcapi.optimize_cfg(seq, consts, nlocals)Modules/_testinternalcapi.c:754optimized InstructionSequence (full pipeline runs internally)L3
_testinternalcapi.assemble_code_object(filename, insts, metadata)Modules/_testinternalcapi.c:785types.CodeType from _PyCompile_AssembleL4
_testinternalcapi.set_cfg_phase_hook(callback) (B.1 patch)test/cpython/patches/0001-cfg-phase-dump.patchinstall a Python callback that receives every per-pass dumpL2

B Reference: the comparison ladder

Each level traps regressions earlier than the next so a divergence lands at the highest level where it occurs and the debug work points at one pass instead of the whole back-end.

LevelWhat's comparedSource of truthCaught when
L0ASTast.dump(tree, indent=2) on both sidesParser / preprocess diverges (caught by spec 1710 gates)
L1Pre-optimize instruction sequencecompiler_codegen -> get_instructions() tuplesCodegen-side AST emission diverges (spec 1714 surface)
L2Per-phase CFG dump (entry, after every pass)patched _PyCfg_OptimizeCodeUnit callbackAny individual optimization pass diverges
L3Post-optimize instruction sequenceoptimize_cfg -> get_instructions() tuplesThe CFG -> sequence bridge diverges
L4Code object byte-equalitymarshal.dumps(co) (spec 1713's gate)Assemble-side metadata, location, or constant order diverges

Reproducibility constraints: both sides run with PYTHONHASHSEED=0 (CPython interns string constants by content hash; without a fixed seed co_consts ordering drifts); the gate pins to one CPython tag (currently v3.14.5); tag bumps go through the spec 1707 audit so the patch and the gopy port move together.

B Reference: dump format

Each phase dump is just the block listing CPython's cfg_dump_to_string returns, with no per-unit header. The harness on both sides composes the header # cfg dump: <phase> outside the dump itself.

Per-block:

B<label.id>: [EH=<eh> CLD=<cold> WRM=<warm> NO_FT=<no_ft>] used: <iused>, depth: <startdepth>, preds: <predecessors> <return-marker>
[<idx:02d>] line: <lineno>, <opname> (<opcode>) <arg-or-target><jump-marker>
  • <return-marker> is "return " when the last instruction is RETURN_VALUE, else empty.
  • <arg-or-target> is "target: B<tgt.label.id> [<oparg>] " for HAS_TARGET opcodes (-1 when target is nil), "arg: <oparg> " for opcodes with HAS_ARG but no target, else empty.
  • <jump-marker> is "jump " when the opcode is a jump, else empty.
  • Numeric fields use the same %d width as CPython (no padding). Boolean fields render as 0 or 1.

No raw pointers, no addresses. gopy's compile.DumpCfg and the patch's cfg_dump_to_string must produce byte-identical strings. Any drift is a gopy bug and fails L2.

Phase C: port whole files

Each phase ports one CPython file in full. No file gets split across phases.

C.1. Finish Python/flowgraph.c

FunctionCPython lineLands inStatusCommit
convert_pseudo_conditional_jumps3478flowgraph_cfg_passes.godonea62563b
calculate_stackdepth (graph version)1352flowgraph_cfg_stackdepth.go (new)done3dfe874
build_cellfixedoffsets3711flowgraph_cfg_passes.godoneea74ea5
insert_prefix_instructions (graph version)3760flowgraph_cfg_passes.godoneea74ea5
fix_cell_offsets3729flowgraph_cfg_passes.godoneea74ea5
prepare_localsplus3768flowgraph_cfg_passes.godoneea74ea5
_PyCfg_OptimizedCfgToInstructionSequence4026flowgraph_cfg_bridge.godone9fb2206
Sweep: diff cfg-side function list vs Python/flowgraph.c; flag elisions and follow-ups--done9fb2206

C.1 sweep findings

A function-by-function diff of Python/flowgraph.c (109 named functions) against gopy's compile/flowgraph_cfg_*.go plus the flat-sequence files lined up cleanly except for two classes:

  1. Migration follow-ups for D.2. Const-folding helpers (evalIntBinop, evalConstUnaryop, safeMultiply, safePower, safeLshift, the safeMod variant, loadsConstValue, nopOutRemaining, appendConst) currently live in compile/flowgraph_passes.go. The cfg-side passes (basicblockFoldConstBinop et al. in flowgraph_cfg_passes.go) already call them, so they are not "missing"; they are in the file D.2 deletes. D.2 must move them to a cfg-side file (suggested flowgraph_cfg_constfold.go) before removing flowgraph_passes.go. Same applies to swaptimize / static-swap helpers (isSwappable, storesTo, swaptimize, emitSwapCycles, runSwaptimize, nextSwappableInstruction, applyStaticSwaps, staticSwapSafe, runApplyStaticSwaps).

  2. Inlined in Go. Trivial CPython helpers map to single Go expressions or methods and need no row of their own: is_jump, is_block_push, basicblock_last_instr, basicblock_next_instr, basicblock_returns, basicblock_nofallthrough, basicblock_exits_scope, basicblock_has_eval_break, basicblock_has_no_lineno, basicblock_append_instructions, basicblock_insert_instruction, copy_basicblock, next_nonempty_block, dump_instr, dump_basicblock, _PyCfgBuilder_DumpGraph, init_cfg_builder, _PyCfgBuilder_New/Free/CheckSize/UseLabel/Addop, cfg_builder_use_next_block, cfg_builder_current_block_is_terminated, cfg_builder_maybe_start_new_block, cfg_builder_check, no_redundant_nops, no_redundant_jumps, get_max_label, push_except_block, pop_except_block, except_stack_top, make_except_stack, copy_except_stack. These live in flowgraph_cfg.go as cfgBuilder / basicblock methods or are one-liners inside their callers.

  3. No truly missing entries. Every CPython flowgraph.c function has a 1:1 gopy port or a documented inline equivalent. The audit table A.1 stays authoritative for the optimizer-stage functions; the helpers in (1) are tracked here so D.2 can resolve them atomically with the file deletions.

  4. Codegen-side MAKE_CELL / COPY_FREE_VARS removal. Until C.1 landed, gopy's codegen emitted MAKE_CELL for each cell var and COPY_FREE_VARS at the start of every function / class / inner comprehension body (see the deleted emitMakeCellAndCopyFree in compile/codegen_stmt_funclike.go). CPython only emits MAKE_CELL inline for inlined comprehensions (Python/codegen.c:4660 codegen_push_inlined_comprehension_locals); for everything else the prologue is produced by insert_prefix_instructions during prepare_localsplus. Now that C.1's prepare_localsplus runs unconditionally, the codegen emissions were duplicate prologue ops and the fix_cell_offsets rewrite landed on top of the wrong opargs. The codegen paths in codegen_stmt_funclike.go, codegen_expr_comp.go, codegen_annotations.go, and codegen_class.go no longer emit MAKE_CELL / COPY_FREE_VARS; enterScope pre-populates u.CellVars from the symtable so the cfg pipeline sees every cell name. The VM handlers (MAKE_CELL, LOAD_CLOSURE, LOAD_DEREF, STORE_DEREF, DELETE_DEREF, LOAD_FROM_DICT_OR_DEREF) now treat oparg as the final localsplus offset post fix_cell_offsets, matching Python/bytecodes.c:1862.

C.2. Finish Python/assemble.c

SplitLands inStatusCommit
makecode, compute_localsplus_info, dict_keys_inorder, _PyAssemble_MakeCodeObject as separate functionsassemble_makecode.gopending-
resolve_jump_offsets, resolve_unconditional_jumps rewritten over cfgBuilderassemble_jumps.gopending-
Public surface (Assemble) plus assemble_emit_instr / assemble_emitassemble.gopending-

C.3. Driver port

ItemLands inStatusCommit
optimize_and_assemble_code_unit four-call sequence ported 1:1 from Python/compile.c:1411compile/compiler_assemble.gopending-
compile/compiler.go:assembleUnit rewired to delegate to the new functioncompile/compiler.gopending-

Phase D: wire and retire

StepWhatStatusCommit
D.1Flip assembleUnit to call the C.3 driver instead of OptimizeWithFlagsdonec124587
D.2Delete compile/flowgraph_passes.go, flowgraph_jumps.go, flowgraph_except.go, flowgraph_locals.go, flowgraph_stackdepth.go (flowgraph.go shrinks to Info + ExceptHandler)done8d12ecf
D.3Rename / split cfg files so the filename map in Goal holds (flowgraph_cfg.go becomes flowgraph_cfg.go + flowgraph_cfg_passes.go + flowgraph_cfg_locals.go + flowgraph_cfg_stackdepth.go)done8d12ecf

Phase E: gates

Each phase keeps existing gates green and adds its own:

GateOwnerStatusCommit
test/gate/TestDisParityLibspec 1713green-
test/gate/TestDisParityspec 1713green-
compile/... package testsspec 1716 (existing)green-
test/gate/cfg_phase_parity_test.go (L2)this spec, B.3greenfbb22f7
test/gate/codegen_parity_test.go (L1)this spec, B.2green6004c1c
test/gate/assemble_parity_test.go (L3 / L4)this spec, B.2 + spec 1713 byte-eqgreen6004c1c

Once D lands, the byte-equality work in 1713 can resume on a substrate that matches CPython, so divergences localize to the pass that produced them rather than to the substrate translation.

Out of scope

  • Marshal byte layout. Spec 1713.
  • Codegen-side instruction sequence DSL. Spec 1714.
  • Specializer / inline cache layout. Spec 1712.
  • The assemble location table format itself (already 1:1 from 1708; this spec only restructures the surrounding code so the file boundary matches CPython).