diff --git a/src/builtins/fns.c b/src/builtins/fns.c index 0394aa4d..63720318 100644 --- a/src/builtins/fns.c +++ b/src/builtins/fns.c @@ -137,7 +137,7 @@ B ud_c2(B t, B w, B x) { usz ria=cia; for (usz i=0; ixia) { B xf = getFillE(x); - MAKE_MUT(r, ria); mut_init(r, el_or(TI(x,elType), selfElType(xf))); - MUTG_INIT(r); + MAKE_MUT_INIT(r, ria, el_or(TI(x,elType), selfElType(xf))); MUTG_INIT(r); mut_copyG(r, 0, x, 0, xia); mut_fillG(r, xia, xf, ria-xia); decG(x); @@ -273,8 +272,7 @@ B shape_c2(B t, B w, B x) { } else { if (TI(x,elType) == el_B) { B xf = getFillQ(x); - MAKE_MUT(m, nia); mut_init(m, el_B); - MUTG_INIT(m); + MAKE_MUT_INIT(m, nia, el_B); MUTG_INIT(m); i64 div = nia/xia; i64 mod = nia%xia; for (i64 i = 0; i < div; i++) mut_copyG(m, i*xia, x, 0, xia); @@ -526,7 +524,6 @@ NOINLINE B takedrop_highrank(bool take, B w, B x) { } else if (ria==0) { // printf("empty result\n"); r = taga(arr_shSetU(emptyArr(x, rr), rr, rsh)); } else { // printf("generic\n"); - MAKE_MUT(rm, ria); mut_init(rm, TI(x,elType)); B xf = getFillR(x); if (anyFill && noFill(xf)) { #if PROPER_FILLS @@ -536,7 +533,7 @@ NOINLINE B takedrop_highrank(bool take, B w, B x) { #endif } - MUTG_INIT(rm); + MAKE_MUT_INIT(rm, ria, TI(x,elType)); MUTG_INIT(rm); if (IA(x)==0) { mut_fillG(rm, 0, xf, ria); } else { // actual generic copying code @@ -657,8 +654,7 @@ B take_c2(B t, B w, B x) { usz xia = IA(x); if (n>xia) { B xf = getFillE(x); - MAKE_MUT(r, n); mut_init(r, el_or(TI(x,elType), selfElType(xf))); - MUTG_INIT(r); + MAKE_MUT_INIT(r, n, el_or(TI(x,elType), selfElType(xf))); MUTG_INIT(r); mut_fillG(r, 0, xf, n-xia); mut_copyG(r, n-xia, x, 0, xia); decG(x); @@ -997,8 +993,7 @@ B couple_c2(B t, B w, B x) { if (!eqShape(w, x)) thrF("≍: 𝕨 and 𝕩 must have equal shapes (%H ≑ ≒𝕨, %H ≑ ≒𝕩)", w, x); usz ia = IA(w); ur wr = RNK(w); - MAKE_MUT(r, ia*2); mut_init(r, el_or(TI(w,elType), TI(x,elType))); - MUTG_INIT(r); + MAKE_MUT_INIT(r, ia*2, el_or(TI(w,elType), TI(x,elType))); MUTG_INIT(r); mut_copyG(r, 0, w, 0, ia); mut_copyG(r, ia, x, 0, ia); Arr* ra = mut_fp(r); @@ -1025,8 +1020,7 @@ B shiftb_c1(B t, B x) { B xf = getFillE(x); usz csz = arr_csz(x); - MAKE_MUT(r, ia); mut_init(r, el_or(TI(x,elType), selfElType(xf))); - MUTG_INIT(r); + MAKE_MUT_INIT(r, ia, el_or(TI(x,elType), selfElType(xf))); MUTG_INIT(r); mut_copyG(r, csz, x, 0, ia-csz); mut_fillG(r, 0, xf, csz); return qWithFill(mut_fcd(r, x), xf); @@ -1038,8 +1032,7 @@ B shiftb_c2(B t, B w, B x) { B f = fill_both(w, x); usz wia = IA(w); usz xia = IA(x); - MAKE_MUT(r, xia); mut_init(r, el_or(TI(w,elType), TI(x,elType))); - MUTG_INIT(r); + MAKE_MUT_INIT(r, xia, el_or(TI(w,elType), TI(x,elType))); MUTG_INIT(r); int mid = wia6 || (xl<3 && xl!=0) || TI(x,elType)==el_B) { B xf = getFillQ(x); if (xr!=1) { - MAKE_MUT(r0, IA(x) * wv) mut_init(r0, TI(x,elType)); MUTG_INIT(r0); + MAKE_MUT_INIT(r0, IA(x) * wv, TI(x,elType)); MUTG_INIT(r0); usz csz = arr_csz(x); ux ri = 0; for (ux i = 0; i < xlen; i++) for (ux j = 0; j < wv; j++) { @@ -971,7 +971,7 @@ B slash_ucw(B t, B o, B w, B x) { usz argIA = IA(arg); B rep = c1(o, arg); if (isAtm(rep) || RNK(rep)!=1 || IA(rep) != argIA) thrF("π”½βŒΎ(a⊸/)𝕩: Result of 𝔽 must have the same shape as a/𝕩 (expected ⟨%s⟩, got %H)", argIA, rep); - MAKE_MUT(r, ia); mut_init(r, el_or(TI(x,elType), TI(rep,elType))); + MAKE_MUT_INIT(r, ia, el_or(TI(x,elType), TI(rep,elType))); SGet(x) SGet(rep) usz repI = 0; @@ -979,7 +979,7 @@ B slash_ucw(B t, B o, B w, B x) { u64* d = bitarr_ptr(w); if (elInt(TI(x,elType)) && elInt(TI(rep,elType))) { if (r->fns->elType!=el_i32) mut_to(r, el_i32); - i32* rp = r->ai32; + i32* rp = r->a; x = toI32Any(x); i32* xp = i32any_ptr(x); rep = toI32Any(rep); i32* np = i32any_ptr(rep); for (usz i = 0; i < ia; i++) { diff --git a/src/ffi.c b/src/ffi.c index e3d046be..066aa3ed 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -408,10 +408,10 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument } arg = chr_squeezeChk(inc(arg)); - MAKE_MUT(tmp, ia+1); mut_init(tmp, el_c32); MUTG_INIT(tmp); + MAKE_MUT_INIT(tmp, ia+1, el_c32); MUTG_INIT(tmp); mut_copyG(tmp, 0, arg, 0, ia); mut_setG(tmp, ia, m_c32(0)); - u32* xp = tmp->ac32; + u32* xp = tmp->a; u32* xpN = xp + ia; BQNFFIEnt t; diff --git a/src/opt/comp.c b/src/opt/comp.c index bfb68a85..155adcd6 100644 --- a/src/opt/comp.c +++ b/src/opt/comp.c @@ -419,7 +419,7 @@ B nativeComp_c2(B t, B w, B x) { B tokens = nc_tokenize(prims, sysvs, xBuf, xia, &fnBlock); decG(xBufO); - // // parse + // parse B objs = emptyHVec(); i32* bc0; B bytecode = m_i32arrv(&bc0, fnBlock? 3 : 0); diff --git a/src/utils/mut.c b/src/utils/mut.c index 2deea3a9..a47f0563 100644 --- a/src/utils/mut.c +++ b/src/utils/mut.c @@ -1,6 +1,42 @@ #include "../core.h" #include "mut.h" +FORCE_INLINE void mut_init(Mut* m, u8 n) { + m->fns = &mutFns[n]; + usz ia = m->ia; + u64 sz; + // hack around inlining of the allocator too many times + switch(n) { default: UD; + case el_bit: sz = BITARR_SZ( ia); break; + case el_i8: case el_c8: sz = TYARR_SZ(I8, ia); break; + case el_i16: case el_c16: sz = TYARR_SZ(I16,ia); break; + case el_i32: case el_c32: sz = TYARR_SZ(I32,ia); break; + case el_f64: sz = TYARR_SZ(F64,ia); break; + case el_B:; + HArr_p t = m_harrUp(ia); + m->val = (Arr*)t.c; + m->a = t.c->a; + return; + } + Arr* a = m_arr(sz, m->fns->valType, ia); + m->val = a; + m->a = ((TyArr*)a)->a; +} + +#if __clang__ +NOINLINE void make_mut_init(Mut* rp, ux ia, u8 el) { + MAKE_MUT(r, ia) + mut_init(r, el); + *rp = r_val; +} +#else +NOINLINE Mut make_mut_init(ux ia, u8 el) { + MAKE_MUT(r, ia) + mut_init(r, el); + return r_val; +} +#endif + static Arr* (*cpyFns[])(B) = { [el_bit] = cpyBitArr, [el_i8] = cpyI8Arr, [el_c8] = cpyC8Arr, @@ -35,8 +71,7 @@ NOINLINE void mut_to(Mut* m, u8 n) { NOINLINE B vec_addF(B w, B x) { usz wia = IA(w); - MAKE_MUT(r, wia+1); mut_init(r, el_or(TI(w,elType), selfElType(x))); - MUTG_INIT(r); + MAKE_MUT_INIT(r, wia+1, el_or(TI(w,elType), selfElType(x))); MUTG_INIT(r); mut_copyG(r, 0, w, 0, wia); mut_setG(r, wia, x); dec(w); @@ -383,16 +418,16 @@ MAKE_CCPY(C32, c32) -static B m_getU_MAX(Mut* m, usz ms) { err("m_setG_MAX"); } -static B m_getU_bit(Mut* m, usz ms) { return m_i32(bitp_get(m->abit, ms)); } -static B m_getU_i8 (Mut* m, usz ms) { return m_i32(m->ai8 [ms]); } -static B m_getU_i16(Mut* m, usz ms) { return m_i32(m->ai16[ms]); } -static B m_getU_i32(Mut* m, usz ms) { return m_i32(m->ai32[ms]); } -static B m_getU_c8 (Mut* m, usz ms) { return m_c32(m->ac8 [ms]); } -static B m_getU_c16(Mut* m, usz ms) { return m_c32(m->ac16[ms]); } -static B m_getU_c32(Mut* m, usz ms) { return m_c32(m->ac32[ms]); } -static B m_getU_f64(Mut* m, usz ms) { return m_f64(m->af64[ms]); } -static B m_getU_B (Mut* m, usz ms) { return m->aB[ms]; } +static B m_getU_MAX(void* a, usz ms) { err("m_setG_MAX"); } +static B m_getU_bit(void* a, usz ms) { return m_i32(bitp_get(((u64*) a), ms)); } +static B m_getU_i8 (void* a, usz ms) { return m_i32(((i8* ) a)[ms]); } +static B m_getU_i16(void* a, usz ms) { return m_i32(((i16*) a)[ms]); } +static B m_getU_i32(void* a, usz ms) { return m_i32(((i32*) a)[ms]); } +static B m_getU_c8 (void* a, usz ms) { return m_c32(((u8* ) a)[ms]); } +static B m_getU_c16(void* a, usz ms) { return m_c32(((u16*) a)[ms]); } +static B m_getU_c32(void* a, usz ms) { return m_c32(((u32*) a)[ms]); } +static B m_getU_f64(void* a, usz ms) { return m_f64(((f64*) a)[ms]); } +static B m_getU_B (void* a, usz ms) { return ((B*) a)[ms]; } M_CopyF copyFns[el_MAX]; M_FillF fillFns[el_MAX]; diff --git a/src/utils/mut.h b/src/utils/mut.h index ce9db936..52bf684c 100644 --- a/src/utils/mut.h +++ b/src/utils/mut.h @@ -3,20 +3,21 @@ /* Usage: -Start with MAKE_MUT(name, itemAmount); -MAKE_MUT allocates the object on the stack, so everything must happen within the scope of it. -Optionally, call mut_init(name, el_something) with an appropriate ElType. -End with mut_f(v|c|cd|p); +Start with MAKE_MUT(name, itemAmount) or MAKE_MUT_INIT(name, itemAmount, elType). +Those allocate the Mut instance on the stack, so it must be finished before the scope ends. +MAKE_MUT doesn't allocate any backing array, and will do so during mut_(set|fill|copy) (one of which must be the next operation on the Mut). +MAKE_MUT_INIT makes a backing array with the specified element type; mut_(set|fill|copy) can still widen it, but the primary use-case is for the G-postfixed functions. -There must be no allocations while a mut object is being built so GC doesn't do bad things. -mut_pfree must be used to free a partially finished `mut` instance safely (e.g. before throwing an error). -Methods ending with G expect that mut_init has been called with a type that can fit the elements that it'll set, and MUTG_INIT afterwards in the same scope. +End with mut_f(v|c|cd|p);. + +There must be no allocations while a mut object is being built so GC doesn't read the partially-initialized object. +mut_pfree must be used to free a partially filled `mut` instance safely (e.g. before throwing an error). */ typedef struct Mut Mut; typedef struct MutFns MutFns; typedef void (*M_SetF)(void*, usz, B); -typedef B (*M_GetF)(Mut*, usz); +typedef B (*M_GetF)(void*, usz); struct MutFns { u8 elType; u8 valType; @@ -31,37 +32,18 @@ struct Mut { MutFns* fns; usz ia; Arr* val; - union { - void* a; B* aB; - i8* ai8; i16* ai16; i32* ai32; - u8* ac8; u16* ac16; u32* ac32; - f64* af64; u64* abit; - }; + void* a; }; -#define MAKE_MUT(N, IA) Mut N##_val; N##_val.fns = &mutFns[el_MAX]; N##_val.ia = (IA); Mut* N = &N##_val; -static void mut_init(Mut* m, u8 n) { - m->fns = &mutFns[n]; - usz ia = PIA(m); - u64 sz; - // hack around inlining of the allocator too many times - switch(n) { default: UD; - case el_bit: sz = BITARR_SZ( ia); break; - case el_i8: case el_c8: sz = TYARR_SZ(I8, ia); break; - case el_i16: case el_c16: sz = TYARR_SZ(I16,ia); break; - case el_i32: case el_c32: sz = TYARR_SZ(I32,ia); break; - case el_f64: sz = TYARR_SZ(F64,ia); break; - case el_B:; - HArr_p t = m_harrUp(ia); - m->val = (Arr*)t.c; - m->aB = t.c->a; - return; - } - Arr* a = m_arr(sz, m->fns->valType, ia); - m->val = a; - m->a = ((TyArr*)a)->a; -} void mut_to(Mut* m, u8 n); +#if __clang__ && __has_attribute(noescape) // workaround for clang not realizing that stack-returned structs aren't captured + void make_mut_init(__attribute__((noescape)) Mut* rp, ux ia, u8 el); + #define MAKE_MUT_INIT(N, IA, EL) Mut N##_val; Mut* N = &N##_val; make_mut_init(N, IA, EL); +#else + Mut make_mut_init(ux ia, u8 el); + #define MAKE_MUT_INIT(N, IA, EL) Mut N##_val = make_mut_init(IA, EL); Mut* N = &N##_val; +#endif +#define MAKE_MUT(N, IA) Mut N##_val; N##_val.fns = &mutFns[el_MAX]; N##_val.ia = (IA); Mut* N = &N##_val; static B mut_fv(Mut* m) { assert(m->fns->elType!=el_MAX); NOGC_E; @@ -70,13 +52,13 @@ static B mut_fv(Mut* m) { assert(m->fns->elType!=el_MAX); SPRNK(a, 1); return taga(a); } -static B mut_fc(Mut* m, B x) { assert(m->fns->elType!=el_MAX); +static B mut_fc(Mut* m, B x) { assert(m->fns->elType!=el_MAX); // doesn't consume x NOGC_E; Arr* a = m->val; arr_shCopy(a, x); return taga(a); } -static B mut_fcd(Mut* m, B x) { assert(m->fns->elType!=el_MAX); +static B mut_fcd(Mut* m, B x) { assert(m->fns->elType!=el_MAX); // consumes x NOGC_E; Arr* a = m->val; arr_shCopy(a, x); @@ -100,25 +82,24 @@ static void mut_set(Mut* m, usz ms, B x) { m->fns->m_set(m, ms, x); } // clears the object (decrements its refcount) at position ms -static void mut_rm(Mut* m, usz ms) { if (m->fns->elType == el_B) dec(m->aB[ms]); } +static void mut_rm(Mut* m, usz ms) { if (m->fns->elType == el_B) dec(((B*)m->a)[ms]); } // gets object at position ms, without increasing refcount -static B mut_getU(Mut* m, usz ms) { return m->fns->m_getU(m, ms); } +static B mut_getU(Mut* m, usz ms) { return m->fns->m_getU(m->a, ms); } // doesn't consume; fills m[ms…ms+l] with x static void mut_fill(Mut* m, usz ms, B x, usz l) { m->fns->m_fill(m, ms, x, l); } -// expects x to be an array, each position must be written to precisely once -// doesn't consume +// doesn't consume; expects x to be an array, each position must be written to precisely once static void mut_copy(Mut* m, usz ms, B x, usz xs, usz l) { assert(isArr(x)); m->fns->m_copy(m, ms, x, xs, l); } + #define MUTG_INIT(N) MutFns N##_mutfns = *N->fns; void* N##_mutarr = N->a -// // mut_set but assumes the type of x already fits in m +// these methods function as the non-G-postfixed ones, except that +// the MAKE_MUT_INIT must have been used, MUTG_INIT called, and x must fit into the array type initialized to #define mut_setG(N, ms, x) N##_mutfns.m_setG(N##_mutarr, ms, x) -// // mut_fill but assumes the type of x already fits in m #define mut_fillG(N, ms, x, l) N##_mutfns.m_fillG(N##_mutarr, ms, x, l) -// // mut_copy but assumes the type of x already fits in m #define mut_copyG(N, ms, x, xs, l) N##_mutfns.m_copyG(N##_mutarr, ms, x, xs, l) @@ -187,7 +168,7 @@ FORCE_INLINE B arr_join_inline(B w, B x, bool consume, bool* reusedW) { case t_harr: if (fsizeof(HArr,a,B,ria)