#include "inc/priv.h" #include "inc/operators.h" #include "inc/cvalues.h" #include "inc/types.h" #include "inc/io.h" #include "inc/equal.h" enum { MAX_INL_SIZE = 384, CVALUE_NWORDS_STR = sizeof(sl_cv)/sizeof(sl_v), CVALUE_NWORDS = sl_offsetof(sl_cv, _space)/sizeof(sl_v), CV_OWNED = 1<<0, }; #define owned(cv) ((uintptr)(cv)->type & CV_OWNED) const sl_type *unboxedtypes[T_UNBOXED_NUM]; sl_v unboxedtypesyms[T_UNBOXED_NUM]; static const sl_type notype; static sl_v specialsym, unboxedsym, cvaluesym; void add_finalizer(sl_cv *cv) { if(sl_unlikely(slvm.nfinalizers == slvm.maxfinalizers)){ sl_cv **nf = MEM_REALLOC(slvm.finalizers, 2*slvm.maxfinalizers*sizeof(slvm.finalizers)); if(sl_unlikely(nf == nil)) cthrow(error(sl_errmem, "no memory for a finalizer"), cv); slvm.finalizers = nf; slvm.maxfinalizers *= 2; } slvm.finalizers[slvm.nfinalizers++] = cv; } void sweep_cv(sl_cv *cv) { const sl_type *t = cv_class(cv); if(t->vtable != nil) t->vtable->finalize(tagptr(cv, TAG_CVALUE)); if(!isinlined(cv) && owned(cv)) MEM_FREE(cv_data(cv)); cv->type = ¬ype; } // remove dead objects from finalization list in-place void sweep_finalizers(void) { sl_cv **lst = slvm.finalizers; usize n = 0, ndel = 0, l = slvm.nfinalizers; sl_cv *tmp; #define SWAP_sf(a, b) (tmp = a, a = b, b = tmp, 1) if(sl_unlikely(l == 0)) return; do{ tmp = lst[n]; if(isforwarded((sl_v)tmp)){ // object is alive lst[n] = ptr(sl_cv, forwardloc((sl_v)tmp)); n++; }else{ sweep_cv(tmp); ndel++; } }while((n < l-ndel) && SWAP_sf(lst[n], lst[n+ndel])); slvm.nfinalizers -= ndel; slvm.malloc_pressure = 0; } // compute the size of the metadata object for a cvalue sl_nonnull(1) static usize cv_nwords(sl_cv *cv) { if(!isinlined(cv)) return cv_isstr(cv) ? CVALUE_NWORDS_STR : CVALUE_NWORDS; usize n = cv_len(cv); if(cv_isstr(cv)) n++; return CVALUE_NWORDS + NWORDS(n); } sl_nonnull(1) void cv_autorelease(sl_cv *cv) { cv->type = (const sl_type*)(((uintptr)cv->type) | CV_OWNED); add_finalizer(cv); } void cvalue_cutdown(sl_v cv, usize nb) { sl_cv *pcv = ptr(sl_cv, cv); bool isstr = cv_isstr(pcv); assert(nb <= pcv->len); if(!isinlined(pcv)){ void *data = MEM_REALLOC(pcv->data, nb+(isstr ? 1 : 0)); if(data != nil) // oh well, that's alright pcv->data = data; } pcv->len = nb; if(isstr) ((char*)pcv->data)[nb] = 0; } sl_v cvalue_(const sl_type *type, usize sz, bool nofinalize) { assert(type != nil); bool str = false; if(type->eltype == sl_utf8type){ if(sz == 0) return sl_emptystr; sz++; str = true; } sl_cv *pcv; if(sz <= MAX_INL_SIZE){ usize nw = CVALUE_NWORDS + NWORDS(sz); pcv = alloc_words(nw); pcv->type = type; pcv->data = cv_space(pcv); if(!nofinalize && type->vtable != nil) add_finalizer(pcv); }else{ if(slvm.malloc_pressure > ALLOC_LIMIT_TRIGGER) sl_gc(false); void *data = MEM_ALLOC(sz); if(sl_unlikely(data == nil)) cthrow(error(sl_errmem, "no memory for a cv"), type); slvm.malloc_pressure += sz; pcv = alloc_words(str ? CVALUE_NWORDS_STR : CVALUE_NWORDS); pcv->type = type; pcv->data = data; cv_autorelease(pcv); } if(str){ ((char*)pcv->data)[--sz] = '\0'; if(!isinlined(pcv)){ pcv->startbytes = 0; pcv->startrune = 0; } } pcv->len = sz; return tagptr(pcv, TAG_CVALUE); } // this effectively dereferences a pointer // just like *p in C, it only removes a level of indirection from the type, // it doesn't copy any data. // this method of creating a cvalue only allocates metadata. // ptr is user-managed; we don't autorelease it unless the // user explicitly calls (autorelease ) on the result of this function. // 'parent' is an optional cvalue that this pointer is known to point // into; NIL if none. sl_v cvalue_from_ref(const sl_type *type, void *ptr, usize sz) { sl_cv *pcv; assert(type != nil); assert(ptr != nil); pcv = alloc_words(type == sl_strtype ? CVALUE_NWORDS_STR : CVALUE_NWORDS); pcv->data = ptr; pcv->len = sz; pcv->type = type; pcv->startbytes = 0; pcv->startrune = 0; return tagptr(pcv, TAG_CVALUE); } sl_v cvalue_str(usize sz) { if(sz == 0) return sl_emptystr; return cvalue(sl_strtype, sz); } sl_v cvalue_static_cstr(const char *str) { if(sl_unlikely(*str == 0)) return sl_emptystr; return cvalue_from_ref(sl_strtype, (char*)str, strlen(str)); } sl_v str_from_cstr(const char *str) { usize n = strlen(str); sl_v v = cvalue_str(n); memcpy(cvalue_data(v), str, n); return v; } bool isstr(sl_v v) { return iscvalue(v) && cv_isstr(ptr(sl_cv, v)); } // convert to malloc representation (fixed address) void cv_pin(sl_cv *cv) { if(!isinlined(cv)) return; usize sz = cv_len(cv); if(cv_isstr(cv)) sz++; void *data = MEM_ALLOC(sz); if(sl_unlikely(data == nil)) cthrow(error(sl_errmem, "no memory to pin a cv"), cv); slvm.malloc_pressure += sz; memcpy(data, cv_data(cv), sz); cv->data = data; cv_autorelease(cv); } sl_nonnull(1, 3) static void cvalue_rune_init(const sl_type *type, sl_v a, void *dest) { Rune r; USED(type); if(isfixnum(a)) r = numval(a); else if(sl_isrune(a)) r = torune(a); else if(iscvalue(a)) r = conv_to_u32(a, cvalue_data(a), cvalue_numtype(a)); else if(isubnum(a)){ sl_fx v = ubnumval(a); sl_numtype nt = ubnumtype(a); #if !defined(BITS64) if(nt > T_P32) nt -= T_P64-T_P32; #endif r = conv_to_u32(a, ubnumptr(v, nt), nt); }else cthrow(type_error(nil, "num", a), type); if(sl_unlikely(r >= 0xd800 && r <= 0xdfff)) cthrow(error(sl_errarg, "invalid rune: utf-16 surrogate pair: #x%"PRIx32, (u32int)r), type); if(sl_unlikely(r > Runemax)) cthrow(error(sl_errarg, "invalid rune: outside of valid range: #x%"PRIx32, (u32int)r), type); *((Rune*)dest) = r; } #define num_init(ctype, cnvt, tag) \ sl_nonnull(1, 3) \ static void \ cvalue_##ctype##_init(const sl_type *type, sl_v a, void *dest) \ { \ ctype n; \ USED(type); \ if(isfixnum(a)){ \ n = (ctype)numval(a); \ }else if(isubnum(a)){ \ sl_fx v = ubnumval(a); \ sl_numtype nt = ubnumtype(a); \ if(sizeof(sl_fx) < sizeof(u64int) && nt > T_P32) \ nt -= T_P64-T_P32; \ n = (ctype)conv_to_##cnvt(a, ubnumptr(v, nt), nt); \ }else if(iscvalue(a)){ \ n = (ctype)conv_to_##cnvt(a, cvalue_data(a), cvalue_numtype(a)); \ }else \ cthrow(type_error(nil, "num", a), type); \ *((ctype*)dest) = n; \ } typedef u32int u32ptr; typedef u64int u64ptr; typedef float f32; typedef double f64; num_init(s8int, s32, T_S8) num_init(u8int, u32, T_U8) num_init(s16int, s32, T_S16) num_init(u16int, u32, T_U16) num_init(s32int, s32, T_S32) num_init(u32int, u32, T_U32) num_init(s64int, s64, T_S64) num_init(u64int, u64, T_U64) num_init(u32ptr, p32, T_P32) num_init(u64ptr, p64, T_P64) num_init(f32, double, T_F32) num_init(f64, double, T_F64) BUILTIN("rune", rune) { argcount_most(1); if(nargs == 0){ PUSH(fixnum(Runeerror)); args = sl.sp-1; } Rune r; cvalue_rune_init(sl_runetype, args[0], &r); return mk_rune(r); } #define num_ctor_init(typenam, ctype, tag) \ static \ BUILTIN(#typenam, typenam) \ { \ argcount_most(1); \ if(sl_unlikely(nargs == 0)){ \ PUSH(fixnum(0)); \ args = sl.sp-1; \ } \ sl_v v = cvalue(sl_##typenam##type, sizeof(ctype)); \ cvalue_##ctype##_init(sl_##typenam##type, args[0], cvalue_data(v)); \ return v; \ } #define num_ctor_init_unboxed(typenam, ctype, tag, issigned) \ static \ BUILTIN(#typenam, typenam) \ { \ argcount_most(1); \ if(sl_unlikely(nargs == 0)){ \ PUSH(fixnum(0)); \ args = sl.sp-1; \ } \ sl_v v; \ ctype n; \ cvalue_##ctype##_init(sl_##typenam##type, args[0], &n); \ if(tag < T_UNBOXED_NUM && \ (sizeof(n) < sizeof(sl_fx) || fits_bits(n, UNBOXED_BITS-issigned))){ \ v = (sl_v)n< T_P32) nt -= T_P64-T_P32; #endif n = conv_to_bignum(a, ubnumptr(v, nt), nt); }else if(iscvalue(a)){ sl_cv *cv = ptr(sl_cv, a); void *p = cv_data(cv); n = conv_to_bignum(a, p, cv_numtype(cv)); }else cthrow(type_error(nil, "num", a), type); *((mpint**)dest) = n; } BUILTIN("bignum", bignum) { argcount_most(1); if(nargs == 0){ PUSH(fixnum(0)); args = sl.sp-1; } mpint *m; cvalue_bignum_init(sl_bignumtype, args[0], &m); sl_v cv = cvalue(sl_bignumtype, sizeof(mpint*)); *((mpint**)cvalue_data(cv)) = m; return cv; } sl_v mk_bignum(mpint *n) { sl_v cv = cvalue(sl_bignumtype, sizeof(mpint*)); *(mpint**)cvalue_data(cv) = n; return cv; } static void free_bignum(sl_v self) { mpint **s = value2c(mpint**, self); if(*s != mpzero && *s != mpone && *s != mptwo) mpfree(*s); } static sl_cvtable bignum_vtable = { nil, nil, free_bignum, nil, nil }; sl_constfn sl_v size_wrap(usize sz) { if(sizeof(usize) == 8) return fits_fixnum(sz) ? fixnum(sz): mk_u64(sz); return fits_fixnum(sz) ? fixnum(sz): mk_u32(sz); } u32int tou32(sl_v n) { if(iscvalue(n)){ sl_cv *cv = ptr(sl_cv, n); return conv_to_u32(n, cv_data(cv), cv_numtype(cv)); } if(isfixnum(n)) return numval(n); if(isubnum(n)) return ubnumval(n); cthrow(type_error(nil, "num", n), n); } u64int tou64(sl_v n) { if(iscvalue(n)){ sl_cv *cv = ptr(sl_cv, n); return conv_to_u64(n, cv_data(cv), cv_numtype(cv)); } if(isfixnum(n)) return numval(n); if(isubnum(n)) return ubnumval(n); cthrow(type_error(nil, "num", n), n); } usize tosize(sl_v n) { if(sl_unlikely(iscvalue(n))){ if(sl_likely(numeric_compare(n, fixnum(0), true) >= 0)){ sl_cv *cv = ptr(sl_cv, n); if(sizeof(usize) > 4) return conv_to_u64(n, cv_data(cv), cv_numtype(cv)); return conv_to_u32(n, cv_data(cv), cv_numtype(cv)); } }else{ sl_fx x; if(sl_likely(isfixnum(n))) x = numval(n); else if(isubnum(n)) x = ubnumval(n); else cthrow(type_error(nil, "num", n), n); if(x >= 0) return x; } cthrow(error(sl_errarg, "tosize: negative value"), n); } soffset tooffset(sl_v n) { if(sl_likely(isfixnum(n))) return numval(n); if(isubnum(n)) return ubnumval(n); if(iscvalue(n)){ sl_cv *cv = ptr(sl_cv, n); if(sizeof(usize) > 4) return conv_to_s64(n, cv_data(cv), cv_numtype(cv)); return conv_to_s32(n, cv_data(cv), cv_numtype(cv)); } cthrow(type_error(nil, "num", n), n); } bool isarr(sl_v v) { return iscvalue(v) && cvalue_class(v)->eltype != nil; } static usize predict_arrlen(sl_v arg) { if(isvec(arg)) return vec_size(arg); if(iscons(arg)) return llength(arg); if(arg == sl_nil) return 0; if(isarr(arg)) return cvalue_arrlen(arg); return 1; } void cvalue_arr_init(const sl_type *ft, sl_v arg, void *dest) { sl_v type = ft->type; usize elsize, i, cnt, sz; const sl_type *eltype = ft->eltype; elsize = ft->elsz; cnt = predict_arrlen(arg); if(iscons(cdr_(cdr_(type)))){ usize tc = tosize(car_(cdr_(cdr_(type)))); if(sl_unlikely(tc != cnt)) cthrow(error(sl_errarg, "size mismatch"), ft); } sz = elsize * cnt; if(isvec(arg)){ assert(cnt <= (usize)vec_size(arg)); for(i = 0; i < cnt; i++){ eltype->init(eltype, vec_elt(arg, i), dest); dest = (char*)dest + elsize; } return; } if(iscons(arg) || arg == sl_nil){ i = 0; while(iscons(arg)){ if(sl_unlikely(i == cnt)){ // trigger error i++; break; } eltype->init(eltype, car_(arg), dest); i++; dest = (char*)dest + elsize; arg = cdr_(arg); } if(sl_unlikely(i != cnt)) cthrow(error(sl_errarg, "size mismatch"), ft); return; } if(iscvalue(arg)){ sl_cv *cv = ptr(sl_cv, arg); if(isarr(arg)){ const sl_type *aet = cv_class(cv)->eltype; if(aet == eltype){ if(cv_len(cv) == sz) memcpy(dest, cv_data(cv), sz); else cthrow(error(sl_errarg, "size mismatch"), ft); return; } // TODO: initialize array from different type elements cthrow(error(sl_errarg, "element type mismatch"), ft); } } if(cnt == 1) eltype->init(eltype, arg, dest); cthrow(type_error(nil, "sequence", arg), ft); } BUILTIN("arr", arr) { argcount_least(1); long cnt = nargs - 1; const sl_type *type = get_arr_type(args[0]); usize elsize = type->elsz; usize sz = elsize * cnt; sl_v cv = cvalue(type, sz); char *dest = cvalue_data(cv); sl_v arg; int i; FOR_ARGS(i, 1, arg, args){ if(sl_unlikely(!sl_isnum(arg) && type->eltype != sl_runetype)) bthrow(type_error("element", "num", arg)); type->eltype->init(type->eltype, arg, dest); dest += elsize; } return cv; } BUILTIN("arr-alloc", arr_alloc) { argcount_least(3); long cnt = tosize(args[1]); if(sl_unlikely(cnt < 0)) bthrow(error(sl_errarg, "invalid size: %"PRIu64, (u64int)cnt)); const sl_type *type = get_arr_type(args[0]); usize elsize = type->elsz; usize sz = elsize * cnt; sl_v cv = cvalue(type, sz); u8int *dest = cvalue_data(cv); long a = 2; for(long i = 0; i < cnt; i++){ sl_v arg = args[a]; type->eltype->init(type->eltype, arg, dest); dest += elsize; if((a = (a + 1) % nargs) < 2) a = 2; } return cv; } // NOTE: v must be an array usize cvalue_arrlen(sl_v v) { sl_cv *cv = ptr(sl_cv, v); return cv_len(cv)/cv_class(cv)->elsz; } usize ctype_sizeof(sl_v type) { sl_sym *s; if(issym(type)){ if((s = ptr(sl_sym, type)) != nil && s->type != nil && s->type->artype != nil) return s->size; }else if(iscons(type)){ sl_v hed = car_(type); if(hed == sl_arrsym){ sl_v t = car(cdr_(type)); if(!iscons(cdr_(cdr_(type)))) cthrow(error(sl_errarg, "incomplete type"), type); if((s = ptr(sl_sym, t)) != nil && s->type != nil && s->type->artype != nil){ sl_v n = car_(cdr_(cdr_(type))); usize sz = tosize(n); return sz * ctype_sizeof(t); }else type = t; } } cthrow(type_error(nil, "c type", type), type); } // get pointer and size for any plain-old-data value u8int * to_sized_ptr(sl_v v, usize *psz, usize *pesz, u64int *tmp) { if(sl_likely(isubnum(v))){ int nt = ubnumtype(v); *tmp = ubnumval(v); *pesz = *psz = unboxedtypes[nt]->size; return ubnumptr(*tmp, nt); } if(iscvalue(v)){ ios *x; if(isio(v) && (x = value2c(ios*, v))->bm == IOS_BUF_MEM){ *pesz = 1; *psz = x->size; return x->buf; } sl_cv *pcv = ptr(sl_cv, v); if(cv_isPOD(pcv)){ *pesz = cv_class(pcv)->elsz; *psz = cv_len(pcv); return cv_data(pcv); } } cthrow(type_error(nil, "plain-old-data", v), v); } BUILTIN("size-of", size_of) { argcount(1); sl_v v = args[0]; if(issym(v) || iscons(v)) return size_wrap(ctype_sizeof(v)); if(sl_unlikely(sl_isrune(v))) return fixnum(runelen(torune(v))); u64int tmp; usize sz, esz; to_sized_ptr(v, &sz, &esz, &tmp); return size_wrap(sz); } BUILTIN("tag-of", tag_of) { argcount(1); switch(tag(args[0])){ case TAG_FIXNUM: return sl_fixnumsym; case TAG_ESPECIAL: return specialsym; case TAG_FN: return sl_fnsym; case TAG_VEC: return sl_vecsym; case TAG_UNBOXED: return unboxedsym; case TAG_CVALUE: return cvaluesym; case TAG_SYM: return sl_symsym; case TAG_CONS: return sl_conssym; } abort(); } BUILTIN("type-of", type_of) { argcount(1); sl_v v = args[0]; switch(tag(v)){ case TAG_VEC: if(isstruct(v)) return mk_list2(sl_structsym, vec_elt(v, 0)); return sl_vecsym; case TAG_CONS: return sl_conssym; case TAG_FIXNUM: return sl_fixnumsym; case TAG_UNBOXED: if(isubnum(v)) return unboxedtypesyms[ubnumtype(v)]; if(sl_isrune(v)) return sl_runesym; abort(); case TAG_SYM: return sl_symsym; case TAG_FN: return sl_fnsym; case TAG_ESPECIAL: if(v == sl_nil) return sl_nullsym; if(v == sl_t) return sl_boolsym; if(v == sl_void) return sl_void; if(v == sl_eof) return sl_eof; abort(); } if(isstr(v)) return sl_strsym; if(iscbuiltin(v)) return sl_fnsym; return cv_type(ptr(sl_cv, v)); } sl_v cvalue_relocate(sl_v v) { usize nw; sl_cv *cv = ptr(sl_cv, v); sl_cv *nv; sl_v ncv; nw = cv_nwords(cv); nv = alloc_words(nw); memcpy(nv, cv, nw*sizeof(sl_v)); if(isinlined(cv)) nv->data = cv_space(nv); ncv = tagptr(nv, TAG_CVALUE); const sl_type *t = cv_class(cv); if(sl_unlikely(t->vtable != nil && t->vtable->relocate != nil)) t->vtable->relocate(v, ncv); forward(v, ncv); return ncv; } static sl_v cvalue_copy(sl_v v) { sl_cv *cv = ptr(sl_cv, v); usize nw = cv_nwords(cv); usize len = cv_len(cv) + (cv_isstr(cv) ? 1 : 0); bool inlined = isinlined(cv); void *data = nil; if(!inlined){ if(sl_unlikely((data = MEM_ALLOC(len)) == nil)) cthrow(error(sl_errmem, "no memory for a cv copy"), v); slvm.malloc_pressure += len; } PUSH(v); sl_cv *ncv = alloc_words(nw); v = POP(); cv = ptr(sl_cv, v); memcpy(ncv, cv, nw*sizeof(sl_v)); if(!inlined){ ncv->data = data; memcpy(ncv->data, cv->data, len); cv_autorelease(ncv); }else{ ncv->data = cv_space(ncv); } return tagptr(ncv, TAG_CVALUE); } BUILTIN("copy", copy) { argcount(1); sl_v v = args[0]; if(iscons(v) || isvec(v)) bthrow(error(sl_errarg, "argument must be a leaf atom")); if(!iscvalue(v)) return v; if(!cv_isPOD(ptr(sl_cv, v)) && !isbignum(v)) bthrow(error(sl_errarg, "argument must be a plain-old-data type")); return cvalue_copy(v); } // (c-value type . args) // this provides (1) a way to allocate values with a shared type for // efficiency, (2) a uniform interface for allocating cvalues of any // type, including user-defined. BUILTIN("c-value", c_value) { argcount_least(1); argcount_most(2); sl_v type = args[0]; const sl_type *ft = get_type(type, 0); sl_v v; if(ft->eltype != nil){ // special case to handle incomplete array types bla[] usize elsz = ft->elsz; usize cnt = 0; if(iscons(cdr_(cdr_(type)))) cnt = tosize(car_(cdr_(cdr_(type)))); else if(nargs == 2) cnt = predict_arrlen(args[1]); v = cvalue(ft, elsz*cnt); if(nargs == 2) cvalue_arr_init(ft, args[1], cvalue_data(v)); else memset(cvalue_data(v), 0, elsz*cnt); }else{ v = cvalue(ft, ft->size); if(nargs == 2) ft->init(ft, args[1], cvalue_data(v)); else memset(cvalue_data(v), 0, ft->size); } return v; } // NOTE: this only compares lexicographically; it ignores numeric formats sl_v cvalue_compare(sl_v a, sl_v b, int bound, bool eq) { sl_cv *ca = ptr(sl_cv, a); sl_cv *cb = ptr(sl_cv, b); const sl_type *ta = cv_class(ca); if(sl_unlikely(ta->vtable != nil && ta->vtable->bounded_compare != nil)){ const sl_type *tb = cv_class(cb); if(tb->vtable == ta->vtable) return ta->vtable->bounded_compare(a, b, bound, eq); } if(!cv_isPOD(ca) || !cv_isPOD(cb)) return fixnum(1); char *adata = cv_data(ca); char *bdata = cv_data(cb); usize asz = cv_len(ca); usize bsz = cv_len(cb); usize minsz = asz < bsz ? asz : bsz; int diff = memcmp(adata, bdata, minsz); if(diff == 0 && asz != bsz) return asz > bsz ? fixnum(1) : fixnum(-1); return fixnum(diff); } sl_nonnull(3) static usize check_addr_args(sl_v arr, sl_v ind, u8int **data) { sl_cv *cv = ptr(sl_cv, arr); *data = cv_data(cv); usize numel = cv_len(cv)/cv_class(cv)->elsz; usize index = tosize(ind); if(sl_unlikely(index >= numel)) cthrow(bounds_error(arr, ind), arr); return index; } sl_v cvalue_arr_aref(sl_v *args) { u8int *data; usize index = check_addr_args(args[0], args[1], &data); const sl_type *eltype = cvalue_class(args[0])->eltype; sl_numtype nt = eltype->numtype; if(nt < T_UNBOXED_NUM){ switch(nt){ case T_S8: return mk_s8(data[index]); case T_U8: return mk_u8(data[index]); case T_S16: return mk_s16(((s16int*)data)[index]); case T_U16: return mk_u16(((u16int*)data)[index]); case T_S32: return mk_s32(((s32int*)data)[index]); case T_U32: return mk_u32(((u32int*)data)[index]); case T_S64: return mk_s64(((s64int*)data)[index]); case T_U64: return mk_u64(((u64int*)data)[index]); case T_P32: return mk_p32(((u32int*)data)[index]); case T_P64: return mk_p64(((u64int*)data)[index]); default: break; } } if(eltype == sl_runetype) return mk_rune(((Rune*)data)[index]); sl_v el = cvalue(eltype, eltype->size); u8int *dest = cvalue_data(el); memcpy(dest, data + index*eltype->size, eltype->size); return el; } sl_v cvalue_arr_aset(sl_v *args) { u8int *data; usize index = check_addr_args(args[0], args[1], &data); const sl_type *eltype = cvalue_class(args[0])->eltype; u8int *dest = data + index*eltype->size; eltype->init(eltype, args[2], dest); return args[2]; } BUILTIN("builtin", builtin) { argcount(1); sl_v v = args[0]; if(iscbuiltin(v)) return v; sl_sym *s = tosym(v); if(!iscbuiltin(s->binding)) bthrow(error(sl_errarg, "builtin \"%s\" not found", s->name)); return s->binding; } sl_v mk_cbuiltin(const char *name, sl_builtinfn f) { sl_cbuiltin *cv = MEM_CALLOC(1, sizeof(*cv)); if(sl_unlikely(cv == nil)) cthrow(error(sl_errmem, "no memory for a builtin"), name); cv->type = sl_builtintype; cv->cbuiltin = f; sl_v sym = mk_sym(name, false); sl_sym *s = ptr(sl_sym, sym); s->binding = tagptr(cv, TAG_CVALUE); ptrhash_put(slvm.reverse_dlsym_lookup, cv, (void*)sym); return s->binding; } #define cv_intern(tok) \ do{ \ sl_##tok##sym = mk_csym(#tok); \ }while(0) #define ctor_cv_intern(tok, nt, ctype) \ do{ \ sl_sym *s; \ cv_intern(tok); \ set(sl_##tok##sym, mk_cbuiltin(#tok, fn_builtin_##tok)); \ if(valid_numtype(nt)){ \ s = ptr(sl_sym, sl_##tok##sym); \ s->numtype = nt; \ s->size = sizeof(ctype); \ } \ }while(0) #define ctor_cv_intern_no_builtin(tok, nt, ctype) \ do{ \ sl_sym *s; \ cv_intern(tok); \ if(valid_numtype(nt)){ \ s = ptr(sl_sym, sl_##tok##sym); \ s->numtype = nt; \ s->size = sizeof(ctype); \ } \ }while(0) #define mk_primtype(name, ctype) \ do{ \ sl_type *pt = get_type(sl_##name##sym, sizeof(ctype)); \ pt->init = cvalue_##ctype##_init; \ pt->artype = get_type(mk_list2(sl_arrsym, sl_##name##sym), 0); \ sl_##name##type = pt; \ }while(0) sl_constfn sl_v return_from_u64(u64int u) { if(fits_fixnum(u)) return fixnum((sl_fx)u); return mk_u64(u); } sl_v return_from_s64(s64int s) { if(fits_fixnum(s)) return fixnum((sl_fx)s); return mk_bignum(vtomp(s, nil)); } #define ACCUM_DEFAULT 0 #define ARITH_OP(a, b) ((a)+(b)) #define MP_OP mpadd #define ARITH_OVERFLOW sadd_overflow_64 sl_v sl_add_any(sl_v *args, u32int nargs) { #include "inc/arith.h" } #define ACCUM_DEFAULT 1 #define ARITH_OP(a, b) ((a)*(b)) #define MP_OP mpmul #define ARITH_OVERFLOW smul_overflow_64 #define ARITH_NOPTR sl_v sl_mul_any(sl_v *args, u32int nargs) { #include "inc/arith.h" } sl_v sl_neg(sl_v n) { s64int i64; u64int ui64; mpint *mp; sl_numtype pt; sl_fx pi; void *a; if(num_to_ptr(n, &pi, &pt, &a)){ switch(pt){ case T_F64: return mk_f64(-*(double*)a); case T_F32: return mk_f32(-*(float*)a); case T_S8: return fixnum(-(sl_fx)*(s8int*)a); case T_U8: return fixnum(-(sl_fx)*(u8int*)a); case T_S16: return fixnum(-(sl_fx)*(s16int*)a); case T_U16: return fixnum(-(sl_fx)*(u16int*)a); case T_U32: i64 = -(s64int)*(u32int*)a; goto i64neg; case T_S32: i64 = -(s64int)*(s32int*)a; goto i64neg; case T_S64: i64 = *(s64int*)a; if(i64 == INT64_MIN) return mk_bignum(uvtomp((u64int)INT64_MAX+1, nil)); i64 = -i64; i64neg: return fits_fixnum(i64) ? fixnum(i64) : mk_bignum(vtomp(i64, nil)); case T_U64: ui64 = *(u64int*)a; if(sl_unlikely(ui64 >= (u64int)INT64_MAX+1)){ mp = uvtomp(ui64, nil); mp->sign = -1; return mk_bignum(mp); } i64 = -(s64int)ui64; goto i64neg; case T_P32: return mk_p32(-*(u32int*)a); case T_P64: return mk_p64(-*(u64int*)a); case T_BIG: mp = mpcopy(*(mpint**)a); mp->sign = -mp->sign; return mk_bignum(mp); case T_NONNUMERIC: break; } abort(); } cthrow(type_error(nil, "num", n), n); } bool num_to_ptr(sl_v a, sl_fx *pi, sl_numtype *pt, void **pp) { if(isubnum(a)){ *pi = ubnumval(a); *pt = ubnumtype(a); #if !defined(BITS64) if(*pt > T_P32) *pt -= T_P64-T_P32; #endif *pp = ubnumptr(*pi, *pt); return true; } if(iscvalue(a)){ sl_cv *cv = ptr(sl_cv, a); *pp = cv_data(cv); *pt = cv_numtype(cv); return valid_numtype(*pt); } if(isfixnum(a)){ *pi = numval(a); *pp = pi; *pt = T_FIXNUM; return true; } return false; } /* returns -1, 0, or 1 based on ordering of a and b eq: consider equality only, returning 0 or nonzero typeerr: if not 0, throws type errors, else returns 2 for type errors */ int numeric_compare(sl_v a, sl_v b, bool typeerr) { sl_fx ai, bi; sl_numtype ta, tb; void *aptr, *bptr; assert(!bothfixnums(a, b)); // this case is always handled outside of the call if(!num_to_ptr(a, &ai, &ta, &aptr)){ if(sl_unlikely(typeerr)) cthrow(type_error("a", "num", a), a); return 2; } if(!num_to_ptr(b, &bi, &tb, &bptr)){ if(sl_unlikely(typeerr)) cthrow(type_error("b", "num", b), a); return 2; } return cmp_numeric(aptr, ta, bptr, tb); } sl_v sl_div2(sl_v a, sl_v b) { double da, db; sl_fx ai, bi; sl_numtype ta, tb; void *aptr, *bptr; if(sl_unlikely(!num_to_ptr(a, &ai, &ta, &aptr))) cthrow(type_error("a", "num", a), a); if(sl_unlikely(!num_to_ptr(b, &bi, &tb, &bptr))) cthrow(type_error("b", "num", b), a); // a pointer is not exactly a number if(sl_unlikely(ta == T_P32 || ta == T_P64)) cthrow(type_error("a", "num", a), a); if(sl_unlikely(tb == T_P32 || tb == T_P64)) cthrow(type_error("b", "num", b), a); da = conv_to_double(a, aptr, ta); db = conv_to_double(b, bptr, tb); return mk_f64(da / db); } sl_v sl_idiv2(sl_v a, sl_v b) { sl_fx ai, bi; sl_numtype ta, tb; void *aptr, *bptr; s64int a64, b64; mpint *x; if(sl_unlikely(!num_to_ptr(a, &ai, &ta, &aptr))) cthrow(type_error("a", "num", a), a); if(sl_unlikely(!num_to_ptr(b, &bi, &tb, &bptr))) cthrow(type_error("b", "num", b), a); // a pointer is not exactly a number if(sl_unlikely(ta == T_P32 || ta == T_P64)) cthrow(type_error("a", "num", a), a); if(sl_unlikely(tb == T_P32 || tb == T_P64)) cthrow(type_error("b", "num", b), a); if(ta == T_BIG){ if(tb == T_BIG){ if(sl_unlikely(mpsignif(*(mpint**)bptr) == 0)) cthrow(divide_by_0_error(), a); x = mpnew(0); mpdiv(*(mpint**)aptr, *(mpint**)bptr, x, nil); return mk_bignum(x); } b64 = conv_to_s64(b, bptr, tb); if(sl_unlikely(b64 == 0)) 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); } if(ta == T_U64){ if(tb == T_U64){ if(sl_unlikely(*(u64int*)bptr == 0)) cthrow(divide_by_0_error(), a); return return_from_u64(*(u64int*)aptr / *(u64int*)bptr); } b64 = conv_to_s64(b, bptr, tb); if(b64 < 0) return return_from_s64(-(s64int)(*(u64int*)aptr / (u64int)(-b64))); if(sl_unlikely(b64 == 0)) cthrow(divide_by_0_error(), a); return return_from_u64(*(u64int*)aptr / (u64int)b64); } if(tb == T_U64){ if(sl_unlikely(*(u64int*)bptr == 0)) 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))); return return_from_u64((u64int)a64 / *(u64int*)bptr); } b64 = conv_to_s64(b, bptr, tb); if(sl_unlikely(b64 == 0)) cthrow(divide_by_0_error(), a); return return_from_s64(conv_to_s64(a, aptr, ta) / b64); } static sl_v sl_bitwise_op(sl_v a, sl_v b, int opcode) { sl_fx ai, bi; sl_numtype ta, tb, itmp; void *aptr = nil, *bptr = nil, *ptmp; mpint *bmp = nil, *resmp = nil; s64int b64; if(!num_to_ptr(a, &ai, &ta, &aptr) || ta >= T_F32) cthrow(type_error("a", "int", a), a); if(!num_to_ptr(b, &bi, &tb, &bptr) || tb >= T_F32) cthrow(type_error("b", "int", b), a); if(ta < tb){ itmp = ta; ta = tb; tb = itmp; ptmp = aptr; aptr = bptr; bptr = ptmp; } // now a's type is larger than or same as b's if(ta == T_BIG){ if(tb == T_BIG){ bmp = *(mpint**)bptr; resmp = mpnew(0); }else{ bmp = conv_to_bignum(b, bptr, tb); resmp = bmp; } b64 = 0; }else b64 = conv_to_s64(b, bptr, tb); switch(opcode){ case 0: switch(ta){ case T_S8: return fixnum(*(s8int *)aptr & (s8int )b64); case T_U8: return fixnum(*(u8int *)aptr & (u8int )b64); case T_S16: return fixnum(*(s16int*)aptr & (s16int)b64); case T_U16: return fixnum(*(u16int*)aptr & (u16int)b64); case T_S32: return mk_s32(*(s32int*)aptr & (s32int)b64); case T_U32: return mk_u32(*(u32int*)aptr & (u32int)b64); case T_S64: return mk_s64(*(s64int*)aptr & b64); case T_U64: return mk_u64(*(u64int*)aptr & (u64int)b64); case T_P32: return mk_p32(*(u32int*)aptr & (u32int)b64); case T_P64: return mk_p64(*(u64int*)aptr & (u64int)b64); case T_BIG: mpand(*(mpint**)aptr, bmp, resmp); return mk_bignum(resmp); case T_F32: case T_F64: case T_NONNUMERIC: break; } break; case 1: switch(ta){ case T_S8: return fixnum(*(s8int *)aptr | (s8int )b64); case T_U8: return fixnum(*(u8int *)aptr | (u8int )b64); case T_S16: return fixnum(*(s16int*)aptr | (s16int)b64); case T_U16: return fixnum(*(u16int*)aptr | (u16int)b64); case T_S32: return mk_s32(*(s32int*)aptr | (s32int)b64); case T_U32: return mk_u32(*(u32int*)aptr | (u32int)b64); case T_S64: return mk_s64(*(s64int*)aptr | b64); case T_U64: return mk_u64(*(u64int*)aptr | (u64int)b64); case T_P32: return mk_p32(*(u32int*)aptr | (u32int)b64); case T_P64: return mk_p64(*(u64int*)aptr | (u64int)b64); case T_BIG: mpor(*(mpint**)aptr, bmp, resmp); return mk_bignum(resmp); case T_F32: case T_F64: case T_NONNUMERIC: break; } break; case 2: switch(ta){ case T_S8: return fixnum(*(s8int *)aptr ^ (s8int )b64); case T_U8: return fixnum(*(u8int *)aptr ^ (u8int )b64); case T_S16: return fixnum(*(s16int*)aptr ^ (s16int)b64); case T_U16: return fixnum(*(u16int*)aptr ^ (u16int)b64); case T_S32: return mk_s32(*(s32int*)aptr ^ (s32int)b64); case T_U32: return mk_u32(*(u32int*)aptr ^ (u32int)b64); case T_S64: return mk_s64(*(s64int*)aptr ^ b64); case T_U64: return mk_u64(*(u64int*)aptr ^ (u64int)b64); case T_P32: return mk_p32(*(u32int*)aptr ^ (u32int)b64); case T_P64: return mk_p64(*(u64int*)aptr ^ (u64int)b64); case T_BIG: mpxor(*(mpint**)aptr, bmp, resmp); return mk_bignum(resmp); case T_F32: case T_F64: case T_NONNUMERIC: break; } } abort(); } BUILTIN("logand", logand) { sl_v v, e; if(sl_unlikely(nargs == 0)) return fixnum(-1); v = args[0]; int i; FOR_ARGS(i, 1, e, args){ if(bothfixnums(v, e)) v = v & e; else v = sl_bitwise_op(v, e, 0); } return v; } BUILTIN("logior", logior) { sl_v v, e; if(sl_unlikely(nargs == 0)) return fixnum(0); v = args[0]; int i; FOR_ARGS(i, 1, e, args){ if(bothfixnums(v, e)) v = v | e; else v = sl_bitwise_op(v, e, 1); } return v; } BUILTIN("logxor", logxor) { sl_v v, e; if(sl_unlikely(nargs == 0)) return fixnum(0); v = args[0]; int i; FOR_ARGS(i, 1, e, args){ if(bothfixnums(v, e)) v = fixnum(numval(v) ^ numval(e)); else v = sl_bitwise_op(v, e, 2); } return v; } BUILTIN("lognot", lognot) { argcount(1); sl_v a = args[0]; if(isfixnum(a)) return fixnum(~numval(a)); if(isubnum(a)){ if(is_nt_sint(ubnumtype(a))){ sl_fx s = ubnumval(a); s = (sl_v)~s << TAG_EXT_BITS; return ((sl_v)s & ~(sl_v)TAG_EXT_MASK) | (a & TAG_EXT_MASK); } sl_v u = ubnumval(a); u = ~u << TAG_EXT_BITS; return (u & ~(sl_v)TAG_EXT_MASK) | (a & TAG_EXT_MASK); } if(iscvalue(a)){ sl_cv *cv = ptr(sl_cv, a); sl_numtype ta = cv_numtype(cv); void *aptr = cv_data(cv); switch(ta){ case T_S8: return mk_s8(~*(s8int*)aptr); case T_U8: return mk_u8(~*(u8int*)aptr); case T_S16: return mk_s16(~*(s16int*)aptr); case T_U16: return mk_u16(~*(u16int*)aptr); case T_S32: return mk_s32(~*(s32int*)aptr); case T_U32: return mk_u32(~*(u32int*)aptr); case T_S64: return mk_s64(~*(s64int*)aptr); case T_U64: return mk_u64(~*(u64int*)aptr); case T_P32: return mk_p32(~*(u32int*)aptr); case T_P64: return mk_p64(~*(u64int*)aptr); case T_BIG:; mpint *m = mpnew(0); mpnot(*(mpint**)aptr, m); return mk_bignum(m); default: abort(); } } bthrow(type_error(nil, "int", a)); } #define sash_overflow_64(a, b, c) ( \ ((a)<0 || (a)>(INT64_MAX>>(b))) \ ? 1 \ : ((*(c)=(a)<<(b)), 0) \ ) #define uash_overflow_64(a, b, c) ( \ ((a)>(UINT64_MAX>>(b))) \ ? 1 \ : ((*(c)=(a)<<(b)), 0) \ ) BUILTIN("ash", ash) { s64int accum; void *aptr; argcount(2); sl_v a = args[0]; sl_fx n = tofixnum(args[1]); if(sl_unlikely(n == 0)) return a; mpint *mp = nil; if(isbignum(a)){ aptr = cvalue_data(a); mpint *ma = *(mpint**)aptr; mp = mpnew(0); if(n < 0){ int si = -mpsignif(ma)+(ma->sign<0); if(n < si) n = si; } mpleft(ma, n, mp); }else if(iscvalue(a)){ sl_cv *cv = ptr(sl_cv, a); sl_numtype ta = cv_numtype(cv); aptr = cv_data(cv); if(ta < T_U64){ accum = conv_to_s64(a, aptr, ta); goto signedshift; } u64int u = conv_to_u64(a, aptr, ta); if(n <= 0){ if(sl_unlikely(n < -63)) n = -63; u >>= -n; return return_from_u64(u); }else if(n >= 64 || uash_overflow_64(u, n, &u)){ mp = uvtomp(u, nil); mpleft(mp, n, mp); }else{ return return_from_u64(u); } }else if(isfixnum(a) || isubnum(a)){ accum = isfixnum(a) ? numval(a) : ubnumval(a); signedshift: if(n <= 0){ if(sl_unlikely(n < -63)) n = -63; return fixnum(accum>>(-n)); } if(n >= 64 || sash_overflow_64(accum, n, &accum)){ mp = vtomp(accum, nil); mpleft(mp, n, mp); }else{ return return_from_s64(accum); } } if(mp != nil){ if(mpsignif(mp) >= FIXNUM_BITS) return mk_bignum(mp); accum = mptov(mp); mpfree(mp); assert(fits_fixnum(accum)); return fixnum((sl_fx)accum); } bthrow(type_error(nil, "int", a)); } void cvalues_init(void) { specialsym = mk_csym("special"); unboxedsym = mk_csym("unboxed"); cvaluesym = mk_csym("cvalue"); ctor_cv_intern(arr, T_NONNUMERIC, int); ctor_cv_intern(rune, T_NONNUMERIC, u32int); sl_sym *sym = ptr(sl_sym, sl_runesym); sym->numtype = T_NONNUMERIC; sym->size = sizeof(Rune); sl_type *t = get_type(sl_runesym, sizeof(Rune)); t->init = cvalue_rune_init; t->artype = get_type(mk_list2(sl_arrsym, sl_runesym), 0); sl_runetype = t; sl_builtintype = define_opaque_type(mk_csym("builtin"), sizeof(sl_builtinfn), nil, nil); ctor_cv_intern(s8, T_S8, s8int); ctor_cv_intern(u8, T_U8, u8int); ctor_cv_intern(s16, T_S16, s16int); ctor_cv_intern(u16, T_U16, u16int); ctor_cv_intern(s32, T_S32, s32int); ctor_cv_intern(u32, T_U32, u32int); ctor_cv_intern(s64, T_S64, s64int); ctor_cv_intern(u64, T_U64, u64int); ctor_cv_intern(p32, T_P32, u32int); ctor_cv_intern(p64, T_P64, u64int); ctor_cv_intern(f32, T_F32, float); ctor_cv_intern(f64, T_F64, double); ctor_cv_intern_no_builtin(utf8, T_U8, u8int); mk_primtype(s8, s8int); mk_primtype(u8, u8int); mk_primtype(s16, s16int); mk_primtype(u16, u16int); mk_primtype(s32, s32int); mk_primtype(u32, u32int); mk_primtype(s64, s64int); mk_primtype(u64, u64int); mk_primtype(p32, u32ptr); mk_primtype(p64, u64ptr); mk_primtype(f32, f32); mk_primtype(f64, f64); mk_primtype(utf8, u8int); sl_ptrsym = mk_csym("ptr"); set(sl_ptrsym, sym_value(sizeof(uintptr) == 8 ? sl_p64sym : sl_p32sym)); ctor_cv_intern(bignum, T_BIG, mpint*); t = get_type(sl_bignumsym, sizeof(mpint*)); t->init = cvalue_bignum_init; t->vtable = &bignum_vtable; sl_bignumtype = t; 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; unboxedtypes[T_P32] = sl_p32type; unboxedtypes[T_P64] = sl_p64type; 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; unboxedtypesyms[T_P32] = sl_p32sym; unboxedtypesyms[T_P64] = sl_p64sym; sl_strtype = get_type(mk_list2(sl_arrsym, sl_utf8sym), 0); sl_emptystr = cvalue_from_ref(sl_strtype, (char*)"", 0); sl_cv *cv = ptr(sl_cv, sl_emptystr); cv->data = cv_space(cv); }