picrin/src/vm.c

690 lines
14 KiB
C
Raw Normal View History

2013-10-12 05:48:35 -04:00
#include <stdio.h>
#include <stdarg.h>
2013-10-28 08:26:39 -04:00
#include <limits.h>
2013-10-11 11:16:19 -04:00
#include "picrin.h"
2013-10-19 23:34:57 -04:00
#include "picrin/pair.h"
2013-10-20 04:06:47 -04:00
#include "picrin/proc.h"
#include "picrin/irep.h"
2013-11-04 22:58:16 -05:00
#include "picrin/blob.h"
#define GET_OPERAND(pic,n) ((pic)->ci->fp[(n)])
2013-11-09 00:12:59 -05:00
struct pic_proc *
pic_get_proc(pic_state *pic)
{
pic_value v = GET_OPERAND(pic,0);
if (! pic_proc_p(v)) {
pic_error(pic, "fatal error");
}
return pic_proc_ptr(v);
}
int
pic_get_args(pic_state *pic, const char *format, ...)
{
char c;
int i = 1, argc = pic->ci->argc;
va_list ap;
bool opt = false;
va_start(ap, format);
while ((c = *format++)) {
switch (c) {
default:
if (argc <= i && ! opt) {
pic_error(pic, "wrong number of arguments");
}
break;
case '|':
break;
}
/* in order to run out of all arguments passed to this function
(i.e. do va_arg for each argument), optional argument existence
check is done in every case closure */
switch (c) {
case '|':
opt = true;
break;
case 'o':
{
pic_value *p;
p = va_arg(ap, pic_value*);
if (i < argc) {
*p = GET_OPERAND(pic,i);
i++;
}
}
break;
2013-10-15 10:25:07 -04:00
case 'f':
{
double *f;
f = va_arg(ap, double *);
if (i < argc) {
2013-10-27 11:33:24 -04:00
pic_value v;
v = GET_OPERAND(pic, i);
2013-10-27 11:40:10 -04:00
switch (pic_type(v)) {
case PIC_TT_FLOAT:
2013-10-27 11:33:24 -04:00
*f = pic_float(v);
2013-10-27 11:40:10 -04:00
break;
case PIC_TT_INT:
2013-10-27 11:33:24 -04:00
*f = pic_int(v);
2013-10-27 11:40:10 -04:00
break;
default:
pic_error(pic, "pic_get_args: expected float or int");
2013-10-27 11:33:24 -04:00
}
2013-10-27 11:40:10 -04:00
i++;
}
2013-10-20 22:42:21 -04:00
}
break;
2013-10-27 11:21:24 -04:00
case 'F':
{
double *f;
bool *e;
f = va_arg(ap, double *);
e = va_arg(ap, bool *);
if (i < argc) {
pic_value v;
v = GET_OPERAND(pic, i);
2013-10-27 11:40:10 -04:00
switch (pic_type(v)) {
case PIC_TT_FLOAT:
2013-10-27 11:21:24 -04:00
*f = pic_float(v);
*e = false;
2013-10-27 11:40:10 -04:00
break;
case PIC_TT_INT:
2013-10-27 11:21:24 -04:00
*f = pic_int(v);
*e = true;
2013-10-27 11:40:10 -04:00
break;
default:
pic_error(pic, "pic_get_args: expected float or int");
2013-10-27 11:21:24 -04:00
}
i++;
}
}
break;
case 'I':
{
int *k;
bool *e;
k = va_arg(ap, int *);
e = va_arg(ap, bool *);
if (i < argc) {
pic_value v;
v = GET_OPERAND(pic, i);
2013-10-27 11:40:10 -04:00
switch (pic_type(v)) {
case PIC_TT_FLOAT:
*k = (int)pic_float(v);
*e = false;
2013-10-27 11:40:10 -04:00
break;
case PIC_TT_INT:
*k = pic_int(v);
*e = true;
2013-10-27 11:40:10 -04:00
break;
default:
pic_error(pic, "pic_get_args: expected float or int");
}
i++;
}
}
break;
2013-11-04 20:53:33 -05:00
case 'i':
{
int *k;
k = va_arg(ap, int *);
if (i < argc) {
pic_value v;
v = GET_OPERAND(pic, i);
switch (pic_type(v)) {
case PIC_TT_FLOAT:
*k = (int)pic_float(v);
break;
case PIC_TT_INT:
*k = pic_int(v);
break;
default:
pic_error(pic, "pic_get_args: expected int");
}
i++;
}
}
break;
2013-10-20 22:42:21 -04:00
case 's':
{
pic_value str;
char **cstr;
size_t *len;
cstr = va_arg(ap, char **);
len = va_arg(ap, size_t *);
if (i < argc) {
str = GET_OPERAND(pic,i);
2013-10-27 11:40:10 -04:00
if (! pic_str_p(str)) {
pic_error(pic, "pic_get_args: expected string");
}
*cstr = pic_str_ptr(str)->str;
*len = pic_str_ptr(str)->len;
i++;
}
2013-10-15 10:25:07 -04:00
}
break;
2013-11-04 20:53:33 -05:00
case 'v':
{
struct pic_vector **vec;
pic_value v;
vec = va_arg(ap, struct pic_vector **);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_vec_p(v)) {
*vec = pic_vec_ptr(v);
}
else {
pic_error(pic, "pic_get_args: expected vector");
}
i++;
}
}
break;
2013-11-04 22:58:16 -05:00
case 'b':
{
struct pic_blob **b;
pic_value v;
b = va_arg(ap, struct pic_blob **);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_blob_p(v)) {
*b = pic_blob_ptr(v);
}
else {
pic_error(pic, "pic_get_args: expected bytevector");
}
i++;
}
}
break;
default:
{
pic_error(pic, "pic_get_args: invalid argument specifier given");
}
}
}
if (argc > i) {
pic_error(pic, "wrong number of arguments");
}
va_end(ap);
return i;
}
2013-11-09 00:13:14 -05:00
pic_value
pic_apply_argv(pic_state *pic, struct pic_proc *proc, size_t argc, ...)
{
va_list ap;
pic_value v;
va_start(ap, argc);
v = pic_nil_value();
while (argc--) {
v = pic_cons(pic, va_arg(ap, pic_value), v);
}
va_end(ap);
return pic_apply(pic, proc, v);
}
2013-11-01 06:02:46 -04:00
#if VM_DEBUG
# define OPCODE_EXEC_HOOK printf("OP = %d\n", c.insn)
#else
# define OPCODE_EXEC_HOOK ((void)0)
#endif
2013-10-17 00:54:48 -04:00
#if PIC_DIRECT_THREADED_VM
# define VM_LOOP JUMP;
2013-11-01 06:02:46 -04:00
# define CASE(x) L_##x: OPCODE_EXEC_HOOK;
# define NEXT c = *++pc; JUMP;
# define JUMP c = *pc; goto *oplabels[pc->insn];
2013-10-17 00:54:48 -04:00
# define VM_LOOP_END
#else
# define VM_LOOP for (;;) { c = *pc; switch (c.insn) {
2013-10-17 00:54:48 -04:00
# define CASE(x) case x:
# define NEXT pc++; break
# define JUMP break
# define VM_LOOP_END } }
#endif
2013-10-12 05:48:35 -04:00
#define PUSH(v) (*pic->sp++ = (v))
#define POP() (*--pic->sp)
#define POPN(i) (pic->sp -= (i))
#define PUSHCI() (++pic->ci)
#define POPCI() (pic->ci--)
2013-10-15 10:29:34 -04:00
2013-10-12 00:06:02 -04:00
pic_value
pic_apply(pic_state *pic, struct pic_proc *proc, pic_value argv)
2013-10-12 00:06:02 -04:00
{
struct pic_code *pc, c;
2013-10-13 04:29:21 -04:00
int ai = pic_gc_arena_preserve(pic);
2013-10-20 10:30:01 -04:00
jmp_buf jmp;
size_t argc, i;
struct pic_code boot[2];
2013-10-12 00:06:02 -04:00
2013-10-17 00:54:48 -04:00
#if PIC_DIRECT_THREADED_VM
static void *oplabels[] = {
2013-10-27 11:21:24 -04:00
&&L_OP_POP, &&L_OP_PUSHNIL, &&L_OP_PUSHTRUE, &&L_OP_PUSHFALSE, &&L_OP_PUSHFLOAT,
2013-11-04 21:37:18 -05:00
&&L_OP_PUSHINT, &&L_OP_PUSHCHAR, &&L_OP_PUSHCONST,
&&L_OP_GREF, &&L_OP_GSET, &&L_OP_LREF, &&L_OP_LSET, &&L_OP_CREF, &&L_OP_CSET,
&&L_OP_JMP, &&L_OP_JMPIF, &&L_OP_CALL, &&L_OP_TAILCALL, &&L_OP_RET, &&L_OP_LAMBDA,
&&L_OP_CONS, &&L_OP_CAR, &&L_OP_CDR, &&L_OP_NILP,
2013-11-06 22:17:37 -05:00
&&L_OP_ADD, &&L_OP_SUB, &&L_OP_MUL, &&L_OP_DIV, &&L_OP_MINUS,
2013-10-24 08:10:13 -04:00
&&L_OP_EQ, &&L_OP_LT, &&L_OP_LE, &&L_OP_STOP
2013-10-17 00:54:48 -04:00
};
#endif
2013-10-20 10:30:01 -04:00
if (setjmp(jmp) == 0) {
pic->jmp = &jmp;
}
else {
goto L_RAISE;
}
argc = pic_length(pic, argv) + 1;
2013-11-01 06:02:46 -04:00
#if VM_DEBUG
puts("== booting VM...");
printf(" proc = ");
pic_debug(pic, pic_obj_value(proc));
puts("");
printf(" argv = ");
pic_debug(pic, argv);
puts("");
if (! proc->cfunc_p) {
printf(" irep = ");
print_irep(pic, proc->u.irep);
}
else {
printf(" cfunc = %p\n", proc->u.cfunc);
}
2013-11-01 06:02:46 -04:00
puts("\nLet's go!");
#endif
PUSH(pic_obj_value(proc));
for (i = 1; i < argc; ++i) {
PUSH(pic_car(pic, argv));
argv = pic_cdr(pic, argv);
}
/* boot! */
boot[0].insn = OP_CALL;
boot[0].u.i = argc;
boot[1].insn = OP_STOP;
2013-10-30 02:40:40 -04:00
pc = boot;
c = *pc;
goto L_CALL;
2013-10-12 00:06:02 -04:00
2013-10-12 05:48:35 -04:00
VM_LOOP {
CASE(OP_POP) {
2013-10-19 14:05:42 -04:00
POPN(1);
NEXT;
}
2013-10-12 05:48:35 -04:00
CASE(OP_PUSHNIL) {
PUSH(pic_nil_value());
2013-10-16 00:17:01 -04:00
NEXT;
}
CASE(OP_PUSHTRUE) {
PUSH(pic_true_value());
NEXT;
}
CASE(OP_PUSHFALSE) {
PUSH(pic_false_value());
2013-10-12 05:48:35 -04:00
NEXT;
2013-10-12 00:06:02 -04:00
}
2013-10-27 11:21:24 -04:00
CASE(OP_PUSHFLOAT) {
PUSH(pic_float_value(c.u.f));
2013-10-12 05:48:35 -04:00
NEXT;
2013-10-12 00:06:02 -04:00
}
2013-10-27 11:21:24 -04:00
CASE(OP_PUSHINT) {
PUSH(pic_int_value(c.u.i));
2013-10-27 11:21:24 -04:00
NEXT;
}
2013-11-04 21:37:18 -05:00
CASE(OP_PUSHCHAR) {
PUSH(pic_char_value(c.u.c));
NEXT;
}
2013-10-20 20:29:56 -04:00
CASE(OP_PUSHCONST) {
PUSH(pic->pool[c.u.i]);
2013-10-20 20:29:56 -04:00
NEXT;
}
2013-10-12 05:48:35 -04:00
CASE(OP_GREF) {
PUSH(pic->globals[c.u.i]);
2013-10-12 05:48:35 -04:00
NEXT;
2013-10-12 01:40:55 -04:00
}
2013-10-12 05:48:35 -04:00
CASE(OP_GSET) {
pic->globals[c.u.i] = POP();
2013-10-12 05:48:35 -04:00
NEXT;
2013-10-12 01:40:55 -04:00
}
2013-10-16 04:20:53 -04:00
CASE(OP_LREF) {
PUSH(pic->ci->fp[c.u.i]);
2013-10-16 04:20:53 -04:00
NEXT;
}
2013-10-23 14:14:32 -04:00
CASE(OP_LSET) {
pic->ci->fp[c.u.i] = POP();
2013-10-23 14:14:32 -04:00
NEXT;
}
2013-10-23 13:04:49 -04:00
CASE(OP_CREF) {
2013-11-04 21:32:09 -05:00
int depth = c.u.r.depth;
2013-10-23 13:04:49 -04:00
struct pic_env *env;
2013-10-28 21:16:56 -04:00
env = pic->ci->env;
2013-10-23 13:04:49 -04:00
while (depth--) {
env = env->up;
}
2013-11-04 21:32:09 -05:00
PUSH(env->values[c.u.r.idx]);
2013-10-23 13:04:49 -04:00
NEXT;
}
2013-10-23 14:14:32 -04:00
CASE(OP_CSET) {
2013-11-04 21:32:09 -05:00
int depth = c.u.r.depth;
2013-10-23 14:14:32 -04:00
struct pic_env *env;
2013-10-28 21:16:56 -04:00
env = pic->ci->env;
2013-10-23 14:14:32 -04:00
while (depth--) {
env = env->up;
}
2013-11-04 21:32:09 -05:00
env->values[c.u.r.idx] = POP();
2013-10-23 14:14:32 -04:00
NEXT;
}
2013-10-16 04:42:47 -04:00
CASE(OP_JMP) {
pc += c.u.i;
2013-10-16 04:42:47 -04:00
JUMP;
}
CASE(OP_JMPIF) {
pic_value v;
v = POP();
if (! pic_false_p(v)) {
pc += c.u.i;
2013-10-16 04:42:47 -04:00
JUMP;
}
NEXT;
}
2013-10-15 06:18:33 -04:00
CASE(OP_CALL) {
pic_value x, v;
pic_callinfo *ci;
2013-10-15 06:18:33 -04:00
struct pic_proc *proc;
L_CALL:
x = pic->sp[-c.u.i];
if (! pic_proc_p(x)) {
2013-10-23 14:38:29 -04:00
pic->errmsg = "invalid application";
goto L_RAISE;
}
proc = pic_proc_ptr(x);
2013-10-15 10:29:34 -04:00
ci = PUSHCI();
ci->argc = c.u.i;
2013-10-16 02:30:52 -04:00
ci->pc = pc;
ci->fp = pic->sp - c.u.i;
ci->env = NULL;
if (pic_proc_cfunc_p(x)) {
2013-10-16 00:24:19 -04:00
v = proc->u.cfunc(pic);
pic->sp = ci->fp;
2013-10-15 22:28:57 -04:00
POPCI();
2013-10-16 00:24:19 -04:00
PUSH(v);
2013-10-16 02:30:52 -04:00
pic_gc_arena_restore(pic, ai);
NEXT;
2013-10-15 22:28:57 -04:00
}
else {
2013-10-23 13:51:02 -04:00
int i;
2013-10-27 05:13:36 -04:00
pic_value rest;
2013-10-23 13:51:02 -04:00
if (ci->argc != proc->u.irep->argc) {
2013-10-27 05:13:36 -04:00
if (! (proc->u.irep->varg && ci->argc >= proc->u.irep->argc)) {
pic->errmsg = "wrong number of arguments";
goto L_RAISE;
}
}
/* prepare rest args */
if (proc->u.irep->varg) {
2013-10-27 05:13:36 -04:00
rest = pic_nil_value();
for (i = 0; i < ci->argc - proc->u.irep->argc; ++i) {
pic_gc_protect(pic, v = POP());
rest = pic_cons(pic, v, rest);
}
PUSH(rest);
}
2013-10-28 21:16:56 -04:00
/* prepare env */
if (proc->u.irep->cv_num == 0) {
ci->env = proc->env;
}
else {
ci->env = (struct pic_env *)pic_obj_alloc(pic, sizeof(struct pic_env), PIC_TT_ENV);
ci->env->up = proc->env;
ci->env->valuec = proc->u.irep->cv_num;
ci->env->values = (pic_value *)pic_calloc(pic, ci->env->valuec, sizeof(pic_value));
for (i = 0; i < ci->env->valuec; ++i) {
ci->env->values[i] = ci->fp[proc->u.irep->cv_tbl[i]];
}
2013-10-23 13:51:02 -04:00
}
2013-10-28 21:16:56 -04:00
2013-10-16 02:30:52 -04:00
pc = proc->u.irep->code;
pic_gc_arena_restore(pic, ai);
JUMP;
2013-10-15 22:28:57 -04:00
}
2013-10-16 02:30:52 -04:00
}
2013-10-29 21:03:46 -04:00
CASE(OP_TAILCALL) {
int argc;
pic_value *argv;
argc = c.u.i;
2013-10-29 21:03:46 -04:00
argv = pic->sp - argc;
for (i = 0; i < argc; ++i) {
pic->ci->fp[i] = argv[i];
}
pic->sp = pic->ci->fp + argc;
pc = POPCI()->pc;
/* c is not changed */
2013-10-29 21:03:46 -04:00
goto L_CALL;
}
2013-10-16 02:30:52 -04:00
CASE(OP_RET) {
pic_value v;
pic_callinfo *ci;
2013-10-16 02:30:52 -04:00
2013-10-20 10:30:01 -04:00
if (pic->errmsg) {
2013-10-20 20:29:56 -04:00
L_RAISE:
2013-10-20 10:30:01 -04:00
goto L_STOP;
}
else {
v = POP();
ci = POPCI();
pc = ci->pc;
pic->sp = ci->fp;
2013-10-20 10:30:01 -04:00
PUSH(v);
}
2013-10-15 22:32:30 -04:00
NEXT;
}
CASE(OP_LAMBDA) {
struct pic_proc *proc;
2013-10-23 13:02:07 -04:00
proc = pic_proc_new(pic, pic->irep[c.u.i], pic->ci->env);
2013-10-15 22:32:30 -04:00
PUSH(pic_obj_value(proc));
pic_gc_arena_restore(pic, ai);
2013-10-15 06:18:33 -04:00
NEXT;
}
2013-10-12 05:48:35 -04:00
CASE(OP_CONS) {
2013-10-12 00:06:02 -04:00
pic_value a, b;
2013-10-13 04:29:21 -04:00
pic_gc_protect(pic, b = POP());
pic_gc_protect(pic, a = POP());
PUSH(pic_cons(pic, a, b));
2013-10-13 04:29:21 -04:00
pic_gc_arena_restore(pic, ai);
2013-10-12 05:48:35 -04:00
NEXT;
2013-10-12 00:06:02 -04:00
}
2013-10-19 14:48:06 -04:00
CASE(OP_CAR) {
pic_value p;
p = POP();
PUSH(pic_car(pic, p));
NEXT;
}
CASE(OP_CDR) {
pic_value p;
p = POP();
PUSH(pic_cdr(pic, p));
NEXT;
}
2013-10-19 14:53:02 -04:00
CASE(OP_NILP) {
pic_value p;
p = POP();
PUSH(pic_bool_value(pic_nil_p(p)));
NEXT;
}
2013-10-27 11:21:24 -04:00
#define DEFINE_ARITH_OP(opcode, op) \
CASE(opcode) { \
pic_value a, b; \
b = POP(); \
a = POP(); \
if (pic_int_p(a) && pic_int_p(b)) { \
2013-10-28 08:26:39 -04:00
double f = (double)pic_int(a) op (double)pic_int(b); \
if (INT_MIN <= f && f <= INT_MAX) { \
PUSH(pic_int_value((int)f)); \
} \
else { \
PUSH(pic_float_value(f)); \
} \
2013-10-27 11:21:24 -04:00
} \
else if (pic_float_p(a) && pic_float_p(b)) { \
PUSH(pic_float_value(pic_float(a) op pic_float(b))); \
} \
else if (pic_int_p(a) && pic_float_p(b)) { \
PUSH(pic_float_value(pic_int(a) op pic_float(b))); \
} \
else if (pic_float_p(a) && pic_int_p(b)) { \
PUSH(pic_float_value(pic_float(a) op pic_int(b))); \
} \
else { \
pic->errmsg = #op " got non-number operands"; \
goto L_RAISE; \
} \
NEXT; \
2013-10-15 08:29:07 -04:00
}
2013-10-27 11:21:24 -04:00
DEFINE_ARITH_OP(OP_ADD, +);
DEFINE_ARITH_OP(OP_SUB, -);
DEFINE_ARITH_OP(OP_MUL, *);
/* special care for (int / int) division */
2013-10-15 08:29:07 -04:00
CASE(OP_DIV) {
pic_value a, b;
b = POP();
a = POP();
2013-10-27 11:21:24 -04:00
if (pic_int_p(a) && pic_int_p(b)) {
PUSH(pic_float_value((double)pic_int(a) / pic_int(b)));
}
else if (pic_float_p(a) && pic_float_p(b)) {
PUSH(pic_float_value(pic_float(a) / pic_float(b)));
}
else if (pic_int_p(a) && pic_float_p(b)) {
PUSH(pic_float_value(pic_int(a) / pic_float(b)));
}
else if (pic_float_p(a) && pic_int_p(b)) {
PUSH(pic_float_value(pic_float(a) / pic_int(b)));
}
else {
pic->errmsg = "/ got non-number operands";
goto L_RAISE;
}
2013-10-24 08:10:13 -04:00
NEXT;
}
2013-10-27 11:21:24 -04:00
2013-11-06 22:17:37 -05:00
CASE(OP_MINUS) {
pic_value n;
n = POP();
if (pic_int_p(n)) {
PUSH(pic_int_value(-pic_int(n)));
}
else if (pic_float_p(n)) {
PUSH(pic_float_value(-pic_float(n)));
}
else {
pic->errmsg = "unary - got a non-number operand";
}
NEXT;
}
2013-10-27 11:21:24 -04:00
#define DEFINE_COMP_OP(opcode, op) \
CASE(opcode) { \
pic_value a, b; \
b = POP(); \
a = POP(); \
if (pic_int_p(a) && pic_int_p(b)) { \
PUSH(pic_bool_value(pic_int(a) op pic_int(b))); \
} \
else if (pic_float_p(a) && pic_float_p(b)) { \
PUSH(pic_bool_value(pic_float(a) op pic_float(b))); \
} \
else if (pic_int_p(a) && pic_int_p(b)) { \
PUSH(pic_bool_value(pic_int(a) op pic_float(b))); \
} \
else if (pic_float_p(a) && pic_int_p(b)) { \
PUSH(pic_bool_value(pic_float(a) op pic_int(b))); \
} \
else { \
pic->errmsg = #op " got non-number operands"; \
goto L_RAISE; \
} \
NEXT; \
2013-10-24 08:10:13 -04:00
}
2013-10-27 11:21:24 -04:00
DEFINE_COMP_OP(OP_EQ, ==);
DEFINE_COMP_OP(OP_LT, <);
DEFINE_COMP_OP(OP_LE, <=);
2013-10-12 05:48:35 -04:00
CASE(OP_STOP) {
2013-10-20 04:26:18 -04:00
pic_value val;
2013-10-12 00:06:02 -04:00
2013-10-20 10:30:01 -04:00
L_STOP:
2013-10-20 04:26:18 -04:00
val = POP();
2013-10-20 10:30:01 -04:00
pic->jmp = NULL;
if (pic->errmsg) {
return pic_undef_value();
}
2013-10-20 00:07:14 -04:00
#if VM_DEBUG
2013-10-20 04:26:18 -04:00
puts("**VM END STATE**");
2013-10-23 02:52:14 -04:00
printf("stbase\t= %p\nsp\t= %p\n", pic->stbase, pic->sp);
printf("cibase\t= %p\nci\t= %p\n", pic->cibase, pic->ci);
2013-10-29 21:05:21 -04:00
if (pic->stbase < pic->sp) {
2013-10-20 04:26:18 -04:00
pic_value *sp;
printf("* stack trace:");
for (sp = pic->stbase; pic->sp != sp; ++sp) {
pic_debug(pic, *sp);
puts("");
}
}
2013-10-29 21:05:21 -04:00
if (pic->stbase > pic->sp) {
puts("*** stack underflow!");
}
2013-10-16 02:30:52 -04:00
#endif
pic_gc_protect(pic, val);
2013-10-20 04:26:18 -04:00
return val;
}
} VM_LOOP_END;
2013-10-12 01:40:27 -04:00
}