All 26 browser modules recompiled with define-library/import forms. Compilation works without vm-compile-adapter (JIT pre-compilation hangs with library wrappers in some JIT paths — skipped for now, CEK compilation is ~34s total). Key fixes: - eval command: import-aware loop that handles define-library/import locally without touching the Python bridge pipe (avoids deadlock) - compile-modules.js: skip vm-compile-adapter, bump timeout 2621/2621 OCaml tests passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
258 lines
8.0 KiB
Plaintext
258 lines
8.0 KiB
Plaintext
;; ==========================================================================
|
|
;; bytecode.sx — SX bytecode format definition
|
|
;;
|
|
;; Universal bytecode for SX evaluation. Produced by compiler.sx,
|
|
;; executed by platform-native VMs (OCaml, JS, WASM).
|
|
;;
|
|
;; Design principles:
|
|
;; - One byte per opcode (~65 ops, fits in u8)
|
|
;; - Variable-length encoding (1-5 bytes per instruction)
|
|
;; - Lexical scope resolved at compile time (no hash lookups)
|
|
;; - Tail calls detected statically (no thunks/trampoline)
|
|
;; - Control flow via jumps (no continuation frames for if/when/etc.)
|
|
;; - Content-addressable (deterministic binary for CID)
|
|
;; ==========================================================================
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Opcode constants
|
|
;; --------------------------------------------------------------------------
|
|
|
|
;; Stack / Constants
|
|
|
|
(define-library (sx bytecode)
|
|
(export
|
|
OP_CONST
|
|
OP_NIL
|
|
OP_TRUE
|
|
OP_FALSE
|
|
OP_POP
|
|
OP_DUP
|
|
OP_LOCAL_GET
|
|
OP_LOCAL_SET
|
|
OP_UPVALUE_GET
|
|
OP_UPVALUE_SET
|
|
OP_GLOBAL_GET
|
|
OP_GLOBAL_SET
|
|
OP_JUMP
|
|
OP_JUMP_IF_FALSE
|
|
OP_JUMP_IF_TRUE
|
|
OP_CALL
|
|
OP_TAIL_CALL
|
|
OP_RETURN
|
|
OP_CLOSURE
|
|
OP_CALL_PRIM
|
|
OP_APPLY
|
|
OP_LIST
|
|
OP_DICT
|
|
OP_APPEND_BANG
|
|
OP_ITER_INIT
|
|
OP_ITER_NEXT
|
|
OP_MAP_OPEN
|
|
OP_MAP_APPEND
|
|
OP_MAP_CLOSE
|
|
OP_FILTER_TEST
|
|
OP_HO_MAP
|
|
OP_HO_FILTER
|
|
OP_HO_REDUCE
|
|
OP_HO_FOR_EACH
|
|
OP_HO_SOME
|
|
OP_HO_EVERY
|
|
OP_SCOPE_PUSH
|
|
OP_SCOPE_POP
|
|
OP_PROVIDE_PUSH
|
|
OP_PROVIDE_POP
|
|
OP_CONTEXT
|
|
OP_EMIT
|
|
OP_EMITTED
|
|
OP_RESET
|
|
OP_SHIFT
|
|
OP_DEFINE
|
|
OP_DEFCOMP
|
|
OP_DEFISLAND
|
|
OP_DEFMACRO
|
|
OP_EXPAND_MACRO
|
|
OP_STR_CONCAT
|
|
OP_STR_JOIN
|
|
OP_SERIALIZE
|
|
OP_ADD
|
|
OP_SUB
|
|
OP_MUL
|
|
OP_DIV
|
|
OP_EQ
|
|
OP_LT
|
|
OP_GT
|
|
OP_NOT
|
|
OP_LEN
|
|
OP_FIRST
|
|
OP_REST
|
|
OP_NTH
|
|
OP_CONS
|
|
OP_NEG
|
|
OP_INC
|
|
OP_DEC
|
|
OP_ASER_TAG
|
|
OP_ASER_FRAG
|
|
BYTECODE_MAGIC
|
|
BYTECODE_VERSION
|
|
CONST_NUMBER
|
|
CONST_STRING
|
|
CONST_BOOL
|
|
CONST_NIL
|
|
CONST_SYMBOL
|
|
CONST_KEYWORD
|
|
CONST_LIST
|
|
CONST_DICT
|
|
CONST_CODE
|
|
opcode-name)
|
|
(begin
|
|
|
|
(define OP_CONST 1) ;; u16 pool_idx — push constant
|
|
(define OP_NIL 2) ;; push nil
|
|
(define OP_TRUE 3) ;; push true
|
|
(define OP_FALSE 4) ;; push false
|
|
(define OP_POP 5) ;; discard TOS
|
|
(define OP_DUP 6) ;; duplicate TOS
|
|
|
|
;; Variable access (resolved at compile time)
|
|
(define OP_LOCAL_GET 16) ;; u8 slot
|
|
(define OP_LOCAL_SET 17) ;; u8 slot
|
|
(define OP_UPVALUE_GET 18) ;; u8 idx
|
|
(define OP_UPVALUE_SET 19) ;; u8 idx
|
|
(define OP_GLOBAL_GET 20) ;; u16 name_idx
|
|
(define OP_GLOBAL_SET 21) ;; u16 name_idx
|
|
|
|
;; Control flow (replaces if/when/cond/and/or frames)
|
|
(define OP_JUMP 32) ;; i16 offset
|
|
(define OP_JUMP_IF_FALSE 33) ;; i16 offset
|
|
(define OP_JUMP_IF_TRUE 34) ;; i16 offset
|
|
|
|
;; Function operations
|
|
(define OP_CALL 48) ;; u8 argc
|
|
(define OP_TAIL_CALL 49) ;; u8 argc — reuse frame (TCO)
|
|
(define OP_RETURN 50) ;; return TOS
|
|
(define OP_CLOSURE 51) ;; u16 code_idx — create closure
|
|
(define OP_CALL_PRIM 52) ;; u16 name_idx, u8 argc — direct primitive
|
|
(define OP_APPLY 53) ;; (apply f args-list)
|
|
|
|
;; Collection construction
|
|
(define OP_LIST 64) ;; u16 count — build list from stack
|
|
(define OP_DICT 65) ;; u16 count — build dict from stack pairs
|
|
(define OP_APPEND_BANG 66) ;; (append! TOS-1 TOS)
|
|
|
|
;; Higher-order forms (inlined loop)
|
|
(define OP_ITER_INIT 80) ;; init iterator on TOS list
|
|
(define OP_ITER_NEXT 81) ;; i16 end_offset — push next or jump
|
|
(define OP_MAP_OPEN 82) ;; push empty accumulator
|
|
(define OP_MAP_APPEND 83) ;; append TOS to accumulator
|
|
(define OP_MAP_CLOSE 84) ;; pop accumulator as list
|
|
(define OP_FILTER_TEST 85) ;; i16 skip — if falsy jump (skip append)
|
|
|
|
;; HO fallback (dynamic callback)
|
|
(define OP_HO_MAP 88) ;; (map fn coll)
|
|
(define OP_HO_FILTER 89) ;; (filter fn coll)
|
|
(define OP_HO_REDUCE 90) ;; (reduce fn init coll)
|
|
(define OP_HO_FOR_EACH 91) ;; (for-each fn coll)
|
|
(define OP_HO_SOME 92) ;; (some fn coll)
|
|
(define OP_HO_EVERY 93) ;; (every? fn coll)
|
|
|
|
;; Scope / dynamic binding
|
|
(define OP_SCOPE_PUSH 96) ;; TOS = name
|
|
(define OP_SCOPE_POP 97)
|
|
(define OP_PROVIDE_PUSH 98) ;; TOS-1 = name, TOS = value
|
|
(define OP_PROVIDE_POP 99)
|
|
(define OP_CONTEXT 100) ;; TOS = name → push value
|
|
(define OP_EMIT 101) ;; TOS-1 = name, TOS = value
|
|
(define OP_EMITTED 102) ;; TOS = name → push collected
|
|
|
|
;; Continuations
|
|
(define OP_RESET 112) ;; i16 body_len — push delimiter
|
|
(define OP_SHIFT 113) ;; u8 k_slot, i16 body_len — capture k
|
|
|
|
;; Define / component
|
|
(define OP_DEFINE 128) ;; u16 name_idx — bind TOS to name
|
|
(define OP_DEFCOMP 129) ;; u16 template_idx
|
|
(define OP_DEFISLAND 130) ;; u16 template_idx
|
|
(define OP_DEFMACRO 131) ;; u16 template_idx
|
|
(define OP_EXPAND_MACRO 132) ;; u8 argc — runtime macro expansion
|
|
|
|
;; String / serialize (hot path)
|
|
(define OP_STR_CONCAT 144) ;; u8 count — concat N values as strings
|
|
(define OP_STR_JOIN 145) ;; (join sep list)
|
|
(define OP_SERIALIZE 146) ;; serialize TOS to SX string
|
|
|
|
;; Inline primitives (hot path — no hashtable lookup)
|
|
(define OP_ADD 160) ;; TOS-1 + TOS → push
|
|
(define OP_SUB 161) ;; TOS-1 - TOS → push
|
|
(define OP_MUL 162) ;; TOS-1 * TOS → push
|
|
(define OP_DIV 163) ;; TOS-1 / TOS → push
|
|
(define OP_EQ 164) ;; TOS-1 = TOS → push bool
|
|
(define OP_LT 165) ;; TOS-1 < TOS → push bool
|
|
(define OP_GT 166) ;; TOS-1 > TOS → push bool
|
|
(define OP_NOT 167) ;; !TOS → push bool
|
|
(define OP_LEN 168) ;; len(TOS) → push number
|
|
(define OP_FIRST 169) ;; first(TOS) → push
|
|
(define OP_REST 170) ;; rest(TOS) → push list
|
|
(define OP_NTH 171) ;; nth(TOS-1, TOS) → push
|
|
(define OP_CONS 172) ;; cons(TOS-1, TOS) → push list
|
|
(define OP_NEG 173) ;; negate TOS → push number
|
|
(define OP_INC 174) ;; TOS + 1 → push
|
|
(define OP_DEC 175) ;; TOS - 1 → push
|
|
|
|
;; Aser specialization (optional, 224-239 reserved)
|
|
(define OP_ASER_TAG 224) ;; u16 tag_name_idx — serialize HTML tag
|
|
(define OP_ASER_FRAG 225) ;; u8 child_count — serialize fragment
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Bytecode module structure
|
|
;; --------------------------------------------------------------------------
|
|
|
|
;; A module contains:
|
|
;; magic: "SXBC" (4 bytes)
|
|
;; version: u16
|
|
;; pool_count: u32
|
|
;; pool: constant pool entries (self-describing tagged values)
|
|
;; code_count: u32
|
|
;; codes: code objects
|
|
;; entry: u32 (index of entry-point code object)
|
|
|
|
(define BYTECODE_MAGIC "SXBC")
|
|
(define BYTECODE_VERSION 1)
|
|
|
|
;; Constant pool tags
|
|
(define CONST_NUMBER 1)
|
|
(define CONST_STRING 2)
|
|
(define CONST_BOOL 3)
|
|
(define CONST_NIL 4)
|
|
(define CONST_SYMBOL 5)
|
|
(define CONST_KEYWORD 6)
|
|
(define CONST_LIST 7)
|
|
(define CONST_DICT 8)
|
|
(define CONST_CODE 9)
|
|
|
|
|
|
;; --------------------------------------------------------------------------
|
|
;; Disassembler
|
|
;; --------------------------------------------------------------------------
|
|
|
|
(define opcode-name
|
|
(fn (op)
|
|
(cond
|
|
(= op 1) "CONST" (= op 2) "NIL"
|
|
(= op 3) "TRUE" (= op 4) "FALSE"
|
|
(= op 5) "POP" (= op 6) "DUP"
|
|
(= op 16) "LOCAL_GET" (= op 17) "LOCAL_SET"
|
|
(= op 20) "GLOBAL_GET" (= op 21) "GLOBAL_SET"
|
|
(= op 32) "JUMP" (= op 33) "JUMP_IF_FALSE"
|
|
(= op 48) "CALL" (= op 49) "TAIL_CALL"
|
|
(= op 50) "RETURN" (= op 52) "CALL_PRIM"
|
|
(= op 128) "DEFINE" (= op 144) "STR_CONCAT"
|
|
:else (str "OP_" op))))
|
|
|
|
|
|
)) ;; end define-library
|
|
|
|
;; Re-export to global namespace for backward compatibility
|
|
(import (sx bytecode))
|