Skip to content

feat(vm): unified-ISA (System B) execution path — Phase 1 of A/B reconciliation - #28

Merged
SuperInstance merged 12 commits into
mainfrom
reconcile/isa-unified-interpreter
Aug 22, 2026
Merged

SuperInstance merged 12 commits into
mainfrom
reconcile/isa-unified-interpreter

Conversation

@SuperInstance

Copy link
Copy Markdown
Owner

Summary

Phase 1 of the FLUX System A/B opcode reconciliation (issue #7). Adds an opt-in unified-ISA (System B) execution path to the interpreter, so it can correctly run converged bytecode without disturbing the legacy System A numbering (which remains the default and fully intact).

The divergence this fixes

  • System A (opcodes.py, live interpreter): HALT=0x80, TELL=0x60.
  • System B (isa_unified.py + signal_compiler.py): HALT=0x00, TELL=0x50.

Consequence: every A2A opcode emitted by the signal compiler mis-executed — TELL (0x50) decoded as VLOAD (SIMD vector load). This PR gives the interpreter a correct System B dispatch so that byte stops mis-decoding.

What changed

  • src/flux/bytecode/opcodes_unified.py (new) — UnifiedOp IntEnum derived from isa_unified.py's build_unified_isa() (single source of truth) + format table.
  • src/flux/vm/interpreter.pyInterpreter(..., isa="system_a"|"unified"); _step() routes to a new _step_unified() when isa="unified". Implements System B dispatch for control, single-reg, MOVI/MOVI16, integer/float arithmetic, compare, memory/move, branches, and the A2A range 0x50-0x5F (Format E, register-based). imm16 decoded big-endian to match the compiler + conformance vectors.
  • tests/test_conformance_unified.py (new) — the previously-missing executable conformance gate: runs all concrete TEST_VECTORS through the unified interpreter, plus an end-to-end signal→VM A2A regression test (TELL/ASK/BCAST dispatch as A2A, not SIMD).

Guardrails honored

  • No opcode mapping deleted — both systems' tables preserved and annotated.
  • System A remains the default; all existing bytecode/tests unaffected.
  • pytest tests/2676 passed (was 2651; +25 new).
  • Lint-neutral (no new ruff violations vs main).

Not in this PR (follow-up phases)

Compiler/encoder/assembler System B modes, full conformance-vector A2A coverage, test migration, docs table, and the final default-ISA cutover — see the plan in memory/flux-ab-plan-2026-08-21.md.

SuperInstance added 12 commits August 21, 2026 19:47
Add an opt-in ISA selector so the interpreter can correctly execute
converged System B bytecode (isa_unified.py numbering) without disturbing
the legacy System A numbering, which remains the default.

- new src/flux/bytecode/opcodes_unified.py: UnifiedOp IntEnum derived from
  isa_unified.py's build_unified_isa() (single source of truth) + format table.
- Interpreter(..., isa="system_a"|"unified"); _step() routes to _step_unified()
  when isa="unified".
- _step_unified() implements System B dispatch: control, single-reg, MOVI/MOVI16,
  integer/float arithmetic, compare, memory/move, branches, and the A2A range
  (0x50-0x5F, Format E register-based) - closing the "TELL decodes as VLOAD"
  divergence. imm16 is big-endian (matches signal_compiler + conformance vectors).
- tests/test_conformance_unified.py: executable conformance gate - runs all
  concrete TEST_VECTORS through the unified interpreter plus an end-to-end
  signal->VM A2A regression test.

No opcode mapping deleted; System A remains default and fully intact.
2676 tests pass.
…dianness/A2A spec landmines

Phase 2 of the A/B reconciliation (see memory/flux-ab-plan-2026-08-21.md):

Landmine 1 - JZ encoding: executable truth is Format F (op, rd, imm16),
not Format E as isa_unified.py labeled. Evidence: signal_compiler
back-patches a big-endian imm16 (parallel to JMP 0x43 Format F) and the
asm-text vectors use two-operand 'JNZ R2, done' style. isa_unified.py
JZ/JNZ/JLT/JGT relabeled Format F; interpreter _step_unified() now decodes
rd + _fetch_i16_be for the branch family.

Landmine 2 - endianness: System B imm16 is big-endian in the executable
ground truth (signal_compiler._emit_format_f; MOVI16 vector
[0x40, rd, 0x10, 0x00] = 4096). isa_unified.py header corrected; the
cross-assembler's unified target packs imm16 big-endian (System A mode
remains little-endian, byte-for-byte unchanged).

Landmine 3 - A2A register population: converged spec (TELL 'Send rs2 to
agent rs1, tag rd'; ASK 'resp→rd') requires operand registers to carry
values. signal_compiler now loads tag/agent/data into rd/rs1/rs2 before
each A2A opcode (symbolic names interned to deterministic 15-bit ids);
BCAST's rs1 references a never-loaded register so the packed agent field
is 0 (all); _dispatch_a2a gains optional rd so ASK responses land in rd
(System A R0 convention unchanged when rd is None).

Toolchain System B modes:
- SignalCompiler(isa=...) selector mirroring the interpreter's
  normalization ('system_b' -> 'unified'; default 'unified' preserves its
  historical emission; 'system_a' rejected - Signal is System B-native).
- CrossAssembler(target='system_a'|'unified'): unified defs derived from
  build_unified_isa() (opcodes_compat.UNIFIED_OPCODE_DEFS); unified
  emitters for formats A-G with BE imm16 and PC-relative jumps.

19 new tests in tests/test_toolchain_unified.py. Full suite: 2695 passed.
No opcode mapping deleted; System A paths untouched.
…mission executed & asserted

Phase 3 of the A/B reconciliation. Extends tests/test_conformance_unified.py
beyond dispatch assertions to full SEMANTICS on the compiler side:

- 16 parametrized Signal→unified-VM semantic vectors: let small/large/
  negative/alias (MOVI/MOVI16-BE/MOV), add/sub/mul/div/mod, chained add,
  literal materialization, eq/lt, and/or/xor — asserting final register
  values, not byte presence.
- Control-flow semantics: if-true/if-else branch selection through JZ
  (Format F, BE imm16) and a counted-loop vector.

FIX (pre-existing, exposed by the loop vector): _compile_loop computed the
LOOP back-offset without the +4 instruction size, so LOOP jumped to itself
and the body ran exactly once regardless of count. No prior test executed
LOOP through a VM, so the bug was invisible. Now matches _resolve_jumps'
target - (offset + 4) convention.

- A2A operand-semantics vectors: TELL payload (tag, agent, data); ASK
  'resp→rd' (handler result in the destination register); DELEG operands;
  BCAST fleet-wide zero agent field; a mixed program asserting event order,
  computed data flow into TELL, and the ASK response landing site.
- Cross-assembler unified-mode conformance: MUL/SUB/MOVI16 battery executed
  on the unified VM; dual-mode divergence test proving the same source
  assembles to different bytes per target and each output runs correctly
  on its own VM.

Full suite: 2721 passed. No System A test or mapping deleted.
…notate legacy suites

Phase 4 of the A/B reconciliation. Per the plan's rule (no mapping
deleted), every System A test is retained and now carries an in-place
annotation noting it intentionally pins the legacy numbering, with a
pointer to the unified equivalents.

- new tests/test_dual_mode_equivalence.py: 18 vectors pairing each core
  ISA semantic (NOP/HALT, MOV, ADD/SUB/MUL/DIV/MOD, INC/DEC, PUSH/POP,
  JMP, JZ taken/not-taken, JNZ, LOAD/STORE round-trip, CALL/RET
  subroutine) encoded in BOTH numberings, executed on their respective
  interpreter paths, asserting identical observable results. Includes
  A2A TELL in both encodings (Format G vs Format E) and a cross-mode
  rejection test proving the selector prevents silent misdecode.
- annotated (retained as-is): tests/conftest.py, test_vm.py,
  test_vm_complete.py, test_cross_assembler.py, test_formats.py,
  test_debugger.py, test_security.py, test_bytecode_verifier.py.

Full suite: 2739 passed (baseline at phase start: 2676).
…aults to unified ISA (System B), System A preserved selectable (legacy byte-compat); RECONCILIATION.md timeline; JZ-Format-F + BE-imm16 corrections documented; examples/toolchain updated
…ames) — lint green for A/B reconciliation
…, annotations, style); 552 manual-rule errors remain
…-Scott step() (u/v vs U/V), N806/B007/RUF manual rules; suite 2,740 green
@SuperInstance
SuperInstance merged commit 3a30c42 into main Aug 22, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant