145 lines
6.4 KiB
Markdown
145 lines
6.4 KiB
Markdown
|
|
-----
|
|
|
|
# High-Performance Backend (Bytecode VM)
|
|
|
|
**Datum:** 21.11.2025
|
|
**Status:** Entwurf / Planungsphase
|
|
**Zielarchitektur:** Stack-based Virtual Machine (ähnlich Lua 5.x / Python)
|
|
|
|
## 1\. Motivation & Architektur-Ziele
|
|
|
|
* **Status Quo:** Der aktuelle AST-Evaluator ist mächtig, flexibel und ideal für Debugging, leidet aber unter "Pointer Chasing" (Cache Misses) und Rekursions-Overhead (CPU Stack Frames).
|
|
* **Ziel:** Maximale Ausführungsgeschwindigkeit für typisierte, numerische Operationen bei gleichzeitiger Beibehaltung der vollen Sprachflexibilität (Closures, dynamische Typen).
|
|
* **Strategie:** Implementierung einer linearen Stack-Maschine.
|
|
* **Compiler:** Transformiert den *spezialisierten* AST in ein flaches Array von Instruktionen.
|
|
* **VM:** Eine "Dispatch Loop", die Instruktionen abarbeitet und Delphi-Funktionen für komplexe Datentypen (Records, Series) als "Fernsteuerung" nutzt.
|
|
|
|
-----
|
|
|
|
## 2\. Phasenplan
|
|
|
|
### Phase 1: Das Fundament (Datenstrukturen)
|
|
|
|
**Ziel:** Definition der binären Repräsentation des Codes und des Laufzeit-Stacks.
|
|
|
|
* **Task 1.1: Stack-Architektur definieren**
|
|
* Implementierung von `TStackSlot` als **Tagged Union** (Variant Record).
|
|
* Größe: Max. 16 Bytes (Alignment-freundlich).
|
|
* Typen: `skEmpty`, `skInt64`, `skDouble`, `skBoolean`, `skPointer` (für RefCounted Objekte).
|
|
* **Task 1.2: OpCodes definieren (`TOpCode`)**
|
|
* Kategorisierung in: Stack Ops, Arithmetik (Getrennt nach `_Int`, `_Flt`, `_Dyn`), Flow Control, Calls.
|
|
* **Task 1.3: Instruktions-Format (`TInstruction`)**
|
|
* Record mit `OpCode` (Byte/Enum) und Argumenten (`Arg1`, `Arg2`, `Arg3`: Integer).
|
|
* **Task 1.4: Container (`TBytecodeChunk`)**
|
|
* Klasse, die `TArray<TInstruction>`, `TArray<TDataValue>` (Constant Pool) und Metadaten (MaxStackSize) hält.
|
|
|
|
### Phase 2: Der Compiler (Core Arithmetic)
|
|
|
|
**Ziel:** Kompilierung einfacher mathematischer Ausdrücke ohne Kontrollfluss.
|
|
|
|
* **Task 2.1: Compiler-Gerüst (`TBytecodeCompiler`)**
|
|
* Implementierung als `TAstVisitor` (oder `IAstVisitor`).
|
|
* Verwaltung des `TBytecodeChunk`.
|
|
* Verwaltung einer virtuellen "Stack Height" zur Berechnung von `MaxStackSize`.
|
|
* **Task 2.2: Konstanten laden**
|
|
* Visitor für `VisitConstant`.
|
|
* Logik: Konstante im Pool suchen/einfügen $\to$ `opLdConst <Index>` emittieren.
|
|
* **Task 2.3: Arithmetik & Typ-Spezialisierung**
|
|
* Visitor für `FunctionCall` (Spezialfall: Binäre Operatoren).
|
|
* Nutzung der `IStaticType`-Informationen aus dem AST.
|
|
* Entscheidunglogik:
|
|
* Sind Operanden `Int64`? $\to$ `opAddInt`.
|
|
* Sind Operanden `Double`? $\to$ `opAddFlt`.
|
|
* Sonst $\to$ `opAdd` (Fallback).
|
|
|
|
### Phase 3: Die Virtual Machine (The Engine)
|
|
|
|
**Ziel:** Ausführung des in Phase 2 generierten Codes.
|
|
|
|
* **Task 3.1: Die VM-Klasse (`TVM`)**
|
|
* Aufbau des `OperandStack` (Array of `TStackSlot`).
|
|
* Register: `IP` (Instruction Pointer), `SP` (Stack Pointer), `BP` (Base/Frame Pointer).
|
|
* **Task 3.2: Dispatch Loop**
|
|
* Implementierung der `Run(Chunk)` Methode.
|
|
* Großes `case Instruction.OpCode of ...`.
|
|
* **Task 3.3: Implementierung der Core-OpCodes**
|
|
* `opLdConst`: Kopieren von Constant-Pool auf Stack.
|
|
* `opAddInt`: Roher Zugriff auf `Stack[SP].AsInt`. (Performance-kritisch\!).
|
|
* `opAdd`: Generischer Pfad (Unboxing, Operation, Boxing).
|
|
|
|
### Phase 4: Variablen & Kontrollfluss
|
|
|
|
**Ziel:** Unterstützung von `if`, lokalen Variablen und einfachen Schleifen (`recur`).
|
|
|
|
* **Task 4.1: Lokale Variablen**
|
|
* Mapping im Compiler: AST `SlotIndex` $\to$ Stack-Relativ-Index.
|
|
* OpCodes: `opLdLocal <Idx>`, `opStLocal <Idx>`.
|
|
* **Task 4.2: Sprünge (Jumps)**
|
|
* Compiler: Handling von `VisitIfExpression`.
|
|
* Logik: Emittieren von Platzhalter-Jumps, Patching der Sprungziele nach Generierung des Branches.
|
|
* OpCodes: `opJmp`, `opJmpFalse`.
|
|
* **Task 4.3: TCO / Recur**
|
|
* `VisitRecurNode`: Generierung von `Move` Instruktionen (Argumente an Position der Parameter kopieren) + `opJmp` zum Start der Funktion.
|
|
|
|
### Phase 5: Funktionen & Closures (Die Kür)
|
|
|
|
**Ziel:** First-Class Functions und Upvalue-Handling.
|
|
|
|
* **Task 5.1: Funktions-Prototypen**
|
|
* Erweiterung `TBytecodeChunk` um Sub-Chunks (Prototypen für innere Funktionen).
|
|
* **Task 5.2: Upvalue-Analyse Integration**
|
|
* Nutzung der Ergebnisse des Binders/UpvalueAnalyzers.
|
|
* Compiler muss wissen, welche Variable Stack-Local ist und welche ein Upvalue ist.
|
|
* **Task 5.3: Closure-Instanziierung**
|
|
* OpCode: `opClosure <ProtoIdx>`.
|
|
* VM: Erzeugt `TClosure` Objekt, sammelt "Capture"-Variablen vom Stack ein (Hoisting) und speichert sie im Closure-Objekt.
|
|
* **Task 5.4: Calls (`opCall`)**
|
|
* VM: Stack-Frame Management (Sichern von `BP`, `IP` auf dem Call-Stack).
|
|
* Umschalten des aktiven Chunks.
|
|
|
|
### Phase 6: Interop & komplexe Typen
|
|
|
|
**Ziel:** Brückenschlag zur existierenden Delphi-Logik.
|
|
|
|
* **Task 6.1: Native Calls (`opCallNative`)**
|
|
* Aufruf von RTL-Funktionen via Funktionszeiger.
|
|
* Konvertierung `TStackSlot` $\leftrightarrow$ Argumente.
|
|
* **Task 6.2: Records & Series**
|
|
* OpCodes: `opNewRecord`, `opSeriesAdd`.
|
|
* VM: Ruft direkt `TScalarRecord.Create` etc. auf. Hier wird keine Logik dupliziert, nur delegiert.
|
|
|
|
-----
|
|
|
|
## 3\. Technische Eckpfeiler
|
|
|
|
### Das Datenmodell (VM Stack)
|
|
|
|
```pascal
|
|
type
|
|
TStackSlot = record
|
|
case Kind: TDataValueKind of
|
|
vkOrdinal: (AsInt: Int64);
|
|
vkFloat: (AsFloat: Double);
|
|
vkObj: (AsPtr: Pointer); // IInterface / TObject / String
|
|
end;
|
|
```
|
|
|
|
### Die Optimierungs-Strategie
|
|
|
|
1. **Binder/TypeChecker:** Leisten die Vorarbeit (Auflösung von Namen zu Indizes, Typ-Inferenz).
|
|
2. **Compiler:** Entscheidet statisch über OpCodes (`ADD_INT` vs `ADD`).
|
|
3. **VM:** Führt "blind" und schnell aus. Typprüfungen nur im `_DYN` Pfad oder als Assert.
|
|
|
|
-----
|
|
|
|
## 4\. Nächste Schritte (Todo)
|
|
|
|
1. [ ] Anlegen der Unit `Myc.Bytecode.Types` (Definition OpCodes, Instruction, StackSlot).
|
|
2. [ ] Implementierung `TBytecodeCompiler` (Skeleton: Nur Constants & Return).
|
|
3. [ ] Implementierung `TVM` (Skeleton: Stack setup, Dispatch loop für Const/Ret).
|
|
4. [ ] Erster Integrationstest: `42` kompiliert $\to$ VM führt aus $\to$ Resultat 42.
|
|
5. [ ] Erweiterung um `BinaryOp` (Add/Sub) inkl. Typ-Spezialisierung.
|
|
|
|
-----
|