Add inline VM opcodes for hot primitives (OP_ADD through OP_DEC)

16 new opcodes (160-175) bypass the CALL_PRIM hashtable lookup for
the most frequently called primitives:

  Arithmetic: OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_INC, OP_DEC, OP_NEG
  Comparison: OP_EQ, OP_LT, OP_GT, OP_NOT
  Collection: OP_LEN, OP_FIRST, OP_REST, OP_NTH, OP_CONS

The compiler (compiler.sx) recognizes these names at compile time and
emits the inline opcode instead of CALL_PRIM. The opcode is self-
contained — no constant pool index, no argc byte. Each primitive is
a single byte in the bytecode stream.

Implementation in all three VMs:
- OCaml (sx_vm.ml): direct pattern match, no allocation
- SX spec (vm.sx): delegates to existing primitives
- JS (transpiled): same as SX spec

66 new tests in spec/tests/vm-inline.sx covering arithmetic, comparison,
collection ops, composition, and edge cases.

Tests: 1314 JS (full), 1114 OCaml, 32 Playwright

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-24 20:10:48 +00:00
parent 5b2ef0a2af
commit eb4233ff36
6 changed files with 245 additions and 7 deletions

View File

@@ -531,6 +531,49 @@
(name (nth consts idx)))
(dict-set! (get vm "globals") name (vm-peek vm)))
;; ---- Inline primitives ----
(= op 160) ;; OP_ADD
(let ((b (vm-pop vm)) (a (vm-pop vm)))
(vm-push vm (+ a b)))
(= op 161) ;; OP_SUB
(let ((b (vm-pop vm)) (a (vm-pop vm)))
(vm-push vm (- a b)))
(= op 162) ;; OP_MUL
(let ((b (vm-pop vm)) (a (vm-pop vm)))
(vm-push vm (* a b)))
(= op 163) ;; OP_DIV
(let ((b (vm-pop vm)) (a (vm-pop vm)))
(vm-push vm (/ a b)))
(= op 164) ;; OP_EQ
(let ((b (vm-pop vm)) (a (vm-pop vm)))
(vm-push vm (= a b)))
(= op 165) ;; OP_LT
(let ((b (vm-pop vm)) (a (vm-pop vm)))
(vm-push vm (< a b)))
(= op 166) ;; OP_GT
(let ((b (vm-pop vm)) (a (vm-pop vm)))
(vm-push vm (> a b)))
(= op 167) ;; OP_NOT
(vm-push vm (not (vm-pop vm)))
(= op 168) ;; OP_LEN
(vm-push vm (len (vm-pop vm)))
(= op 169) ;; OP_FIRST
(vm-push vm (first (vm-pop vm)))
(= op 170) ;; OP_REST
(vm-push vm (rest (vm-pop vm)))
(= op 171) ;; OP_NTH
(let ((n (vm-pop vm)) (coll (vm-pop vm)))
(vm-push vm (nth coll n)))
(= op 172) ;; OP_CONS
(let ((coll (vm-pop vm)) (x (vm-pop vm)))
(vm-push vm (cons x coll)))
(= op 173) ;; OP_NEG
(vm-push vm (- 0 (vm-pop vm)))
(= op 174) ;; OP_INC
(vm-push vm (inc (vm-pop vm)))
(= op 175) ;; OP_DEC
(vm-push vm (dec (vm-pop vm)))
:else
(error (str "VM: unknown opcode " op))))))