shithub: sl

Download patch

ref: f4933d23cf33c130b28616804b4ddde32018f3d8
parent: b42fcc3ec951c1b3db0760f25ea5d9eb0b518659
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue Apr 8 18:50:13 EDT 2025

report instruction pointer of C builtins in the stack trace

--- a/boot/sl.boot
+++ b/boot/sl.boot
@@ -269,16 +269,16 @@
   NIL NIL NIL NIL :from-end 3) identity #0# #fn("n10S;J;04AF0<51513500<:92<0=61:" #() find-if-)
   list-tail list-head reverse!) find-if)
             fits-i8 #fn("n10Y;3<0470r\xaf0r\xb063:" #(>=) fits-i8) fn-disasm
-            #fn("\x871000.///W1000J60q?14z20[3:07060:@30q482JG07101E534725047060:@30q482<2305124051II252687>1?:5142527187>2?;514E288851b<I8<<8=L23\x8a24292:888<>2q7;53E8<<L23907250@30q4E87K~2<|48<8<<KM_48>2=8?2>523[08;8>8<<r45348:897?888<<52G5148<8<<r4M_@\x1912=8?2@523V08;8>8<<K5348:89888<<GG5148<8<<KM_@\xea12=8?2A523e08;8>8<<K5347B2C888<<G8>2DC70r3@30EM515148<8<<KM_@\xac12=8?2E523\\08;8>8<<r45347B2C7?888<<52515148<8<<r4M_@w12=8?2F523\xb808;8>8<<r88>2GC70r4@30EM5347B2C7?888<<52512H5248<8<<r4M_47B2C7?888<<52515148<8<<r4M_48>2GCY07B2H5147B2C7?888<<52512H5248<8<<r4M_@30q@\xe608?2Ic3^08;8>8<<r45347B2C7?888<<52512H5248<8<<r4M_@\xb202=8?2J523b08;8>8<<r25347B2K7L8<<7M888<<52M515248<8<<r2M_@w02=8?2N523b08;8>8<<r45347B2K7L8<<7?888<<52M515248<8<<r4M_@<08;8>8<<E53^1^1@\xd0-:" #(void
-  fn-disasm newline #fn(fn-code) #fn(fn-vals) #1# #fn("n10\\3F00[JA070504710qAKM63:72061:" #(newline
-  fn-disasm print) print-val) #fn("n370A3U0FEl23N071A72151523A0A182ML237023@4024751K~512602765:" #(princ
-  >= 1- " >" "  " hex5 ":  " " ") print-inst) #fn(length)
-  #fn(table-foldl) #fn("n382;J@041AF<Gl2;34040:") Instructions #fn("n1702161:" #(princ "\t"))
-  #fn(memq) (loadv.l loadg.l setg.l) ref-s32-LE (loadv loadg setg)
+            #fn("\x871000.///W1000J60q?14z20[3I07021122534735047460:@30q482JG07501E534735047460:@30q482<2605127051II282987>1?:514282:187>2?;514E2;8851b<I8<<8=L23\x8a242<2=888<>2q7>53E8<<L23907350@30q4E87K~2?|48<8<<KM_48>2@8?2A523[08;8>8<<r45348:897B888<<52G5148<8<<r4M_@\x1912@8?2C523V08;8>8<<K5348:89888<<GG5148<8<<KM_@\xea12@8?2D523e08;8>8<<K534702E888<<G8>2FC70r3@30EM515148<8<<KM_@\xac12@8?2G523\\08;8>8<<r4534702E7B888<<52515148<8<<r4M_@w12@8?2H523\xb808;8>8<<r88>2IC70r4@30EM534702E7B888<<52512J5248<8<<r4M_4702E7B888<<52515148<8<<r4M_48>2ICY0702J514702E7B888<<52512J5248<8<<r4M_@30q@\xe608?2Kc3^08;8>8<<r4534702E7B888<<52512J5248<8<<r4M_@\xb202@8?2L523b08;8>8<<r2534702M7N8<<7O888<<52M515248<8<<r2M_@w02@8?2P523b08;8>8<<r4534702M7N8<<7B888<<52M515248<8<<r4M_@<08;8>8<<E53^1^1@\xd0-:" #(princ
+  " > " " // PC" newline void fn-disasm #fn(fn-code)
+  #fn(fn-vals) #1# #fn("n10\\3F00[JA070504710qAKM63:72061:" #(newline fn-disasm print) print-val)
+  #fn("n370A3U0FEl23N071A72151523A0A182ML237023@4024751K~512602765:" #(princ >= 1- " >" "  " hex5
+                                                                       ":  " " ") print-inst)
+  #fn(length) #fn(table-foldl) #fn("n382;J@041AF<Gl2;34040:") Instructions #fn("n1702161:" #(princ
+  "\t")) #fn(memq) (loadv.l loadg.l setg.l) ref-s32-LE (loadv loadg setg)
   (loada seta loadc call tcall list + - * / < = vec argc vargc loadi8 apply tapply closure box
-   shift aref) princ #fn(num->str) aref (loada.l seta.l loadc.l argc.l vargc.l call.l tcall.l box.l)
-  (optargs keyargs) keyargs " " brbound (jmp brne brnn brn) "@" hex5 ref-s16-LE (jmp.l brne.l
-                                                                                       brnn.l brn.l)) fn-disasm)
+   shift aref) #fn(num->str) aref (loada.l seta.l loadc.l argc.l vargc.l call.l tcall.l box.l) (optargs
+  keyargs) keyargs " " brbound (jmp brne brnn brn) "@" hex5 ref-s16-LE (jmp.l brne.l brnn.l brn.l)) fn-disasm)
             foldl #fn("n382J401:700082<15282=63:" #(foldl) foldl) foldr
             #fn("n382J401:082<700182=5362:" #(foldr) foldr) get-defined-vars #fn("n170A<05161:" #(delete-duplicates) #(#2=(#fn("n10H340q:0<20Cd00=B3^00TR;37040Te1;JM040TB;3E0471051R;3:0471051e1:0<22C?07324A<0=52}2:q:" #(def
   caadr begin nconc #fn(map)) #(#2#)))))
--- a/src/builtins.c
+++ b/src/builtins.c
@@ -39,8 +39,9 @@
 			while(iscons(c->cdr))
 				c = ptr(c->cdr);
 			pcdr = &c->cdr;
-		}else if(lst != sl_nil)
-			type_error("cons", lst);
+		}else if(lst != sl_nil){
+			bthrow(type_error("cons", lst));
+		}
 	}
 	*pcdr = lst;
 	return first;
@@ -117,7 +118,7 @@
 	}
 	if(a == sl_nil)
 		return fixnum(0);
-	type_error("sequence", a);
+	bthrow(type_error("sequence", a));
 }
 
 _Noreturn
@@ -124,7 +125,7 @@
 BUILTIN("raise", raise)
 {
 	argcount(nargs, 1);
-	sl_raise(args[0]);
+	bthrow(sl_raise(args[0]));
 }
 
 _Noreturn
@@ -160,7 +161,7 @@
 	argcount(nargs, 1);
 	sl_sym *sym = tosym(args[0]);
 	if(sym->binding == UNBOUND)
-		unbound_error(args[0]);
+		bthrow(unbound_error(args[0]));
 	return sym->binding;
 }
 
@@ -169,7 +170,7 @@
 	argcount(nargs, 2);
 	sl_sym *sym = tosym(args[0]);
 	if(sl_unlikely(isconst(sym)))
-		const_error(sym);
+		bthrow(const_error(sym));
 	sym->binding = args[1];
 	return args[1];
 }
@@ -179,7 +180,7 @@
 	argcount(nargs, 1);
 	sl_sym *sym = tosym(args[0]);
 	if(sl_unlikely(isconst(sym)))
-		const_error(sym);
+		bthrow(const_error(sym));
 	sym->binding = UNBOUND;
 	return sl_void;
 }
@@ -273,7 +274,7 @@
 		sl_cv *p = ptr(v);
 		return fixnum(conv_to_s64(v, cv_data(p), cv_numtype(p)));
 	}
-	type_error("num", v);
+	bthrow(type_error("num", v));
 }
 
 BUILTIN("truncate", truncate)
@@ -305,7 +306,7 @@
 			return return_from_s64((s64int)d);
 		}
 	}
-	type_error("num", v);
+	bthrow(type_error("num", v));
 }
 
 BUILTIN("vec-alloc", vec_alloc)
@@ -350,7 +351,7 @@
 		sl_numtype nt = cv_numtype(cv);
 		return conv_to_double(a, cv_data(cv), nt);
 	}
-	type_error("num", a);
+	bthrow(type_error("num", a));
 }
 
 BUILTIN("time->str", time_str)
@@ -380,12 +381,12 @@
 	if(nargs == 0){
 		char buf[4096];
 		if(getcwd(buf, sizeof(buf)) == nil)
-			lerrorf(sl_errio, "could not get current dir");
+			bthrow(lerrorf(sl_errio, "could not get current dir"));
 		return str_from_cstr(buf);
 	}
 	char *ptr = tostr(args[0]);
 	if(chdir(ptr) != 0)
-		lerrorf(sl_errio, "could not cd to %s", ptr);
+		bthrow(lerrorf(sl_errio, "could not cd to %s", ptr));
 	return sl_void;
 }
 
@@ -401,7 +402,7 @@
 	argcount(nargs, 1);
 	const char *path = tostr(args[0]);
 	if(remove(path) != 0)
-		lerrorf(sl_errio, "could not remove %s", path);
+		bthrow(lerrorf(sl_errio, "could not remove %s", path));
 	return sl_void;
 }
 
@@ -427,6 +428,6 @@
 		result = setenv(name, val, 1);
 	}
 	if(result != 0)
-		lerrorf(sl_errarg, "invalid environment variable");
+		bthrow(lerrorf(sl_errarg, "invalid environment variable"));
 	return sl_t;
 }
--- a/src/compiler.sl
+++ b/src/compiler.sl
@@ -763,6 +763,8 @@
 
 (def (fn-disasm f (ip NIL) . lev?)
   (when (builtin? f)
+    (princ " > " ip " // PC")
+    (newline)
     (return (void)))
   (when (not lev?)
     (fn-disasm f ip 0)
--- a/src/compress.c
+++ b/src/compress.c
@@ -14,7 +14,7 @@
 		argcount(nargs, 2);
 
 	if(!isarr(args[0]))
-		type_error("arr", args[0]);
+		bthrow(type_error("arr", args[0]));
 	u8int *in;
 	usize insz;
 	uintptr u;
@@ -37,7 +37,7 @@
 		: blz_pack(in, out, insz, work);
 	MEM_FREE(work);
 	if(n == BLZ_ERROR)
-		lerrorf(sl_errarg, "blz error");
+		bthrow(lerrorf(sl_errarg, "blz error"));
 	cvalue_len(v) = n;
 	return v;
 }
@@ -51,7 +51,7 @@
 	uintptr u;
 	to_sized_ptr(args[0], &in, &insz, &u);
 	if(!isarr(args[0]))
-		type_error("arr", args[0]);
+		bthrow(type_error("arr", args[0]));
 	usize outsz;
 	u8int *out;
 	sl_v v;
@@ -63,11 +63,11 @@
 		v = args[2];
 		to_sized_ptr(v, &out, &outsz, &u);
 	}else{
-		lerrorf(sl_errarg, "either :size or :to must be specified");
+		bthrow(lerrorf(sl_errarg, "either :size or :to must be specified"));
 	}
 	unsigned long n = blz_depack_safe(in, insz, out, outsz);
 	if(n == BLZ_ERROR)
-		lerrorf(sl_errarg, "blz error");
+		bthrow(lerrorf(sl_errarg, "blz error"));
 	cvalue_len(v) = n;
 	return v;
 }
--- a/src/cvalues.c
+++ b/src/cvalues.c
@@ -203,7 +203,7 @@
 		sl_fx v = ubnumval(a);
 		r = conv_to_u32(a, &v, ubnumtype(a));
 	}else
-		type_error("num", a);
+		cthrow(type_error("num", a), type);
 	*((Rune*)dest) = r;
 }
 
@@ -221,7 +221,7 @@
 		}else if(iscvalue(a)){ \
 			n = (ctype)conv_to_##cnvt(a, cv_data(ptr(a)), cv_numtype(ptr(a))); \
 		}else \
-			type_error("num", a); \
+			cthrow(type_error("num", a), type); \
 		*((ctype*)dest) = n; \
 	}
 
@@ -346,7 +346,7 @@
 		void *p = cv_data(cv);
 		n = conv_to_bignum(a, p, cv_numtype(cv));
 	}else
-		type_error("num", a);
+		cthrow(type_error("num", a), type);
 	*((mpint**)dest) = n;
 }
 
@@ -404,7 +404,7 @@
 			return conv_to_u64(n, cv_data(cv), cv_numtype(cv));
 		return conv_to_u32(n, cv_data(cv), cv_numtype(cv));
 	}
-	type_error("num", n);
+	cthrow(type_error("num", n), n);
 }
 
 soffset
@@ -420,7 +420,7 @@
 			return conv_to_s64(n, cv_data(cv), cv_numtype(cv));
 		return conv_to_s32(n, cv_data(cv), cv_numtype(cv));
 	}
-	type_error("num", n);
+	cthrow(type_error("num", n), n);
 }
 
 bool
@@ -456,7 +456,7 @@
 	if(iscons(cdr_(cdr_(type)))){
 		usize tc = tosize(car_(cdr_(cdr_(type))));
 		if(tc != cnt)
-			lerrorf(sl_errarg, "size mismatch");
+			cthrow(lerrorf(sl_errarg, "size mismatch"), ft);
 	}
 
 	sz = elsize * cnt;
@@ -482,7 +482,7 @@
 			arg = cdr_(arg);
 		}
 		if(i != cnt)
-			lerrorf(sl_errarg, "size mismatch");
+			cthrow(lerrorf(sl_errarg, "size mismatch"), ft);
 		return;
 	}
 	if(iscvalue(arg)){
@@ -493,17 +493,17 @@
 				if(cv_len(cv) == sz)
 					memcpy(dest, cv_data(cv), sz);
 				else
-					lerrorf(sl_errarg, "size mismatch");
+					cthrow(lerrorf(sl_errarg, "size mismatch"), ft);
 				return;
 			}else{
 				// TODO: initialize array from different type elements
-				lerrorf(sl_errarg, "element type mismatch");
+				cthrow(lerrorf(sl_errarg, "element type mismatch"), ft);
 			}
 		}
 	}
 	if(cnt == 1)
 		cvalue_init(eltype, arg, dest);
-	type_error("sequence", arg);
+	cthrow(type_error("sequence", arg), ft);
 }
 
 BUILTIN("arr", arr)
@@ -524,7 +524,7 @@
 	int i;
 	FOR_ARGS(i, 1, arg, args){
 		if(!sl_isnum(arg) && type->eltype != sl_runetype)
-			type_error("num", arg);
+			bthrow(type_error("num", arg));
 		cvalue_init(type->eltype, arg, dest);
 		dest += elsize;
 	}
@@ -540,7 +540,7 @@
 		argcount(nargs, 3);
 	cnt = tosize(args[1]);
 	if(cnt < 0)
-		lerrorf(sl_errarg, "invalid size: %"PRIu64, (u64int)cnt);
+		bthrow(lerrorf(sl_errarg, "invalid size: %"PRIu64, (u64int)cnt));
 
 	sl_type *type = get_arr_type(args[0]);
 	elsize = type->elsz;
@@ -552,7 +552,7 @@
 	for(i = 0; i < cnt; i++){
 		sl_v arg = args[a];
 		if(!sl_isnum(arg))
-			type_error("num", arg);
+			bthrow(type_error("num", arg));
 		cvalue_init(type->eltype, arg, dest);
 		dest += elsize;
 		if((a = (a + 1) % nargs) < 2)
@@ -584,7 +584,7 @@
 		if(hed == sl_arrsym){
 			sl_v t = car(cdr_(type));
 			if(!iscons(cdr_(cdr_(type))))
-				lerrorf(sl_errarg, "incomplete type");
+				cthrow(lerrorf(sl_errarg, "incomplete type"), type);
 			sl_v n = car_(cdr_(cdr_(type)));
 			usize sz = tosize(n);
 			return sz * ctype_sizeof(t);
@@ -591,7 +591,7 @@
 		}
 	}
 
-	lerrorf(sl_errarg, "invalid c type");
+	cthrow(lerrorf(sl_errarg, "invalid c type"), type);
 }
 
 // get pointer and size for any plain-old-data value
@@ -624,7 +624,7 @@
 			return;
 		}
 	}
-	type_error("plain-old-data", v);
+	cthrow(type_error("plain-old-data", v), v);
 }
 
 BUILTIN("sizeof", sizeof)
@@ -723,11 +723,11 @@
 {
 	argcount(nargs, 1);
 	if(iscons(args[0]) || isvec(args[0]))
-		lerrorf(sl_errarg, "argument must be a leaf atom");
+		bthrow(lerrorf(sl_errarg, "argument must be a leaf atom"));
 	if(!iscvalue(args[0]))
 		return args[0];
 	if(!cv_isPOD(ptr(args[0])))
-		lerrorf(sl_errarg, "argument must be a plain-old-data type");
+		bthrow(lerrorf(sl_errarg, "argument must be a plain-old-data type"));
 	return cvalue_copy(args[0]);
 }
 
@@ -747,7 +747,7 @@
 {
 	cvinitfunc_t f = type->init;
 	if(f == nil)
-		lerrorf(sl_errarg, "invalid c type");
+		cthrow(lerrorf(sl_errarg, "invalid c type"), type);
 	f(type, v, dest);
 }
 
@@ -816,7 +816,7 @@
 	numel = cv_len(cv)/cv_class(cv)->elsz;
 	*index = tosize(ind);
 	if(*index < 0 || *index >= numel)
-		bounds_error(arr, ind);
+		cthrow(bounds_error(arr, ind), arr);
 }
 
 sl_v
@@ -868,7 +868,7 @@
 	argcount(nargs, 1);
 	sl_sym *s = tosym(args[0]);
 	if(!iscbuiltin(s->binding))
-		lerrorf(sl_errarg, "function \"%s\" not found", s->name);
+		bthrow(lerrorf(sl_errarg, "function \"%s\" not found", s->name));
 	return s->binding;
 }
 
@@ -1015,7 +1015,7 @@
 		}
 	}
 
-	type_error("num", n);
+	cthrow(type_error("num", n), n);
 }
 
 bool
@@ -1065,12 +1065,12 @@
 	}
 	if(!num_to_ptr(a, &ai, &ta, &aptr)){
 		if(typeerr)
-			type_error("num", a);
+			cthrow(type_error("num", a), a);
 		return 2;
 	}
 	if(!num_to_ptr(b, &bi, &tb, &bptr)){
 		if(typeerr)
-			type_error("num", b);
+			cthrow(type_error("num", b), a);
 		return 2;
 	}
 	if(eq && eqnans && ((ta >= T_FLOAT) != (tb >= T_FLOAT)))
@@ -1099,20 +1099,20 @@
 	void *aptr, *bptr;
 
 	if(!num_to_ptr(a, &ai, &ta, &aptr))
-		type_error("num", a);
+		cthrow(type_error("num", a), a);
 	if(!num_to_ptr(b, &bi, &tb, &bptr))
-		type_error("num", b);
+		cthrow(type_error("num", b), a);
 	// a pointer is not exactly a number
 	if(ta == T_PTR)
-		type_error("num", a);
+		cthrow(type_error("num", a), a);
 	if(tb == T_PTR)
-		type_error("num", b);
+		cthrow(type_error("num", b), a);
 
 	da = conv_to_double(a, aptr, ta);
 	db = conv_to_double(b, bptr, tb);
 
 	if(db == 0 && tb < T_FLOAT)  // exact 0
-		divide_by_0_error();
+		cthrow(divide_by_0_error(), a);
 
 	da = da/db;
 
@@ -1131,19 +1131,19 @@
 	mpint *x;
 
 	if(!num_to_ptr(a, &ai, &ta, &aptr))
-		type_error("num", a);
+		cthrow(type_error("num", a), a);
 	if(!num_to_ptr(b, &bi, &tb, &bptr))
-		type_error("num", b);
+		cthrow(type_error("num", b), a);
 	// a pointer is not exactly a number
 	if(ta == T_PTR)
-		type_error("num", a);
+		cthrow(type_error("num", a), a);
 	if(tb == T_PTR)
-		type_error("num", b);
+		cthrow(type_error("num", b), a);
 
 	if(ta == T_BIGNUM){
 		if(tb == T_BIGNUM){
 			if(mpsignif(*(mpint**)bptr) == 0)
-				goto div_error;
+				cthrow(divide_by_0_error(), a);
 			x = mpnew(0);
 			mpdiv(*(mpint**)aptr, *(mpint**)bptr, x, nil);
 			return mk_bignum(x);
@@ -1150,7 +1150,7 @@
 		}else{
 			b64 = conv_to_s64(b, bptr, tb);
 			if(b64 == 0)
-				goto div_error;
+				cthrow(divide_by_0_error(), a);
 			x = tb == T_U64 ? uvtomp(b64, nil) : vtomp(b64, nil);
 			mpdiv(*(mpint**)aptr, x, x, nil);
 			return mk_bignum(x);
@@ -1159,7 +1159,7 @@
 	if(ta == T_U64){
 		if(tb == T_U64){
 			if(*(u64int*)bptr == 0)
-				goto div_error;
+				cthrow(divide_by_0_error(), a);
 			return return_from_u64(*(u64int*)aptr / *(u64int*)bptr);
 		}
 		b64 = conv_to_s64(b, bptr, tb);
@@ -1166,12 +1166,12 @@
 		if(b64 < 0)
 			return return_from_s64(-(s64int)(*(u64int*)aptr / (u64int)(-b64)));
 		if(b64 == 0)
-			goto div_error;
+			cthrow(divide_by_0_error(), a);
 		return return_from_u64(*(u64int*)aptr / (u64int)b64);
 	}
 	if(tb == T_U64){
 		if(*(u64int*)bptr == 0)
-			goto div_error;
+			cthrow(divide_by_0_error(), a);
 		a64 = conv_to_s64(a, aptr, ta);
 		if(a64 < 0)
 			return return_from_s64(-((s64int)((u64int)(-a64) / *(u64int*)bptr)));
@@ -1180,11 +1180,9 @@
 
 	b64 = conv_to_s64(b, bptr, tb);
 	if(b64 == 0)
-		goto div_error;
+		cthrow(divide_by_0_error(), a);
 
 	return return_from_s64(conv_to_s64(a, aptr, ta) / b64);
-div_error:
-	divide_by_0_error();
 }
 
 static sl_v
@@ -1197,9 +1195,9 @@
 	s64int b64;
 
 	if(!num_to_ptr(a, &ai, &ta, &aptr) || ta >= T_FLOAT)
-		type_error("int", a);
+		cthrow(type_error("int", a), a);
 	if(!num_to_ptr(b, &bi, &tb, &bptr) || tb >= T_FLOAT)
-		type_error("int", b);
+		cthrow(type_error("int", b), a);
 
 	if(ta < tb){
 		itmp = ta; ta = tb; tb = itmp;
@@ -1341,7 +1339,7 @@
 		default: abort();
 		}
 	}
-	type_error("int", a);
+	bthrow(type_error("int", a));
 }
 
 #define sash_overflow_64(a, b, c) ( \
@@ -1407,7 +1405,7 @@
 		assert(fits_fixnum(accum));
 		return fixnum((sl_fx)accum);
 	}
-	type_error("int", a);
+	bthrow(type_error("int", a));
 }
 
 void
--- a/src/dos/platform.h
+++ b/src/dos/platform.h
@@ -43,6 +43,9 @@
 	exit(1); \
 }while(0)
 
+#define sys_setcallpc(arg) do{}while(0)
+#define sys_setcurrpc(dummy) do{}while(0)
+
 #define PATHSEP '\\'
 #define PATHSEPSTRING "\\"
 #define PATHLISTSEP ':'
--- a/src/io.c
+++ b/src/io.c
@@ -71,7 +71,7 @@
 toio(sl_v v)
 {
 	if(sl_unlikely(!isio(v)))
-		type_error("io", v);
+		cthrow(type_error("io", v), v);
 	return value2c(sl_ios*, v);
 }
 
@@ -98,7 +98,7 @@
 	char *fname = tostr(args[0]);
 	sl_ios *s = value2c(sl_ios*, f);
 	if(ios_file(s, fname, r, w, c, t) == nil)
-		lerrorf(sl_errio, "could not open \"%s\"", fname);
+		bthrow(lerrorf(sl_errio, "could not open \"%s\"", fname));
 	if(a)
 		ios_seek_end(s);
 	return f;
@@ -129,7 +129,7 @@
 	else if(a == sl_memorysym)
 		bm = bm_mem;
 	if(bm < 0 || ios_bufmode(s, bm) != 0)
-		lerrorf(sl_errarg, "invalid buffer mode");
+		bthrow(lerrorf(sl_errarg, "invalid buffer mode"));
 	return sl_void;
 }
 
@@ -140,7 +140,7 @@
 	sl_v f = cvalue(sl_iotype, sizeof(sl_ios));
 	sl_ios *s = value2c(sl_ios*, f);
 	if(ios_mem(s, 0) == nil)
-		lerrorf(sl_errmem, "could not allocate in-memory io");
+		bthrow(lerrorf(sl_errmem, "could not allocate in-memory io"));
 	return f;
 }
 
@@ -171,10 +171,10 @@
 	Rune r;
 	int res;
 	if((res = ios_peekrune(s, &r)) == IOS_EOF)
-		//lerrorf(sl_errio, "end of file reached");
+		//bthrow(lerrorf(sl_errio, "end of file reached"));
 		return sl_eof;
 	if(res == 0)
-		lerrorf(sl_errio, "invalid UTF-8 sequence");
+		bthrow(lerrorf(sl_errio, "invalid UTF-8 sequence"));
 	return mk_rune(r);
 }
 
@@ -185,10 +185,10 @@
 	Rune r;
 	int res;
 	if((res = ios_getrune(s, &r)) == IOS_EOF)
-		//lerrorf(sl_errio, "end of file reached");
+		//bthrow(lerrorf(sl_errio, "end of file reached"));
 		return sl_eof;
 	if(res == 0)
-		lerrorf(sl_errio, "invalid UTF-8 sequence");
+		bthrow(lerrorf(sl_errio, "invalid UTF-8 sequence"));
 	return mk_rune(r);
 }
 
@@ -197,7 +197,7 @@
 	argcount(nargs, 2);
 	sl_ios *s = toio(args[0]);
 	if(!isrune(args[1]))
-		type_error("rune", args[1]);
+		bthrow(type_error("rune", args[1]));
 	return fixnum(ios_putrune(s, torune(args[1])));
 }
 
@@ -230,7 +230,7 @@
 	argcount(nargs, 2);
 	sl_ios *s = toio(args[0]);
 	if(ios_trunc(s, tooffset(args[1])) < 0)
-		lerrorf(sl_errio, "truncation failed");
+		bthrow(lerrorf(sl_errio, "truncation failed"));
 	return sl_void;
 }
 
@@ -293,7 +293,7 @@
 	}else{
 		ft = get_type(args[1]);
 		if(ft->eltype != nil && !iscons(cdr_(cdr_(args[1]))))
-			lerrorf(sl_errarg, "incomplete type");
+			bthrow(lerrorf(sl_errarg, "incomplete type"));
 		n = ft->size;
 	}
 	sl_v cv = cvalue(ft, n);
@@ -300,7 +300,7 @@
 	u8int *data = cvalue_data(cv);
 	usize got = ios_read(s, data, n);
 	if(got < n)
-		//lerrorf(sl_errio, "end of input reached");
+		//bthrow(lerrorf(sl_errio, "end of input reached"));
 		return sl_eof;
 	return cv;
 }
@@ -313,7 +313,7 @@
 		*offs = tosize(args[1]);
 		*nb = nargs > 2 ? tosize(args[2]) : sz - *offs;
 		if(*offs >= sz || *offs + *nb > sz)
-			bounds_error(args[0], args[1]);
+			cthrow(bounds_error(args[0], args[1]), args);
 	}
 }
 
@@ -325,7 +325,7 @@
 	sl_v v = args[1];
 	if(isrune(v)){
 		if(nargs > 2)
-			lerrorf(sl_errarg, "offset argument not supported for characters");
+			bthrow(lerrorf(sl_errarg, "offset argument not supported for characters"));
 		return fixnum(ios_putrune(s, torune(v)));
 	}
 	u8int *data;
@@ -347,7 +347,7 @@
 	if(uldelim > 0x7f){
 		// runes > 0x7f, or anything else > 0xff, are out of range
 		if(isrune(arg) || uldelim > 0xff)
-			lerrorf(sl_errarg, "delimiter out of range");
+			cthrow(lerrorf(sl_errarg, "delimiter out of range"), arg);
 	}
 	return (u8int)uldelim;
 }
@@ -468,7 +468,7 @@
 	argcount(nargs, 1);
 	sl_ios *src = toio(args[0]);
 	if(src->bm != bm_mem)
-		lerrorf(sl_errarg, "requires in-memory io");
+		bthrow(lerrorf(sl_errarg, "requires in-memory io"));
 	bool eof = ios_eof(src);
 	sl_v v = io_to_str(&args[0]);
 	if(eof && v == sl_emptystr)
--- a/src/macos/platform.h
+++ b/src/macos/platform.h
@@ -46,6 +46,9 @@
 	exit(1); \
 }while(0)
 
+#define sys_setcallpc(arg) do{}while(0)
+#define sys_setcurrpc(dummy) do{}while(0)
+
 #define PATHSEP '/'
 #define PATHSEPSTRING "/"
 #define PATHLISTSEP ':'
--- a/src/operators.c
+++ b/src/operators.c
@@ -18,7 +18,7 @@
 	case T_FLOAT:  return dtomp(*(float*)data, nil);
 	case T_DOUBLE: return dtomp(*(double*)data, nil);
 	}
-	type_error("num", v);
+	cthrow(type_error("num", v), v);
 }
 
 sl_purefn
@@ -44,7 +44,7 @@
 	case T_FLOAT:  return *(float*)data;
 	case T_DOUBLE: return *(double*)data;
 	}
-	type_error("num", v);
+	cthrow(type_error("num", v), v);
 }
 
 // FIXME sign with mpint
@@ -67,7 +67,7 @@
 	case T_FLOAT:  return (ctype)*(float*)data; \
 	case T_DOUBLE: return (ctype)*(double*)data; \
 	} \
-	type_error("num", v); \
+	cthrow(type_error("num", v), v); \
 }
 
 CONV_TO_INTTYPE(s64, s64int)
@@ -104,7 +104,7 @@
 		s = *(double*)data;
 		return s;
 	}
-	type_error("num", v);
+	cthrow(type_error("num", v), v);
 }
 
 sl_purefn
--- a/src/plan9/lsd.c
+++ b/src/plan9/lsd.c
@@ -42,7 +42,7 @@
 
 	rp = rname(name);
 	if(rp == nil)
-		lerrorf(sl_errio, "invalid register name %s", name);
+		cthrow(lerrorf(sl_errio, "invalid register name %s", name), map);
 
 	switch(rp->rformat){
 	default:
@@ -54,7 +54,7 @@
 		break;
 	}
 	if(r < 0)
-		lerrorf(sl_errio, "could not get register %s: %r", name);
+		cthrow(lerrorf(sl_errio, "could not get register %s: %r", name), map);
 	return v;
 }
 
@@ -123,7 +123,7 @@
 }
 
 static void
-load(void)
+load(int dummy)
 {
 	int fd;
 	long nsym;
@@ -131,21 +131,21 @@
 
 	fd = open(aout, OREAD);
 	if(fd < 0)
-		lerrorf(sl_errio, "could not open \"%s\"", aout);
+		cthrow(lerrorf(sl_errio, "could not open \"%s\"", aout), dummy);
 	if(!crackhdr(fd, &fhdr)){
 		close(fd);
-		lerrorf(sl_errio, "could not decode file header for \"%s\"", aout);
+		cthrow(lerrorf(sl_errio, "could not decode file header for \"%s\"", aout), dummy);
 	}
 	machbytype(fhdr.type);
 	symmap = loadmap(nil, fd, &fhdr);
 	if(symmap == nil){
 		close(fd);
-		lerrorf(sl_errio, "could not load segments for \"%s\"", aout);
+		cthrow(lerrorf(sl_errio, "could not load segments for \"%s\"", aout), dummy);
 	}
 	nsym = syminit(fd, &fhdr);
 	if(nsym < 0){
 		close(fd);
-		lerrorf(sl_errio, "could not initialize the symbol table for \"%s\"", aout);
+		cthrow(lerrorf(sl_errio, "could not initialize the symbol table for \"%s\"", aout), dummy);
 	}
 	close(fd);
 	ios_printf(ios_stdout, "%s:%s\n", aout, fhdr.name);
@@ -177,12 +177,12 @@
 	snprint(buf, sizeof(buf), "/proc/%d/mem", pid);
 	fd = open(buf, ORDWR);
 	if(fd < 0)
-		lerrorf(sl_errio, "could not open \"%s\"", buf);
+		cthrow(lerrorf(sl_errio, "could not open \"%s\"", buf), pid);
 
 	freecore();
 	coremap = attachproc(pid, 0, fd, &fhdr);
 	if(coremap == nil)
-		lerrorf(sl_errio, "could not make coremap: %r");
+		cthrow(lerrorf(sl_errio, "could not make coremap: %r"), pid);
 }
 
 static int
@@ -192,11 +192,11 @@
 	char buf[128];
 
 	if(pipe(p) < 0)
-		lerrorf(sl_errio, "could not create a pipe");
+		cthrow(lerrorf(sl_errio, "could not create a pipe"), argv);
 	pid = rfork(RFPROC|RFFDG|RFREND|RFNAMEG|RFNOTEG);
 	switch(pid){
 	case -1:
-		lerrorf(sl_errio, "could not fork");
+		cthrow(lerrorf(sl_errio, "could not fork"), argv);
 	case 0:
 		snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
 		fd = open(buf, ORDWR);
@@ -221,7 +221,7 @@
 static void
 trlist(Map *map, uvlong retpc, uvlong sp, Symbol *fn)
 {
-	sl_v  v;
+	sl_v v;
 
 	USED(map);
 	v = alloc_vec(5, 0);
@@ -243,7 +243,7 @@
 	argcount(nargs, 3);
 	for(a = args; a < args+3; a++)
 		if(sl_unlikely(!sl_isnum(*a)))
-			type_error("num", *a);
+			bthrow(type_error("num", *a));
 
 	pc = tosize(args[0]);
 	sp = tosize(args[1]);
@@ -252,7 +252,7 @@
 	sl_gc_handle(&tracelist);
 	if(machdata->ctrace(coremap, pc, sp, res, trlist) <= 0){
 		sl_free_gc_handles(1);
-		lerrorf(sl_errio, "could not retrieve stack frame: %r");
+		bthrow(lerrorf(sl_errio, "could not retrieve stack frame: %r"));
 	}
 	sl_free_gc_handles(1);
 	return tracelist;
@@ -275,7 +275,7 @@
 	pid = -1;
 	argcount(nargs, 1);
 	if(sl_unlikely(!sl_isstr(args[0]) && !sl_isnum(args[0])))
-		type_error("str|num", args[0]);
+		bthrow(type_error("str|num", args[0]));
 
 	if(sl_isnum(args[0])){
 		pid = tosize(args[0]);
@@ -283,11 +283,11 @@
 	}else{
 		len = cv_len(ptr(args[0]));
 		if(len+1 > sizeof(aout))
-			lerrorf(sl_errio, "path too long");
+			bthrow(lerrorf(sl_errio, "path too long"));
 		strecpy(aout, aout+sizeof(aout), cvalue_data(args[0]));
 	}
 
-	load();
+	load(0);
 	attach(pid);
 
 	registers = sl_nil;
@@ -332,11 +332,11 @@
 	static char *argv[512];
 
 	if(nargs+2 >= nelem(argv))
-		lerrorf(sl_errio, "too many arguments");
+		bthrow(lerrorf(sl_errio, "too many arguments"));
 	argv[0] = aout;
 	for(i = 0; i < nargs; i++){
 		if(sl_unlikely(!sl_isstr(args[i])))
-	  		type_error("str", args[i]);
+			bthrow(type_error("str", args[i]));
 		argv[i+1] = cvalue_data(args[i]);
 	}
 	argv[i+1] = nil;
@@ -365,12 +365,12 @@
 
 	argcount(nargs, 1);
 	if(sl_unlikely(!sl_isnum(args[0])))
-		type_error("num", args[0]);
+		bthrow(type_error("num", args[0]));
 	addr = tosize(args[0]);
 
 	n = machdata->foll(coremap, addr, rget, f);
 	if(n < 0)
-		lerrorf(sl_errio, "follow(%ux): %r", addr);
+		bthrow(lerrorf(sl_errio, "follow(%ux): %r", addr));
 
 	foll = sl_nil;
 	sl_gc_handle(&foll);
@@ -387,11 +387,11 @@
 
 	argcount(nargs, 1);
 	if(sl_unlikely(!sl_isnum(args[0])))
-		type_error("num", args[0]);
+		bthrow(type_error("num", args[0]));
 
 	addr = tosize(args[0]);
 	if(machdata->das(coremap, addr, 'i', buf, sizeof(buf)) < 0)
-		lerrorf(sl_errio, "could not disassemble at %ud", addr);
+		bthrow(lerrorf(sl_errio, "could not disassemble at %ud", addr));
 	return str_from_cstr(buf);
 }
 
@@ -402,12 +402,12 @@
 
 	argcount(nargs, 1);
 	if(sl_unlikely(!sl_isnum(args[0])))
-		type_error("num", args[0]);
+		bthrow(type_error("num", args[0]));
 
 	addr = tosize(args[0]);
 	sz = machdata->instsize(coremap, addr);
 	if(sz < 0)
-		lerrorf(sl_errio, "could not get instruction size at %ud", addr);
+		bthrow(lerrorf(sl_errio, "could not get instruction size at %ud", addr));
 	return size_wrap(sz);
 }
 
@@ -418,11 +418,11 @@
 
 	argcount(nargs, 1);
 	if(sl_unlikely(!sl_isnum(args[0])))
-		type_error("num", args[0]);
+		bthrow(type_error("num", args[0]));
 
 	addr = tosize(args[0]);
 	if(!fileline(buf, sizeof(buf), addr))
-		lerrorf(sl_errio, "could not locate source code line at %ud", addr);
+		bthrow(lerrorf(sl_errio, "could not locate source code line at %ud", addr));
 	return str_from_cstr(buf);
 }
 
@@ -434,15 +434,15 @@
 
 	argcount(nargs, 2);
 	if(sl_unlikely(!sl_isstr(args[0])))
-		type_error("str", args[0]);
+		bthrow(type_error("str", args[0]));
 	if(sl_unlikely(!isfixnum(args[1])))
-		type_error("num", args[1]);
+		bthrow(type_error("num", args[1]));
 
 	file = cvalue_data(args[0]);
 	line = numval(args[1]);
 	addr = file2pc(file, line);
 	if(addr == ~0)
-		lerrorf(sl_errio, "could not find address of %s:%d", file, line);
+		bthrow(lerrorf(sl_errio, "could not find address of %s:%d", file, line));
 	return size_wrap(addr);
 }
 
@@ -453,10 +453,10 @@
 
 	argcount(nargs, 1);
 	if(sl_unlikely(!sl_isnum(args[0])))
-		type_error("num", args[0]);
+		bthrow(type_error("num", args[0]));
 
 	addr = tosize(args[0]);
 	if(!findsym(addr, CTEXT, &s))
-		lerrorf(sl_errio, "could not locate sym near %ud", addr);
+		bthrow(lerrorf(sl_errio, "could not locate sym near %ud", addr));
 	return mk_symbol(&s);
 }
--- a/src/plan9/lsd.sl
+++ b/src/plan9/lsd.sl
@@ -477,7 +477,7 @@
           (lsd-file2pc (car s) line)))))
 
 (def (global-symbol s (:text NIL) (:data NIL))
-  «Return a symbol from the attached proc's symbol table or `NIL`. 
+  «Return a symbol from the attached proc's symbol table or `NIL`.
 
    Input is a `str`. Optionally specify whether to search only text
    symbols or data symbols. The default is to search both.
--- a/src/plan9/platform.h
+++ b/src/plan9/platform.h
@@ -29,6 +29,9 @@
 #define sl_setjmp(e) setjmp((e))
 #define sl_longjmp(e, v) longjmp((e), (v))
 
+#define sys_setcallpc(arg) do{sl.cpc = getcallerpc(&arg);}while(0)
+void sys_setcurrpc(int dummy);
+
 extern double D_PNAN, D_PINF;
 
 #if defined(__amd64__) || \
--- a/src/plan9/sys.c
+++ b/src/plan9/sys.c
@@ -103,6 +103,13 @@
 }
 
 void
+sys_setcurrpc(int dummy)
+{
+	USED(dummy);
+	sys_setcallpc(dummy);
+}
+
+void
 sys_init(void)
 {
 	lsd_init();
--- a/src/posix/platform.h
+++ b/src/posix/platform.h
@@ -73,6 +73,10 @@
 	exit(1); \
 }while(0)
 
+#define getcallerpc(a) (uintptr)__builtin_extract_return_addr(__builtin_return_address(0))
+#define sys_setcallpc(arg) do{sl.cpc = getcallerpc(&arg);}while(0)
+void sys_setcurrpc(int dummy);
+
 #define nil NULL
 #define USED(x) ((void)(x))
 #define nelem(x) (int)(sizeof(x)/sizeof((x)[0]))
--- a/src/posix/sys.c
+++ b/src/posix/sys.c
@@ -101,8 +101,17 @@
 	}
 }
 
+void __attribute__((noinline))
+sys_setcurrpc(int dummy)
+{
+	USED(dummy);
+	sys_setcallpc(dummy);
+}
+
 void
-sys_init(void){}
+sys_init(void)
+{
+}
 
 static const u8int boot[] = {
 #include "sl.boot.h"
--- a/src/sl.c
+++ b/src/sl.c
@@ -197,7 +197,7 @@
 	{ \
 		if(sl_likely(is##type(v))) \
 			return (ctype)cnvt(v); \
-		type_error(#type, v); \
+		cthrow(type_error(#type, v), v); \
 	}
 SAFECAST_OP(cons, sl_cons*, ptr)
 SAFECAST_OP(sym, sl_sym*, ptr)
@@ -369,7 +369,7 @@
 sl_gc_handle(sl_v *pv)
 {
 	if(sl_unlikely(slg.ngchandles >= N_GC_HANDLES))
-		lerrorf(sl_errmem, "out of gc handles");
+		cthrow(lerrorf(sl_errmem, "out of gc handles"), pv);
 	slg.gchandles[slg.ngchandles++] = pv;
 }
 
@@ -583,11 +583,11 @@
 	else if(sl_likely(isbuiltin(f))){
 		sl_v tab = sym_value(sl_builtinssym);
 		if(sl_unlikely(ptr(tab) == nil))
-			unbound_error(tab);
+			cthrow(unbound_error(tab), n);
 		saveSP[-n-1] = vec_elt(tab, uintval(f));
 		v = apply_cl(n);
 	}else{
-		type_error("fn", f);
+		cthrow(type_error("fn", f), n);
 	}
 	sl.sp = saveSP;
 	return v;
@@ -605,7 +605,7 @@
 		v = cdr_(v);
 	}
 	if(v != sl_nil)
-		lerrorf(sl_errarg, "apply: last argument: not a list");
+		cthrow(lerrorf(sl_errarg, "apply: last argument: not a list"), f);
 	v = _applyn(n);
 	sl.sp = saveSP;
 	return v;
@@ -780,9 +780,9 @@
 	sl_v s3 = sl.sp[-3];
 	sl_v s4 = sl.sp[-4];
 	if(sl_unlikely(nargs < nreq))
-		lerrorf(sl_errarg, "too few arguments");
+		cthrow(lerrorf(sl_errarg, "too few arguments"), kwtable);
 	if(sl_unlikely(extr > nelem(args)))
-		lerrorf(sl_errarg, "too many arguments");
+		cthrow(lerrorf(sl_errarg, "too many arguments"), kwtable);
 	for(i = 0; i < extr; i++)
 		args[i] = UNBOUND;
 	for(i = nreq; i < nargs; i++){
@@ -800,7 +800,7 @@
 	do{
 		i++;
 		if(sl_unlikely(i >= nargs))
-			lerrorf(sl_errarg, "keyword %s requires an argument", sym_name(v));
+			cthrow(lerrorf(sl_errarg, "keyword %s requires an argument", sym_name(v)), kwtable);
 		sl_v hv = fixnum(((sl_sym*)ptr(v))->hash);
 		sl_fx lx = numval(hv);
 		uintptr x = 2*((lx < 0 ? -lx : lx) % n);
@@ -813,7 +813,7 @@
 				args[idx] = bp[i];
 			}
 		}else{
-			lerrorf(sl_errarg, "unsupported keyword %s", sym_name(v));
+			cthrow(lerrorf(sl_errarg, "unsupported keyword %s", sym_name(v)), kwtable);
 		}
 		i++;
 		if(i >= nargs)
@@ -823,7 +823,7 @@
 no_kw:
 	nrestargs = nargs - i;
 	if(sl_unlikely(!va && nrestargs > 0))
-		lerrorf(sl_errarg, "too many arguments");
+		cthrow(lerrorf(sl_errarg, "too many arguments"), kwtable);
 	nargs = ntot + nrestargs;
 	if(nrestargs)
 		memmove(bp+ntot, bp+i, nrestargs*sizeof(sl_v));
@@ -917,9 +917,9 @@
 		sl_v *bp = top-4-sz;
 		sl_v fn = bp[0];
 		sl_v v = alloc_vec(sz+1, 0);
-		if(iscbuiltin(fn))
-			vec_elt(v, 0) = fn;
-		else{
+		if(iscbuiltin(fn)){
+			vec_elt(v, 0) = lst == sl_nil ? mk_ptr(sl.cpc) : fn; mk_ptr((uintptr)ip1);
+		}else{
 			/* -1: ip1 is *after* the one that was being executed */
 			intptr ip = ip1 - (const u8int*)cvalue_data(fn_bcode(fn)) - 1;
 			vec_elt(v, 0) = fixnum(ip);
@@ -955,12 +955,12 @@
 	if(nargs < 1 || nargs > 4)
 		argcount(nargs, 1);
 	if(sl_unlikely(!sl_isstr(args[0])))
-		type_error("str", args[0]);
+		bthrow(type_error("str", args[0]));
 	sl_v vals = sl_emptyvec;
 	if(nargs > 1){
 		vals = args[1];
 		if(sl_unlikely(!isvec(vals)))
-			type_error("vec", vals);
+			bthrow(type_error("vec", vals));
 	}
 	sl_cv *arr = ptr(args[0]);
 	cv_pin(arr);
@@ -986,12 +986,12 @@
 			fn->env = args[2];
 			if(nargs > 3){
 				if(sl_unlikely(!issym(args[3])))
-					type_error("sym", args[3]);
+					bthrow(type_error("sym", args[3]));
 				fn->name = args[3];
 			}
 		}
 		if(sl_unlikely(isgensym(fn->name)))
-			lerrorf(sl_errarg, "name should not be a gensym");
+			bthrow(lerrorf(sl_errarg, "name should not be a gensym"));
 	}
 	return fv;
 }
@@ -1004,7 +1004,7 @@
 	if(sl_unlikely(iscbuiltin(v)))
 		return v;
 	if(sl_unlikely(!isfn(v)))
-		type_error("fn", v);
+		bthrow(type_error("fn", v));
 	return fn_bcode(v);
 }
 
@@ -1016,7 +1016,7 @@
 	if(sl_unlikely(iscbuiltin(v)))
 		return sl_emptyvec;
 	if(sl_unlikely(!isfn(v)))
-		type_error("fn", v);
+		bthrow(type_error("fn", v));
 	return fn_vals(v);
 }
 
@@ -1028,7 +1028,7 @@
 	if(sl_unlikely(iscbuiltin(v)))
 		return sl_nil;
 	if(sl_unlikely(!isfn(v)))
-		type_error("fn", v);
+		bthrow(type_error("fn", v));
 	return fn_env(v);
 }
 
@@ -1046,7 +1046,7 @@
 			return sl_nil;
 		return v;
 	}
-	type_error("fn", v);
+	bthrow(type_error("fn", v));
 }
 
 BUILTIN("copy-list", copy_list)
@@ -1073,7 +1073,7 @@
 				cdr_(lastcons) = lst;
 			lastcons = tagptr((((sl_cons*)slg.curheap)-1), TAG_CONS);
 		}else if(lst != sl_nil){
-			type_error("cons", lst);
+			bthrow(type_error("cons", lst));
 		}
 	}
 	sl_free_gc_handles(2);
@@ -1099,7 +1099,7 @@
 BUILTIN("map", map)
 {
 	if(sl_unlikely(nargs < 2))
-		lerrorf(sl_errarg, "too few arguments");
+		argcount(nargs, 2);
 	sl_v *k = sl.sp;
 	PUSH(sl_nil);
 	PUSH(sl_nil);
@@ -1130,7 +1130,7 @@
 BUILTIN("for-each", for_each)
 {
 	if(sl_unlikely(nargs < 2))
-		lerrorf(sl_errarg, "too few arguments");
+		argcount(nargs, 2);
 	for(usize n = 0;; n++){
 		PUSH(args[0]);
 		int pargs = 0;
--- a/src/sl.h
+++ b/src/sl.h
@@ -290,10 +290,13 @@
 _Noreturn void unbound_error(sl_v sym);
 _Noreturn void arity_error(int nargs, int c);
 
+#define bthrow(stmt) do{ sys_setcurrpc(0); stmt; }while(1)
+#define cthrow(stmt, a) do{ sys_setcallpc(a); stmt; }while(1)
+
 #define argcount(nargs, c) \
 	do{ \
 		if(sl_unlikely(nargs != c)) \
-			arity_error(nargs, c); \
+			bthrow(arity_error(nargs, c)); \
 	}while(0)
 
 typedef struct {
@@ -393,6 +396,7 @@
 	sl_fx p_level;
 	int scr_width;
 	ssize hpos, vpos;
+	uintptr cpc;
 };
 
 struct Slg {
--- a/src/str.c
+++ b/src/str.c
@@ -22,17 +22,17 @@
 	if(nargs < 1 || nargs > 3)
 		argcount(nargs, 1);
 	if(!sl_isstr(args[0]))
-		type_error("str", args[0]);
+		bthrow(type_error("str", args[0]));
 	usize len = cv_len(ptr(args[0]));
 	usize stop = len;
 	if(nargs > 1){
 		start = tosize(args[1]);
 		if(start > len)
-			bounds_error(args[0], args[1]);
+			bthrow(bounds_error(args[0], args[1]));
 		if(nargs > 2){
 			stop = tosize(args[2]);
 			if(stop > len)
-				bounds_error(args[0], args[2]);
+				bthrow(bounds_error(args[0], args[2]));
 			if(stop <= start)
 				return fixnum(0);
 		}
@@ -49,7 +49,7 @@
 		return w < 0 ? sl_nil : fixnum(w);
 	}
 	if(!sl_isstr(args[0]))
-		type_error("str", args[0]);
+		bthrow(type_error("str", args[0]));
 	char *str = tostr(args[0]);
 	usize len = cv_len(ptr(args[0]));
 	ssize w = u8_strwidth(str, len);
@@ -60,7 +60,7 @@
 {
 	argcount(nargs, 1);
 	if(!sl_isstr(args[0]))
-		type_error("str", args[0]);
+		bthrow(type_error("str", args[0]));
 	usize len = cv_len(ptr(args[0]));
 	sl_v ns = cvalue_str(len);
 	u8_reverse(cvalue_data(ns), cvalue_data(args[0]), len);
@@ -110,7 +110,7 @@
 	// it can only be a :trim X now
 	if(nargs > n){
 		if(args[n] != sl_trimsym)
-			lerrorf(sl_errarg, "invalid argument at position %d", n);
+			bthrow(lerrorf(sl_errarg, "invalid argument at position %d", n));
 		n++;
 		if(nargs <= n)
 			argcount(nargs, n+1);
@@ -167,7 +167,7 @@
 	for(startbytes = n = 0; n < startrune && startbytes < lenbytes; n++)
 		startbytes += u8_seqlen(s+startbytes);
 	if(n != startrune)
-		bounds_error(args[0], args[1]);
+		bthrow(bounds_error(args[0], args[1]));
 	usize endbytes = lenbytes;
 	if(nargs == 3){
 		usize endrune = tosize(args[2]);
@@ -174,7 +174,7 @@
 		for(endbytes = startbytes; n < endrune && endbytes < lenbytes; n++)
 			endbytes += u8_seqlen(s+endbytes);
 		if(n != endrune)
-			bounds_error(args[0], args[2]);
+			bthrow(bounds_error(args[0], args[2]));
 	}
 	sl_v ns = cvalue_str(endbytes-startbytes);
 	s = cvalue_data(args[0]); // reload after alloc
@@ -191,7 +191,7 @@
 	for(startbytes = n = 0; n < startrune && startbytes < lenbytes; n++)
 		startbytes += u8_seqlen(s+startbytes);
 	if(n != startrune || startbytes >= lenbytes)
-		bounds_error(args[0], args[1]);
+		bthrow(bounds_error(args[0], args[1]));
 	Rune r;
 	chartorune(&r, s+startbytes);
 	return mk_rune(r);
@@ -202,7 +202,7 @@
 {
 	argcount(nargs, 1);
 	if(!isrune(args[0]))
-		type_error("rune", args[0]);
+		bthrow(type_error("rune", args[0]));
 	return mk_rune(toupperrune(torune(args[0])));
 }
 
@@ -211,7 +211,7 @@
 {
 	argcount(nargs, 1);
 	if(!isrune(args[0]))
-		type_error("rune", args[0]);
+		bthrow(type_error("rune", args[0]));
 	return mk_rune(tolowerrune(torune(args[0])));
 }
 
@@ -220,7 +220,7 @@
 {
 	argcount(nargs, 1);
 	if(!isrune(args[0]))
-		type_error("rune", args[0]);
+		bthrow(type_error("rune", args[0]));
 	return mk_rune(totitlerune(torune(args[0])));
 }
 
@@ -229,7 +229,7 @@
 {
 	argcount(nargs, 1);
 	if(!isrune(args[0]))
-		type_error("rune", args[0]);
+		bthrow(type_error("rune", args[0]));
 	return isalpharune(torune(args[0])) ? sl_t : sl_nil;
 }
 
@@ -238,7 +238,7 @@
 {
 	argcount(nargs, 1);
 	if(!isrune(args[0]))
-		type_error("rune", args[0]);
+		bthrow(type_error("rune", args[0]));
 	return islowerrune(torune(args[0])) ? sl_t : sl_nil;
 }
 
@@ -247,7 +247,7 @@
 {
 	argcount(nargs, 1);
 	if(!isrune(args[0]))
-		type_error("rune", args[0]);
+		bthrow(type_error("rune", args[0]));
 	return isupperrune(torune(args[0])) ? sl_t : sl_nil;
 }
 
@@ -256,7 +256,7 @@
 {
 	argcount(nargs, 1);
 	if(!isrune(args[0]))
-		type_error("rune", args[0]);
+		bthrow(type_error("rune", args[0]));
 	return istitlerune(torune(args[0])) ? sl_t : sl_nil;
 }
 
@@ -265,7 +265,7 @@
 {
 	argcount(nargs, 1);
 	if(!isrune(args[0]))
-		type_error("rune", args[0]);
+		bthrow(type_error("rune", args[0]));
 	return isdigitrune(torune(args[0])) ? sl_t : sl_nil;
 }
 
@@ -274,7 +274,7 @@
 {
 	argcount(nargs, 1);
 	if(!isrune(args[0]))
-		type_error("rune", args[0]);
+		bthrow(type_error("rune", args[0]));
 	return isspacerune(torune(args[0])) ? sl_t : sl_nil;
 }
 
@@ -305,7 +305,7 @@
 		ndbytes = cv_len(cv);
 		nd = (char*)cv_data(cv);
 	}else{
-		type_error("str or rune", args[1]);
+		bthrow(type_error("str or rune", args[1]));
 	}
 	if(ndbytes == 0)
 		return size_wrap(startrune);
@@ -317,7 +317,7 @@
 	for(i = n = 0; n < startrune && i < sbytes; n++)
 		i += u8_seqlen(s+i);
 	if(n != startrune)
-		bounds_error(args[0], fixnum(startrune));
+		bthrow(bounds_error(args[0], fixnum(startrune)));
 	// now search for the needle
 	for(; i < sbytes-ndbytes+1; n++){
 		if(s[i] == nd[0] && memcmp(&s[i+1], nd+1, ndbytes-1) == 0)
@@ -332,7 +332,7 @@
 {
 	unsigned long radix = tosize(arg);
 	if(radix < 2 || radix > 36)
-		lerrorf(sl_errarg, "invalid radix");
+		cthrow(lerrorf(sl_errarg, "invalid radix"), arg);
 	return radix;
 }
 
@@ -352,7 +352,7 @@
 		num = ubnumval(n);
 	else if(isbignum(n)){
 		if(radix != 16 && radix != 10 && radix != 8 && radix != 4 && radix != 2)
-			lerrorf(sl_errarg, "invalid radix with bignum");
+			bthrow(lerrorf(sl_errarg, "invalid radix with bignum"));
 		mpint *i = tobignum(n);
 		char *s = mptoa(i, radix, nil, 0);
 		assert(s != nil);
@@ -368,11 +368,11 @@
 		if(cv_numtype(data) < T_FLOAT)
 			num = conv_to_u64(n, cv_data(data), cv_numtype(data));
 		else if(radix != 10)
-			lerrorf(sl_errarg, "invalid radix with floating point");
+			bthrow(lerrorf(sl_errarg, "invalid radix with floating point"));
 		else
 			return fn_builtin_str(args, nargs);
 	}else{
-		type_error("int", n);
+		bthrow(type_error("int", n));
 	}
 	if(numval(sl_compare(args[0], fixnum(0), false)) < 0){
 		num = -num;
--- a/src/table.c
+++ b/src/table.c
@@ -85,7 +85,7 @@
 totable(sl_v v)
 {
 	if(!ishashtable(v))
-		type_error("table", v);
+		cthrow(type_error("table", v), v);
 	return cvalue_data(v);
 }
 
@@ -93,7 +93,7 @@
 {
 	int cnt = nargs;
 	if(cnt & 1)
-		lerrorf(sl_errarg, "arguments must come in pairs");
+		bthrow(lerrorf(sl_errarg, "arguments must come in pairs"));
 	sl_v nt;
 	// prevent small tables from being added to finalizer list
 	if(cnt <= HT_N_INLINE)
@@ -148,7 +148,7 @@
 	if(v == (sl_v)HT_NOTFOUND){
 		if(nargs == 3)
 			return args[2];
-		key_error(args[1]);
+		bthrow(key_error(args[1]));
 	}
 	return v;
 }
@@ -168,7 +168,7 @@
 	argcount(nargs, 2);
 	sl_htable *h = totable(args[0]);
 	if(!equalhash_remove(h, (void*)args[1]))
-		key_error(args[1]);
+		bthrow(key_error(args[1]));
 	return args[0];
 }
 
--- a/src/vm.h
+++ b/src/vm.h
@@ -86,19 +86,17 @@
 #endif
 		}
 	}else if(sl_likely(iscbuiltin(v))){
-		bp = sp-nargs;
+		builtin_t f = ((sl_cv*)ptr(v))->cbuiltin;
 		*sp++ = sl_nil; // fn->env;
 		*sp++ = (sl_v)sl.curr_frame;
 		*sp++ = n;
-		*sp++ = v; // ip
+		*sp++ = (sl_v)f; // ip
 		sl.curr_frame = sp;
 		sl.sp = sp;
-		v = ((sl_cv*)ptr(v))->cbuiltin(sp-n-4, n);
+		v = f(sp-n-4, n);
 		sp = sl.curr_frame;
 		sl.curr_frame = (sl_v*)sp[-3];
 		sp -= 4+n;
-		n = sl.curr_frame[-2];
-		bp = sl.curr_frame - 4 - n;
 		sp[-1] = v;
 		NEXT_OP;
 	}