summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--eval.c2
-rw-r--r--eval.h1
-rw-r--r--genvmop.txr18
-rw-r--r--lib.c88
-rw-r--r--lib.h3
-rw-r--r--lisplib.c34
-rw-r--r--share/txr/stdlib/asm.tl564
-rw-r--r--vm.c818
-rw-r--r--vm.h31
-rw-r--r--vmop.h61
11 files changed, 1585 insertions, 37 deletions
diff --git a/Makefile b/Makefile
index d1b483c8..dc1f4f62 100644
--- a/Makefile
+++ b/Makefile
@@ -50,7 +50,7 @@ EXTRA_OBJS-y :=
OBJS := txr.o lex.yy.o y.tab.o match.o lib.o regex.o gc.o unwind.o stream.o
OBJS += arith.o hash.o utf8.o filter.o eval.o parser.o rand.o combi.o sysif.o
OBJS += args.o lisplib.o cadr.o struct.o itypes.o buf.o jmp.o protsym.o ffi.o
-OBJS += strudel.o
+OBJS += strudel.o vm.o
OBJS-$(debug_support) += debug.o
OBJS-$(have_syslog) += syslog.o
OBJS-$(have_glob) += glob.o
diff --git a/eval.c b/eval.c
index 1d70f953..4731c046 100644
--- a/eval.c
+++ b/eval.c
@@ -431,7 +431,7 @@ val lookup_var(val env, val sym)
return lookup_global_var(sym);
}
-static val lookup_sym_lisp1(val env, val sym)
+val lookup_sym_lisp1(val env, val sym)
{
uses_or2;
diff --git a/eval.h b/eval.h
index 8cd5516e..6261821d 100644
--- a/eval.h
+++ b/eval.h
@@ -51,6 +51,7 @@ val lookup_global_var(val sym);
loc lookup_var_l(val env, val sym);
loc lookup_global_var_l(val sym);
val lookup_fun(val env, val sym);
+val lookup_sym_lisp1(val env, val sym);
val set_dyn_env(val de);
val funcall_interp(val interp_fun, struct args *);
val boundp(val sym);
diff --git a/genvmop.txr b/genvmop.txr
new file mode 100644
index 00000000..31c6062a
--- /dev/null
+++ b/genvmop.txr
@@ -0,0 +1,18 @@
+@(include "asm")
+@(in-package :sys)
+@(bind oc @(keep-if .code %oc-list%))
+@(next "vm.h")
+@(collect)
+@{copyright}
+@(until)
+
+@(end)
+@(output "vmop.h")
+@{copyright "\n"}
+
+typedef enum vm_op {
+@ (repeat :vars (oc))
+ @{oc.symbol :filter :upcase} = @{oc.code},
+@ (end)
+} vm_op_t;
+@(end)
diff --git a/lib.c b/lib.c
index 9d312007..014fd227 100644
--- a/lib.c
+++ b/lib.c
@@ -59,6 +59,7 @@
#include "utf8.h"
#include "filter.h"
#include "eval.h"
+#include "vm.h"
#include "sysif.h"
#include "regex.h"
#include "parser.h"
@@ -6001,6 +6002,19 @@ val func_interp(val env, val form)
return obj;
}
+val func_vm(val closure, val desc, int fixparam, int reqargs, int variadic)
+{
+ val obj = make_obj();
+ obj->f.type = FUN;
+ obj->f.functype = FVM;
+ obj->f.env = closure;
+ obj->f.f.vm_desc = desc;
+ obj->f.fixparam = reqargs;
+ obj->f.optargs = fixparam - reqargs;
+ obj->f.variadic = variadic;
+ return obj;
+}
+
val func_get_form(val fun)
{
type_check(fun, FUN);
@@ -6188,6 +6202,8 @@ val generic_funcall(val fun, struct args *args_in)
return fun->f.f.n7(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), z(arg[5]), z(arg[6]));
case N8:
return fun->f.f.n8(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), z(arg[5]), z(arg[6]), z(arg[7]));
+ case FVM:
+ return vm_execute_closure(fun, args);
case FINTERP:
internal_error("unsupported function type");
}
@@ -6206,42 +6222,47 @@ val generic_funcall(val fun, struct args *args_in)
if (args->fill < reqargs)
callerror(fun, lit("missing required arguments"));
- {
- args_decl(args_copy, max(args->fill - fixparam, ARGS_MIN));
- args = args_cat_zap_from(args_copy, args, fixparam);
- }
-
switch (fun->f.functype) {
case FINTERP:
return funcall_interp(fun, args);
- case F0:
- return fun->f.f.f0v(fun->f.env, args);
- case F1:
- return fun->f.f.f1v(fun->f.env, z(arg[0]), args);
- case F2:
- return fun->f.f.f2v(fun->f.env, z(arg[0]), z(arg[1]), args);
- case F3:
- return fun->f.f.f3v(fun->f.env, z(arg[0]), z(arg[1]), z(arg[2]), args);
- case F4:
- return fun->f.f.f4v(fun->f.env, z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), args);
- case N0:
- return fun->f.f.n0v(args);
- case N1:
- return fun->f.f.n1v(z(arg[0]), args);
- case N2:
- return fun->f.f.n2v(z(arg[0]), z(arg[1]), args);
- case N3:
- return fun->f.f.n3v(z(arg[0]), z(arg[1]), z(arg[2]), args);
- case N4:
- return fun->f.f.n4v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), args);
- case N5:
- return fun->f.f.n5v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), args);
- case N6:
- return fun->f.f.n6v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), z(arg[5]), args);
- case N7:
- return fun->f.f.n7v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), z(arg[5]), z(arg[6]), args);
- case N8:
- return fun->f.f.n8v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), z(arg[5]), z(arg[6]), z(arg[7]), args);
+ case FVM:
+ return vm_execute_closure(fun, args);
+ default:
+ {
+ args_decl(args_copy, max(args->fill - fixparam, ARGS_MIN));
+ args = args_cat_zap_from(args_copy, args, fixparam);
+ }
+
+ switch (fun->f.functype) {
+ case F0:
+ return fun->f.f.f0v(fun->f.env, args);
+ case F1:
+ return fun->f.f.f1v(fun->f.env, z(arg[0]), args);
+ case F2:
+ return fun->f.f.f2v(fun->f.env, z(arg[0]), z(arg[1]), args);
+ case F3:
+ return fun->f.f.f3v(fun->f.env, z(arg[0]), z(arg[1]), z(arg[2]), args);
+ case F4:
+ return fun->f.f.f4v(fun->f.env, z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), args);
+ case N0:
+ return fun->f.f.n0v(args);
+ case N1:
+ return fun->f.f.n1v(z(arg[0]), args);
+ case N2:
+ return fun->f.f.n2v(z(arg[0]), z(arg[1]), args);
+ case N3:
+ return fun->f.f.n3v(z(arg[0]), z(arg[1]), z(arg[2]), args);
+ case N4:
+ return fun->f.f.n4v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), args);
+ case N5:
+ return fun->f.f.n5v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), args);
+ case N6:
+ return fun->f.f.n6v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), z(arg[5]), args);
+ case N7:
+ return fun->f.f.n7v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), z(arg[5]), z(arg[6]), args);
+ case N8:
+ return fun->f.f.n8v(z(arg[0]), z(arg[1]), z(arg[2]), z(arg[3]), z(arg[4]), z(arg[5]), z(arg[6]), z(arg[7]), args);
+ }
}
}
@@ -11802,6 +11823,7 @@ void init(val *stack_bottom)
rand_init();
stream_init();
strudel_init();
+ vm_init();
#if HAVE_POSIX_SIGS
sig_init();
#endif
diff --git a/lib.h b/lib.h
index f80f497a..ca72f970 100644
--- a/lib.h
+++ b/lib.h
@@ -70,6 +70,7 @@ typedef enum type {
typedef enum functype
{
FINTERP, /* Interpreted function. */
+ FVM, /* VM function. */
F0, F1, F2, F3, F4, /* Intrinsic functions with env. */
N0, N1, N2, N3, N4, N5, N6, N7, N8 /* No-env intrinsics. */
} functype_t;
@@ -148,6 +149,7 @@ struct func {
val env;
union {
val interp_fun;
+ val vm_desc;
val (*f0)(val);
val (*f1)(val, val);
val (*f2)(val, val, val);
@@ -890,6 +892,7 @@ val func_n1ov(val (*fun)(val, varg), int reqargs);
val func_n2ov(val (*fun)(val, val, varg), int reqargs);
val func_n3ov(val (*fun)(val, val, val, varg), int reqargs);
val func_interp(val env, val form);
+val func_vm(val closure, val desc, int fixparam, int reqargs, int variadic);
val func_get_form(val fun);
val func_get_env(val fun);
val func_set_env(val fun, val env);
diff --git a/lisplib.c b/lisplib.c
index 503765b0..50c52356 100644
--- a/lisplib.c
+++ b/lisplib.c
@@ -46,10 +46,10 @@ val dl_table;
int opt_dbg_autoload;
val trace_loaded;
-void set_dlt_entries(val dlt, val *name, val fun)
+static void set_dlt_entries_impl(val dlt, val *name, val fun, val package)
{
for (; *name; name++) {
- val sym = intern(*name, user_package);
+ val sym = intern(*name, package);
if (fun)
sethash(dlt, sym, fun);
@@ -58,6 +58,16 @@ void set_dlt_entries(val dlt, val *name, val fun)
}
}
+void set_dlt_entries(val dlt, val *name, val fun)
+{
+ set_dlt_entries_impl(dlt, name, fun, user_package);
+}
+
+static void set_dlt_entries_sys(val dlt, val *name, val fun)
+{
+ set_dlt_entries_impl(dlt, name, fun, system_package);
+}
+
static void intern_only(val *name)
{
for (; *name; name++)
@@ -611,6 +621,25 @@ static val stream_wrap_instantiate(val set_fun)
return nil;
}
+static val asm_instantiate(val set_fun)
+{
+ funcall1(set_fun, nil);
+ load(format(nil, lit("~aasm.tl"), stdlib_path, nao));
+ return nil;
+}
+
+static val asm_set_entries(val dlt, val fun)
+{
+ val name[] = {
+ lit("assembler"),
+ nil
+ };
+
+ set_dlt_entries_sys(dlt, name, fun);
+ return nil;
+}
+
+
static val op_set_entries(val dlt, val fun)
{
val name[] = {
@@ -670,6 +699,7 @@ void lisplib_init(void)
dlt_register(dl_table, ffi_instantiate, ffi_set_entries);
dlt_register(dl_table, doloop_instantiate, doloop_set_entries);
dlt_register(dl_table, stream_wrap_instantiate, stream_wrap_set_entries);
+ dlt_register(dl_table, asm_instantiate, asm_set_entries);
if (!opt_compat || opt_compat >= 185)
dlt_register(dl_table, op_instantiate, op_set_entries);
diff --git a/share/txr/stdlib/asm.tl b/share/txr/stdlib/asm.tl
new file mode 100644
index 00000000..c6a7efc9
--- /dev/null
+++ b/share/txr/stdlib/asm.tl
@@ -0,0 +1,564 @@
+(in-package :sys)
+
+(defstruct oc-base nil
+ (:method synerr (me fmt . args)
+ (error `opcode @{me.symbol}: @fmt` . args))
+
+ (:method chk-arg-count (me n syntax)
+ (when (neq (length (rest syntax)) n)
+ me.(synerr "~s arguments required; ~s is invalid"
+ n syntax)))
+
+ (:method chk-arg-count-min (me n syntax)
+ (when (< (length (rest syntax)) n)
+ me.(synerr "~s arguments required; ~s is invalid"
+ n syntax)))
+
+ (:method backpatch (me asm at offs)
+ (error `assembler: @{me.symbol} doesn't backpatch`)))
+
+(defstruct assembler nil
+ buf
+ (bstr (make-buf-stream))
+ (labdef (hash))
+ (labref (hash))
+ (:static imm-width (relate '(si mi bi) '(10 16 32)))
+ (:static sign-bits (relate '(fixnum bignum chr) '(1 1 0)))
+ (:static operand-name (relate '(si mi bi l r rs d ds n o)
+ '("small immediate"
+ "medium immediate"
+ "big immediate"
+ "label"
+ "register operand"
+ "register small operand"
+ "register destination operand"
+ "register small destination operand"
+ "integer"
+ "any object")))
+
+ (:postinit (me)
+ (set me.buf (get-buf-from-stream me.bstr)))
+
+ (:method cur-pos (me)
+ (seek-stream me.bstr 0 :from-current))
+
+ (:method set-pos (me pos)
+ (seek-stream me.bstr pos :from-start))
+
+ (:method lookup-label (me sym oc)
+ (condlet
+ (((n [me.labdef sym])) n)
+ (t (push (cons oc (trunc me.(cur-pos) 4)) [me.labref sym])
+ 0)))
+
+ (:method define-label (me sym)
+ (let* ((pos me.(cur-pos))
+ (ins (trunc pos 4)))
+ (set [me.labdef sym] ins)
+ (each ((entry (del [me.labref sym])))
+ (tree-bind (oc . offs) entry
+ me.(set-pos (* 4 offs))
+ oc.(backpatch me (* 4 offs) ins)))
+ me.(set-pos pos)
+ ins))
+
+ (:method read-buf (me bytes)
+ (let ((buf (make-buf bytes)))
+ (when (neql (fill-buf buf 0 me.bstr) bytes)
+ (error "assembler: read past instruction block"))
+ buf))
+
+ (:method put-word (me word)
+ (let* ((buf (make-buf 0)))
+ (buf-put-u32 buf 0 word)
+ (put-buf buf 0 me.bstr)))
+
+ (:method put-insn (me code extension operand)
+ (let ((word (logior (ash code 26) (ash extension 16) operand))
+ (buf (make-buf 0)))
+ (buf-put-u32 buf 0 word)
+ (put-buf buf 0 me.bstr)))
+
+ (:method put-pair (me op1 op2)
+ (let ((word (logior (ash op1 16) op2))
+ (buf (make-buf 0)))
+ (buf-put-u32 buf 0 word)
+ (put-buf buf 0 me.bstr)))
+
+ (:method get-word (me)
+ (let* ((buf me.(read-buf (sizeof uint32))))
+ (buf-get-u32 buf 0)))
+
+ (:method get-insn (me)
+ (let* ((buf me.(read-buf (sizeof uint32)))
+ (word (buf-get-u32 buf 0)))
+ (list (ash word -26)
+ (logtrunc (ash word -16) 10)
+ (logtrunc word 16))))
+
+ (:method get-pair (me)
+ (let* ((buf me.(read-buf (sizeof uint32)))
+ (word (buf-get-u32 buf 0)))
+ (list (ash word -16) (logtrunc word 16))))
+
+ (:method immediate-fits-type (me arg operand-type)
+ (and (member (typeof arg)
+ '(fixnum bignum chr))
+ (<= (+ (width arg)
+ [me.sign-bits (typeof arg)]
+ 2)
+ [me.imm-width operand-type])))
+
+ (:method parse-args (me oc syntax pattern)
+ (mapcar (lambda (type arg n)
+ (let ((parg (caseql type
+ ((si mi bi)
+ (when me.(immediate-fits-type arg type)
+ arg))
+ (l (cond
+ ((keywordp arg) me.(lookup-label arg oc))
+ ((integerp arg) arg)))
+ (n (if (integerp arg) arg))
+ (o arg)
+ ((r rs d ds)
+ (cond
+ ((null arg) 0)
+ ((symbolp arg)
+ (parse-operand (symbol-name arg)))))
+ (t (error "assembler: invalid arg type spec")))))
+ (unless (or parg (eq type 'o))
+ oc.(synerr "argument ~a of ~s invalid; ~a expected"
+ n syntax [me.operand-name type]))
+ (when (and (member type '(d ds))
+ (or (zerop parg) (<= 256 parg 511)))
+ oc.(synerr "argument ~a of ~s cannot be destination"
+ n syntax))
+ (when (and (member type '(rs ds))
+ (not (< parg 1024)))
+ oc.(synerr "argument ~a of ~s isn't a small register"
+ n syntax))
+ parg))
+ pattern (rest syntax) (range 1)))
+
+ (:method asm-one (me syntax)
+ (let ((oc (cond
+ ((keywordp syntax) [%oc-hash% 'label])
+ ((consp syntax) [%oc-hash% (car syntax)]))))
+ (unless oc
+ (error "assembler: invalid instruction ~s" syntax))
+ oc.(asm me syntax)))
+
+ (:method asm (me insns)
+ (each ((i insns))
+ me.(asm-one i))
+ (unless (empty me.labref)
+ (error "assembler: dangling label references"))
+ (whenlet ((n (cdr [find-max me.labdef : cdr])))
+ (unless (< 0 n (len me.buf))
+ (error "assembler: labels outside of code"))))
+
+ (:method dis-one (me)
+ (tree-bind (code extension operand) me.(get-insn)
+ (let ((oc [%oc-hash% code]))
+ oc.(dis me extension operand))))
+
+ (:method dis (me)
+ me.(set-pos 0)
+ (build
+ (while (< me.(cur-pos) (len me.buf))
+ (add me.(dis-one)))))
+
+ (:method dis-listing (me : (stream *stdout*))
+ (let ((p 0)
+ (l (len me.buf)))
+ me.(set-pos p)
+ (while (< p l)
+ (let* ((dis me.(dis-one))
+ (dis-txt (cat-str [mapcar tostring dis] " "))
+ (q me.(cur-pos)))
+ me.(set-pos p)
+ (format t "~,5d: ~,08X ~a\n" (trunc p 4) me.(get-word) dis-txt)
+ (while (< (inc p 4) q)
+ (format t "~,5d: ~,08X\n" (trunc p 4) me.(get-word)))
+ me.(set-pos q)
+ (set p q))))))
+
+(defvarl %oc-list-builder% (new list-builder))
+
+(defsymacro %oc-list% %oc-list-builder%.(get))
+
+(defvarl %oc-hash% (hash))
+
+(defvarl %oc-code% 0)
+
+(defun register-opcode (oc)
+ %oc-list-builder%.(add oc)
+ (set [%oc-hash% oc.symbol] oc)
+ (set [%oc-hash% oc.code] oc))
+
+(defun parse-operand (str)
+ (cond
+ ((r^$ #/t[0-9A-Fa-f][0-9A-Fa-f]?/ str)
+ (int-str [str 1..:] 16))
+ ((r^$ #/d[0-9A-Fa-f][0-9A-Fa-f]?/ str)
+ (+ 256 (int-str [str 1..:] 16)))
+ ((r^$ #/v[0-9A-Fa-f]?[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]/ str)
+ (let ((xn (int-str [`0@{str[1..:]}` -4..-2] 16))
+ (yn (int-str [str -2..:] 16)))
+ (+ (* 256 (+ 2 xn)) yn)))))
+
+(defun operand-to-sym (val)
+ (let ((xn (ash val -8))
+ (yn (logtrunc val 8)))
+ (caseql xn
+ (0 (if (eql yn 0)
+ nil
+ (intern (fmt "t~,02X" yn))))
+ (1 (intern (fmt "d~,02X" yn)))
+ (t (intern (fmt "v~,02X~,02X" (- xn 2) yn))))))
+
+(defun bits-to-obj (bits width)
+ (let ((tag (logtrunc bits 2))
+ (val (ash bits -2)))
+ (caseq tag
+ (1 (sign-extend val (- width 2)))
+ (2 (chr-int val))
+ (t (error "assembler: bad immediate operand: ~s" bits)))))
+
+(defmacro defopcode (class symbol code . slot-defs)
+ ^(symacrolet ((auto (pinc %oc-code%)))
+ (defstruct ,class oc-base
+ (:static symbol ',symbol)
+ (:static code ,code)
+ ,*slot-defs)
+ (register-opcode (new ,class))))
+
+(defmacro defopcode-derived (class symbol code orig-class)
+ ^(symacrolet ((auto (pinc %oc-code%)))
+ (defstruct ,class ,orig-class
+ (:static symbol ',symbol)
+ (:static code ,code))
+ (register-opcode (new ,class))))
+
+(defopcode op-label label nil
+ (:method asm (me asm syntax)
+ (unless (keywordp syntax)
+ asm.(synerr "label must be keyword"))
+ asm.(define-label syntax))
+
+ (:method dis (me asm extension operand)))
+
+(defopcode op-noop noop auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 0 syntax)
+ asm.(put-insn me.code 0 0))
+
+ (:method dis (me asm extension operand)
+ ^(,me.symbol)))
+
+(defopcode op-frame frame auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (lev size) asm.(parse-args me syntax '(n n))
+ (unless (<= 1 lev 255)
+ me.(synerr "level must range from 2 to 256"))
+ (unless (<= 0 size 256)
+ me.(synerr "size must range from 0 to 256"))
+ asm.(put-insn me.code lev size)))
+ (:method dis (me asm lev size)
+ ^(,me.symbol ,lev ,size)))
+
+(defopcode-derived op-dframe dframe auto op-frame)
+
+(defopcode op-end end auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 1 syntax)
+ (let ((res (car asm.(parse-args me syntax '(r)))))
+ asm.(put-insn me.code 0 res)))
+ (:method dis (me asm extension res)
+ ^(,me.symbol ,(operand-to-sym res))))
+
+(defopcode-derived op-fin fin auto op-end)
+
+(defopcode op-call call auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count-min 2 syntax)
+ (let* ((nargs (pred (len syntax)))
+ (syn-pat (repeat '(r) (succ nargs)))
+ (funargs (ppred nargs))
+ (args asm.(parse-args me syntax syn-pat)))
+ asm.(put-insn me.code funargs (pop args))
+ (while args
+ (let ((x (pop args))
+ (y (or (pop args) 0)))
+ asm.(put-pair y x)))))
+
+ (:method dis (me asm funargs arg0)
+ (build
+ (add me.symbol)
+ (add (operand-to-sym arg0))
+ (inc funargs 1)
+ (while (> funargs 0)
+ (dec funargs 2)
+ (tree-bind (y x) asm.(get-pair)
+ (add (operand-to-sym x))
+ (unless (minusp funargs)
+ (add (operand-to-sym y))))))))
+
+(defopcode-derived op-apply apply auto op-call)
+
+(defopcode op-movrs movrs auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (dst src) asm.(parse-args me syntax '(d rs))
+ asm.(put-insn me.code src dst)))
+
+ (:method dis (me asm src dst)
+ ^(,me.symbol ,(operand-to-sym dst) ,(operand-to-sym src))))
+
+(defopcode op-movsr movsr auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (dst src) asm.(parse-args me syntax '(ds r))
+ asm.(put-insn me.code dst src)))
+
+ (:method dis (me asm dst src)
+ ^(,me.symbol ,(operand-to-sym dst) ,(operand-to-sym src))))
+
+(defopcode op-movrr movrr auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (dst src) asm.(parse-args me syntax '(d r))
+ asm.(put-insn me.code 0 dst)
+ asm.(put-pair 0 src)))
+
+ (:method dis (me asm extension dst)
+ (let ((src (cadr asm.(get-pair))))
+ ^(,me.symbol ,(operand-to-sym dst) ,(operand-to-sym src)))))
+
+(defopcode op-mov-pseudo mov nil
+ (:method asm (me asm syntax)
+ (tree-bind (dst src) asm.(parse-args me syntax '(d r))
+ (let ((real [%oc-hash% (cond
+ ((< dst 1024) 'movsr)
+ ((< src 1024) 'movrs)
+ (t 'movrr))]))
+ real.(asm asm syntax)))))
+
+(defopcode op-movrsi movrsi auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (dst imm) asm.(parse-args me syntax '(d si))
+ asm.(put-insn me.code (logtrunc (sys:bits imm) 10) dst)))
+
+ (:method dis (me asm imm dst)
+ ^(,me.symbol ,(operand-to-sym dst) ,(bits-to-obj imm 10))))
+
+(defopcode op-movsmi movsmi auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (dst imm) asm.(parse-args me syntax '(ds mi))
+ asm.(put-insn me.code dst (logtrunc (sys:bits imm) 16))))
+
+ (:method dis (me asm dst imm )
+ ^(,me.symbol ,(operand-to-sym dst) ,(bits-to-obj imm 16))))
+
+(defopcode op-movrbi movrbi auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (dst imm) asm.(parse-args me syntax '(ds bi))
+ asm.(put-insn me.code 0 dst)
+ asm.(put-word (logtrunc (sys:bits imm) 32))))
+
+ (:method dis (me asm extension dst)
+ (let ((imm asm.(get-word)))
+ ^(,me.symbol ,(operand-to-sym dst) ,(bits-to-obj imm 32)))))
+
+(defopcode op-movi-pseudo movi nil
+ (:method asm (me asm syntax)
+ (tree-bind (dst src) asm.(parse-args me syntax '(d bi))
+ (let ((real [%oc-hash% (cond
+ (asm.(immediate-fits-type src 'si) 'movrsi)
+ ((and asm.(immediate-fits-type src 'si)
+ (< dst 1024)) 'movsmi)
+ (t 'movrbi))]))
+ real.(asm asm syntax)))))
+
+(defopcode op-jmp jmp auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 1 syntax)
+ (let ((dst (car asm.(parse-args me syntax '(l)))))
+ asm.(put-insn me.code (ash dst -16) (logtrunc dst 16))))
+
+ (:method backpatch (me asm at dst)
+ asm.(put-insn me.code (ash dst -16) (logtrunc dst 16)))
+
+ (:method dis (me asm high16 low16)
+ ^(,me.symbol ,(logior (ash high16 16) low16))))
+
+(defopcode op-if if auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (reg dst) asm.(parse-args me syntax '(r l))
+ asm.(put-insn me.code (ash dst -16) (logtrunc dst 16))
+ asm.(put-pair 0 reg)))
+
+ (:method backpatch (me asm at dst)
+ asm.(put-insn me.code (ash dst -16) (logtrunc dst 16)))
+
+ (:method dis (me asm high16 low16)
+ (let ((dst (logior (ash high16 16) low16))
+ (reg (cadr asm.(get-pair))))
+ ^(,me.symbol ,(operand-to-sym reg) ,dst))))
+
+(defopcode-derived op-uwprot uwprot auto op-jmp)
+
+(defopcode op-block block auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 3 syntax)
+ (tree-bind (outreg blname exitpt) asm.(parse-args me syntax '(r r l))
+ asm.(put-insn me.code (ash exitpt -16) (logtrunc exitpt 16))
+ asm.(put-pair outreg blname)))
+
+ (:method backpatch (me asm at exitpt)
+ asm.(put-insn me.code (ash exitpt -16) (logtrunc exitpt 16)))
+
+ (:method dis (me asm high16 low16)
+ (let ((exitpt (logior (ash high16 16) low16)))
+ (tree-bind (outreg blname) asm.(get-pair)
+ ^(,me.symbol ,(operand-to-sym outreg) ,(operand-to-sym blname)
+ ,exitpt)))))
+
+(defopcode op-retsr retsr auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (name reg) asm.(parse-args me syntax '(rs r))
+ asm.(put-insn me.code name reg)))
+
+ (:method dis (me asm name reg)
+ ^(,me.symbol ,(operand-to-sym name) ,(operand-to-sym reg))))
+
+(defopcode op-retrs retrs auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (name reg) asm.(parse-args me syntax '(r rs))
+ asm.(put-insn me.code reg name)))
+
+ (:method dis (me asm reg name)
+ ^(,me.symbol ,(operand-to-sym name) ,(operand-to-sym reg))))
+
+(defopcode op-retrr retrr auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (name reg) asm.(parse-args me syntax '(r r))
+ asm.(put-insn me.code 0 reg)
+ asm.(put-pair 0 name)))
+
+ (:method dis (me asm extension reg)
+ (let ((name (cadr asm.(get-pair))))
+ ^(,me.symbol ,(operand-to-sym name) ,(operand-to-sym reg)))))
+
+(defopcode op-ret-pseudo ret nil
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (name reg) asm.(parse-args me syntax '(r r))
+ (let ((real [%oc-hash% (cond
+ ((< name 1024) 'retsr)
+ ((< reg 1024) 'retrs)
+ (t 'retrr))]))
+ real.(asm asm syntax)))))
+
+(defopcode op-catch catch auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 4 syntax)
+ (tree-bind (sym args catch-syms dst) asm.(parse-args me syntax '(r r r l))
+ asm.(put-insn me.code (ash dst -16) (logtrunc dst 16))
+ asm.(put-pair sym args)
+ asm.(put-pair 0 catch-syms)))
+
+ (:method backpatch (me asm at dst)
+ asm.(put-insn me.code (ash dst -16) (logtrunc dst 16)))
+
+ (:method dis (me asm high16 low16)
+ (let ((dst (logior (ash high16 16) low16)))
+ (tree-bind (sym args) asm.(get-pair)
+ (let ((catch-syms (cadr asm.(get-pair))))
+ ^(,me.symbol ,(operand-to-sym sym) ,(operand-to-sym args)
+ ,(operand-to-sym catch-syms) ,dst))))))
+
+(defopcode op-handle handle auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (fun handle-syms) asm.(parse-args me syntax '(r r))
+ asm.(put-insn me.code 0 fun)
+ asm.(put-pair fun handle-syms)))
+
+ (:method dis (me asm extension fun)
+ (let ((handle-syms (cadr asm.(get-pair))))
+ ^(,me.symbol ,(operand-to-sym fun) ,(operand-to-sym handle-syms)))))
+
+(defopcode op-getv getv auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (reg name) asm.(parse-args me syntax '(ds r))
+ asm.(put-insn me.code reg name)))
+ (:method dis (me asm reg name)
+ ^(,me.symbol ,(operand-to-sym reg) ,(operand-to-sym name))))
+
+(defopcode-derived op-getf getf auto op-getv)
+
+(defopcode-derived op-getl1 getl1 auto op-getv)
+
+(defopcode-derived op-getvb getvb auto op-getv)
+
+(defopcode-derived op-getfb getfb auto op-getv)
+
+(defopcode-derived op-getl1b getl1b auto op-getv)
+
+(defopcode op-setv setv auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count 2 syntax)
+ (tree-bind (reg name) asm.(parse-args me syntax '(s r))
+ asm.(put-insn me.code reg name)))
+ (:method dis (me asm reg name)
+ ^(,me.symbol ,(operand-to-sym reg) ,(operand-to-sym name))))
+
+(defopcode-derived op-setl1 setl1 auto op-setv)
+
+(defopcode-derived op-bindv bindv auto op-setv)
+
+(defopcode op-close close auto
+ (:method asm (me asm syntax)
+ me.(chk-arg-count-min 6 syntax)
+ (let* ((syn-pat (repeat '(d) (- (length syntax) 7))))
+ (tree-bind (reg frsize dst fix req vari . regs)
+ asm.(parse-args me syntax ^(d n l n n o ,*syn-pat))
+ (unless (<= 0 frsize 255)
+ me.(synerr "frame size must be 0 to 255"))
+ asm.(put-insn me.code (ash dst -16) (logtrunc dst 16))
+ asm.(put-pair (logior (ash (if vari 1 0) 8) frsize) reg)
+ asm.(put-pair req fix)
+ (while regs
+ (let ((x (pop regs))
+ (y (or (pop regs) 0)))
+ asm.(put-pair y x))))))
+
+ (:method backpatch (me asm at dst)
+ asm.(put-insn me.code (ash dst -16) (logtrunc dst 16)))
+
+ (:method dis (me asm high16 low16)
+ (let ((dst (logior (ash high16 16) low16)))
+ (tree-bind (vari-frsize reg) asm.(get-pair)
+ (let ((vari (bit vari-frsize 8)))
+ (tree-bind (req fix) asm.(get-pair)
+ (build
+ (add me.symbol (operand-to-sym reg) (logtrunc vari-frsize 8)
+ dst fix req vari)
+ (when vari
+ (inc fix))
+ (while (> fix 0)
+ (dec fix 2)
+ (tree-bind (y x) asm.(get-pair)
+ (add (operand-to-sym x))
+ (unless (minusp fix)
+ (add (operand-to-sym y))))))))))))
diff --git a/vm.c b/vm.c
new file mode 100644
index 00000000..1591644e
--- /dev/null
+++ b/vm.c
@@ -0,0 +1,818 @@
+/* Copyright 2018
+ * Kaz Kylheku <kaz@kylheku.com>
+ * Vancouver, Canada
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <signal.h>
+#include <assert.h>
+#include "config.h"
+#include ALLOCA_H
+#include "lib.h"
+#include "hash.h"
+#include "eval.h"
+#include "signal.h"
+#include "unwind.h"
+#include "gc.h"
+#include "args.h"
+#include "itypes.h"
+#include "buf.h"
+#include "vmop.h"
+#include "vm.h"
+
+typedef u32_t vm_word_t;
+
+#if HAVE_LITTLE_ENDIAN
+#define vm_opcode(vw) ((vw) >> 26)
+#define vm_oparg(vw) (((vw) >> 16) & 0x3FF)
+#else
+#define vm_opcode(vw) ((vw) >> 26)
+#define vm_oparg(vw) (((vw) >> 16) & 0x3FF)
+#endif
+
+#define zalloca(size) memset(alloca(size), 0, size)
+
+struct vm_desc {
+ val self;
+ int nlvl;
+ int nreg;
+ int frsz;
+ val bytecode;
+ val datavec;
+ vm_word_t *code;
+ val *data;
+};
+
+struct vm_env {
+ val *mem;
+ val vec;
+};
+
+struct vm {
+ struct vm_desc *vd;
+ int nlvl;
+ int lev;
+ unsigned ip;
+ vm_word_t *code;
+ struct vm_env *dspl;
+};
+
+struct vm_closure {
+ struct vm_desc *vd;
+ int frsz;
+ int nlvl;
+ unsigned ip;
+ struct vm_env *dspl;
+};
+
+val vm_desc_s, vm_closure_s;
+
+static_forward(struct cobj_ops vm_desc_ops);
+
+static_forward(struct cobj_ops vm_closure_ops);
+
+static struct vm_desc *vm_desc_struct(val obj)
+{
+ return coerce(struct vm_desc *, cobj_handle(obj, vm_desc_s));
+}
+
+val vm_make_desc(val nlevels, val nregs, val bytecode, val datavec)
+{
+ val self = lit("sys:vm-make-desc");
+ int nlvl = c_int(nlevels, self), nreg = c_int(nregs, self);
+
+ if (nlvl < 2 || nlvl > 256)
+ uw_throwf(error_s, lit("~a: nlevels must be 2 to 256; ~s given"),
+ self, nlevels, nao);
+
+ if (nreg < 1 || nreg > 256)
+ uw_throwf(error_s, lit("~a: nregs must be 1 to 256; ~s given"),
+ self, nregs, nao);
+
+ {
+ mem_t *code = buf_get(bytecode, self);
+ loc data_loc = vecref_l(datavec, zero);
+ struct vm_desc *vd = coerce(struct vm_desc *, chk_malloc(sizeof *vd));
+ val desc;
+
+ vd->nlvl = nlvl;
+ vd->nreg = nreg;
+ vd->code = coerce(vm_word_t *, code);
+ vd->data = valptr(data_loc);
+
+ vd->bytecode = nil;
+ vd->datavec = nil;
+
+ vd->frsz = nlvl * 2 + nreg;
+
+ vd->self = nil;
+
+ desc = cobj(coerce(mem_t *, vd), vm_desc_s, &vm_desc_ops);
+
+ vd->bytecode = bytecode;
+ vd->datavec = datavec;
+ vd->self = desc;
+
+ return desc;
+ }
+}
+
+static val vm_desc_bytecode(val desc)
+{
+ struct vm_desc *vd = vm_desc_struct(desc);
+ return vd->bytecode;
+}
+
+static void vm_desc_mark(val obj)
+{
+ struct vm_desc *vd = coerce(struct vm_desc *, obj->co.handle);
+ gc_mark(vd->bytecode);
+ gc_mark(vd->datavec);
+}
+
+static val vm_make_closure(struct vm *vm, int frsz)
+{
+ struct vm_env *dspl = coerce(struct vm_env *,
+ chk_calloc(vm->nlvl, sizeof *dspl));
+ struct vm_closure *vc = coerce(struct vm_closure *, chk_malloc(sizeof *vc));
+ val closure;
+ int i, j;
+
+ vc->frsz = frsz;
+ vc->ip = vm->ip;
+ vc->nlvl = vm->lev + 1;
+ vc->vd = vm->vd;
+ vc->dspl = dspl;
+
+ closure = cobj(coerce(mem_t *, vc), vm_closure_s, &vm_closure_ops);
+
+ for (i = 2; i < vc->nlvl; i++) {
+ struct vm_env *sdi = &vm->dspl[i];
+ struct vm_env *cdi = &dspl[i];
+ val vec = sdi->vec;
+ val *mem = sdi->mem;
+
+ switch (type(vec)) {
+ case NIL:
+ cdi->vec = nil;
+ cdi->mem = 0;
+ break;
+ case NUM:
+ {
+ int n = c_num(vec);
+ val heap_vec = vector(vec, nil);
+ cdi->vec = heap_vec;
+ cdi->mem = heap_vec->v.vec;
+ for (j = 0; j < n; j++)
+ heap_vec->v.vec[j] = mem[j];
+ mut(closure);
+ *sdi = *cdi;
+ break;
+ }
+ case VEC:
+ cdi->vec = vec;
+ cdi->mem = mem;
+ break;
+ default:
+ internal_error("bad vector in vm display");
+ }
+ }
+
+ return closure;
+}
+
+static void vm_closure_mark(val obj)
+{
+ struct vm_closure *vc = coerce(struct vm_closure *, obj->co.handle);
+ int i;
+
+ gc_mark(vc->vd->self);
+
+ for (i = 2; i < vc->nlvl; i++)
+ gc_mark(vc->dspl[i].vec);
+}
+
+static void vm_reset(struct vm *vm, struct vm_desc *vd,
+ struct vm_env *dspl,
+ int start_lev, unsigned start_ip)
+{
+ vm->vd = vd;
+ vm->nlvl = vd->nlvl;
+ vm->lev = start_lev;
+ vm->ip = start_ip;
+ vm->code = vd->code;
+ vm->dspl = dspl;
+}
+
+#define vm_insn_opcode(insn) coerce(vm_op_t, ((insn) >> 26))
+#define vm_insn_operand(insn) ((insn) & 0xFFFFU)
+#define vm_insn_extra(insn) (((insn) >> 16) & 0x3FF)
+#define vm_insn_bigop(insn) (((insn) & 0x3FFFFFFU))
+#define vm_arg_operand_lo(arg) ((arg) & 0xFFFFU)
+#define vm_arg_operand_hi(arg) ((arg) >> 16)
+
+static val vm_execute(struct vm *vm);
+
+INLINE val vm_get(struct vm_env *dspl, unsigned ref)
+{
+ return dspl[ref >> 8].mem[ref & 0xFF];
+}
+
+INLINE void vm_set(struct vm_env *dspl, unsigned ref, val newval)
+{
+ unsigned d = ref >> 8;
+ unsigned i = ref & 0xFF;
+ struct vm_env *env = &dspl[d];
+
+ if (d == 1)
+ uw_throwf(error_s, lit("modification of VM static data"), nao);
+
+ if (ref == 0)
+ uw_throwf(error_s, lit("modification of t00/nil"), nao);
+
+ env->mem[i] = newval;
+
+ if (is_ptr(env->vec))
+ mut(env->vec);
+}
+
+static void vm_frame(struct vm *vm, vm_word_t insn)
+{
+ int lev = vm_insn_extra(insn);
+ int size = vm_insn_operand(insn);
+
+ if (lev != vm->lev + 1)
+ uw_throwf(error_s, lit("frame level mismatch"), nao);
+
+ vm->lev = lev;
+ vm->dspl[lev].mem = coerce(val *, zalloca(size * sizeof (val *)));
+ vm->dspl[lev].vec = num_fast(size);
+ vm_execute(vm);
+ vm->lev = lev - 1;
+}
+
+static void vm_dframe(struct vm *vm, vm_word_t insn)
+{
+ val saved_dyn_env = dyn_env;
+ dyn_env = make_env(nil, nil, dyn_env);
+ vm_frame(vm, insn);
+ dyn_env = saved_dyn_env;
+}
+
+static val vm_end(struct vm *vm, vm_word_t insn)
+{
+ return vm_get(vm->dspl, vm_insn_operand(insn));
+}
+
+static val vm_fin(struct vm *vm, vm_word_t insn)
+{
+ vm->ip--;
+ return vm_get(vm->dspl, vm_insn_operand(insn));
+}
+
+static void vm_call(struct vm *vm, vm_word_t insn)
+{
+ unsigned nargs = vm_insn_extra(insn);
+ unsigned dest = vm_insn_operand(insn);
+ vm_word_t argw = vm->code[vm->ip++];
+ unsigned fun = vm_arg_operand_lo(argw);
+ val result;
+ args_decl (args, nargs < ARGS_MIN ? ARGS_MIN : nargs);
+
+ if (nargs--) {
+ args_add(args, vm_get(vm->dspl, vm_arg_operand_hi(argw)));
+
+ while (nargs >= 2) {
+ nargs -= 2;
+ argw = vm->code[vm->ip++];
+ args_add(args, vm_get(vm->dspl, vm_arg_operand_lo(argw)));
+ args_add(args, vm_get(vm->dspl, vm_arg_operand_hi(argw)));
+ }
+
+ if (nargs) {
+ argw = vm->code[vm->ip++];
+ args_add(args, vm_get(vm->dspl, vm_arg_operand_lo(argw)));
+ }
+ }
+
+ result = generic_funcall(vm_get(vm->dspl, fun), args);
+ vm_set(vm->dspl, dest, result);
+}
+
+static void vm_apply(struct vm *vm, vm_word_t insn)
+{
+ unsigned nargs = vm_insn_extra(insn);
+ unsigned dest = vm_insn_operand(insn);
+ vm_word_t argw = vm->code[vm->ip++];
+ unsigned fun = vm_arg_operand_lo(argw);
+ val result;
+ args_decl (args, nargs < ARGS_MIN ? ARGS_MIN : nargs);
+
+ if (nargs--) {
+ args_add(args, vm_get(vm->dspl, vm_arg_operand_hi(argw)));
+
+ while (nargs >= 2) {
+ nargs -= 2;
+ argw = vm->code[vm->ip++];
+ args_add(args, vm_get(vm->dspl, vm_arg_operand_lo(argw)));
+ args_add(args, vm_get(vm->dspl, vm_arg_operand_hi(argw)));
+ }
+
+ if (nargs) {
+ argw = vm->code[vm->ip++];
+ args_add(args, vm_get(vm->dspl, vm_arg_operand_lo(argw)));
+ }
+ }
+
+ result = apply_intrinsic(vm_get(vm->dspl, fun), args_get_list(args));
+ vm_set(vm->dspl, dest, result);
+}
+
+static void vm_movrs(struct vm *vm, vm_word_t insn)
+{
+ val datum = vm_get(vm->dspl, vm_insn_extra(insn));
+ vm_set(vm->dspl, vm_insn_operand(insn), datum);
+}
+
+static void vm_movsr(struct vm *vm, vm_word_t insn)
+{
+ val datum = vm_get(vm->dspl, vm_insn_operand(insn));
+ vm_set(vm->dspl, vm_insn_extra(insn), datum);
+}
+
+static void vm_movrr(struct vm *vm, vm_word_t insn)
+{
+ vm_word_t arg = vm->code[vm->ip++];
+ val datum = vm_get(vm->dspl, vm_arg_operand_lo(arg));
+ vm_set(vm->dspl, vm_insn_operand(insn), datum);
+}
+
+static void vm_movrsi(struct vm *vm, vm_word_t insn)
+{
+ unsigned dst = vm_insn_operand(insn);
+ ucnum negmask = ~convert(ucnum, 0x3FF);
+ ucnum imm = vm_insn_extra(insn);
+
+ if ((imm & TAG_MASK) == NUM && (imm & 0x200))
+ imm |= negmask;
+
+ vm_set(vm->dspl, dst, coerce(val, imm));
+}
+
+static void vm_movsmi(struct vm *vm, vm_word_t insn)
+{
+ unsigned dst = vm_insn_extra(insn);
+ ucnum negmask = ~convert(ucnum, 0xFFFF);
+ ucnum imm = vm_insn_operand(insn);
+
+ if ((imm & TAG_MASK) == NUM && (imm & 0x8000))
+ imm |= negmask;
+
+ vm_set(vm->dspl, dst, coerce(val, imm));
+}
+
+static void vm_movrbi(struct vm *vm, vm_word_t insn)
+{
+ unsigned dst = vm_insn_operand(insn);
+ ucnum negmask = ~convert(ucnum, 0xFFFFFFFF);
+ ucnum imm = vm->code[vm->ip++];
+
+ if ((imm & TAG_MASK) == NUM && (imm & 0x80000000))
+ imm |= negmask;
+
+ vm_set(vm->dspl, dst, coerce(val, imm));
+}
+
+static void vm_jmp(struct vm *vm, vm_word_t insn)
+{
+ vm->ip = vm_insn_bigop(insn);
+}
+
+static void vm_if(struct vm *vm, vm_word_t insn)
+{
+ unsigned ip = vm_insn_bigop(insn);
+ vm_word_t arg = vm->code[vm->ip++];
+ val test = vm_get(vm->dspl, vm_arg_operand_lo(arg));
+
+ if (!test)
+ vm->ip = vm_insn_bigop(ip);
+}
+
+static void vm_uwprot(struct vm *vm, vm_word_t insn)
+{
+ int saved_lev = vm->lev;
+ unsigned cleanup_ip = vm_insn_bigop(insn);
+
+ uw_simple_catch_begin;
+
+ vm_execute(vm);
+
+ uw_unwind {
+ vm->lev = saved_lev;
+ vm->ip = cleanup_ip;
+ vm_execute(vm);
+ }
+
+ uw_catch_end;
+}
+
+static void vm_block(struct vm *vm, vm_word_t insn)
+{
+ unsigned exitpt = vm_insn_bigop(insn);
+ vm_word_t arg = vm->code[vm->ip++];
+ unsigned outreg = vm_arg_operand_hi(arg);
+ unsigned blname = vm_arg_operand_lo(arg);
+ int saved_lev = vm->lev;
+
+ uw_block_begin (vm_get(vm->dspl, blname), result);
+ result = vm_execute(vm);
+ uw_block_end;
+
+ vm_set(vm->dspl, outreg, result);
+ vm->ip = exitpt;
+ vm->lev = saved_lev;
+}
+
+
+static void vm_retsr(struct vm *vm, vm_word_t insn)
+{
+ val res = vm_get(vm->dspl, vm_insn_operand(insn));
+ val tag = vm_get(vm->dspl, vm_insn_extra(insn));
+
+ uw_block_return(tag, res);
+}
+
+static void vm_retrs(struct vm *vm, vm_word_t insn)
+{
+ val res = vm_get(vm->dspl, vm_insn_extra(insn));
+ val tag = vm_get(vm->dspl, vm_insn_operand(insn));
+
+ uw_block_return(tag, res);
+}
+
+static void vm_retrr(struct vm *vm, vm_word_t insn)
+{
+ vm_word_t arg = vm->code[vm->ip++];
+ val res = vm_get(vm->dspl, vm_insn_operand(insn));
+ val tag = vm_get(vm->dspl, vm_arg_operand_lo(arg));
+
+ uw_block_return(tag, res);
+}
+
+static void vm_catch(struct vm *vm, vm_word_t insn)
+{
+ unsigned catch_ip = vm_insn_bigop(insn);
+ vm_word_t arg1 = vm->code[vm->ip++];
+ vm_word_t arg2 = vm->code[vm->ip++];
+ unsigned sym_reg = vm_arg_operand_hi(arg1);
+ unsigned args_reg = vm_arg_operand_lo(arg1);
+ val catch_syms = vm_get(vm->dspl, vm_arg_operand_lo(arg2));
+ int saved_lev = vm->lev;
+
+ uw_catch_begin (catch_syms, exsym, exvals);
+
+ vm_execute(vm);
+
+ uw_catch(exsym, exvals) {
+ vm_set(vm->dspl, sym_reg, exsym);
+ vm_set(vm->dspl, args_reg, exvals);
+
+ vm->ip = catch_ip;
+ vm->lev = saved_lev;
+
+ vm_execute(vm);
+ }
+
+ uw_unwind;
+
+ uw_catch_end;
+}
+
+static void vm_handle(struct vm *vm, vm_word_t insn)
+{
+ val fun = vm_get(vm->dspl, vm_insn_operand(insn));
+ vm_word_t arg1 = vm->code[vm->ip++];
+ val handle_syms = vm_get(vm->dspl, vm_arg_operand_lo(arg1));
+ uw_frame_t uw_handler;
+
+ uw_push_handler(&uw_handler, handle_syms, fun);
+
+ vm_execute(vm);
+
+ uw_pop_frame(&uw_handler);
+}
+
+static val vm_get_binding(struct vm *vm, vm_word_t insn,
+ val (*lookup_fn)(val env, val sym),
+ val kind_str)
+{
+ val sym = vm_get(vm->dspl, vm_insn_operand(insn));
+ val binding = lookup_fn(nil, sym);
+
+ if (nilp(binding))
+ eval_error(vm->vd->bytecode, lit("unbound ~a ~s"), kind_str, sym, nao);
+
+ return binding;
+}
+
+static void vm_getsym(struct vm *vm, vm_word_t insn,
+ val (*lookup_fn)(val env, val sym),
+ val kind_str)
+{
+ val binding = vm_get_binding(vm, insn, lookup_fn, kind_str);
+ int dst = vm_insn_extra(insn);
+ vm_set(vm->dspl, dst, cdr(binding));
+}
+
+static void vm_getbind(struct vm *vm, vm_word_t insn,
+ val (*lookup_fn)(val env, val sym),
+ val kind_str)
+{
+ val binding = vm_get_binding(vm, insn, lookup_fn, kind_str);
+ int dst = vm_insn_extra(insn);
+ vm_set(vm->dspl, dst, binding);
+}
+
+static void vm_setsym(struct vm *vm, vm_word_t insn,
+ val (*lookup_fn)(val env, val sym),
+ val kind_str)
+{
+ val binding = vm_get_binding(vm, insn, lookup_fn, kind_str);
+ int src = vm_insn_extra(insn);
+ rplacd(binding, vm_get(vm->dspl, src));
+}
+
+static void vm_bindv(struct vm *vm, vm_word_t insn)
+{
+ val sym = vm_get(vm->dspl, vm_insn_operand(insn));
+ int src = vm_insn_extra(insn);
+
+ if (nilp(dyn_env))
+ eval_error(vm->vd->bytecode,
+ lit("no environment for dynamic binding"), nao);
+
+ env_vbind(dyn_env, sym, vm_get(vm->dspl, src));
+}
+
+static void vm_close(struct vm *vm, vm_word_t insn)
+{
+ unsigned dst = vm_insn_bigop(insn);
+ vm_word_t arg1 = vm->code[vm->ip++];
+ vm_word_t arg2 = vm->code[vm->ip++];
+ unsigned vari_fr = vm_arg_operand_hi(arg1);
+ int variadic = vari_fr & 0x100;
+ int frsz = vari_fr & 0xFF;
+ unsigned reg = vm_arg_operand_lo(arg1);
+ int reqargs = vm_arg_operand_hi(arg2);
+ int fixparam = vm_arg_operand_lo(arg2);
+ val closure = vm_make_closure(vm, frsz);
+ val vf = func_vm(closure, vm->vd->self, fixparam, reqargs, variadic);
+
+ vm_set(vm->dspl, reg, vf);
+ vm->ip = dst;
+}
+
+static val vm_execute(struct vm *vm)
+{
+ for (;;) {
+ vm_word_t insn = vm->code[vm->ip++];
+ vm_op_t opcode = vm_insn_opcode(insn);
+
+ switch (opcode) {
+ case NOOP:
+ break;
+ case FRAME:
+ vm_frame(vm, insn);
+ break;
+ case DFRAME:
+ vm_dframe(vm, insn);
+ break;
+ case END:
+ return vm_end(vm, insn);
+ case FIN:
+ return vm_fin(vm, insn);
+ case CALL:
+ vm_call(vm, insn);
+ break;
+ case APPLY:
+ vm_apply(vm, insn);
+ break;
+ case MOVRS:
+ vm_movrs(vm, insn);
+ break;
+ case MOVSR:
+ vm_movsr(vm, insn);
+ break;
+ case MOVRR:
+ vm_movrr(vm, insn);
+ break;
+ case MOVRSI:
+ vm_movrsi(vm, insn);
+ break;
+ case MOVSMI:
+ vm_movsmi(vm, insn);
+ break;
+ case MOVRBI:
+ vm_movrbi(vm, insn);
+ break;
+ case JMP:
+ vm_jmp(vm, insn);
+ break;
+ case IF:
+ vm_if(vm, insn);
+ break;
+ case UWPROT:
+ vm_uwprot(vm, insn);
+ break;
+ case BLOCK:
+ vm_block(vm, insn);
+ break;
+ case RETSR:
+ vm_retsr(vm, insn);
+ break;
+ case RETRS:
+ vm_retrs(vm, insn);
+ break;
+ case RETRR:
+ vm_retrr(vm, insn);
+ break;
+ case CATCH:
+ vm_catch(vm, insn);
+ break;
+ case HANDLE:
+ vm_handle(vm, insn);
+ break;
+ case GETV:
+ vm_getsym(vm, insn, lookup_var, lit("variable"));
+ break;
+ case GETF:
+ vm_getsym(vm, insn, lookup_fun, lit("function"));
+ break;
+ case GETL1:
+ vm_getsym(vm, insn, lookup_sym_lisp1, lit("variable/function"));
+ break;
+ case GETVB:
+ vm_getbind(vm, insn, lookup_var, lit("variable"));
+ break;
+ case GETFB:
+ vm_getbind(vm, insn, lookup_fun, lit("function"));
+ break;
+ case GETL1B:
+ vm_getbind(vm, insn, lookup_sym_lisp1, lit("variable/function"));
+ break;
+ case SETV:
+ vm_setsym(vm, insn, lookup_var, lit("variable"));
+ break;
+ case SETL1:
+ vm_setsym(vm, insn, lookup_sym_lisp1, lit("variable/function"));
+ break;
+ case BINDV:
+ vm_bindv(vm, insn);
+ break;
+ case CLOSE:
+ vm_close(vm, insn);
+ break;
+ default:
+ uw_throwf(error_s, lit("invalid opcode ~s"), num_fast(opcode), nao);
+ }
+ }
+}
+
+val vm_execute_toplevel(val desc)
+{
+ struct vm_desc *vd = vm_desc_struct(desc);
+ struct vm vm;
+ val *frame = coerce(val *, alloca(sizeof *frame * vd->frsz));
+ struct vm_env *dspl = coerce(struct vm_env *, frame + vd->nreg);
+
+ vm_reset(&vm, vd, dspl, 1, 0);
+
+ vm.dspl = coerce(struct vm_env *, frame + vd->nreg);
+
+ frame[0] = nil;
+
+ vm.dspl[0].mem = frame;
+ vm.dspl[0].vec = nil;
+
+ vm.dspl[1].mem = vd->data;
+ vm.dspl[0].vec = vd->datavec;
+
+ return vm_execute(&vm);
+}
+
+val vm_execute_closure(val fun, struct args *args)
+{
+ val closure = fun->f.env;
+ val desc = fun->f.f.vm_desc;
+ int fixparam = fun->f.fixparam;
+ int variadic = fun->f.variadic;
+ int nargs = fixparam + variadic;
+ struct vm_desc *vd = vm_desc_struct(desc);
+ struct vm_closure *vc = coerce(struct vm_closure *, closure->co.handle);
+ struct vm vm;
+ val *frame = coerce(val *, alloca(sizeof *frame * vd->frsz));
+ struct vm_env *dspl = coerce(struct vm_env *, frame + vd->nreg);
+ val vargs = if3(variadic, args_get_rest(args, nargs), nil);
+ cnum ix = 0;
+ vm_word_t argw = 0;
+
+ vm_reset(&vm, vd, dspl, vc->nlvl - 1, vc->ip);
+
+ vm.dspl = coerce(struct vm_env *, frame + vd->nreg);
+
+ frame[0] = nil;
+
+ vm.dspl[0].mem = frame;
+ vm.dspl[0].vec = nil;
+
+ vm.dspl[1].mem = vd->data;
+ vm.dspl[1].vec = vd->datavec;
+
+ memcpy(vm.dspl + 2, vc->dspl + 2, (vc->nlvl - 2) * sizeof *vm.dspl);
+
+ if (vc->frsz != 0) {
+ vm.lev++;
+ vm.dspl[vm.lev].mem = coerce(val *, zalloca(vc->frsz * sizeof (val *)));
+ vm.dspl[vm.lev].vec = num_fast(vc->frsz);
+ }
+
+ while (nargs >= 2) {
+ nargs -= 2;
+ argw = vm.code[vm.ip++];
+ unsigned xreg = vm_arg_operand_lo(argw);
+ unsigned yreg = vm_arg_operand_hi(argw);
+ vm_set(dspl, xreg, args_get(args, &ix));
+ vm_set(dspl, yreg, args_get(args, &ix));
+ }
+
+ if (nargs) {
+ argw = vm.code[vm.ip++];
+ unsigned xreg = vm_arg_operand_lo(argw);
+ vm_set(dspl, xreg, args_get(args, &ix));
+ }
+
+ if (variadic) {
+ unsigned vreg;
+ if (!nargs) {
+ argw = vm.code[vm.ip++];
+ vreg = vm_arg_operand_lo(argw);
+ } else {
+ vreg = vm_arg_operand_hi(argw);
+ }
+
+ vm_set(dspl, vreg, vargs);
+ }
+
+ return vm_execute(&vm);
+}
+
+static_def(struct cobj_ops vm_desc_ops =
+ cobj_ops_init(eq,
+ cobj_print_op,
+ cobj_destroy_free_op,
+ vm_desc_mark,
+ cobj_eq_hash_op));
+
+static_def(struct cobj_ops vm_closure_ops =
+ cobj_ops_init(eq,
+ cobj_print_op,
+ cobj_destroy_free_op,
+ vm_closure_mark,
+ cobj_eq_hash_op));
+
+void vm_init(void)
+{
+ vm_desc_s = intern(lit("vm-desc"), system_package);
+ vm_closure_s = intern(lit("vm-closure"), system_package);
+ reg_fun(intern(lit("vm-make-desc"), system_package), func_n4(vm_make_desc));
+ reg_fun(intern(lit("vm-desc-bytecode"), system_package), func_n1(vm_desc_bytecode));
+ reg_fun(intern(lit("vm-interpret-toplevel"), system_package), func_n1(vm_execute_toplevel));
+}
diff --git a/vm.h b/vm.h
new file mode 100644
index 00000000..42bd2b36
--- /dev/null
+++ b/vm.h
@@ -0,0 +1,31 @@
+/* Copyright 2018
+ * Kaz Kylheku <kaz@kylheku.com>
+ * Vancouver, Canada
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+val vm_make_desc(val nlevels, val nregs, val bytecode, val datavec);
+val vm_execute_toplevel(val desc);
+val vm_execute_closure(val fun, struct args *);
+void vm_init(void);
diff --git a/vmop.h b/vmop.h
new file mode 100644
index 00000000..6f4d518a
--- /dev/null
+++ b/vmop.h
@@ -0,0 +1,61 @@
+/* Copyright 2018
+ * Kaz Kylheku <kaz@kylheku.com>
+ * Vancouver, Canada
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+typedef enum vm_op {
+ NOOP = 0,
+ FRAME = 1,
+ DFRAME = 2,
+ END = 3,
+ FIN = 4,
+ CALL = 5,
+ APPLY = 6,
+ MOVRS = 7,
+ MOVSR = 8,
+ MOVRR = 9,
+ MOVRSI = 10,
+ MOVSMI = 11,
+ MOVRBI = 12,
+ JMP = 13,
+ IF = 14,
+ UWPROT = 15,
+ BLOCK = 16,
+ RETSR = 17,
+ RETRS = 18,
+ RETRR = 19,
+ CATCH = 20,
+ HANDLE = 21,
+ GETV = 22,
+ GETF = 23,
+ GETL1 = 24,
+ GETVB = 25,
+ GETFB = 26,
+ GETL1B = 27,
+ SETV = 28,
+ SETL1 = 29,
+ BINDV = 30,
+ CLOSE = 31,
+} vm_op_t;