Files
RustAst/docs/VM 1.md
Michael Schimmel 494bf554d2 Old Docs added
2026-02-20 10:09:22 +01:00

6.4 KiB


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)

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.