shithub: sl

ref: fda7b9cf12d964a4a482fc252ef26dc29e7eaed1
dir: /src/vm.h/

View raw version
#define fixnum_neg(x) ( \
	i64 = -(s64int)(numval(x)), \
	i64 == INT64_MIN \
		? mk_bignum(uvtomp((u64int)INT64_MAX+1, nil)) \
		: (fits_fixnum(i64) ? fixnum(i64) : mk_bignum(vtomp(i64, nil))) \
)

#define ENTER do{ \
	sl.stack[ipd] = (uintptr)ip; \
}while(0)

#define STACK(n) do{ \
	while(sl.sp+(n) > sl.stackend-4) \
		sl_stack_grow(); \
}while(0)

OP(OP_LOADA0)
	PUSH(sl.stack[obp+0]);
	NEXT_OP;

OP(OP_CALL) {
	tail = false;
	if(0){
OP(OP_TCALL)
		tail = true;
	}
	n = *ip++; // nargs
	if(0){
OP(OP_TCALLL)
		tail = true;
		if(0){
OP(OP_CALLL)
			tail = false;
		}
		n = GET_S32(ip);
		ip += 4;
	}
LABEL(do_call):
	ENTER;
	sl_v v = sl.sp[-n-1];
	if(tag(v) == TAG_FN){
		if(!isfnbuiltin(v)){
			nargs = n;
			if(tail){
				sl.curr_frame = sl.stack[sl.curr_frame-3];
				for(sl_fx s = -1; s < (sl_fx)n; s++)
					sl.stack[obp+s] = sl.sp[s-n];
				sl.sp = &sl.stack[obp+n];
			}else{
LABEL(apply_func):
				obp = sl.sp-sl.stack-nargs;
			}
			sl_fn *fn = (sl_fn*)ptr(sl.stack[obp-1]);
			assert(ismanaged(fn));
			stack_for(fn->maxstack);
			ip = cvalue_data(fn->bcode);
			assert(!ismanaged((uintptr)ip));
			PUSH(fn->env);
			PUSH(sl.curr_frame);
			PUSH(nargs);
			ipd = sl.sp++ - sl.stack;
			sl.curr_frame = ipd + 1;
			NEXT_OP;
		}
		int i = uintval(v);
		if(!isbuiltin(v))
			type_error("builtin", "fn", v);
		sl_fx s = builtins[i].nargs;
		if(s >= 0)
			argcount(n, s);
		else if(s != ANYARGS && n < -s)
			argcount(n, -s);
		// remove function arg
		for(sl_v *p = sl.sp-n-1; p < sl.sp-1; p++)
			p[0] = p[1];
		POPN(1);
		switch(i){
		case OP_VEC: goto LABEL(apply_vec);
		case OP_ADD: goto LABEL(apply_add);
		case OP_LIST: goto LABEL(apply_list);
		case OP_APPLY: goto LABEL(apply_apply);
		case OP_SUB: goto LABEL(apply_sub);
		case OP_MUL: goto LABEL(apply_mul);
		case OP_DIV: goto LABEL(apply_div);
		case OP_AREF: goto LABEL(apply_aref);
		case OP_ASET: goto LABEL(apply_aset);
		case OP_LT: goto LABEL(apply_lt);
		case OP_NUMEQP: goto LABEL(apply_numeqp);
		default:
#if defined(COMPUTED_GOTO)
			goto *ops[i];
#else
			op = i;
			continue;
#endif
		}
	}else if(sl_likely(iscbuiltin(v))){
		builtin_t f = ((sl_cv*)ptr(v))->cbuiltin;
		stack_for(4);
		PUSH(sl_nil); // fn->env;
		PUSH(sl.curr_frame);
		PUSH(n);
		PUSH(f); // ip
		sl.curr_frame = sl.sp - sl.stack;
		v = f(sl.sp-n-4, n);
		sl.sp = sl.stack + sl.curr_frame;
		sl.curr_frame = sl.sp[-3];
		sl.sp -= 4+n;
		sl.sp[-1] = v;
		NEXT_OP;
	}
	type_error(tail ? "tcall" : "call", "fn", v);
}

OP(OP_ARGC) {
	int na = *ip++;
	if(0){
OP(OP_ARGCL)
		na = GET_S32(ip);
		ip += 4;
	}
	if(sl_unlikely(nargs != na)){
		ENTER;
		arity_error(nargs, na);
	}
	NEXT_OP;
}

OP(OP_LOADA1)
	PUSH(sl.stack[obp+1]);
	NEXT_OP;

OP(OP_RET) {
	sl_v v = POP();
	sl.sp = sl.stack + sl.curr_frame;
	sl.curr_frame = sl.sp[-3];
	if(sl.curr_frame == top_frame)
		return v;
	sl.sp -= 4+nargs;
	ipd = sl.curr_frame-1;
	ip = (u8int*)sl.stack[ipd];
	nargs = sl.stack[sl.curr_frame-2];
	obp = sl.curr_frame-4-nargs;
	sl.sp[-1] = v;
	NEXT_OP;
}

OP(OP_LOAD1)
	PUSH(fixnum(1));
	NEXT_OP;

OP(OP_LOADA)
	PUSH(sl.stack[obp + *ip++]);
	NEXT_OP;

OP(OP_BRN)
	ip += POP() == sl_nil ? GET_S16(ip) : 2;
	NEXT_OP;

OP(OP_LOADG) {
	sl_v v = fn_vals(sl.stack[obp-1]);
	assert(*ip < vec_size(v));
	v = vec_elt(v, *ip);
	ip++;
	if(0){
OP(OP_LOADGL)
		v = fn_vals(sl.stack[obp-1]);
		v = vec_elt(v, GET_S32(ip));
		ip += 4;
	}
	assert(issym(v));
	sl_sym *sym = ptr(v);
	if(sl_unlikely(sym->binding == sl_unbound)){
		ENTER;
		unbound_error(v);
	}
	PUSH(sym->binding);
	NEXT_OP;
}

OP(OP_LT) {
	n = *ip++;
LABEL(apply_lt):;
	int i = n;
	sl_v a = sl.sp[-i], b, v;
	for(v = sl_t; i > 1; a = b){
		i--;
		b = sl.sp[-i];
		if(bothfixnums(a, b)){
			if((sl_fx)a >= (sl_fx)b){
				v = sl_nil;
				break;
			}
		}else{
			int x = numeric_compare(a, b, false, false, false);
			if(x > 1)
				x = numval(sl_compare(a, b, false));
			if(x >= 0){
				v = sl_nil;
				break;
			}
		}
	}
	sl.sp -= n;
	PUSH(v);
	NEXT_OP;
}

OP(OP_LOADV) {
	sl_v v = fn_vals(sl.stack[obp-1]);
	assert(*ip < vec_size(v));
	PUSH(vec_elt(v, *ip++));
	assert(sl.sp < sl.stackend);
	NEXT_OP;
}

OP(OP_ADD2) {
	sl_fx a, b, q;
	sl_v v;
LABEL(do_add2):
	ENTER;
	if(0){
OP(OP_SUB2)
LABEL(do_sub2):
		ENTER;
		v = sl.sp[-1];
		s64int i64;
		b = isfixnum(v) ? fixnum_neg(v) : sl_neg(v);
	}else{
		b = sl.sp[-1];
	}
	a = sl.sp[-2];
	if(bothfixnums(a, b) && !sadd_overflow(a, b, &q))
		v = q;
	else{
		sl.sp[-1] = b;
		v = sl_add_any(sl.sp-2, 2);
	}
	POPN(1);
	sl.sp[-1] = v;
	NEXT_OP;
}

OP(OP_LOADI8)
	PUSH(fixnum((s8int)*ip++));
	NEXT_OP;

OP(OP_POP)
	POPN(1);
	NEXT_OP;

OP(OP_BRNN)
	ip += POP() != sl_nil ? GET_S16(ip) : 2;
	NEXT_OP;

OP(OP_DUP)
	sl.sp[0] = sl.sp[-1];
	sl.sp++;
	NEXT_OP;

OP(OP_LOADC0)
	PUSH(vec_elt(sl.stack[obp+nargs], 0));
	NEXT_OP;

OP(OP_CAR) {
	sl_v v = sl.sp[-1];
	if(sl_likely(iscons(v)))
		v = car_(v);
	else if(sl_unlikely(v != sl_nil)){
		ENTER;
		type_error("car", "cons", v);
	}
	sl.sp[-1] = v;
	NEXT_OP;
}

OP(OP_CLOSURE) {
	int x = *ip++;
	assert(x > 0);
	ENTER;
	sl_v v = alloc_vec(x, false);
	for(int i = 0; i < x; i++)
		vec_elt(v, i) = sl.sp[-x+i];
	POPN(x);
	PUSH(v);
	sl_fn *fn = (sl_fn*)alloc_words(sizeof(sl_fn)/sizeof(sl_v));
	sl_v e = sl.sp[-2];  // closure to copy
	assert(isfn(e));
	fn->vals = fn_vals(e);
	fn->bcode = fn_bcode(e);
	fn->env = sl.sp[-1];
	fn->name = fn_name(e);
	fn->maxstack = fn_maxstack(e);
	POPN(1);
	sl.sp[-1] = tagptr(fn, TAG_FN);
	NEXT_OP;
}

OP(OP_CONS) {
	if(sl_unlikely((sl_v*)slg.curheap > (sl_v*)slg.lim))
		sl_gc(false);
	sl_cons *c = (sl_cons*)slg.curheap;
	slg.curheap += sizeof(sl_cons);
	c->car = sl.sp[-2];
	c->cdr = sl.sp[-1];
	sl.sp[-2] = tagptr(c, TAG_CONS);
	POPN(1);
	NEXT_OP;
}

OP(OP_BRNE)
	ip += sl.sp[-2] != sl.sp[-1] ? GET_S16(ip) : 2;
	sl.sp -= 2;
	NEXT_OP;

OP(OP_CDR) {
	sl_v v = sl.sp[-1];
	if(sl_likely(iscons(v)))
		v = cdr_(v);
	else if(sl_unlikely(v != sl_nil)){
		ENTER;
		type_error("cdr", "cons", v);
	}
	sl.sp[-1] = v;
	NEXT_OP;
}

OP(OP_LOADVOID)
	PUSH(sl_void);
	NEXT_OP;

OP(OP_NOT)
	sl.sp[-1] = sl.sp[-1] == sl_nil ? sl_t : sl_nil;
	NEXT_OP;

OP(OP_SETA)
	sl.stack[obp + *ip++] = sl.sp[-1];
	NEXT_OP;

OP(OP_VARGC) {
	int i = *ip++;
	if(0){
OP(OP_VARGCL)
		i = GET_S32(ip);
		ip += 4;
	}
	sl_fx s = (sl_fx)nargs - (sl_fx)i;
	ENTER;
	if(s > 0){
		sl_v v = list(&sl.stack[obp+i], s, false);
		sl.stack[obp+i] = v;
		if(s > 1){
			sl.stack[obp+i+1] = sl.stack[obp+nargs+0];
			sl.stack[obp+i+2] = sl.stack[obp+nargs+1];
			sl.stack[obp+i+3] = i+1;
			sl.stack[obp+i+4] = 0;
			sl.sp = sl.stack+obp+i+5;
			sl.curr_frame = sl.sp - sl.stack;
		}
	}else if(sl_unlikely(s < 0)){
		lerrorf(sl_errarg, "too few arguments");
	}else{
		sl.sp++;
		sl.sp[-2] = i+1;
		sl.sp[-3] = sl.sp[-4];
		sl.sp[-4] = sl.sp[-5];
		sl.sp[-5] = sl_nil;
		sl.curr_frame = sl.sp - sl.stack;
	}
	ipd = sl.curr_frame-1;
	nargs = i+1;
	NEXT_OP;
}

OP(OP_SHIFT) {
	int i = *ip++;
	sl.sp[-1-i] = sl.sp[-1];
	sl.sp -= i;
	NEXT_OP;
}

OP(OP_SETCAR) {
	sl_v v = sl.sp[-2];
	if(sl_unlikely(!iscons(v))){
		ENTER;
		type_error("set-car!", "cons", v);
	}
	car_(v) = sl.sp[-1];
	POPN(1);
	NEXT_OP;
}

OP(OP_LOADNIL)
	PUSH(sl_nil);
	NEXT_OP;

OP(OP_BOX) {
	int i = *ip++;
	ENTER;
	sl_v v = alloc_cons();
	car_(v) = sl.stack[obp+i];
	cdr_(v) = sl_nil;
	sl.stack[obp+i] = v;
	NEXT_OP;
}

OP(OP_JMP)
	ip += GET_S16(ip);
	NEXT_OP;

OP(OP_ATOMP)
	sl.sp[-1] = iscons(sl.sp[-1]) ? sl_nil : sl_t;
	NEXT_OP;

OP(OP_AREF2) {
	ENTER;
	n = 2;
	if(0){
OP(OP_AREF)
	ENTER;
	n = 3 + *ip++;
	}
LABEL(apply_aref):;
	sl_v v = sl.sp[-n];
	for(int i = n-1; i > 0; i--){
		sl_v e = sl.sp[-i];
		usize isz = tosize(e);
		if(isarr(v)){
			sl.sp[-i-1] = v;
			v = cvalue_arr_aref(sl.sp-i-1);
			continue;
		}
		if(isvec(v)){
			if(sl_unlikely(isz >= vec_size(v)))
				bounds_error(v, e);
			v = vec_elt(v, isz);
			continue;
		}
		if(!iscons(v))
			type_error("aref", "sequence", v);
		for(sl_v v0 = v;; isz--){
			if(isz == 0){
				v = car_(v);
				break;
			}
			v = cdr_(v);
			if(sl_unlikely(!iscons(v)))
				bounds_error(v0, e);
		}
	}
	sl.sp -= n;
	PUSH(v);
	NEXT_OP;
}

OP(OP_NANP) {
	sl_v v = sl.sp[-1];
	if(!iscvalue(v))
		v = sl_nil;
	else{
		sl_cv *p = ptr(v);
		switch(cv_numtype(p)){
		case T_F64:
			v = isnan(*(double*)cv_data(p)) ? sl_t : sl_nil;
			break;
		case T_F32:
			v = isnan(*(float*)cv_data(p)) ? sl_t : sl_nil;
			break;
		default:
			v = sl_nil;
			break;
		}
	}
	sl.sp[-1] = v;
	NEXT_OP;
}

OP(OP_LOAD0)
	PUSH(fixnum(0));
	NEXT_OP;

OP(OP_SETCDR) {
	sl_v v = sl.sp[-2];
	if(sl_unlikely(!iscons(v))){
		ENTER;
		type_error("set-cdr!", "cons", v);
	}
	cdr_(v) = sl.sp[-1];
	POPN(1);
	NEXT_OP;
}

OP(OP_LOADC1)
	PUSH(vec_elt(sl.stack[obp+nargs], 1));
	NEXT_OP;

OP(OP_ASET) {
	ENTER;
	sl_v v = sl.sp[-3];
	n = 3;
	if(0){
LABEL(apply_aset):
		v = sl.sp[-n];
		for(int i = n-1; i >= 3; i--){
			if(isarr(v)){
				sl.sp[-i-1] = v;
				v = cvalue_arr_aref(sl.sp-i-1);
				continue;
			}
			sl_v e = sl.sp[-i];
			usize isz = tosize(e);
			if(isvec(v)){
				if(sl_unlikely(isz >= vec_size(v)))
					bounds_error(v, e);
				v = vec_elt(v, isz);
				continue;
			}
			if(sl_unlikely(!iscons(v)))
				type_error("aset!", "sequence", v);
			for(sl_v v0 = v;; isz--){
				if(isz == 0){
					v = car_(v);
					break;
				}
				v = cdr_(v);
				if(sl_unlikely(!iscons(v)))
					bounds_error(v0, e);
			}
		}
		sl.sp[-3] = v;
	}
	sl_v e = sl.sp[-2];
	usize isz = tosize(e);
	if(isvec(v)){
		if(sl_unlikely(isz >= vec_size(v)))
			bounds_error(v, e);
		vec_elt(v, isz) = (e = sl.sp[-1]);
	}else if(iscons(v)){
		for(sl_v v0 = v;; isz--){
			if(isz == 0){
				car_(v) = (e = sl.sp[-1]);
				break;
			}
			v = cdr_(v);
			if(sl_unlikely(!iscons(v)))
				bounds_error(v0, e);
		}
	}else if(isarr(v)){
		e = cvalue_arr_aset(sl.sp-3);
	}else{
		type_error("aset!", "sequence", v);
	}
	sl.sp -= n;
	PUSH(e);
	NEXT_OP;
}

OP(OP_EQUALP) {
	sl_v a = sl.sp[-2], b = sl.sp[-1];
	POPN(1);
	sl.sp[-1] = (a == b || sl_compare(a, b, true) == 0) ? sl_t : sl_nil;
	NEXT_OP;
}

OP(OP_CONSP)
	sl.sp[-1] = iscons(sl.sp[-1]) ? sl_t : sl_nil;
	NEXT_OP;

OP(OP_LOADC) {
	sl_v v = sl.stack[obp+nargs];
	int i = *ip++;
	assert(isvec(v));
	assert(i < (int)vec_size(v));
	PUSH(vec_elt(v, i));
	NEXT_OP;
}

OP(OP_SYMP)
	sl.sp[-1] = issym(sl.sp[-1]) ? sl_t : sl_nil;
	NEXT_OP;

OP(OP_NUMP)
	sl.sp[-1] = isnum(sl.sp[-1]) ? sl_t : sl_nil;
	NEXT_OP;

OP(OP_BOUNDA)
	PUSH(sl.stack[obp+GET_S32(ip)] != sl_unbound ? sl_t : sl_nil);
	ip += 4;
	NEXT_OP;

OP(OP_OPTARGS) {
	int i = GET_S32(ip);
	ip += 4;
	int x = GET_S32(ip);
	ip += 4;
	if(sl_unlikely(nargs < i)){
		ENTER;
		lerrorf(sl_errarg, "too few arguments");
	}
	if(x > 0){
		if(sl_unlikely(nargs > x)){
			ENTER;
			lerrorf(sl_errarg, "too many arguments");
		}
	}else
		x = -x;
	if(sl_likely(x > nargs)){
		x -= nargs;
		sl.sp += x;
		sl.sp[-1] = sl.sp[-x-1];
		sl.sp[-2] = nargs+x;
		sl.sp[-3] = sl.sp[-x-3];
		sl.sp[-4] = sl.sp[-x-4];
		sl.curr_frame = sl.sp - sl.stack;
		ipd = sl.curr_frame - 1;
		for(i = 0; i < x; i++)
			sl.stack[obp+nargs+i] = sl_unbound;
		nargs += x;
	}
	NEXT_OP;
}

OP(OP_EQP)
	sl.sp[-2] = sl.sp[-2] == sl.sp[-1] ? sl_t : sl_nil;
	POPN(1);
	NEXT_OP;

OP(OP_LIST) {
	n = *ip++;
LABEL(apply_list):;
	ENTER;
	sl_v v = list(sl.sp-n, n, false);
	sl.sp -= n;
	PUSH(v);
	NEXT_OP;
}

OP(OP_BOUNDP) {
	ENTER;
	sl_sym *sym = tosym(sl.sp[-1]);
	sl.sp[-1] = sym->binding == sl_unbound ? sl_nil : sl_t;
	NEXT_OP;
}

OP(OP_NUMEQP) {
	n = *ip++;
LABEL(apply_numeqp):;
	int i = n;
	sl_v a = sl.sp[-i], b, v;
	for(v = sl_t; i > 1; a = b){
		i--;
		b = sl.sp[-i];
		if(bothfixnums(a, b)){
			if(a != b){
				v = sl_nil;
				break;
			}
		}else if(numeric_compare(a, b, true, false, true) != 0){
			v = sl_nil;
			break;
		}
	}
	sl.sp -= n;
	PUSH(v);
	NEXT_OP;
}

OP(OP_CADR) {
	sl_v v = sl.sp[-1];
	if(sl_likely(iscons(v))){
		v = cdr_(v);
		if(sl_likely(iscons(v)))
			v = car_(v);
		else
			goto LABEL(cadr_nil);
	}else{
LABEL(cadr_nil):
		if(sl_unlikely(v != sl_nil)){
			ENTER;
			type_error("cadr", "cons", v);
		}
	}
	sl.sp[-1] = v;
	NEXT_OP;
}

OP(OP_TAPPLY) {
	tail = true;
	if(0){
OP(OP_APPLY)
		tail = false;
	}
	n = *ip++;
LABEL(apply_apply):;
	sl_v v = POP(); // arglist
	n -= 2; // n-2 == # leading arguments not in the list
	if(v == sl_nil || iscons(v)){
		while(iscons(v)){
			n++;
			PUSH_SAFE(car_(v));
			v = cdr_(v);
		}
		if(v != sl_nil){
			ENTER;
			lerrorf(sl_errarg, "apply: last argument: not a list");
		}
	}else if(isvec(v)){
		usize s = vec_size(v);
		stack_for(s);
		memmove(sl.sp, &vec_elt(v, 0), s*sizeof(sl_v));
		sl.sp += s;
		n += s;
	}else{
		ENTER;
		lerrorf(sl_errarg, "apply: not a list or a vec");
	}
	goto LABEL(do_call);
}

OP(OP_LOADT)
	PUSH(sl_t);
	NEXT_OP;

OP(OP_BUILTINP) {
	sl_v v = sl.sp[-1];
	sl.sp[-1] = (isbuiltin(v) || iscbuiltin(v)) ? sl_t : sl_nil;
	NEXT_OP;
}

OP(OP_NEG) {
LABEL(do_neg):
	ENTER;
	sl_v v = sl.sp[-1];
	s64int i64;
	sl.sp[-1] = isfixnum(v) ? fixnum_neg(v) : sl_neg(v);
	NEXT_OP;
}

OP(OP_FIXNUMP)
	sl.sp[-1] = isfixnum(sl.sp[-1]) ? sl_t : sl_nil;
	NEXT_OP;

OP(OP_MUL) {
	n = *ip++;
LABEL(apply_mul):
	ENTER;
	sl_v v = sl_mul_any(sl.sp-n, n);
	sl.sp -= n;
	PUSH(v);
	NEXT_OP;
}

OP(OP_DIV0) {
	sl_v a = sl.sp[-2];
	sl_v b = sl.sp[-1];
	if(sl_unlikely(b == 0)){
		ENTER;
		divide_by_0_error();
	}
	sl_v v;
	if(bothfixnums(a, b))
		v = fixnum((sl_fx)a / (sl_fx)b);
	else{
		ENTER;
		v = sl_idiv2(a, b);
	}
	POPN(1);
	sl.sp[-1] = v;
	NEXT_OP;
}

OP(OP_DIV) {
	n = *ip++;
LABEL(apply_div):
	ENTER;
	sl_v *p = sl.sp-n;
	if(n == 1){
		sl.sp[-1] = sl_div2(fixnum(1), *p);
	}else{
		if(sl_unlikely(n > 2)){
			PUSH(*p);
			*p = fixnum(1);
			p[1] = sl_mul_any(p, n);
			*p = POP();
		}
		sl_v v = sl_div2(p[0], p[1]);
		sl.sp -= n;
		PUSH(v);
	}
	NEXT_OP;
}

OP(OP_VEC) {
	n = *ip++;
LABEL(apply_vec):;
	ENTER;
	int type = VEC_VEC;
	sl.sp -= n;
	if(*sl.sp == sl_vecstructsym){
		if(n < 2)
			arity_error(n, 2);
		sl.sp++;
		n--;
		type = VEC_STRUCT;
	}
	sl_v v = alloc_vec(n, 0);
	memcpy(&vec_elt(v, 0), sl.sp, n*sizeof(sl_v));
	if(type != VEC_VEC){
		POPN(1);
		vec_setsize(v, vec_size(v), type);
	}
	PUSH(v);
	NEXT_OP;
}

OP(OP_COMPARE)
	sl.sp[-2] = sl_compare(sl.sp[-2], sl.sp[-1], false);
	POPN(1);
	NEXT_OP;

OP(OP_FOR) {
	usize p = sl.sp - sl.stack;
	sl_v v;
	sl_fx s = tofixnum(sl.stack[p-3]);
	sl_fx hi = tofixnum(sl.stack[p-2]);
	sl.sp += 2;
	ENTER;
	for(v = sl_void; s <= hi; s++){
		sl.stack[p+0] = sl.stack[p-1];
		sl.stack[p+1] = fixnum(s);
		v = _applyn(1);
	}
	sl.sp -= 4;
	sl.stack[p+1] = v;
	NEXT_OP;
}

OP(OP_SETG) {
	int i = *ip++;
	if(0){
OP(OP_SETGL)
		i = GET_S32(ip);
		ip += 4;
	}
	sl_v v = fn_vals(sl.stack[obp-1]);
	assert(i < (int)vec_size(v));
	v = vec_elt(v, i);
	assert(issym(v));
	sl_sym *sym = ptr(v);
	if(sl_unlikely(isconst(sym))){
		ENTER;
		const_error(v);
	}
	sym->binding = sl.sp[-1];
	NEXT_OP;
}

OP(OP_VECP)
	sl.sp[-1] = isvec(sl.sp[-1]) && !isstruct(sl.sp[-1]) ? sl_t : sl_nil;
	NEXT_OP;

OP(OP_TRYCATCH) {
	ENTER;
	sl_v v = do_trycatch();
	POPN(1);
	sl.sp[-1] = v;
	NEXT_OP;
}

OP(OP_ADD) {
	n = *ip++;
	if(n == 2)
		goto LABEL(do_add2);
LABEL(apply_add):
	ENTER;
	sl_v v = sl_add_any(sl.sp-n, n);
	sl.sp -= n;
	PUSH(v);
	NEXT_OP;
}

OP(OP_LOADAL)
	assert(nargs > 0);
	PUSH(sl.stack[obp+GET_S32(ip)]);
	ip += 4;
	NEXT_OP;

OP(OP_EQVP) {
	sl_v a = sl.sp[-2], b = sl.sp[-1];
	sl.sp[-2] = (a == b || (leafp(a) && leafp(b) && sl_compare(a, b, true) == 0)) ? sl_t : sl_nil;
	POPN(1);
	NEXT_OP;
}

OP(OP_KEYARGS) {
	sl_v v = fn_vals(sl.stack[obp-1]);
	v = vec_elt(v, 0);
	int i = GET_S32(ip);
	ip += 4;
	int x = GET_S32(ip);
	ip += 4;
	sl_fx s = GET_S32(ip);
	ip += 4;
	ENTER;
	nargs = process_keys(v, i, x, labs(s)-(i+x), obp, nargs, s<0);
	ipd = sl.sp-sl.stack-1;
	NEXT_OP;
}

OP(OP_SUB) {
	n = *ip++;
LABEL(apply_sub):
	if(n == 2)
		goto LABEL(do_sub2);
	if(n == 1)
		goto LABEL(do_neg);
	ENTER;
	sl_v *p = sl.sp-n;
	// we need to pass the full arglist on to sl_add_any
	// so it can handle rest args properly
	PUSH(*p);
	*p = fixnum(0);
	sl_v v = sl_add_any(p, n);
	s64int i64;
	p[1] = isfixnum(v) ? fixnum_neg(v) : sl_neg(v);
	p[0] = POP();
	v = sl_add_any(p, 2);
	sl.sp -= n;
	PUSH(v);
	NEXT_OP;
}

OP(OP_BRNL)
	ip += POP() == sl_nil ? GET_S32(ip) : 4;
	NEXT_OP;

OP(OP_SETAL)
	sl.stack[obp+GET_S32(ip)] = sl.sp[-1];
	ip += 4;
	NEXT_OP;

OP(OP_BOXL) {
	int i = GET_S32(ip);
	ip += 4;
	ENTER;
	sl_v v = alloc_cons();
	car_(v) = sl.stack[obp+i];
	cdr_(v) = sl_nil;
	sl.stack[obp+i] = v;
	NEXT_OP;
}

OP(OP_FNP)
	sl.sp[-1] = isfn(sl.sp[-1]) ? sl_t : sl_nil;
	NEXT_OP;

OP(OP_JMPL)
	ip += GET_S32(ip);
	NEXT_OP;

OP(OP_BRNEL)
	ip += sl.sp[-2] != sl.sp[-1] ? GET_S32(ip) : 4;
	sl.sp -= 2;
	NEXT_OP;

OP(OP_BRNNL)
	ip += POP() != sl_nil ? GET_S32(ip) : 4;
	NEXT_OP;

OP(OP_LOADCL)
	ip += 4;
	PUSH(vec_elt(sl.stack[obp+nargs], GET_S32(ip)));
	ip += 4;
	NEXT_OP;

OP(OP_LOADVL) {
	sl_v v = fn_vals(sl.stack[obp-1]);
	v = vec_elt(v, GET_S32(ip));
	ip += 4;
	PUSH(v);
	NEXT_OP;
}

#undef fixnum_neg
#undef ENTER