Files
TerminalBasic/openspec/changes/archive/2026-09-02-phase-2-bytecode-vm/design.md
Chili Palmer a6f005d023 OpenSpec: phase-2-bytecode-vm archiviert, Delta-Specs in Hauptspecs gesynct
- Neue Hauptspecs: bytecode-kompilat, vm-ausfuehrung,
  vm-fehlerbehandlung, konsolen-basisbibliothek
- Erweitert: sprach-frontend (Typisiertes HIR als Sema-Ausgabe),
  kompat-testkorpus (Laufzeitvergleich, Phase-2-Korpusabdeckung)
- Change nach openspec/changes/archive/2026-09-02-phase-2-bytecode-vm
  verschoben; openspec validate --all: 9/9

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-02 11:33:51 +02:00

6.4 KiB
Raw Blame History

Design — Phase 2: Bytecode und VM

Context

Motivation siehe proposal.md („Why"). Ausgangslage: tb-frontend liefert AST + Diagnosen, wirft aber alle Sema-Ergebnisse (Typen, Slots, Konstanten) weg; tb-vm ist ein Stub. Rahmenbedingungen aus PLAN.md und docs/tbvm-design.md: Instant-Compile (Single-Pass, keine Optimierungspasses), Interpreter-Tempo in Lua-Klasse, Werte-Modell als Tagged Enum mit Rc (kein GC — der Dialekt kennt keine Zyklen), step()-basierte, unterbrechbare VM. Entscheidungen dieses Dokuments wurden am 2026-09-02 in einer Explore-Session getroffen und bestätigt.

Goals / Non-Goals

Goals:

  • Durchgängige Pipeline Quelltext → HIR → Bytecode → Ausführung mit byte-genauer Korpus-Ausgabe.
  • Debugger-taugliche VM-Architektur, ohne dass der Debugger selbst (Phase 5) schon gebaut wird.
  • Builtin-ABI, die Phase 3 nur noch „auffüllt".

Non-Goals:

  • Mehrmodul-Linking und COMMON-Verknüpfung über Moduleinheiten (die Prozedurtabelle ist modul-qualifiziert angelegt, verknüpft wird ab Phase 5 mit .MAK-Projekten).
  • Datei-E/A, Ereigniszustellung (Hooks über Tick-Flag-Wort genügen), tbc build --exe, Optimierungspasses jeder Art.

Decisions

D1 — Typisiertes HIR als Sema-Ausgabe (statt Doppel-Resolution oder Side-Tables)

Die Sema wird vom reinen Checker zum Lowering-Pass: Ausgabe ist ein eigener HIR-Baum (Namen → Slots, Typen an jedem Ausdruck, Konstanten gefaltet, implizite Konvertierungen als explizite Knoten). Verworfen: (a) Codegen resolved selbst nochmal — dupliziert die Typisierungslogik, Divergenz-Bugs; (b) Side-Tables am AST — der AST hat keine Node-IDs, Pointer-Keys sind fragil. Das HIR hält das Single-Pass-Budget ein (alle Pässe linear, keine globale Analyse) und macht den Codegen zum dummen Tree-Walk.

D2 — Monomorpher Opcode-Satz, Konvertierungen als eigene Opcodes

Da das HIR jede Konvertierung materialisiert, sind alle Arithmetik-, Vergleichs- und Konvertierungs-Opcodes typrein (ADD_I16, ADD_F64, CONV_I16_F64, STR_EQ …). Kein Tag-Dispatch in heißen Pfaden; die Konvertierungsmatrix lebt an genau einer Stelle (HIR-Erzeugung), der Interpreter bleibt eine enge match-Schleife. Preis: breiter Opcode-Satz (~150200) — Fleiß, keine Komplexität. Encoding: Opcode 1 Byte; Slots u16; Konstantenpool-Index u16 (Wide-Variante bei Bedarf); Sprünge u32-absolut innerhalb der Prozedur (fixup-freundlich).

D3 — Host-Trait mit blockierenden Aufrufen (statt Yield-Modell)

Alle Konsolenwirkungen laufen über ein Host-Trait (Ausgabe, Zeile lesen, Abbruchprüfung). Host-Calls dürfen blockieren und sind aus VM-Sicht atomar; Ticks (Breakpoints, Einzelschritt, Abbruch) greifen an Anweisungsgrenzen. Verworfen: VM „yieldet" bei E/A und der Einbetter erfüllt Requests — debuggerfreundlicher, aber deutlich komplexer; für den IDE-Debugger reicht die Anweisungsgranularität. Implementierungen: Konsolen-Host (tbc run), Capture-Host (Test-Harness), später Zellenpuffer-Host (Phase 3/5).

D4 — Fehlerbehandlung: Handler-Zustand modul-global + pro Frame

ON ERROR GOTO setzt den modulweiten Handler, ON LOCAL ERROR einen Frame-lokalen, der ihn verdeckt. Beim Fehler: aktiven Handler suchen (erst Frame-Kette nach LOCAL-Handlern, sonst Modul-Handler), Stack bis zum Handler-Frame abwickeln, gemerkten Anweisungs-Offset + Frame für RESUME sichern, ERR/ERL setzen. Fehler bei aktiver Behandlung → sofortiger Abbruch (keine Kaskade). Grundlage ist die Original-Hilfe (Topics ON ERROR, RESUME); ein Emulator-Spike ist nur nötig, falls die Doku Lücken lässt.

D5 — GOSUB-Stack pro Frame

GOSUB legt die Rücksprungadresse auf einen separaten Stack im aktuellen Frame (kein Call-Frame); RETURN bedient zuerst diesen Stack. Prozedurende mit nicht-leerem GOSUB-Stack verwirft ihn (wie beim Vorbild beim Verlassen der Prozedur).

D6 — Runner-Verhalten STOP/CONT

STOP außerhalb der IDE terminiert mit Meldung + Exit-Code ≠ 0; END/ Programmende → Exit-Code 0. CONT ist Direktmodus-Konzept der IDE (Phase 5) — die step()-Architektur liefert Suspend/Resume dort gratis.

D7 — .tbc mit eigenem Writer/Reader (kein serde)

Format bleibt stabil, dokumentiert und unabhängig von Crate-Versionen; Abschnittstabelle erlaubt spätere Abschnitte (FORMS ab Phase 4) ohne Versionsbruch. Prozedurtabelle trägt von Beginn an die Modulkennung.

D8 — Benchmarks als früher Bestandteil, nicht als Abschluss

Das Compile-Budget-Harness (generiertes ~50k-Zeilen-Projekt) entsteht früh in der Phase, damit Architekturfehler (z. B. quadratische Fixup-Listen) sofort auffallen. Criterion-Benches für VM-Durchsatz (Schleifen/Strings); Ergebnisse werden in docs/tbvm-design.md festgehalten. Ein Dispatch-Mikrobenchmark vorab wurde verworfen (vorzeitige Optimierung — gemessen wird am echten Interpreter).

Risks / Trade-offs

  • [Sema-Umbau destabilisiert Phase-1-Ergebnis] → Diagnostik-Verhalten ist durch die 33 Frontend-Tests + Korpus fixiert; Umbau gilt erst als fertig, wenn alle Bestandstests grün sind.
  • [Opcode-Breite macht Interpreter-Match unübersichtlich] → Opcodes systematisch gruppieren (Makro/Tabelle generiert Emit + Dispatch konsistent); Dokumentationspflicht in tbvm-design.md.
  • [Konvertierungsmatrix enthält falsch rekonstruierte Zellen] → jede Zelle mit Fundstelle in der Original-Hilfe belegen; unklare Zellen als TODO verify im Korpus markieren (Konvention besteht).
  • [PRINT-Zahlformatierung (SINGLE/DOUBLE-Stellen) weicht ab] → byte-genaue Korpustests decken Randfälle ab; Formatierungsregeln aus der Original-Hilfe ableiten, Abweichungen dokumentieren.
  • [Blockierende Host-Calls verhindern Abbruch während INPUT] → akzeptiert für Phase 2 (Abbruchprüfung obliegt dem Host); die Ereignisschleifen-Architektur (Phase 4) kann den Host asynchroner gestalten, ohne die VM zu ändern.

Migration Plan

Rein additiv (neue Crate-Inhalte, neue Sema-Ausgabe neben bestehender Diagnostik). Kein Rollback-Plan nötig; PLAN.md-Haken und docs/tbvm-design.md-Fortschreibung erfolgen beim Abschluss der Phase.

Open Questions

  • Exakte Ergebnistyp-Regel für ^ sowie Stellenzahl der SINGLE/DOUBLE-Textdarstellung: wird beim Ausformulieren der Matrix aus der Original-Hilfe übernommen; ändert weder Ansatz noch Task-Zuschnitt.
  • Ob ERL beim Vorbild auch Zeilennummern aus GOSUB-Zielen o. ä. Sonderfällen meldet: bei Doku-Lücke entscheidet ein kurzer Emulator-Check; betrifft nur eine Matrix-/Handlerzelle.