diff --git a/build/src/build.bqn b/build/src/build.bqn index 2d602270..dde00761 100755 --- a/build/src/build.bqn +++ b/build/src/build.bqn @@ -514,7 +514,8 @@ cachedBin‿linkerCache ← { ⟨"src/jit/", "nvm.c"⟩ ⟨"src/utils/", "ryu.c", "utf.c", "hash.c", "file.c", "mut.c", "each.c", "bits.c"⟩ ⟩ - singeliMap ← {po.arch≡"aarch64"? 𝕩/˜(1⊑¨𝕩)∊"cmp"‿"bits"‿"equal"‿"dyarith"; 𝕩} ⟨ + singeliMap ← {po.arch≡"aarch64"? 𝕩/˜(1⊑¨𝕩)∊"cmp"‿"bits"‿"equal"‿"dyarith"‿"monarith"; 𝕩} ⟨ + "src/builtins/arithm.c"‿"monarith", "src/core/stuff.c"‿"equal", "src/utils/mut.c"‿"copy", "src/utils/bits.c"‿"bits" "src/builtins/arithd.c"‿"dyarith", "src/builtins/cmp.c"‿"cmp", "src/builtins/squeeze.c"‿"squeeze" "src/builtins/select.c"‿"select", "src/builtins/fold.c"‿"fold", "src/builtins/scan.c"‿"scan" diff --git a/makefile b/makefile index f17a1c43..4d3b1553 100644 --- a/makefile +++ b/makefile @@ -298,7 +298,7 @@ endif @"${MAKE}" i_singeli=0 singeli=0 force_build_dir=build/obj/presingeli REPLXX=0 f= lf= postmsg="singeli sources:" i_t=presingeli i_f='-O1 -DPRE_SINGELI' FFI=0 OUTPUT=build/obj/presingeli/BQN c -build_singeli: ${addprefix src/singeli/gen/, cmp.c dyarith.c copy.c equal.c squeeze.c select.c fold.c scan.c neq.c slash.c constrep.c bits.c} +build_singeli: ${addprefix src/singeli/gen/, cmp.c dyarith.c monarith.c copy.c equal.c squeeze.c select.c fold.c scan.c neq.c slash.c constrep.c bits.c} @echo $(postmsg) src/singeli/gen/%.c: src/singeli/src/%.singeli preSingeliBin @echo $< | cut -c 17- | sed 's/^/ /' diff --git a/src/builtins/arithm.c b/src/builtins/arithm.c index 41069110..936fe790 100644 --- a/src/builtins/arithm.c +++ b/src/builtins/arithm.c @@ -22,33 +22,6 @@ B bit_negate(B x) { // consumes return r; } -#define GC1i(SYMB,NAME,FEXPR,IBAD,IEXPR,SQF,TMIN,RMIN) B NAME##_c1(B t, B x) { \ - if (isF64(x)) { f64 v = x.f; return m_f64(FEXPR); } \ - if (RARE(!isArr(x))) thrM(SYMB ": Expected argument to be a number"); \ - u8 xe = TI(x,elType); \ - if (xe<=TMIN) return RMIN; \ - i64 sz = IA(x); \ - if (xe==el_i8) { i8 MAX=I8_MAX; i8 MIN=I8_MIN; i8* xp=i8any_ptr(x); i8* rp; B r=m_i8arrc(&rp,x); \ - for (i64 i = 0; i < sz; i++) { i8 v = xp[i]; if (RARE(IBAD)) { decG(r); goto base; } rp[i] = IEXPR; } \ - decG(x); (void)MIN;(void)MAX; return r; \ - } \ - if (xe==el_i16) { i16 MAX=I16_MAX; i16 MIN=I16_MIN; i16* xp=i16any_ptr(x); i16* rp; B r=m_i16arrc(&rp,x); \ - for (i64 i = 0; i < sz; i++) { i16 v = xp[i]; if (RARE(IBAD)) { decG(r); goto base; } rp[i] = IEXPR; } \ - decG(x); (void)MIN;(void)MAX; return r; \ - } \ - if (xe==el_i32) { i32 MAX=I32_MAX; i32 MIN=I32_MIN; i32* xp=i32any_ptr(x); i32* rp; B r=m_i32arrc(&rp,x); \ - for (i64 i = 0; i < sz; i++) { i32 v = xp[i]; if (RARE(IBAD)) { decG(r); goto base; } rp[i] = IEXPR; } \ - decG(x); (void)MIN;(void)MAX; return r; \ - } \ - if (xe==el_f64) { f64* xp = f64any_ptr(x); \ - f64* rp; B r = m_f64arrc(&rp, x); \ - for (i64 i = 0; i < sz; i++) { f64 v = xp[i]; rp[i] = FEXPR; } \ - decG(x); return SQF? num_squeeze(r) : r; \ - } \ - base: SLOW1(SYMB"𝕩", x); return arith_recm(NAME##_c1, x); \ -} - - B add_c1(B t, B x) { if (isF64(x)) return x; if (!isArr(x)) thrM("+: Argument must consist of numbers"); @@ -56,13 +29,69 @@ B add_c1(B t, B x) { dec(eachm_fn(m_f64(0), inc(x), add_c1)); return x; } +#if SINGELI + #define SINGELI_FILE monarith + #include "../utils/includeSingeli.h" +#endif -GC1i("-", sub, -v, v== MIN, -v, 0, el_bit, bit_sel(x,m_f64(0),m_f64(-1))) // change icond to v==-v to support ¯0 (TODO that won't work for i8/i16) -GC1i("|", stile, fabs(v), v== MIN, v<0?-v:v,0, el_bit, x) -GC1i("⌊", floor, floor(v), 0, v, 1, el_i32, x) -GC1i("⌈", ceil, ceil(v), 0, v, 1, el_i32, x) -GC1i("×", mul, v==0?0:v>0?1:-1, 0,v==0?0:v>0?1:-1,1, el_bit, x) -GC1i("¬", not, 1-v, v<=-MAX, 1-v, 0, el_bit, bit_negate(x)) +#define GC1i(SYMB,NAME,FEXPR,TMIN,RMIN,MAIN) B NAME##_c1(B t, B x) { \ + if (isF64(x)) { f64 v = x.f; return m_f64(FEXPR); } \ + if (RARE(!isArr(x))) thrM(SYMB ": Expected argument to be a number"); \ + u8 xe = TI(x,elType); \ + if (elNum(xe)) { \ + if (xe<=TMIN) return RMIN; \ + MAIN(FEXPR) \ + } \ + SLOW1(SYMB"𝕩", x); return arith_recm(NAME##_c1, x); \ +} + +#define LOOP_BODY(INIT, EXPR, POST) { \ + i64 ia = IA(x); INIT; \ + void* xp = tyany_ptr(x); \ + switch(xe) { default: UD; \ + case el_i8: for(usz i=0; i0? 1 : c==0? 0 : -1; +#define SIGN_MAIN(FEXPR) LOOP_BODY(i8* rp; B r=m_i8arrc(&rp,x);, SIGN_EXPR,) + +#if SINGELI + #define STILE_BODY(FEXPR) { usz ia = IA(x); B r; retry: \ + void* rp = m_tyarrlc(&r, elWidth(xe), x, el2t(xe)); \ + u64 got = simd_abs[xe-el_i8](rp, tyany_ptr(x), ia); \ + if (LIKELY(got==ia)) { decG(x); return r; } \ + tyarr_freeF(v(r)); \ + xe++;if (xe==el_i16) x=taga(cpyI16Arr(x)); \ + else if (xe==el_i32) x=taga(cpyI32Arr(x)); \ + else x=taga(cpyF64Arr(x)); \ + goto retry; \ + } +#else + #define STILE_EXPR(T, C) if(C) goto bad; ((T*)rp)[i] = c>=0? c : -c; + #define STILE_BODY(FEXPR) LOOP_BODY(B r; void* rp = m_tyarrlc(&r, elWidth(xe), x, el2t(xe));, STILE_EXPR, bad: tyarr_freeF(v(r));) +#endif + +#define FLOAT_BODY(FEXPR) { i64 ia = IA(x); \ + assert(xe==el_f64); f64* xp = f64any_ptr(x); \ + f64* rp; B r = m_f64arrc(&rp, x); \ + for (usz i = 0; i < ia; i++) { f64 v=xp[i]; rp[i]=FEXPR; } \ + decG(x); return num_squeeze(r); \ +} +B sub_c2(B,B,B); +#define SUB_BODY(FEXPR) return sub_c2(t, m_f64(0), x); +#define NOT_BODY(FEXPR) x = num_squeezeChk(x); return TI(x,elType)==el_bit? bit_negate(x) : sub_c2(m_f64(0), m_f64(1), x); + +GC1i("-", sub, -v, el_bit, bit_sel(x,m_f64(0),m_f64(-1)), SUB_BODY) +GC1i("|", stile, fabs(v), el_bit, x, STILE_BODY) +GC1i("⌊", floor, floor(v), el_i32, x, FLOAT_BODY) +GC1i("⌈", ceil, ceil(v), el_i32, x, FLOAT_BODY) +GC1i("×", mul, v==0?0:v>0?1:-1, el_bit, x, SIGN_MAIN) +GC1i("¬", not, 1-v, el_bit, bit_negate(x), NOT_BODY) #define GC1f(N, F, MSG) B N##_c1(B t, B x) { \ if (isF64(x)) { f64 xv=o2fG(x); return m_f64(F); } \ @@ -86,6 +115,16 @@ GC1i("¬", not, 1-v, v<=-MAX, 1-v, 0, el_bit, bit_negate(x)) GC1f( div, 1/xv, "÷: Getting reciprocal of non-number") GC1f(root, sqrt(xv), "√: Getting square root of non-number") +#undef GC1i +#undef LOOP_BODY +#undef SIGN_EXPR +#undef SIGN_MAIN +#undef STILE_BODY +#undef STILE_EXPR +#undef STILE_BODY +#undef FLOAT_BODY +#undef SUB_BODY +#undef NOT_BODY #undef GC1f f64 fact(f64 x) { return tgamma(x+1); } diff --git a/src/singeli/src/dyarith.singeli b/src/singeli/src/dyarith.singeli index 108b9455..d96f218c 100644 --- a/src/singeli/src/dyarith.singeli +++ b/src/singeli/src/dyarith.singeli @@ -164,7 +164,7 @@ def arithAAimpl{vw, mode, F, W, X, R, w, x, r, len} = { def run = runner{match{overflow, 0}, R, F} - def unr = tern{mode==0, 2, 1} # 2x unroll non-overflowing cases; surpresses clang's default unrolling, which unrolls a lot more; 2x appears to be plenty + def unr = tern{mode==0, 2, 1} # 2x unroll non-overflowing cases; suppresses clang's default unrolling, which unrolls a lot more; 2x appears to be plenty muLoop{bulk, unr, len, {is, M} => { def cw = loadBatch{*W~~w, is, ty_sc{W, TY}} def cx = loadBatch{*X~~x, is, ty_sc{X, TY}} diff --git a/src/singeli/src/monarith.singeli b/src/singeli/src/monarith.singeli new file mode 100644 index 00000000..cf804203 --- /dev/null +++ b/src/singeli/src/monarith.singeli @@ -0,0 +1,28 @@ +include './base' +include './f64' +include './cbqnDefs' +if (hasarch{'X86_64'}) { + include './sse3' + include './avx' + include './avx2' +} else if (hasarch{'AARCH64'}) { + include './neon' +} +include './bitops' +include './mask' + + +absFn{T}(r:*void, x:*void, len:u64) : u64 = { + def bulk = arch_defvw/width{T} + def VT = [bulk]T + muLoop{bulk, tern{T==f64, 2, 1}, len, {is, M} => { + def cx = loadBatch{*T~~x, is, VT} + if (T!=f64 and homAny{M{tree_fold{&, eachx{==, cx, VT**minvalue{T}}}}}) return{tupsel{0,is}*bulk} + storeBatch{*T~~r, is, each{abs, cx}, M} + }} + len +} + +def tup_abs = each{{T}=>absFn{T}, tup{i8, i16, i32, f64}} +t_abs:*type{tupsel{0,tup_abs}} = tup_abs +'simd_abs' = t_abs \ No newline at end of file