shithub: sl

ref: 300cfb6caacb43cbe98e61d2d0e4e071fb507f75
dir: /src/vm.inc/

View raw version

#define fixnum_neg(x) ( \
	i64 = -(int64_t)(numval(x)), \
	i64 == INT64_MIN \
		? mk_mpint(uvtomp((uint64_t)INT64_MAX+1, nil)) \
		: (fits_fixnum(i64) ? fixnum(i64) : mk_mpint(vtomp(i64, nil))) \
)

OP(OP_LOADA0)
	PUSH(*bp);
	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_INT32(ip);
		ip += 4;
	}
LABEL(do_call):
	*ipd = (uintptr_t)ip;
	value_t v = FL(sp)[-n-1];
	if(tag(v) == TAG_FUNCTION){
		if(v > (N_BUILTINS<<3)){
			if(tail){
				FL(curr_frame) = (value_t*)FL(curr_frame)[-3];
				for(fixnum_t s = -1; s < (fixnum_t)n; s++)
					bp[s] = FL(sp)[s-n];
				FL(sp) = bp+n;
			}
			nargs = n;
			goto apply_cl_top;
		}
		int i = uintval(v);
		assert(isbuiltin(v));
		fixnum_t s = builtins[i].nargs;
		if(s >= 0)
			argcount(n, s);
		else if(s != ANYARGS && n < -s)
			argcount(n, -s);
		// remove function arg
		for(value_t *p = FL(sp)-n-1; p < FL(sp)-1; p++)
			p[0] = p[1];
		FL(sp)--;
		switch(i){
		case OP_VECTOR: goto LABEL(apply_vector);
		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_NUMEQ:  goto LABEL(apply_numeq);
		default:
#if defined(COMPUTED_GOTO)
			goto *ops[i];
#else
			op = i;
			continue;
#endif
		}
	}else if(fl_likely(iscbuiltin(v))){
		value_t *p = FL(sp) - n;
		v = ((cvalue_t*)ptr(v))->cbuiltin(p, n);
		FL(sp) = p;
		p[-1] = v;
		NEXT_OP;
	}
	type_error("function", v);
}

OP(OP_ARGC) {
	int na = *ip++;
	if(0){
OP(OP_ARGCL)
		na = GET_INT32(ip);
		ip += 4;
	}
	if(fl_unlikely(nargs != na)){
		*ipd = (uintptr_t)ip;
		arity_error(nargs, na);
	}
	NEXT_OP;
}

OP(OP_LOADA1)
	PUSH(bp[1]);
	NEXT_OP;

OP(OP_RET) {
	value_t v = POP();
	FL(sp) = FL(curr_frame);
	FL(curr_frame) = (value_t*)FL(sp)[-3];
	if(FL(curr_frame) == top_frame)
		return v;
	FL(sp) -= 4+nargs;
	ipd = FL(curr_frame)-1;
	ip = (uint8_t*)*ipd;
	nargs = FL(curr_frame)[-2];
	bp = FL(curr_frame) - 4 - nargs;
	FL(sp)[-1] = v;
	NEXT_OP;
}

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

OP(OP_LOADA)
	PUSH(bp[*ip++]);
	NEXT_OP;

OP(OP_BRN)
	ip += POP() == FL_nil ? GET_INT16(ip) : 2;
	NEXT_OP;

OP(OP_LOADG) {
	value_t v = fn_vals(bp[-1]);
	assert(*ip < vector_size(v));
	v = vector_elt(v, *ip);
	ip++;
	if(0){
OP(OP_LOADGL)
		v = fn_vals(bp[-1]);
		v = vector_elt(v, GET_INT32(ip));
		ip += 4;
	}
	assert(issymbol(v));
	symbol_t *sym = ptr(v);
	if(fl_unlikely(sym->binding == UNBOUND)){
		*ipd = (uintptr_t)ip;
		unbound_error(v);
	}
	PUSH(sym->binding);
	NEXT_OP;
}

OP(OP_LT)
	n = *ip++;
LABEL(apply_lt):
	{
		int i = n;
		value_t a = FL(sp)[-i], b, v;
		for(v = FL_t; i > 1; a = b){
			i--;
			b = FL(sp)[-i];
			if(bothfixnums(a, b)){
				if((fixnum_t)a >= (fixnum_t)b){
					v = FL_nil;
					break;
				}
			}else{
				int x = numeric_compare(a, b, false, false, false);
				if(x > 1)
					x = numval(fl_compare(a, b, false));
				if(x >= 0){
					v = FL_nil;
					break;
				}
			}
		}
		POPN(n);
		PUSH(v);
	}
	NEXT_OP;

OP(OP_LOADV) {
	value_t v = fn_vals(bp[-1]);
	assert(*ip < vector_size(v));
	PUSH(vector_elt(v, *ip++));
	NEXT_OP;
}

OP(OP_ADD2) {
	value_t v;
LABEL(do_add2):
	*ipd = (uintptr_t)ip;
	if(0){
OP(OP_SUB2)
LABEL(do_sub2):
		*ipd = (uintptr_t)ip;
		v = FL(sp)[-1];
		int64_t i64;
		FL(sp)[-1] = isfixnum(v) ? fixnum_neg(v) : fl_neg(v);
	}
	value_t a, b, q;
	a = FL(sp)[-2];
	b = FL(sp)[-1];
	if(bothfixnums(a, b) && !sadd_overflow(numval(a), numval(b), &q) && fits_fixnum(q))
		v = fixnum(q);
	else
		v = fl_add_any(FL(sp)-2, 2);
	POPN(1);
	FL(sp)[-1] = v;
	NEXT_OP;
}

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

OP(OP_POP)
	POPN(1);
	NEXT_OP;

OP(OP_BRNN)
	ip += POP() != FL_nil ? GET_INT16(ip) : 2;
	NEXT_OP;

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

OP(OP_LOADC0)
	PUSH(vector_elt(bp[nargs], 0));
	NEXT_OP;

OP(OP_CAR) {
	value_t v = FL(sp)[-1];
	if(fl_likely(iscons(v)))
		v = car_(v);
	else if(fl_unlikely(v != FL_nil)){
		*ipd = (uintptr_t)ip;
		type_error("cons", v);
	}
	FL(sp)[-1] = v;
	NEXT_OP;
}

OP(OP_CLOSURE) {
	int x = *ip++;
	assert(x > 0);
	value_t *pv = alloc_words(
		1+x+
#if !defined(BITS64)
		!(x&1)+
#endif
		sizeof(function_t)/sizeof(value_t));
	value_t v = tagptr(pv, TAG_VECTOR);
	*pv++ = fixnum(x);
	for(int i = 0; i < x; i++)
		*pv++ = FL(sp)[-x+i];
	POPN(x);
	PUSH(v);
#if !defined(BITS64)
	if((x & 1) == 0)
		pv++;
#endif
	function_t *f = (function_t*)pv;
	value_t e = FL(sp)[-2];  // closure to copy
	assert(isfunction(e));
	f->bcode = fn_bcode(e);
	f->vals = fn_vals(e);
	f->env = FL(sp)[-1];
	f->name = fn_name(e);
	POPN(1);
	FL(sp)[-1] = tagptr(f, TAG_FUNCTION);
	NEXT_OP;
}

OP(OP_CONS) {
	if(FL(curheap) > FL(lim))
		fl_gc(0);
	cons_t *c = (cons_t*)FL(curheap);
	FL(curheap) += sizeof(cons_t);
	c->car = FL(sp)[-2];
	c->cdr = FL(sp)[-1];
	FL(sp)[-2] = tagptr(c, TAG_CONS);
	POPN(1);
	NEXT_OP;
}

OP(OP_BRNE)
	ip += FL(sp)[-2] != FL(sp)[-1] ? GET_INT16(ip) : 2;
	POPN(2);
	NEXT_OP;

OP(OP_CDR) {
	value_t v = FL(sp)[-1];
	if(fl_likely(iscons(v)))
		v = cdr_(v);
	else if(fl_unlikely(v != FL_nil)){
		*ipd = (uintptr_t)ip;
		type_error("cons", v);
	}
	FL(sp)[-1] = v;
	NEXT_OP;
}

OP(OP_LOADVOID)
	PUSH(FL_void);
	NEXT_OP;

OP(OP_NOT)
	FL(sp)[-1] = FL(sp)[-1] == FL_nil ? FL_t : FL_nil;
	NEXT_OP;

OP(OP_SETA)
	bp[*ip++] = FL(sp)[-1];
	NEXT_OP;

OP(OP_VARGC) {
	int i = *ip++;
	if(0){
OP(OP_VARGCL)
		i = GET_INT32(ip);
		ip += 4;
	}
	fixnum_t s = (fixnum_t)nargs - (fixnum_t)i;
	if(s > 0){
		value_t v = list(bp+i, s, 0);
		bp[i] = v;
		if(s > 1){
			bp[i+1] = bp[nargs+0];
			bp[i+2] = bp[nargs+1];
			bp[i+3] = i+1;
			bp[i+4] = 0;
			FL(sp) = bp+i+5;
			FL(curr_frame) = FL(sp);
		}
	}else if(fl_unlikely(s < 0)){
		*ipd = (uintptr_t)ip;
		lerrorf(FL_ArgError, "too few arguments");
	}else{
		FL(sp)++;
		FL(sp)[-2] = i+1;
		FL(sp)[-3] = FL(sp)[-4];
		FL(sp)[-4] = FL(sp)[-5];
		FL(sp)[-5] = FL_nil;
		FL(curr_frame) = FL(sp);
	}
	ipd = FL(sp)-1;
	nargs = i+1;
	NEXT_OP;
}

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

OP(OP_SETCAR) {
	value_t v = FL(sp)[-2];
	if(fl_unlikely(!iscons(v))){
		*ipd = (uintptr_t)ip;
		type_error("cons", v);
	}
	car_(v) = FL(sp)[-1];
	POPN(1);
	NEXT_OP;
}

OP(OP_LOADNIL)
	PUSH(FL_nil);
	NEXT_OP;

OP(OP_BOX) {
	int i = *ip++;
	value_t v = mk_cons();
	car_(v) = bp[i];
	cdr_(v) = FL_nil;
	bp[i] = v;
	NEXT_OP;
}

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

OP(OP_ATOMP)
	FL(sp)[-1] = iscons(FL(sp)[-1]) ? FL_nil : FL_t;
	NEXT_OP;

OP(OP_AREF2) {
	n = 2;
	if(0){
OP(OP_AREF)
	*ipd = (uintptr_t)ip;
	n = 3 + *ip++;
	}
LABEL(apply_aref):;
	value_t v = FL(sp)[-n];
	for(int i = n-1; i > 0; i--){
		if(isarray(v)){
			FL(sp)[-i-1] = v;
			v = cvalue_array_aref(FL(sp)-i-1);
			continue;
		}
		value_t e = FL(sp)[-i];
		size_t isz = tosize(e);
		if(isvector(v)){
			if(fl_unlikely(isz >= vector_size(v)))
				bounds_error(v, e);
			v = vector_elt(v, isz);
			continue;
		}
		if(!iscons(v) && v != FL_nil)
			type_error("sequence", v);
		for(value_t v0 = v;; isz--){
			if(isz == 0){
				v = car_(v);
				break;
			}
			v = cdr_(v);
			if(fl_unlikely(!iscons(v)))
				bounds_error(v0, e);
		}
	}
	POPN(n);
	PUSH(v);
	NEXT_OP;
}

OP(OP_NANP) {
	value_t v = FL(sp)[-1];
	if(!iscprim(v))
		v = FL_nil;
	else{
		void *p = ptr(v);
		switch(cp_numtype(p)){
		case T_DOUBLE:
			v = isnan(*(double*)cp_data(p)) ? FL_t : FL_nil;
			break;
		case T_FLOAT:
			v = isnan(*(float*)cp_data(p)) ? FL_t : FL_nil;
			break;
		default:
			v = FL_nil;
			break;
		}
	}
	FL(sp)[-1] = v;
	NEXT_OP;
}

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

OP(OP_SETCDR) {
	value_t v = FL(sp)[-2];
	if(fl_unlikely(!iscons(v))){
		*ipd = (uintptr_t)ip;
		type_error("cons", v);
	}
	cdr_(v) = FL(sp)[-1];
	POPN(1);
	NEXT_OP;
}

OP(OP_LOADC1)
	PUSH(vector_elt(bp[nargs], 1));
	NEXT_OP;

OP(OP_ASET) {
	*ipd = (uintptr_t)ip;
	value_t v = FL(sp)[-3];
	n = 3;
	if(0){
LABEL(apply_aset):
		v = FL(sp)[-n];
		for(int i = n-1; i >= 3; i--){
			if(isarray(v)){
				FL(sp)[-i-1] = v;
				v = cvalue_array_aref(FL(sp)-i-1);
				continue;
			}
			value_t e = FL(sp)[-i];
			size_t isz = tosize(e);
			if(isvector(v)){
				if(fl_unlikely(isz >= vector_size(v)))
					bounds_error(v, e);
				v = vector_elt(v, isz);
				continue;
			}
			if(!iscons(v) && v != FL_nil)
				type_error("sequence", v);
			for(value_t v0 = v;; isz--){
				if(isz == 0){
					v = car_(v);
					break;
				}
				v = cdr_(v);
				if(fl_unlikely(!iscons(v)))
					bounds_error(v0, e);
			}
		}
		FL(sp)[-3] = v;
	}
	value_t e = FL(sp)[-2];
	size_t isz = tosize(e);
	if(isvector(v)){
		if(fl_unlikely(isz >= vector_size(v)))
			bounds_error(v, e);
		vector_elt(v, isz) = (e = FL(sp)[-1]);
	}else if(iscons(v) || v == FL_nil){
		for(value_t v0 = v;; isz--){
			if(isz == 0){
				car_(v) = (e = FL(sp)[-1]);
				break;
			}
			v = cdr_(v);
			if(fl_unlikely(!iscons(v)))
				bounds_error(v0, e);
		}
	}else if(isarray(v)){
		e = cvalue_array_aset(FL(sp)-3);
	}else{
		type_error("sequence", v);
	}
	POPN(n);
	PUSH(e);
	NEXT_OP;
}

OP(OP_EQUAL) {
	value_t v;
	if(FL(sp)[-2] == FL(sp)[-1])
		v = FL_t;
	else
		v = fl_compare(FL(sp)[-2], FL(sp)[-1], true) == 0 ? FL_t : FL_nil;
	FL(sp)[-2] = v;
	POPN(1);
	NEXT_OP;
}

OP(OP_CONSP)
	FL(sp)[-1] = iscons(FL(sp)[-1]) ? FL_t : FL_nil;
	NEXT_OP;

OP(OP_LOADC) {
	value_t v = bp[nargs];
	int i = *ip++;
	assert(isvector(v));
	assert(i < (int)vector_size(v));
	PUSH(vector_elt(v, i));
	NEXT_OP;
}

OP(OP_SYMBOLP)
	FL(sp)[-1] = issymbol(FL(sp)[-1]) ? FL_t : FL_nil;
	NEXT_OP;

OP(OP_NUMBERP)
	FL(sp)[-1] = fl_isnumber(FL(sp)[-1]) ? FL_t : FL_nil;
	NEXT_OP;

OP(OP_BRBOUND)
	PUSH(bp[GET_INT32(ip)] != UNBOUND ? FL_t : FL_nil);
	ip += 4;
	NEXT_OP;

OP(OP_OPTARGS) {
	int i = GET_INT32(ip);
	ip += 4;
	int x = GET_INT32(ip);
	ip += 4;
	if(fl_unlikely(nargs < i)){
		*ipd = (uintptr_t)ip;
		lerrorf(FL_ArgError, "too few arguments");
	}
	if(x > 0){
		if(fl_unlikely(nargs > x)){
			*ipd = (uintptr_t)ip;
			lerrorf(FL_ArgError, "too many arguments");
		}
	}else
		x = -x;
	if(fl_likely(x > nargs)){
		x -= nargs;
		FL(sp) += x;
		FL(sp)[-1] = FL(sp)[-x-1];
		FL(sp)[-2] = nargs+x;
		FL(sp)[-3] = FL(sp)[-x-3];
		FL(sp)[-4] = FL(sp)[-x-4];
		FL(curr_frame) = FL(sp);
		ipd = FL(sp)-1;
		for(i = 0; i < x; i++)
			bp[nargs+i] = UNBOUND;
		nargs += x;
	}
	NEXT_OP;
}

OP(OP_EQ)
	FL(sp)[-2] = FL(sp)[-2] == FL(sp)[-1] ? FL_t : FL_nil;
	POPN(1);
	NEXT_OP;

OP(OP_LIST)
	n = *ip++;
LABEL(apply_list):
	if(n > 0){
		value_t v = list(FL(sp)-n, n, 0);
		POPN(n);
		PUSH(v);
	}else{
		PUSH(FL_nil);
	}
	NEXT_OP;

OP(OP_BOUNDP) {
	*ipd = (uintptr_t)ip;
	symbol_t *sym = tosymbol(FL(sp)[-1]);
	FL(sp)[-1] = sym->binding == UNBOUND ? FL_nil : FL_t;
	NEXT_OP;
}

OP(OP_NUMEQ)
	n = *ip++;
LABEL(apply_numeq):
	{
		int i = n;
		value_t a = FL(sp)[-i], b, v;
		for(v = FL_t; i > 1; a = b){
			i--;
			b = FL(sp)[-i];
			if(bothfixnums(a, b)){
				if(a != b){
					v = FL_nil;
					break;
				}
			}else if(numeric_compare(a, b, true, false, true) != 0){
				v = FL_nil;
				break;
			}
		}
		POPN(n);
		PUSH(v);
	}
	NEXT_OP;

OP(OP_CADR) {
	value_t v = FL(sp)[-1];
	if(fl_likely(iscons(v))){
		v = cdr_(v);
		if(fl_likely(iscons(v)))
			v = car_(v);
		else
			goto LABEL(cadr_nil);
	}else{
LABEL(cadr_nil):
		if(fl_unlikely(v != FL_nil)){
			*ipd = (uintptr_t)ip;
			type_error("cons", v);
		}
	}
	FL(sp)[-1] = v;
	NEXT_OP;
}

OP(OP_TAPPLY) {
	tail = true;
	if(0){
OP(OP_APPLY)
		tail = false;
	}
	n = *ip++;
LABEL(apply_apply):;
	value_t v = POP();	 // arglist
	value_t *p = FL(sp)-(n-2);  // n-2 == # leading arguments not in the list
	while(iscons(v)){
		PUSH(car_(v));
		v = cdr_(v);
	}
	if(v != FL_nil){
		*ipd = (uintptr_t)ip;
		lerrorf(FL_ArgError, "apply: last argument: not a list");
	}
	n = FL(sp)-p;
	goto LABEL(do_call);
}

OP(OP_LOADT)
	PUSH(FL_t);
	NEXT_OP;

OP(OP_BUILTINP) {
	value_t v = FL(sp)[-1];
	FL(sp)[-1] = (isbuiltin(v) || iscbuiltin(v)) ? FL_t : FL_nil;
	NEXT_OP;
}

OP(OP_NEG) {
LABEL(do_neg):
	*ipd = (uintptr_t)ip;
	value_t v = FL(sp)[-1];
	int64_t i64;
	FL(sp)[-1] = isfixnum(v) ? fixnum_neg(v) : fl_neg(v);
	NEXT_OP;
}

OP(OP_FIXNUMP)
	FL(sp)[-1] = isfixnum(FL(sp)[-1]) ? FL_t : FL_nil;
	NEXT_OP;

OP(OP_MUL) {
	n = *ip++;
LABEL(apply_mul):
	*ipd = (uintptr_t)ip;
	value_t v = fl_mul_any(FL(sp)-n, n);
	POPN(n);
	PUSH(v);
	NEXT_OP;
}

OP(OP_IDIV) {
	value_t a = FL(sp)[-2];
	value_t b = FL(sp)[-1];
	value_t v;
	if(bothfixnums(a, b)){
		if(b == 0){
			*ipd = (uintptr_t)ip;
			divide_by_0_error();
		}
		v = fixnum(numval(a) / numval(b));
	}else{
		*ipd = (uintptr_t)ip;
		v = fl_idiv2(a, b);
	}
	POPN(1);
	FL(sp)[-1] = v;
	NEXT_OP;
}

OP(OP_DIV) {
	n = *ip++;
LABEL(apply_div):
	*ipd = (uintptr_t)ip;
	value_t *p = FL(sp)-n;
	if(n == 1){
		FL(sp)[-1] = fl_div2(fixnum(1), *p);
	}else{
		if(fl_unlikely(n > 2)){
			PUSH(*p);
			*p = fixnum(1);
			p[1] = fl_mul_any(p, n);
			*p = POP();
		}
		value_t v = fl_div2(p[0], p[1]);
		POPN(n);
		PUSH(v);
	}
	NEXT_OP;
}

OP(OP_VECTOR) {
	n = *ip++;
LABEL(apply_vector):;
	value_t v = alloc_vector(n, 0);
	if(n){
		memcpy(&vector_elt(v, 0), FL(sp)-n, n*sizeof(value_t));
		POPN(n);
	}
	PUSH(v);
	NEXT_OP;
}

OP(OP_COMPARE)
	FL(sp)[-2] = fl_compare(FL(sp)[-2], FL(sp)[-1], false);
	POPN(1);
	NEXT_OP;

OP(OP_FOR) {
	*ipd = (uintptr_t)ip;
	value_t *p = FL(sp);
	value_t v;
	fixnum_t s = tofixnum(p[-3]);
	fixnum_t hi = tofixnum(p[-2]);
	FL(sp) += 2;
	for(v = FL_void; s <= hi; s++){
		p[0] = p[-1];
		p[1] = fixnum(s);
		v = _applyn(1);
	}
	POPN(4);
	p[1] = v;
	NEXT_OP;
}

OP(OP_SETG) {
	int i = *ip++;
	if(0){
OP(OP_SETGL)
		i = GET_INT32(ip);
		ip += 4;
	}
	value_t v = fn_vals(bp[-1]);
	assert(i < (int)vector_size(v));
	v = vector_elt(v, i);
	assert(issymbol(v));
	symbol_t *sym = ptr(v);
	v = FL(sp)[-1];
	if(!isconstant(sym))
		sym->binding = v;
	NEXT_OP;
}

OP(OP_VECTORP)
	FL(sp)[-1] = isvector(FL(sp)[-1]) ? FL_t : FL_nil;
	NEXT_OP;

OP(OP_TRYCATCH) {
	*ipd = (uintptr_t)ip;
	value_t v = do_trycatch();
	POPN(1);
	FL(sp)[-1] = v;
	NEXT_OP;
}

OP(OP_ADD) {
	n = *ip++;
	if(n == 2)
		goto LABEL(do_add2);
LABEL(apply_add):
	*ipd = (uintptr_t)ip;
	value_t v = fl_add_any(FL(sp)-n, n);
	POPN(n);
	PUSH(v);
	NEXT_OP;
}

OP(OP_LOADAL)
	assert(nargs > 0);
	PUSH(bp[GET_INT32(ip)]);
	ip += 4;
	NEXT_OP;

OP(OP_EQV) {
	value_t v;
	if(FL(sp)[-2] == FL(sp)[-1])
		v = FL_t;
	else if(!leafp(FL(sp)[-2]) || !leafp(FL(sp)[-1]))
		v = FL_nil;
	else
		v = fl_compare(FL(sp)[-2], FL(sp)[-1], true) == 0 ? FL_t : FL_nil;
	FL(sp)[-2] = v;
	POPN(1);
	NEXT_OP;
}

OP(OP_KEYARGS) {
	value_t v = fn_vals(bp[-1]);
	v = vector_elt(v, 0);
	int i = GET_INT32(ip);
	ip += 4;
	int x = GET_INT32(ip);
	ip += 4;
	fixnum_t s = GET_INT32(ip);
	ip += 4;
	*ipd = (uintptr_t)ip;
	nargs = process_keys(v, i, x, labs(s)-(i+x), bp, nargs, s<0);
	ipd = FL(sp)-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);
	*ipd = (uintptr_t)ip;
	value_t *p = FL(sp)-n;
	// we need to pass the full arglist on to fl_add_any
	// so it can handle rest args properly
	PUSH(*p);
	*p = fixnum(0);
	value_t v = fl_add_any(p, n);
	int64_t i64;
	p[1] = isfixnum(v) ? fixnum_neg(v) : fl_neg(v);
	p[0] = POP();
	v = fl_add_any(p, 2);
	POPN(n);
	PUSH(v);
	NEXT_OP;
}

OP(OP_BRNL)
	ip += POP() == FL_nil ? GET_INT32(ip) : 4;
	NEXT_OP;

OP(OP_SETAL)
	bp[GET_INT32(ip)] = FL(sp)[-1];
	ip += 4;
	NEXT_OP;

OP(OP_BOXL) {
	int i = GET_INT32(ip);
	ip += 4;
	value_t v = mk_cons();
	car_(v) = bp[i];
	cdr_(v) = FL_nil;
	bp[i] = v;
	NEXT_OP;
}

OP(OP_FUNCTIONP) {
	value_t v = FL(sp)[-1];
	FL(sp)[-1] =
		((tag(v) == TAG_FUNCTION &&
		  (isbuiltin(v) || v>(N_BUILTINS<<3))) ||
		 iscbuiltin(v)) ? FL_t : FL_nil;
	NEXT_OP;
}

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

OP(OP_BRNEL)
	ip += FL(sp)[-2] != FL(sp)[-1] ? GET_INT32(ip) : 4;
	POPN(2);
	NEXT_OP;

OP(OP_BRNNL)
	ip += POP() != FL_nil ? GET_INT32(ip) : 4;
	NEXT_OP;

OP(OP_LOADCL)
	ip += 4;
	PUSH(vector_elt(bp[nargs], GET_INT32(ip)));
	ip += 4;
	NEXT_OP;

OP(OP_LOADVL) {
	value_t v = fn_vals(bp[-1]);
	v = vector_elt(v, GET_INT32(ip));
	ip += 4;
	PUSH(v);
	NEXT_OP;
}