Zum Inhalt

Jade VM — Instruction Set v0.1 (Core + Extended)

Normativ (v0.1)

Diese Spezifikation definiert das ausführbare Kern-Instruktionsset der Jade VM und die Regeln, wie höhere Konstrukte in dieses Set abgesenkt werden.
Ältere Entwürfe (z.B. Jade VM – Core Instruction Set (Draft, with HALT)) gelten als archivierter Kontext und sind nicht normativ.


0. Ziele und Nicht-Ziele

Ziele

  • Ein kleines, stabiles Execution-Core-Instruktionsset, das die VM direkt ausführt.
  • Eine klare Trennung: Extended Layer existiert nur in IR/Frontend und wird vor Verifikation vollständig zu Core abgesenkt.
  • Ein Operand- und Encoding-Modell, das:
  • kompakt ist,
  • Debug-Infos getrennt hält,
  • Optimierungen wie Inline Caches / Quickening ermöglicht, ohne das Opcode-Set zu sprengen.

Nicht-Ziele

  • Kein „Opcode pro Feature“-Zoo (Profiling/Bench/Tests sind kein VM-Opcode-Thema).
  • Keine Festlegung auf JIT/AOT. Die Spez beschreibt das Semantikmodell, nicht die Codegen-Strategie.

1. Begriffe

  • Core (Execution Core): minimaler Befehlssatz, der von der VM direkt ausgeführt wird.
  • Extended Layer: höhere Instruktionen (Sugar, IR-Komfort), die vor Verifikation vollständig in Core + Runtime-Calls transformiert werden.
  • Verifier: prüft ein Modul nach dem Lowering auf Invarianten (CFG, Typ-/Operandregeln, Stack/Reg-Grenzen, Termination).
  • Function Prototype (Proto): kompiliertes Funktionsobjekt (Bytecode + Const-Pool + Register-Layout + Debug-Info).

2. Architektur: Core vs Extended

Die Trennung ist verpflichtend:

  • Extended-Instruktionen dürfen im Frontend/IR existieren.
  • Vor Verifikation und Ausführung müssen sie komplett eliminiert sein.
  • Plugins dürfen Extended erweitern, aber nicht den Core.

(Design-Konstitution: Core vs Extended ist ein zentrales Stabilitätsprinzip.)


3. Ausführungsmodell (Register-VM)

3.1 Registerdatei und Frames

  • Jede Funktion besitzt eine feste Anzahl Register R0..R(n-1).
  • Ein Call erzeugt einen neuen Frame mit eigener Registerdatei.
  • Rückgabewert ist ein einzelnes JadeValue.

Calling Convention v0.1 (einfach, robust):

  • Call { dst, func, argsBase, argc }
  • func enthält ein Callable (Funktionsproto, Closure, native/builtin).
  • Argumente liegen in aufeinanderfolgenden Registern ab argsBase.
  • Ergebnis wird in dst geschrieben.

3.2 Werte

JadeValue ist ein getaggter Wertcontainer (Immediate oder Handle in die Memory Engine).
Details (Handles, Arenas, Policies) sind in den Runtime-Spezifikationen beschrieben.


4. Encoding & Operanden

4.1 Grundformat

Ein Core-Instruktionsstream besteht aus:

  • opcode: u8 oder u16 (Implementationswahl)
  • 0..k Operanden (fixed oder variable length)

Normative Anforderung:

  • Decoding muss deterministisch sein.
  • Verifier muss jede Instruktion ohne Ausführung vollständig validieren können.

4.2 Operanden-Typen

  • Reg(u16) — Registerindex
  • Const(u32) — Index in den Konstantenpool der Funktion (oder des Protos)
  • Jump(i32) — relativer Sprung-Offset oder BlockId(u32) (Implementationswahl, aber im Modul konsistent)
  • Upval(u16) — Upvalue-Index innerhalb einer Closure-Umgebung
  • Imm — kleine Immediate-Varianten (optional, s.u.)

Hinweis: Viele „kleine Convenience“-Loads (LoadNull, LoadI8, …) sind als Extended erlaubt.


5. Debug-Informationen (SourceLoc)

Core-Instruktionen tragen semantisch ein SourceLoc, aber nicht inline im Opcode-Stream.

Normativ:

  • Debug-Infos werden als Side-Table gespeichert: pc -> SourceLoc.
  • Builds dürfen Debug-Infos vollständig strippen.
  • Runtime-Fehler (Trap) müssen im Debug-Build eine Quellposition reporten können.

6. Core Instruction Set v0.1

6.1 Konvention

Notation:

  • dst, src, lhs, rhs, obj, arr, idx, cond, func, argsBase sind Registeroperanden.
  • k ist ein Const-Index.
  • j ist ein Jump-Ziel.

6.2 Constants

  • LoadK dst, k
    Lädt Konstante k in dst.

  • LoadBool dst, immBool
    Lädt true/false in dst.

Optional (Extended, empfohlen): LoadI{8,16,32}, LoadNull, LoadUnit.

6.3 Arithmetic (numerisch)

  • Add dst, lhs, rhs
  • Sub dst, lhs, rhs
  • Mul dst, lhs, rhs
  • Div dst, lhs, rhs
  • Mod dst, lhs, rhs
  • Neg dst, src

Semantik:

  • Operanden müssen numerisch kompatibel sein.
  • Overflow/NaN/Division-by-zero Verhalten ist durch den jeweiligen Zahltyp definiert.
  • Unzulässige Operationen führen zu Trap (oder werden statisch ausgeschlossen).

6.4 Vergleich & Bool

  • Eq dst, lhs, rhsbool
  • Lt dst, lhs, rhsbool

  • Not dst, src

Hinweis (normativ):

  • Short-Circuit-Logik (and/or) ist Extended und wird über JmpIf/Jmp abgesenkt.

Hinweis: Ne/Le/Gt/Ge sind Extended und werden auf Eq/Lt/Not abgesenkt.

6.5 Control Flow

  • Jmp j
  • JmpIf cond, j
    springt, wenn cond == true.

Verifier-Anforderung: Sprünge dürfen nur auf gültige Ziele innerhalb derselben Funktion zeigen.

6.6 Functions

  • Call dst, func, argsBase, argc
  • Return src

Return: beendet den aktuellen Frame und liefert src an den Caller.

Tailcalls: Extended (z.B. TailCall) und werden zu Call + Return oder einer VM-Optimierung abgesenkt.

6.7 Closures & Upvalues

  • Closure dst, kProto
    erzeugt eine Closure aus einem Funktionsproto (Const-Pool Eintrag).

  • LoadUp dst, up

  • StoreUp up, src
  • CloseUp baseReg

CloseUp: schließt alle offenen Upvalues, die auf Stack-Slots ≥ baseReg zeigen.

6.8 Aggregates (Struct/Array)

  • NewStruct dst, kTypeId
  • GetField dst, obj, kField
  • SetField obj, kField, src

  • NewArray dst, lenReg

  • GetIndex dst, arr, idxReg
  • SetIndex arr, idxReg, src

Hinweis: Maps/Sets/Listen sind Library-level und werden über Runtime-Calls/FFI implementiert.

6.9 Moves & Lifetime

  • Move dst, src kopiert den Wert von src nach dst und markiert src als nicht mehr verwendbar.

Normativ: - Der Verifier stellt sicher, dass src nach Move nicht mehr gelesen wird. - Die VM darf in Debug/Profiling Builds src poisonen und Use-after-move zu Trap machen. - In Release ist Use-after-move ein Verifier-Fehler (nicht „UB“).

  • Drop src signalisiert End-of-Lifetime. Für Handles: Destruction/RC-Update gemäß Policy.

Verifier/Runtime:

  • Use-after-drop ist in Release ein Verifier-Fehler; in Debug/Profiling darf es zusätzlich als Runtime-Fehler (Trap) detektiert werden.
  • Copy/Clone ist Extended und wird über type-spezifische Calls + Move/Drop ausgedrückt.

6.10 Termination

  • Trap kReason
    beendet mit Runtime-Fehler. Erzeugt strukturierte Error-Info (Reason + SourceLoc + Stacktrace wenn aktiv).

  • Halt
    normale Terminierung des aktuellen Tasks/Programms (genaue Semantik über Scheduler-Spezifikation).


7. Verifier-Invarianten (v0.1)

Der Verifier prüft mindestens:

1) Lowering vollständig: keine Extended-Instruktionen im Core-Stream. 2) CFG-Korrektheit: - Sprünge auf gültige Ziele, - Basic Blocks enden in Terminator (Jmp, JmpIf, Return, Trap, Halt) oder fallthrough ist explizit erlaubt (Implementationswahl, aber konsistent). 3) Operand-Grenzen: - Registerindex < regCount, - Constindex < constCount, - Upvalindex < upvalCount. 4) Stack/Frame-Regeln: - Call/Return korrekt verschachtelt. 5) Debug-Info Konsistenz: (nur Debug-Build) pc→SourceLoc vollständig für trap-relevante Stellen.


8. Intrinsics, Profiling, Benchmarks (ohne Opcode-Explosion)

8.1 Grundsatz

  • Tests/Benchmarks sind Runner/Framework-Themen, nicht VM-Opcodes.
  • Profiling/Tracing darf nicht zu einem Opcode-Zoo führen.

8.2 Mechanismus v0.1

Es gibt zwei zulässige Implementationsstrategien (beide normativ kompatibel):

A) Builtin/Intrinsic Calls über Call (kein neuer Opcode):

  • @profile wird zu Aufrufen eines compiler-known builtins abgesenkt:
  • __intrinsic.prof_begin(labelId)
  • __intrinsic.prof_end()

B) Ein generischer Intrinsic-Opcode (max. 1 zusätzlicher Opcode in der VM):

  • Intrinsic subcode, a, b
  • Subcodes: PROF_BEGIN, PROF_END, COUNTER, INSTANT, MONO_NS, BLACK_BOX, …

Normativ: In release werden Instrumentationsknoten per Profile/CFG-Pruning entfernt, so dass im Production-Bytecode nichts verbleibt.


9. Extended Lowering Patterns (v0.1, informativ)

Diese Patterns sind informativ (nicht normativ), helfen aber dabei, das Core-Set klein zu halten.

9.1 Short-Circuit Bool

a and b (Extended) wird typischerweise zu:

  • eval acond
  • JmpIf cond, L_eval_b else → dst = false; Jmp L_end
  • L_eval_b: eval bdst
  • L_end:

a or b analog.

9.2 „_K“ Superinstructions

Sequenzen wie LoadK tmp, k; GetField dst, obj, tmp können im IR zu GetFieldK dst, obj, k fusioniert werden (Extended) und vor Verifikation wieder auf Core zurückgehen.

Ziel: weniger Dispatch/Registers, ohne Core-Opcodes zu vermehren.


10. Quickening & Inline Caches (Implementation Notes)

Quickening ist eine VM-Optimierung, keine Semantik.

Empfehlung:

  • Eligible Instructions: GetField, SetField, Call, GetIndex, SetIndex, ggf. numerische Ops.
  • Die VM darf pro Instruction-Site einen kleinen Inline-Cache (mono/poly) führen.
  • Bei Guard-Fail: Fallback auf generischen Pfad; optional Re-Spezialisierung.

Wichtig:

  • Cache-Daten dürfen die beobachtbare Semantik nicht ändern.
  • Profiling/Debug können Quickening deaktivieren oder einfrieren, um Messungen stabil zu halten.

11. Beziehung zu archivierten Entwürfen

Der frühere Draft Jade VM – Core Instruction Set (Draft, with HALT) beschreibt bereits die Idee eines minimalen Cores und wird als historischer Kontext aufbewahrt. Dieses Dokument ersetzt ihn als normativer Stand (v0.1).