feat(vm): unified-ISA (System B) execution path — Phase 1 of A/B reconciliation - #28
Merged
Merged
Conversation
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
…op vars (lint pass 3a)
…-Scott step() (u/v vs U/V), N806/B007/RUF manual rules; suite 2,740 green
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
opcodes.py, live interpreter):HALT=0x80,TELL=0x60.isa_unified.py+signal_compiler.py):HALT=0x00,TELL=0x50.Consequence: every A2A opcode emitted by the signal compiler mis-executed —
TELL (0x50)decoded asVLOAD(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) —UnifiedOpIntEnum derived fromisa_unified.py'sbuild_unified_isa()(single source of truth) + format table.src/flux/vm/interpreter.py—Interpreter(..., isa="system_a"|"unified");_step()routes to a new_step_unified()whenisa="unified". Implements System B dispatch for control, single-reg, MOVI/MOVI16, integer/float arithmetic, compare, memory/move, branches, and the A2A range0x50-0x5F(Format E, register-based).imm16decoded big-endian to match the compiler + conformance vectors.tests/test_conformance_unified.py(new) — the previously-missing executable conformance gate: runs all concreteTEST_VECTORSthrough the unified interpreter, plus an end-to-end signal→VM A2A regression test (TELL/ASK/BCAST dispatch as A2A, not SIMD).Guardrails honored
pytest tests/→ 2676 passed (was 2651; +25 new).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.