shithub: sl

Download patch

ref: d5715fb848737d39f465381c60ddc2a81b8b34c5
parent: e28a69a81ebef87d000b2ab7bb5f920868784e61
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue Apr 1 00:15:12 EDT 2025

unboxed: store/pass most of C primitives by values on stack

This covers [su]{8,16} (and [su]32 on 64-bit CPUs).

References: https://todo.sr.ht/~ft/sl/46

--- a/src/builtins.c
+++ b/src/builtins.c
@@ -226,7 +226,7 @@
 {
 	argcount(nargs, 1);
 	sl_v v = args[0];
-	if(isfixnum(v) || ismp(v))
+	if(isfixnum(v) || isunboxednum(v) || ismp(v))
 		return sl_t;
 	if(iscprim(v)){
 		sl_numtype nt = cp_numtype(ptr(v));
@@ -256,7 +256,7 @@
 {
 	argcount(nargs, 1);
 	sl_v v = args[0];
-	return (isfixnum(v) || ismp(v) ||
+	return (isfixnum(v) || isunboxednum(v) || ismp(v) ||
 			(iscprim(v) && cp_numtype(ptr(v)) < T_FLOAT)) ?
 		sl_t : sl_nil;
 }
@@ -274,6 +274,8 @@
 	sl_v v = args[0];
 	if(isfixnum(v))
 		return v;
+	if(isunboxednum(v))
+		return isunboxedunum(v) ? fixnum(unboxednumuval(v)) : fixnum(unboxednumsval(v));
 	if(iscprim(v)){
 		void *p = ptr(v);
 		return fixnum(conv_to_s64(cp_data(p), cp_numtype(p)));
@@ -291,7 +293,7 @@
 {
 	argcount(nargs, 1);
 	sl_v v = args[0];
-	if(isfixnum(v) || ismp(v))
+	if(isfixnum(v) || isunboxednum(v) || ismp(v))
 		return v;
 	if(iscprim(v)){
 		sl_cprim *cp = ptr(v);
@@ -352,6 +354,8 @@
 {
 	if(isfixnum(a))
 		return (double)numval(a);
+	if(isunboxednum(a))
+		return isunboxedunum(a) ? (double)unboxednumuval(a) : (double)unboxednumsval(a);
 	if(iscprim(a)){
 		sl_cprim *cp = ptr(a);
 		sl_numtype nt = cp_numtype(cp);
--- a/src/compress.c
+++ b/src/compress.c
@@ -17,7 +17,8 @@
 		type_error("arr", args[0]);
 	u8int *in;
 	usize insz;
-	to_sized_ptr(args[0], &in, &insz);
+	uintptr u;
+	to_sized_ptr(args[0], &in, &insz, &u);
 	int level = nargs > 1 ? tofixnum(args[1]) : 0;
 	if(level < 0)
 		level = 0;
@@ -47,7 +48,8 @@
 
 	u8int *in;
 	usize insz;
-	to_sized_ptr(args[0], &in, &insz);
+	uintptr u;
+	to_sized_ptr(args[0], &in, &insz, &u);
 	if(!isarr(args[0]))
 		type_error("arr", args[0]);
 	usize outsz;
@@ -59,7 +61,7 @@
 		out = cvalue_data(v);
 	}else if(args[1] == sl_tosym){
 		v = args[2];
-		to_sized_ptr(v, &out, &outsz);
+		to_sized_ptr(v, &out, &outsz, &u);
 	}else{
 		lerrorf(sl_errarg, "either :size or :to must be specified");
 	}
--- a/src/cvalues.c
+++ b/src/cvalues.c
@@ -15,6 +15,9 @@
 #define owned(cv) ((uintptr)(cv)->type & CV_OWNED)
 #define isinlined(cv) ((cv)->data == (cv)->_space)
 
+sl_type *unboxedtypes[T_UNBOXED_NUM];
+sl_v unboxedtypesyms[T_UNBOXED_NUM];
+
 static void cvalue_init(sl_type *type, sl_v v, void *dest);
 
 void
@@ -199,21 +202,29 @@
 
 #define num_init(ctype, cnvt, tag) \
 	static void \
-	cvalue_##ctype##_init(sl_type *type, sl_v arg, void *dest) \
+	cvalue_##ctype##_init(sl_type *type, sl_v a, void *dest) \
 	{ \
 		ctype n; \
 		USED(type); \
-		if(isfixnum(arg)) \
-			n = (ctype)numval(arg); \
-		else if(iscprim(arg)){ \
-			sl_cprim *cp = ptr(arg); \
+		if(isfixnum(a)) \
+			n = (ctype)numval(a); \
+		else if(isunboxednum(a)){ \
+			uintptr v; \
+			void *p = &v; \
+			if(isunboxedunum(a)) \
+				v = unboxednumuval(a); \
+			else \
+				v = unboxednumsval(a); \
+			n = (ctype)conv_to_##cnvt(p, unboxednumtype(a)); \
+		}else if(iscprim(a)){ \
+			sl_cprim *cp = ptr(a); \
 			void *p = cp_data(cp); \
 			n = (ctype)conv_to_##cnvt(p, cp_numtype(cp)); \
-		}else if(ismp(arg)){ \
-			void *p = cv_data(ptr(arg)); \
+		}else if(ismp(a)){ \
+			void *p = cv_data(ptr(a)); \
 			n = (ctype)conv_to_##cnvt(p, T_MP); \
 		}else \
-			type_error("num", arg); \
+			type_error("num", a); \
 		*((ctype*)dest) = n; \
 	}
 
@@ -236,29 +247,70 @@
 			PUSH(fixnum(0)); \
 			args = sl.sp-1; \
 		} \
-		sl_v cp = cprim(sl_##typenam##type, sizeof(ctype)); \
-		cvalue_##ctype##_init(sl_##typenam##type, args[0], cp_data(ptr(cp))); \
-		return cp; \
+		sl_v v = cprim(sl_##typenam##type, sizeof(ctype)); \
+		cvalue_##ctype##_init(sl_##typenam##type, args[0], cp_data(ptr(v))); \
+		return v; \
 	}
 
+#define num_ctor_init_unboxed(typenam, ctype, tag) \
+	static \
+	BUILTIN(#typenam, typenam) \
+	{ \
+		if(nargs == 0){ \
+			PUSH(fixnum(0)); \
+			args = sl.sp-1; \
+		} \
+		sl_v v; \
+		if(tag < T_UNBOXED_NUM && sizeof(ctype) < sizeof(sl_v) && sl_##typenam##type != sl_runetype){ \
+			ctype u; \
+			cvalue_##ctype##_init(sl_##typenam##type, args[0], &u); \
+			if(tag & 1) \
+				v = (uintptr)u; \
+			else \
+				v = (intptr)u; \
+			v = v<<8 | tag<<4 | TAG_UNBOXED; \
+		}else{ \
+			v = cprim(sl_##typenam##type, sizeof(ctype)); \
+			cvalue_##ctype##_init(sl_##typenam##type, args[0], cp_data(ptr(v))); \
+		} \
+		return v; \
+	}
+
 #define num_ctor_ctor(typenam, ctype, tag) \
 	sl_v mk_##typenam(ctype n) \
 	{ \
-		sl_v cp = cprim(sl_##typenam##type, sizeof(ctype)); \
-		*(ctype*)cp_data(ptr(cp)) = n; \
-		return cp; \
+		sl_v v = cprim(sl_##typenam##type, sizeof(n)); \
+		*(ctype*)cp_data(ptr(v)) = n; \
+		return v; \
 	}
 
+#define num_ctor_ctor_unboxed(typenam, ctype, tag) \
+	sl_v mk_##typenam(ctype n) \
+	{ \
+		sl_v v; \
+		if(tag < T_UNBOXED_NUM && sizeof(n) < sizeof(sl_v) && sl_##typenam##type != sl_runetype){ \
+			v = n<<8 | tag<<4 | TAG_UNBOXED; \
+		}else{ \
+			v = cprim(sl_##typenam##type, sizeof(n)); \
+			*(ctype*)cp_data(ptr(v)) = n; \
+		} \
+		return v; \
+	}
+
 #define num_ctor(typenam, ctype, tag) \
 	num_ctor_init(typenam, ctype, tag) \
 	num_ctor_ctor(typenam, ctype, tag)
 
-num_ctor_init(s8, s8int, T_S8)
-num_ctor_init(u8, u8int, T_U8)
-num_ctor_init(s16, s16int, T_S16)
-num_ctor_init(u16, u16int, T_U16)
-num_ctor(s32, s32int, T_S32)
-num_ctor(u32, u32int, T_U32)
+#define num_ctor_unboxed(typenam, ctype, tag) \
+	num_ctor_init_unboxed(typenam, ctype, tag) \
+	num_ctor_ctor_unboxed(typenam, ctype, tag)
+
+num_ctor_init_unboxed(s8, s8int, T_S8)
+num_ctor_init_unboxed(u8, u8int, T_U8)
+num_ctor_init_unboxed(s16, s16int, T_S16)
+num_ctor_init_unboxed(u16, u16int, T_U16)
+num_ctor_unboxed(s32, s32int, T_S32)
+num_ctor_unboxed(u32, u32int, T_U32)
 num_ctor(s64, s64int, T_S64)
 num_ctor(u64, u64int, T_U64)
 num_ctor_init(utf8, u8int, T_U8)
@@ -267,22 +319,30 @@
 num_ctor(rune, u32int, T_U32)
 
 static void
-cvalue_mp_init(sl_type *type, sl_v arg, void *dest)
+cvalue_mp_init(sl_type *type, sl_v a, void *dest)
 {
 	mpint *n;
 	USED(type);
-	if(isfixnum(arg)){
-		n = vtomp(numval(arg), nil);
-	}else if(iscvalue(arg)){
-		sl_cv *cv = ptr(arg);
+	if(isfixnum(a))
+		n = vtomp(numval(a), nil);
+	else if(isunboxednum(a)){
+		uintptr v;
+		void *p = &v;
+		if(isunboxedunum(a))
+			v = unboxednumuval(a);
+		else
+			v = unboxednumsval(a);
+		n = conv_to_mp(p, unboxednumtype(a));
+	}else if(iscvalue(a)){
+		sl_cv *cv = ptr(a);
 		void *p = cv_data(cv);
 		n = conv_to_mp(p, cv_numtype(cv));
-	}else if(iscprim(arg)){
-		sl_cprim *cp = ptr(arg);
+	}else if(iscprim(a)){
+		sl_cprim *cp = ptr(a);
 		void *p = cp_data(cp);
 		n = conv_to_mp(p, cp_numtype(cp));
 	}else
-		type_error("num", arg);
+		type_error("num", a);
 	*((mpint**)dest) = n;
 }
 
@@ -330,6 +390,8 @@
 {
 	if(isfixnum(n))
 		return (usize)numval(n);
+	if(isunboxednum(n))
+		return (usize)unboxednumuval(n);
 	if(iscprim(n)){
 		sl_cprim *cp = ptr(n);
 		if(sizeof(usize) == 8)
@@ -344,6 +406,10 @@
 {
 	if(isfixnum(n))
 		return numval(n);
+	if(isunboxedunum(n))
+		return unboxednumuval(n);
+	if(isunboxedsnum(n))
+		return unboxednumsval(n);
 	if(iscprim(n)){
 		sl_cprim *cp = ptr(n);
 		return conv_to_s64(cp_data(cp), cp_numtype(cp));
@@ -522,8 +588,20 @@
 
 // get pointer and size for any plain-old-data value
 void
-to_sized_ptr(sl_v v, u8int **pdata, usize *psz)
+to_sized_ptr(sl_v v, u8int **pdata, usize *psz, uintptr *u)
 {
+	if(isunboxedunum(v)){
+		*u = unboxednumuval(v);
+		*pdata = (u8int*)u;
+		*psz = unboxedtypes[unboxednumtype(v)]->size;
+		return;
+	}
+	if(isunboxedsnum(v)){
+		*u = unboxednumsval(v);
+		*pdata = (u8int*)u;
+		*psz = unboxedtypes[unboxednumtype(v)]->size;
+		return;
+	}
 	if(iscvalue(v)){
 		sl_cv *pcv = ptr(v);
 		sl_ios *x;
@@ -554,7 +632,8 @@
 		return size_wrap(ctype_sizeof(args[0]));
 	usize n;
 	u8int *data;
-	to_sized_ptr(args[0], &data, &n);
+	uintptr u;
+	to_sized_ptr(args[0], &data, &n, &u);
 	return size_wrap(n);
 }
 
@@ -565,6 +644,7 @@
 	switch(tag(args[0])){
 	case TAG_CONS: return sl_conssym;
 	case TAG_FIXNUM: return sl_fixnumsym;
+	case TAG_UNBOXED: return unboxedtypesyms[unboxednumtype(args[0])];
 	case TAG_SYM: return sl_symsym;
 	case TAG_VEC:return sl_vecsym;
 	case TAG_FN:
@@ -647,7 +727,8 @@
 BUILTIN("plain-old-data?", plain_old_datap)
 {
 	argcount(nargs, 1);
-	return (iscprim(args[0]) ||
+	return (isunboxednum(args[0]) ||
+	        iscprim(args[0]) ||
 			(iscvalue(args[0]) && cv_isPOD(ptr(args[0])))) ?
 		sl_t : sl_nil;
 }
@@ -927,6 +1008,14 @@
 		*pp = pi;
 		*pt = T_FIXNUM;
 		return true;
+	}else if(isunboxednum(a)){
+		if(isunboxedunum(a))
+			*pi = unboxednumuval(a);
+		else
+			*pi = unboxednumsval(a);
+		*pp = pi;
+		*pt = unboxednumtype(a);
+		return true;
 	}else if(iscprim(a)){
 		cp = ptr(a);
 		*pp = cp_data(cp);
@@ -1215,6 +1304,10 @@
 
 	if(isfixnum(a))
 		return fixnum(~numval(a));
+	if(isunboxedunum(a))
+		return (~unboxednumuval(a) & ~0xff) | (a & 0xff);
+	if(isunboxedsnum(a))
+		return (~unboxednumsval(a) & ~0xff) | (a & 0xff);
 	if(iscprim(a)){
 		cp = ptr(a);
 		ta = cp_numtype(cp);
@@ -1248,6 +1341,7 @@
 {
 	sl_fx n;
 	s64int accum;
+	u64int u;
 	sl_cprim *cp;
 	int ta;
 	mpint *mp;
@@ -1259,8 +1353,8 @@
 	if(n == 0)
 		return a;
 	mp = nil;
-	if(isfixnum(a)){
-		accum = numval(a);
+	if(isfixnum(a) || isunboxedsnum(a)){
+		accum = isfixnum(a) ? numval(a) : unboxednumsval(a);
 		if(n > -64 && n < 0)
 			return fixnum(accum>>(-n));
 		if(n < 0 || n >= 64 || sash_overflow_64(accum, n, &accum)){
@@ -1269,6 +1363,15 @@
 		}else
 			return fits_fixnum(accum) ? fixnum(accum) : return_from_s64(accum);
 	}
+	if(isunboxedunum(a)){
+		u = unboxednumuval(a);
+		if(n > 0)
+			u = n < 64 ? u<<n : 0;
+		else
+			u = n > -64 ? u>>-n : 0;
+		accum = u;
+		return (accum >= 0 && fits_fixnum(accum)) ? fixnum(accum) : return_from_u64(u);
+	}
 	if(iscprim(a)){
 		cp = ptr(a);
 		ta = cp_numtype(cp);
@@ -1346,6 +1449,23 @@
 	sl_mptype = get_type(sl_bignumsym);
 	sl_mptype->init = cvalue_mp_init;
 	sl_mptype->vtable = &mp_vtable;
+
+	unboxedtypes[T_S8] = sl_s8type;
+	unboxedtypes[T_U8] = sl_u8type;
+	unboxedtypes[T_S16] = sl_s16type;
+	unboxedtypes[T_U16] = sl_u16type;
+	unboxedtypes[T_S32] = sl_s32type;
+	unboxedtypes[T_U32] = sl_u32type;
+	unboxedtypes[T_S64] = sl_s64type;
+	unboxedtypes[T_U64] = sl_u64type;
+	unboxedtypesyms[T_S8] = sl_s8sym;
+	unboxedtypesyms[T_U8] = sl_u8sym;
+	unboxedtypesyms[T_S16] = sl_s16sym;
+	unboxedtypesyms[T_U16] = sl_u16sym;
+	unboxedtypesyms[T_S32] = sl_s32sym;
+	unboxedtypesyms[T_U32] = sl_u32sym;
+	unboxedtypesyms[T_S64] = sl_s64sym;
+	unboxedtypesyms[T_U64] = sl_u64sym;
 
 	sl_strtype = get_type(mk_list2(sl_arrsym, sl_utf8sym));
 	sl_emptystr = cvalue_from_ref(sl_strtype, (char*)"", 0);
--- a/src/cvalues.h
+++ b/src/cvalues.h
@@ -26,7 +26,7 @@
 void cvalue_arr_init(sl_type *ft, sl_v arg, void *dest);
 usize cvalue_arrlen(sl_v v) sl_purefn;
 usize ctype_sizeof(sl_v type);
-void to_sized_ptr(sl_v v, u8int **pdata, usize *psz);
+void to_sized_ptr(sl_v v, u8int **pdata, usize *psz, uintptr *u);
 sl_v cvalue_relocate(sl_v v);
 sl_v cvalue_copy(sl_v v);
 sl_v cvalue_compare(sl_v a, sl_v b) sl_purefn;
--- a/src/equal.c
+++ b/src/equal.c
@@ -15,9 +15,6 @@
 #define doublehash(a) int64to32hash(a)
 #endif
 
-// comparable tag
-#define cmptag(v) (isfixnum(v) ? TAG_FIXNUM : tag(v))
-
 static sl_v
 eq_class(sl_htable *table, sl_v key)
 {
@@ -77,12 +74,14 @@
 	if(bound <= 0)
 		return sl_nil;
 	int taga = tag(a);
-	int tagb = cmptag(b);
+	int tagb = tag(b);
 	int c;
 	switch(taga){
 	case TAG_FIXNUM:
 		if(isfixnum(b))
 			return (sl_fx)a < (sl_fx)b ? fixnum(-1) : fixnum(1);
+		if(isunboxednum(b))
+			return fixnum(numeric_compare(a, b, eq, true, false));
 		if(iscprim(b)){
 			if(cp_class(ptr(b)) == sl_runetype)
 				return fixnum(1);
@@ -94,6 +93,20 @@
 				return fixnum(numeric_compare(a, b, eq, true, false));
 		}
 		return fixnum(-1);
+	case TAG_UNBOXED:
+		if(isfixnum(b) || isunboxednum(b))
+			return fixnum(numeric_compare(a, b, eq, true, false));
+		if(iscprim(b)){
+			if(cp_class(ptr(b)) == sl_runetype)
+				return fixnum(1);
+			return fixnum(numeric_compare(a, b, eq, true, false));
+		}
+		if(iscvalue(b)){
+			cv = ptr(b);
+			if(valid_numtype(cv_numtype(cv)))
+				return fixnum(numeric_compare(a, b, eq, true, false));
+		}
+		return fixnum(-1);
 	case TAG_SYM:
 		if(eq || tagb < TAG_SYM)
 			return fixnum(1);
@@ -333,6 +346,14 @@
 	switch(tg){
 	case TAG_FIXNUM:
 		u.d = (double)numval(a);
+		return doublehash(u.i64);
+	case TAG_UNBOXED:
+		if(isunboxedunum(a))
+			u.d = unboxednumuval(a);
+		else if(isunboxedsnum(a))
+			u.d = unboxednumsval(a);
+		else // FIXME(sigrid): unboxed
+			u.d = 0;
 		return doublehash(u.i64);
 	case TAG_FN:
 		if(uintval(a) > N_BUILTINS)
--- a/src/io.c
+++ b/src/io.c
@@ -334,7 +334,8 @@
 	}
 	u8int *data;
 	usize sz, offs = 0;
-	to_sized_ptr(v, &data, &sz);
+	uintptr u;
+	to_sized_ptr(v, &data, &sz, &u);
 	usize nb = sz;
 	if(nargs > 2){
 		get_start_count_args(args+1, nargs-1, sz, &offs, &nb);
--- a/src/plan9/lsd.c
+++ b/src/plan9/lsd.c
@@ -242,8 +242,8 @@
 
 	argcount(nargs, 3);
 	for(a = args; a < args+3; a++)
-	if(sl_unlikely(!sl_isnum(*a)))
-		type_error("num", *a);
+		if(sl_unlikely(!sl_isnum(*a)))
+			type_error("num", *a);
 
 	pc = tosize(args[0]);
 	sp = tosize(args[1]);
@@ -277,8 +277,8 @@
 	if(sl_unlikely(!sl_isstr(args[0]) && !sl_isnum(args[0])))
 		type_error("str|num", args[0]);
 
-	if(isfixnum(args[0])){
-		pid = numval(args[0]);
+	if(sl_isnum(args[0])){
+		pid = tosize(args[0]);
 		snprint(aout, sizeof(aout), "/proc/%d/text", pid);
 	}else{
 		len = cv_len(ptr(args[0]));
--- a/src/print.c
+++ b/src/print.c
@@ -187,7 +187,7 @@
 	if(sl_isstr(v))
 		return cv_len(ptr(v)) < SMALL_STR_LEN;
 	return (
-		isfixnum(v) || isbuiltin(v) || iscprim(v) ||
+		isfixnum(v) || isunboxed(v) || isbuiltin(v) || iscprim(v) ||
 		v == sl_t || v == sl_nil || v == sl_eof || v == sl_void
 	);
 }
@@ -385,6 +385,7 @@
 	}
 }
 
+static void unboxed_print(sl_ios *f, sl_v v);
 static void cvalue_print(sl_ios *f, sl_v v);
 
 static bool
@@ -427,6 +428,9 @@
 			lerrorf(sl_errio, "write failed");
 		sl.hpos += n;
 		break;
+	case TAG_UNBOXED:
+		unboxed_print(f, v);
+		break;
 	case TAG_SYM:
 		name = sym_name(v);
 		if(sl.print_princ)
@@ -846,6 +850,23 @@
 }
 
 static void
+unboxed_print(sl_ios *f, sl_v v)
+{
+	uintptr u;
+
+	if(isunboxedunum(v))
+		u = unboxednumuval(v);
+	else if(isunboxedsnum(v))
+		u = unboxednumsval(v);
+	else // FIXME(sigrid): unboxed
+		u = 0;
+	int numtype = unboxednumtype(v);
+	sl_v typesym = unboxedtypesyms[numtype];
+	sl_type *type = unboxedtypes[numtype];
+	cvalue_printdata(f, &u, type->size, typesym, 0);
+}
+
+static void
 cvalue_print(sl_ios *f, sl_v v)
 {
 	sl_cv *cv = ptr(v);
@@ -874,10 +895,13 @@
 static void
 set_print_width(void)
 {
-	sl_v pw = sym_value(sl_printwidthsym);
-	if(!isfixnum(pw))
-		return;
-	sl.scr_width = numval(pw);
+	sl_v v = sym_value(sl_printwidthsym);
+	if(isfixnum(v))
+		sl.scr_width = numval(v);
+	else if(isunboxedunum(v))
+		sl.scr_width = unboxednumuval(v);
+	else if(isunboxedsnum(v))
+		sl.scr_width = unboxednumsval(v);
 }
 
 void
@@ -888,9 +912,9 @@
 		set_print_width();
 	sl.print_princ = sym_value(sl_printreadablysym) == sl_nil;
 	sl_v pl = sym_value(sl_printlengthsym);
-	sl.print_length = isfixnum(pl) ? numval(pl) : -1;
+	sl.print_length = sl_isnum(pl) ? numtofx(pl) : -1;
 	pl = sym_value(sl_printlevelsym);
-	sl.print_level = isfixnum(pl) ? numval(pl) : -1;
+	sl.print_level = sl_isnum(pl) ? numtofx(pl) : -1;
 	sl.p_level = 0;
 
 	sl.printlabel = 0;
--- a/src/sl.c
+++ b/src/sl.c
@@ -385,7 +385,7 @@
 {
 	sl_v a, d, nc, first, *pcdr;
 
-	if(isfixnum(v))
+	if(isfixnum(v) || isunboxed(v))
 		return v;
 
 	uintptr t = tag(v);
@@ -692,12 +692,16 @@
 bool
 sl_isnum(sl_v v)
 {
-	if(isfixnum(v) || ismp(v))
+	if(isfixnum(v))
 		return true;
+	if(isunboxednum(v))
+		return true;
 	if(iscprim(v)){
 		sl_cprim *c = ptr(v);
 		return c->type != sl_runetype && valid_numtype(c->type->numtype);
 	}
+	if(ismp(v))
+		return true;
 	return false;
 }
 
--- a/src/sl.h
+++ b/src/sl.h
@@ -15,11 +15,13 @@
 	TAG_CPRIM,
 	TAG_FN,
 	TAG_VEC,
-	TAG_EXT,
+	TAG_UNBOXED,
 	TAG_CVALUE,
 	TAG_SYM,
 	TAG_CONS,
 
+	TAG_BITS = 3,
+
 	/* those were set to 7 and 3 strategically on purpose */
 	TAG_NONLEAF_MASK = TAG_CONS & TAG_VEC,
 };
@@ -34,9 +36,9 @@
 	T_S16, T_U16,
 	T_S32, T_U32,
 	T_S64, T_U64,
-	T_MP,
-	T_FLOAT,
-	T_DOUBLE,
+	T_UNBOXED_NUM,
+	T_MP = T_UNBOXED_NUM,
+	T_FLOAT, T_DOUBLE,
 }sl_numtype;
 
 typedef uintptr sl_v;
@@ -43,13 +45,13 @@
 
 #if defined(BITS64)
 typedef s64int sl_fx;
-#define FIXNUM_BITS 61
+#define FIXNUM_BITS (64-TAG_BITS)
 #define TOP_BIT (1ULL<<63)
 #define T_FIXNUM T_S64
 #define PRIdFIXNUM PRId64
 #else
 typedef s32int sl_fx;
-#define FIXNUM_BITS 29
+#define FIXNUM_BITS (32-TAG_BITS)
 #define TOP_BIT (1U<<31)
 #define T_FIXNUM T_S32
 #define PRIdFIXNUM PRId32
@@ -100,12 +102,15 @@
 #define valid_numtype(v) ((v) <= T_DOUBLE)
 #define UNBOUND ((sl_v)1) // an invalid value
 #define tag(x) ((x) & 7)
+#define tagext(x) ((x) & 0xff)
 #define ptr(x) ((void*)((uintptr)(x) & (~(uintptr)7)))
 #define tagptr(p, t) ((sl_v)(p) | (t))
-#define fixnum(x) ((sl_v)(x)<<3)
-#define numval(x)  ((sl_fx)(x)>>3)
-#define uintval(x) (((unsigned int)(x))>>3)
-#define builtin(n) tagptr(((sl_v)n<<3), TAG_FN)
+#define fixnum(x) ((sl_v)(x)<<TAG_BITS)
+#define numval(x) ((sl_fx)(x)>>TAG_BITS)
+#define unboxednumsval(x) ((intptr)(x)>>(TAG_BITS+1+4))
+#define unboxednumuval(x) ((uintptr)(x)>>(TAG_BITS+1+4))
+#define uintval(x) (((unsigned int)(x))>>TAG_BITS)
+#define builtin(n) tagptr(((sl_v)n<<TAG_BITS), TAG_FN)
 #define iscons(x) (tag(x) == TAG_CONS)
 #define issym(x) (tag(x) == TAG_SYM)
 #define isfixnum(x) (tag(x) == TAG_FIXNUM)
@@ -113,9 +118,24 @@
 #define isvec(x) (tag(x) == TAG_VEC)
 #define iscvalue(x) (tag(x) == TAG_CVALUE)
 #define iscprim(x) (tag(x) == TAG_CPRIM)
+#define isunboxed(x) (tag(x) == TAG_UNBOXED)
 // doesn't lead to other values
 #define leafp(a) (((a)&TAG_NONLEAF_MASK) != TAG_NONLEAF_MASK)
 
+/* UNBOXED
+ * integers/runes: ...|xxxxxxxx|xxxxxxxx|xxxxxxxx|tttt0100|
+ * strings:        ...|xxxxxxxx|xxxxxxxx|xxxxxxxx|sss01100|
+ * special:        ...|xxxxxxxx|xxxxxxxx|xxxxxxxx|00011100|
+ */
+#define isunboxednum(x)  ((tagext(x) & ((1<<(TAG_BITS+1))-1)) == (0<<TAG_BITS | TAG_UNBOXED))
+#define isunboxedsnum(x) ((tagext(x) & ((2<<(TAG_BITS+1))-1)) == (0<<TAG_BITS | TAG_UNBOXED))
+#define isunboxedunum(x) ((tagext(x) & ((2<<(TAG_BITS+1))-1)) == (2<<TAG_BITS | TAG_UNBOXED))
+#define unboxednumtype(x) ((tagext(x)>>(TAG_BITS+1))&(0xff>>(TAG_BITS+1)))
+extern sl_type *unboxedtypes[T_UNBOXED_NUM];
+extern sl_v unboxedtypesyms[T_UNBOXED_NUM];
+
+#define numtofx(v) (sl_fx)tooffset(v)
+
 // allocate n consecutive conses
 #define cons_reserve(n) tagptr(alloc_words((n)*2), TAG_CONS)
 #define cons_index(c) (((sl_cons*)ptr(c))-((sl_cons*)slg.fromspace))
@@ -130,8 +150,8 @@
 		*(sl_v*)ptr(v) = (sl_v)(to) | FWD_BIT; \
 	}while(0)
 
-#define vec_size(v) (((usize*)ptr(v))[0]>>3)
-#define vec_setsize(v, n) (((usize*)ptr(v))[0] = ((n)<<3))
+#define vec_size(v) (((usize*)ptr(v))[0]>>TAG_BITS)
+#define vec_setsize(v, n) (((usize*)ptr(v))[0] = ((n)<<TAG_BITS))
 #define vec_elt(v, i) (((sl_v*)ptr(v))[1+(i)])
 #define vec_grow_amt(x) ((x)<8 ? 5 : 6*((x)>>3))
 // functions ending in _ are unsafe, faster versions
@@ -156,7 +176,7 @@
 #define sym_to_numtype(s) (((sl_sym*)ptr(s))->numtype)
 #define ismanaged(v) ((((u8int*)ptr(v)) >= slg.fromspace) && (((u8int*)ptr(v)) < slg.fromspace+slg.heapsize))
 #define isgensym(x)  (issym(x) && ismanaged(x))
-#define isfn(x) (tag(x) == TAG_FN && (x) > (N_BUILTINS<<3))
+#define isfn(x) (tag(x) == TAG_FN && (x) > (N_BUILTINS<<TAG_BITS))
 #define iscbuiltin(x) (iscvalue(x) && cv_class(ptr(x)) == sl_builtintype)
 // utility for iterating over all arguments in a builtin
 // i=index, i0=start index, arg = var for each arg, args = arg array
@@ -278,7 +298,7 @@
 	void (*relocate)(sl_v oldv, sl_v newv);
 	void (*finalize)(sl_v self);
 	void (*print_traverse)(sl_v self);
-} sl_cvtable;
+}sl_cvtable;
 
 typedef void (*cvinitfunc_t)(sl_type*, sl_v, void*);
 
--- a/src/sl_arith_any.h
+++ b/src/sl_arith_any.h
@@ -20,7 +20,15 @@
 		if(isfixnum(arg))
 			x = numval(arg);
 		else{
-			if(iscprim(arg)){
+			if(isunboxedunum(arg)){
+				u64 = unboxednumuval(arg);
+				a = &u64;
+				pt = unboxednumtype(arg);
+			}else if(isunboxedsnum(arg)){
+				u64 = unboxednumsval(arg);
+				a = &u64;
+				pt = unboxednumtype(arg);
+			}else if(iscprim(arg)){
 				cp = ptr(arg);
 				a = cp_data(cp);
 				pt = cp_numtype(cp);
--- a/src/str.c
+++ b/src/str.c
@@ -358,6 +358,10 @@
 		radix = get_radix_arg(args[1]);
 	if(isfixnum(n))
 		num = numval(n);
+	else if(isunboxedunum(n))
+		num = unboxednumuval(n);
+	else if(isunboxedsnum(n))
+		num = unboxednumsval(n);
 	else if(iscprim(n)){
 		void *data = ptr(n);
 		if(cp_numtype(data) < T_FLOAT)
--