diff --git a/build/src/clangd.bqn b/build/src/clangd.bqn index 8da82d22..56f65ce0 100644 --- a/build/src/clangd.bqn +++ b/build/src/clangd.bqn @@ -48,7 +48,7 @@ ModifyCBQNFlags ← { {𝕩 WantsIncludes ⟨"core.h"⟩}¨ ⟨ "ns.h", "vm.h", "builtins.h" "opt/mm_malloc.c", "opt/mm_buddy.c", "opt/mm_2buddy.c", "opt/mm_buddyTemplate.c", "opt/mm_buddyTemplate.h", "opt/gc.c", "opt/gc.h" - "builtins/radix.h" + "builtins/radix.h", "singeli/c/arithdDispatch.h" "jit/nvm.h" ⟩ # and all the utils/ onces diff --git a/src/singeli/c/arithdDispatch.c b/src/singeli/c/arithdDispatch.c index 80f34263..5c3979d9 100644 --- a/src/singeli/c/arithdDispatch.c +++ b/src/singeli/c/arithdDispatch.c @@ -1,34 +1,9 @@ #include "../../core.h" #include "../../utils/each.h" +#include "../../builtins.h" +#include "arithdDispatch.h" #include -// #define ARITH_DEBUG 1 -typedef u64 (*CheckedFn)(void* r, void* w, void* x, u64 len); -typedef void (*UncheckedFn)(void* r, void* w, void* x, u64 len); -#define FOR_ExecAA(F) \ - F(fail) /* first to allow zero-initialization to be fail implicitly */ \ - F(swap) /* swap 𝕨 and 𝕩, then run ex2 */ \ - /* cast the specified argument up to the specified size, then either swap or don't, then run ex2 */ \ - F(wi8_reg) F(xi8_reg) F(wi8_swap) F(xi8_swap) \ - F(wi16_reg) F(xi16_reg) F(wi16_swap) F(xi16_swap) \ - F(wi32_reg) F(xi32_reg) F(wi32_swap) F(xi32_swap) \ - F(wf64_reg) F(xf64_reg) F(wf64_swap) F(xf64_swap) \ - F(wc16_reg) F(xc16_reg) F(wc16_swap) F(xc16_swap) \ - F(wc32_reg) F(xc32_reg) F(wc32_swap) F(xc32_swap) \ - /* c_* - overflow-checked; u_* - no overflow check */ \ - F(c_call_rbyte) /* arguments are already the wanted widths; result isn't a bitarr */ \ - F(u_call_rbyte) /* ↑ */ \ - F(e_call_rbyte) /* calls CheckedFn but errors on non-zero result */ \ - F(u_call_bit) /* result and arguments are bitarrs */ \ - F(u_call_wxf64sq) /* convert both args up to f64 if needed, make f64arr, and squeeze result; i.e. lazy float fallback case */ \ - F(c_call_wxi8) /* convert both args (which need to be bitarrs) to i8arrs, and invoke checked function (no good reason for it to fail the check, but this allows reusing a c‿i8‿i8 impl) */ \ - F(e_call_sqx) /* squeeze f64arr 𝕩, error if can't; else re-dispatch to new entry */ - -enum ExecAA { - #define F(X) X, - FOR_ExecAA(F) - #undef F -}; #if ARITH_DEBUG char* execAA_repr(u8 ex) { @@ -40,23 +15,6 @@ char* execAA_repr(u8 ex) { } #endif - -typedef struct FnInfoAA { - union { CheckedFn cFn; UncheckedFn uFn; }; - u8 ex1, ex2; // ExecAA - u8 type; // t_*; unused for u_call_bit - u8 width; // width in bytes; unused for u_call_bit -} FnInfoAA; -typedef struct EntAA { - FnInfoAA bundles[2]; -} EntAA; - -typedef struct DyTableAA { - EntAA entsAA[el_B*el_B]; // one for each instruction - FC2 mainFn; - char* repr; -} DyTableAA; - NOINLINE B dyArith_AA(DyTableAA* table, B w, B x) { u8 we = TI(w, elType); if (we==el_B) goto rec; u8 xe = TI(x, elType); if (xe==el_B) goto rec; @@ -155,25 +113,6 @@ NOINLINE B dyArith_AA(DyTableAA* table, B w, B x) { } -typedef struct DyTableSA DyTableSA; -typedef bool (*ForBitsel)(DyTableSA*, B w, B* r); -typedef u64 (*AtomArrFnC)(void* r, u64 w, void* x, u64 len); -typedef B (*DyArithChrFn)(DyTableSA*, B, B, usz, u8); - -typedef struct { - // >=el_i8 el_bit - union { AtomArrFnC f1; ForBitsel bitsel; }; - union { AtomArrFnC f2; }; -} EntSA; - - -struct DyTableSA { - EntSA ents[el_B]; - FC2 mainFn; - char* repr; - u8 fill[2][2]; // 0:none 1:int 2:char - DyTableSA* chrAtom; -}; bool bad_forBitselNN_SA(DyTableSA* table, B w, B* r) { return false; } #define bad_forBitselCN_SA bad_forBitselNN_SA @@ -191,7 +130,7 @@ u8 nextType[] = { [t_f64arr] = t_empty, }; -B dyArith_SA(DyTableSA* table, B w, B x) { +NOINLINE B dyArith_SA(DyTableSA* table, B w, B x) { usz ia = IA(x); u8 xe = TI(x,elType); @@ -296,7 +235,7 @@ B dyArith_SA(DyTableSA* table, B w, B x) { cpy_i32: x = taga(cpyI32Arr(x)); width=2; e=&table->ents[el_i32]; goto f1; cpy_f64: x = taga(cpyF64Arr(x)); width=3; e=&table->ents[el_f64]; goto f1; - AtomArrFnC fn; B r; + ChkFnSA fn; B r; f1: fn = e->f1; u64 got = fn(m_tyarrlc(&r, width, x, type), wa, tyany_ptr(x), ia); if (got==ia) goto decG_ret; @@ -362,3 +301,39 @@ static NOINLINE B or_SA(B t, B w, B x) { #define SINGELI_FILE arTables #include "../../utils/includeSingeli.h" + +DyTableAA* dyTableAAFor(B f) { + assert(isFun(f)); + switch(v(f)->extra-1) { default: return NULL; + case n_add: return &addDyTableAA; + case n_sub: return &subDyTableAA; + case n_mul: return &mulDyTableAA; + case n_and: return &andDyTableAA; + case n_or: return &orDyTableAA; + case n_floor: return &floorDyTableAA; + case n_ceil: return &ceilDyTableAA; + } +} + +DyTableSA* dyTableSAFor(B f, bool atomChar) { + assert(isFun(f)); + switch(v(f)->extra-1) { default: return NULL; + case n_add: return atomChar? &addDyTableCA : &addDyTableNA; + case n_sub: return atomChar? &subDyTableCA : &subDyTableNA; + case n_mul: return atomChar? NULL : &mulDyTableNA; + case n_and: return atomChar? NULL : &andDyTableNA; + case n_floor: return atomChar? NULL : &floorDyTableNA; + case n_ceil: return atomChar? NULL : &ceilDyTableNA; + } +} +DyTableSA* dyTableASFor(B f, bool atomChar) { + assert(isFun(f)); + switch(v(f)->extra-1) { default: return NULL; + case n_add: return atomChar? &addDyTableCA : &addDyTableNA; + case n_sub: return atomChar? &subDyTableAC : &subDyTableAN; + case n_mul: return atomChar? NULL : &mulDyTableNA; + case n_and: return atomChar? NULL : &andDyTableNA; + case n_floor: return atomChar? NULL : &floorDyTableNA; + case n_ceil: return atomChar? NULL : &ceilDyTableNA; + } +} diff --git a/src/singeli/c/arithdDispatch.h b/src/singeli/c/arithdDispatch.h new file mode 100644 index 00000000..0035948b --- /dev/null +++ b/src/singeli/c/arithdDispatch.h @@ -0,0 +1,82 @@ +#pragma once +#if SINGELI_SIMD +// #define ARITH_DEBUG 1 + +#define FOR_ExecAA(F) \ + F(fail) /* first to allow zero-initialization to be fail implicitly */ \ + F(swap) /* swap 𝕨 and 𝕩, then run ex2 */ \ + /* cast the specified argument up to the specified size, then either swap or don't, then run ex2 */ \ + F(wi8_reg) F(xi8_reg) F(wi8_swap) F(xi8_swap) \ + F(wi16_reg) F(xi16_reg) F(wi16_swap) F(xi16_swap) \ + F(wi32_reg) F(xi32_reg) F(wi32_swap) F(xi32_swap) \ + F(wf64_reg) F(xf64_reg) F(wf64_swap) F(xf64_swap) \ + F(wc16_reg) F(xc16_reg) F(wc16_swap) F(xc16_swap) \ + F(wc32_reg) F(xc32_reg) F(wc32_swap) F(xc32_swap) \ + /* c_* - overflow-checked; u_* - no overflow check */ \ + F(c_call_rbyte) /* arguments are already the wanted widths; result isn't a bitarr */ \ + F(u_call_rbyte) /* ↑ */ \ + F(e_call_rbyte) /* calls ChkFnAA but errors on non-zero result */ \ + F(u_call_bit) /* result and arguments are bitarrs */ \ + F(u_call_wxf64sq) /* convert both args up to f64 if needed, make f64arr, and squeeze result; i.e. lazy float fallback case */ \ + F(c_call_wxi8) /* convert both args (which need to be bitarrs) to i8arrs, and invoke checked function (no good reason for it to fail the check, but this allows reusing a c‿i8‿i8 impl) */ \ + F(e_call_sqx) /* squeeze f64arr 𝕩, error if can't; else re-dispatch to new entry */ + +enum ExecAA { + #define F(X) X, + FOR_ExecAA(F) + #undef F +}; + +#if ARITH_DEBUG +char* execAA_repr(u8 ex); +#endif + +typedef u64 (*ChkFnAA)(void* r, void* w, void* x, u64 len); +typedef void (*UnchkFnAA)(void* r, void* w, void* x, u64 len); + +typedef struct FnInfoAA { + union { ChkFnAA cFn; UnchkFnAA uFn; }; + u8 ex1, ex2; // ExecAA + u8 type; // t_*; unused for u_call_bit + u8 width; // width in bytes; unused for u_call_bit +} FnInfoAA; + +typedef struct EntAA { + FnInfoAA bundles[2]; +} EntAA; + +typedef struct DyTableAA { + EntAA entsAA[el_B*el_B]; // one for each instruction + FC2 mainFn; + char* repr; +} DyTableAA; + + + + + +typedef struct DyTableSA DyTableSA; +typedef bool (*ForBitsel)(DyTableSA*, B w, B* r); +typedef u64 (*ChkFnSA)(void* r, u64 w, void* x, u64 len); + +typedef struct { + // >=el_i8 el_bit + union { ChkFnSA f1; ForBitsel bitsel; }; + union { ChkFnSA f2; }; +} EntSA; + + +struct DyTableSA { + EntSA ents[el_B]; + FC2 mainFn; + char* repr; + u8 fill[2][2]; // 0:none 1:int 2:char + DyTableSA* chrAtom; +}; + +// all assume f is a function, and return NULL if a table is unavailable (either because f isn't applicable dyadic arith, or the requested case always errors) +DyTableAA* dyTableAAFor(B f); +DyTableSA* dyTableSAFor(B f, bool atomChar); +DyTableSA* dyTableASFor(B f, bool atomChar); // returns table taking arguments in reverse order + +#endif diff --git a/src/singeli/src/genArithTables.bqn b/src/singeli/src/genArithTables.bqn index ee9da3a9..624916e2 100644 --- a/src/singeli/src/genArithTables.bqn +++ b/src/singeli/src/genArithTables.bqn @@ -10,7 +10,7 @@ i32←3 ⋄ c32←7 f64←4 u‿c‿e‿rsq‿xsq ← ↕5 tmNoNext ← 1‿0‿1‿1‿1 -tmCheckedFn ← 0‿1‿1‿0‿0 +tmChkFnAA ← 0‿1‿1‿0‿0 tmLit ← "uceue" tyWidth ← 99‿0‿1‿2‿3‿0‿1‿2 # in log2(bytes) @@ -103,7 +103,7 @@ Make ← {𝕊ns: cSrc∾↩ ∾⟨ "B ", cbqn, "_c2(B,B,B);", nl - "static DyTableAA ", cbqn,"DyTableAA = {", nl + "DyTableAA ", cbqn,"DyTableAA = {", nl " .entsAA = {", nl ∾{ ⟨instrs, we‿xe⟩: { @@ -117,7 +117,7 @@ Make ← {𝕊ns: ".ex1=",@⊸≢◶⟨"",∾⟜"_"⟩ cast,swap⊑"reg,"‿"swap,"⟩} two, ".ex", '1'+two, "=", ex, @⊸≢◶""‿{∾⟨",.type=t_",𝕩⊑tyName,"arr,.width=",•Repr 𝕩⊑tyWidth⟩} rt, - @⊸≢◶""‿{∾⟨",.uFn"‿",.cFn"⊑˜t⊑tmCheckedFn,"=",𝕩⟩} fn + @⊸≢◶""‿{∾⟨",.uFn"‿",.cFn"⊑˜t⊑tmChkFnAA,"=",𝕩⟩} fn ⟩ }¨ instrs ∾⟨" [",•Repr we,"*8+",•Repr xe,"] = {{{", i0, "}, {", i1, "}}},", nl⟩ @@ -150,7 +150,7 @@ Make ← {𝕊ns: P ← {∾⟨"{",𝕨,",",𝕩,"}"⟩}´ cSrc∾↩ ∾⟨ - "static DyTableSA ", cbqn,"DyTable", ⌽⍟swap (charAtm⊑"NC")∾"A", " = {", nl + "DyTableSA ", cbqn,"DyTable", ⌽⍟swap (charAtm⊑"NC")∾"A", " = {", nl " .ents = {", nl " {.bitsel=", hasNumArr◶⟨"bad", {𝕊:id∾swap/"R"}⟩ @, "_forBitsel", charAtm⊑"NC", "N_SA},", nl ∾{ 𝕊 num: