- 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>
130 lines
6.4 KiB
Markdown
130 lines
6.4 KiB
Markdown
# 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 (~150–200) — 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.
|