358 lines
14 KiB
C
358 lines
14 KiB
C
#include "../../core.h"
|
||
#include "../../utils/each.h"
|
||
#include <math.h>
|
||
// #define ARITH_DEBUG 1
|
||
|
||
typedef u64 (*CheckedFn)(u8* r, u8* w, u8* x, u64 len);
|
||
typedef void (*UncheckedFn)(u8* r, u8* w, u8* 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) {
|
||
switch(ex) { default: return "(unknown)";
|
||
#define F(X) case X: return #X;
|
||
FOR_ExecAA(F)
|
||
#undef F
|
||
}
|
||
}
|
||
#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
|
||
BBB2B 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;
|
||
ur wr = RNK(w);
|
||
ur xr = RNK(x);
|
||
if (wr!=xr || !eqShPart(SH(w), SH(x), wr)) goto rec;
|
||
|
||
B r, t;
|
||
usz ia = IA(w);
|
||
|
||
EntAA* e = &table->entsAA[we*8 + xe];
|
||
newEnt:
|
||
|
||
FnInfoAA* fn = &e->bundles[0];
|
||
newFn:
|
||
|
||
u8 ex = fn->ex1;
|
||
newEx:
|
||
#if ARITH_DEBUG
|
||
printf("opcode %d / %s\n", ex, execAA_repr(ex));
|
||
#endif
|
||
switch(ex) { default: UD;
|
||
case wi8_reg: w=taga( cpyI8Arr(w)); goto do_ex2; case xi8_reg: x=taga( cpyI8Arr(x)); goto do_ex2;
|
||
case wi16_reg: w=taga(cpyI16Arr(w)); goto do_ex2; case xi16_reg: x=taga(cpyI16Arr(x)); goto do_ex2;
|
||
case wi32_reg: w=taga(cpyI32Arr(w)); goto do_ex2; case xi32_reg: x=taga(cpyI32Arr(x)); goto do_ex2;
|
||
case wf64_reg: w=taga(cpyF64Arr(w)); goto do_ex2; case xf64_reg: x=taga(cpyF64Arr(x)); goto do_ex2;
|
||
case wc16_reg: w=taga(cpyC16Arr(w)); goto do_ex2; case xc16_reg: x=taga(cpyC16Arr(x)); goto do_ex2;
|
||
case wc32_reg: w=taga(cpyC32Arr(w)); goto do_ex2; case xc32_reg: x=taga(cpyC32Arr(x)); goto do_ex2;
|
||
case wi8_swap: t=x; x=taga( cpyI8Arr(w)); w=t; goto do_ex2; case xi8_swap: t=w; w=taga( cpyI8Arr(x)); x=t; goto do_ex2;
|
||
case wi16_swap: t=x; x=taga(cpyI16Arr(w)); w=t; goto do_ex2; case xi16_swap: t=w; w=taga(cpyI16Arr(x)); x=t; goto do_ex2;
|
||
case wi32_swap: t=x; x=taga(cpyI32Arr(w)); w=t; goto do_ex2; case xi32_swap: t=w; w=taga(cpyI32Arr(x)); x=t; goto do_ex2;
|
||
case wf64_swap: t=x; x=taga(cpyF64Arr(w)); w=t; goto do_ex2; case xf64_swap: t=w; w=taga(cpyF64Arr(x)); x=t; goto do_ex2;
|
||
case wc16_swap: t=x; x=taga(cpyC16Arr(w)); w=t; goto do_ex2; case xc16_swap: t=w; w=taga(cpyC16Arr(x)); x=t; goto do_ex2;
|
||
case wc32_swap: t=x; x=taga(cpyC32Arr(w)); w=t; goto do_ex2; case xc32_swap: t=w; w=taga(cpyC32Arr(x)); x=t; goto do_ex2;
|
||
case swap: t=w; w=x; x=t; goto do_ex2;
|
||
do_ex2: ex = fn->ex2; goto newEx;
|
||
|
||
case c_call_rbyte: { c_call_rbyte:
|
||
u64 got = fn->cFn(m_tyarrlc(&r, fn->width, x, fn->type), tyany_ptr(w), tyany_ptr(x), ia);
|
||
if (got==ia) goto decG_ret;
|
||
decG(r);
|
||
fn++;
|
||
goto newFn;
|
||
}
|
||
case u_call_rbyte: {
|
||
fn->uFn(m_tyarrlc(&r, fn->width, x, fn->type), tyany_ptr(w), tyany_ptr(x), ia);
|
||
goto decG_ret;
|
||
}
|
||
case e_call_rbyte: {
|
||
u64 got = fn->cFn(m_tyarrlc(&r, fn->width, x, fn->type), tyany_ptr(w), tyany_ptr(x), ia);
|
||
if (got) goto rec;
|
||
goto decG_ret;
|
||
}
|
||
case u_call_bit: {
|
||
u64* rp; r = m_bitarrc(&rp, x);
|
||
fn->uFn((u8*)rp, tyany_ptr(w), tyany_ptr(x), ia);
|
||
goto decG_ret;
|
||
}
|
||
|
||
case u_call_wxf64sq: {
|
||
f64* rp; r = m_f64arrc(&rp, x);
|
||
fn->uFn((u8*)rp, tyany_ptr(w = toF64Any(w)), tyany_ptr(x = toF64Any(x)), ia);
|
||
r = num_squeeze(r);
|
||
goto decG_ret;
|
||
}
|
||
case c_call_wxi8: {
|
||
assert(TI(x,elType)==el_bit && TI(w,elType)==el_bit);
|
||
w = taga(cpyI8Arr(w));
|
||
x = taga(cpyI8Arr(x));
|
||
goto c_call_rbyte;
|
||
}
|
||
case e_call_sqx: {
|
||
assert(TI(x,elType)==el_f64);
|
||
x = num_squeezeChk(x);
|
||
u8 xe = TI(x,elType);
|
||
if (xe==el_f64) goto rec;
|
||
e = &table->entsAA[TI(w,elType)*8 + xe];
|
||
goto newEnt;
|
||
}
|
||
case fail: goto rec;
|
||
}
|
||
|
||
rec:
|
||
return arith_recd(table->mainFn, w, x);
|
||
decG_ret:
|
||
decG(w); decG(x);
|
||
return r;
|
||
}
|
||
|
||
|
||
typedef struct DyTableSA DyTableSA;
|
||
typedef bool (*ForBitsel)(DyTableSA*, B w, B* r);
|
||
typedef u64 (*AtomArrFnC)(u8* r, u64 w, u8* 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];
|
||
BBB2B 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
|
||
|
||
B bad_chrAtomSA(DyTableSA* table, B w, B x, usz ia, u8 xe) { return arith_recd(table->mainFn, w, x); }
|
||
#define bad_chrAtomAS bad_chrAtomSA
|
||
|
||
u64 failAtomArr1(u8* r, u64 w, u8* x, u64 len) { return 0; }
|
||
u64 failAtomArr2(u8* r, u64 w, u8* x, u64 len) { return 1; }
|
||
|
||
u8 nextType[] = {
|
||
[t_i8arr ] = t_i16arr, [t_c8arr ] = t_c16arr,
|
||
[t_i16arr] = t_i32arr, [t_c16arr] = t_c32arr,
|
||
[t_i32arr] = t_f64arr, [t_c32arr] = t_empty,
|
||
[t_f64arr] = t_empty,
|
||
};
|
||
|
||
B dyArith_SA(DyTableSA* table, B w, B x) {
|
||
usz ia = IA(x);
|
||
u8 xe = TI(x,elType);
|
||
|
||
u8 width, type; // the currently supported character functions (+ and -) have the result type be character when left xor right is character, so it can be hard-coded
|
||
u64 wa;
|
||
|
||
EntSA* e;
|
||
|
||
if (ia==0) {
|
||
u8 fillVal;
|
||
bool charX;
|
||
if (xe!=el_B) {
|
||
charX = elChr(xe);
|
||
} else {
|
||
B xf = getFillQ(x);
|
||
if (isNum(xf)) charX=0;
|
||
else if (isC32(xf)) charX=1;
|
||
else if (noFill(xf)) { fillVal=0; goto fillSel; }
|
||
else { dec(xf); goto rec; } // whatever
|
||
}
|
||
bool charW;
|
||
if (isNum(w)) charW=0;
|
||
else if (isC32(w)) charW=1;
|
||
else goto rec;
|
||
|
||
fillVal = table->fill[charW][charX];
|
||
fillSel:
|
||
if (RNK(x)==1) {
|
||
decG(x);
|
||
if (fillVal==1) return emptyIVec();
|
||
if (fillVal==0) return emptyHVec();
|
||
assert(fillVal==2);
|
||
return emptyCVec();
|
||
} else {
|
||
Arr* r;
|
||
if (fillVal==1) { u64* rp; r = m_bitarrp(&rp, 0); }
|
||
else if (fillVal==0) { r = (Arr*) m_harrUp(0).c; }
|
||
else { assert(fillVal==2); u8* rp; r = m_c8arrp (&rp, 0); }
|
||
arr_shCopy(r, x);
|
||
decG(x);
|
||
return taga(r);
|
||
}
|
||
}
|
||
|
||
if (!isF64(w)) {
|
||
if (!isC32(w)) goto rec;
|
||
DyTableSA* t2 = table->chrAtom;
|
||
if (t2==NULL) goto rec;
|
||
table = t2;
|
||
|
||
|
||
wa = o2cG(w);
|
||
newXEc:
|
||
switch(xe) { default: UD;
|
||
case el_bit: goto bitsel;
|
||
case el_i8: if (wa==( u8)wa) { e=&table->ents[el_i8 ]; width=0; type=t_c8arr; goto f1; } else goto cwiden_i8;
|
||
case el_i16: if (wa==(u16)wa) { e=&table->ents[el_i16]; width=1; type=t_c16arr; goto f1; } else goto cwiden_i16;
|
||
case el_i32: e=&table->ents[el_i32]; width=2; type=t_c32arr; goto f1;
|
||
case el_f64: x=num_squeezeChk(x); xe=TI(x,elType); if (xe!=el_f64) goto newXEc; else goto rec;
|
||
case el_c8: if (wa==( u8)wa) { e=&table->ents[el_c8 ]; width=0; type=t_i8arr; goto f1; } goto cwiden_c8; // TODO check for & use unsigned w
|
||
case el_c16: if (wa==(u16)wa) { e=&table->ents[el_c16]; width=1; type=t_i16arr; goto f1; } goto cwiden_c16;
|
||
case el_c32: e=&table->ents[el_c32]; width=2; type=t_i32arr; goto f1;
|
||
case el_B: goto rec;
|
||
}
|
||
|
||
cwiden_i8: if (wa==(u16)wa) { type=t_c16arr; goto cpy_i16; }
|
||
cwiden_i16: { type=t_c32arr; goto cpy_i32; }
|
||
|
||
cwiden_c8: if (wa==(u16)wa) { type=t_i16arr; goto cpy_c16; }
|
||
cwiden_c16: { type=t_i32arr; goto cpy_c32; }
|
||
goto rec;
|
||
}
|
||
|
||
switch(xe) { default: UD;
|
||
case el_bit: goto bitsel;
|
||
case el_i8: if (q_i8 (w)) { e=&table->ents[el_i8 ]; width=0; type=t_i8arr; goto wint; } goto iwiden_i8;
|
||
case el_i16: if (q_i16(w)) { e=&table->ents[el_i16]; width=1; type=t_i16arr; goto wint; } goto iwiden_i16;
|
||
case el_i32: if (q_i32(w)) { e=&table->ents[el_i32]; width=2; type=t_i32arr; goto wint; } goto iwiden_i32;
|
||
case el_f64: e=&table->ents[el_f64]; width=3; type=t_f64arr; goto wf64;
|
||
case el_c8: if (q_i8 (w)) { e=&table->ents[el_c8 ]; width=0; type=t_c8arr; goto wint; } goto iwiden_c8;
|
||
case el_c16: if (q_i16(w)) { e=&table->ents[el_c16]; width=1; type=t_c16arr; goto wint; } goto iwiden_c16;
|
||
case el_c32: if (q_i32(w)) { e=&table->ents[el_c32]; width=2; type=t_c32arr; goto wint; } goto rec;
|
||
case el_B: goto rec;
|
||
}
|
||
|
||
wint: wa=(u32)o2iG(w); goto f1;
|
||
wf64: wa=w.u; goto f1;
|
||
|
||
iwiden_i8: if (q_i16(w)) { wa=(u32)o2iG(w); type=t_i16arr; goto cpy_i16; }
|
||
iwiden_i16: if (q_i32(w)) { wa=(u32)o2iG(w); type=t_i32arr; goto cpy_i32; }
|
||
iwiden_i32: { wa=w.u; type=t_f64arr; goto cpy_f64; }
|
||
|
||
iwiden_c8: if (q_i16(w)) { wa=(u32)o2iG(w); type=t_c16arr; goto cpy_c16; }
|
||
iwiden_c16: if (q_i32(w)) { wa=(u32)o2iG(w); type=t_c32arr; goto cpy_c32; }
|
||
goto rec;
|
||
|
||
// TODO reuse the copied array for the result; maybe even alternate copy & operation to stay in cache
|
||
cpy_c16: x = taga(cpyC16Arr(x)); width=1; e=&table->ents[el_c16]; goto f1;
|
||
cpy_c32: x = taga(cpyC32Arr(x)); width=2; e=&table->ents[el_c32]; goto f1;
|
||
|
||
cpy_i16: x = taga(cpyI16Arr(x)); width=1; e=&table->ents[el_i16]; goto f1;
|
||
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;
|
||
f1: fn = e->f1;
|
||
u64 got = fn(m_tyarrlc(&r, width, x, type), wa, tyany_ptr(x), ia);
|
||
if (got==ia) goto decG_ret;
|
||
decG(r);
|
||
|
||
type = nextType[type];
|
||
if (type==t_empty) goto rec;
|
||
fn = e->f2;
|
||
if (fn(m_tyarrlc(&r, width+1, x, type), wa, tyany_ptr(x), ia)==0) goto decG_ret;
|
||
decG(r);
|
||
goto rec;
|
||
|
||
|
||
decG_ret: decG(x); return r;
|
||
|
||
rec: return arith_recd(table->mainFn, w, x);
|
||
|
||
bitsel: {
|
||
B opts[2];
|
||
if (!table->ents[el_bit].bitsel(table, w, opts)) goto rec;
|
||
return bit_sel(x, opts[0], 1, opts[1], 1);
|
||
}
|
||
}
|
||
|
||
#pragma GCC diagnostic push
|
||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||
#include "../gen/dyarith.c"
|
||
#pragma GCC diagnostic pop
|
||
|
||
static void rootAAu_f64_f64_f64(u8* r, u8* w, u8* x, u64 len) { for (u64 i = 0; i < len; i++) ((f64*)r)[i] = pow(((f64*)x)[i], 1.0/((f64*)w)[i]); }
|
||
static void powAAu_f64_f64_f64(u8* r, u8* w, u8* x, u64 len) { for (u64 i = 0; i < len; i++) ((f64*)r)[i] = pow(((f64*)w)[i], ((f64*)x)[i]); }
|
||
static void stileAAu_f64_f64_f64(u8* r, u8* w, u8* x, u64 len) { for (u64 i = 0; i < len; i++) ((f64*)r)[i] = pfmod(((f64*)x)[i], ((f64*)w)[i]); }
|
||
static void logAAu_f64_f64_f64(u8* r, u8* w, u8* x, u64 len) { for (u64 i = 0; i < len; i++) ((f64*)r)[i] = log(((f64*)x)[i])/log(((f64*)w)[i]); }
|
||
|
||
bool add_forBitselNN_SA (DyTableSA* table, B w, B* r) { f64 f=o2fG(w); r[0] = m_f64(f+0); r[1] = m_f64(f+1); return true; }
|
||
bool sub_forBitselNN_SA (DyTableSA* table, B w, B* r) { f64 f=o2fG(w); r[0] = m_f64(f-0); r[1] = m_f64(f-1); return true; }
|
||
bool subR_forBitselNN_SA(DyTableSA* table, B w, B* r) { f64 f=o2fG(w); r[0] = m_f64(0-f); r[1] = m_f64(1-f); return true; }
|
||
bool mul_forBitselNN_SA (DyTableSA* table, B w, B* r) { f64 f=o2fG(w); r[0] = m_f64(f*0); r[1] = m_f64(f*1); return true; }
|
||
bool min_forBitselNN_SA (DyTableSA* table, B w, B* r) { f64 f=o2fG(w); r[0] = m_f64(f<=0?f:0); r[1] = m_f64(f<=1?f:1); return true; }
|
||
bool max_forBitselNN_SA (DyTableSA* table, B w, B* r) { f64 f=o2fG(w); r[0] = m_f64(f>=0?f:0); r[1] = m_f64(f>=1?f:1); return true; }
|
||
|
||
bool add_forBitselCN_SA(DyTableSA* table, B w, B* r) { u32 wc=o2cG(w); if(wc+1<=CHR_MAX) return false; r[0] = m_c32(wc); r[1] = m_c32(wc+1); return true; }
|
||
bool sub_forBitselCN_SA(DyTableSA* table, B w, B* r) { u32 wc=o2cG(w); if(wc !=0 ) return false; r[0] = m_c32(wc); r[1] = m_c32(wc-1); return true; }
|
||
|
||
B sub_c2R(B t, B w, B x) { return sub_c2(t, x, w); }
|
||
|
||
static NOINLINE B or_SA(B t, B w, B x) {
|
||
if (!isF64(w)) return arith_recd(or_c2, w, x);
|
||
if (LIKELY(TI(x,elType)==el_bit)) {
|
||
bitsel:
|
||
f64 wf = o2fG(w);
|
||
return bit_sel(x, m_f64(bqn_or(wf, 0)), 1, m_f64(bqn_or(wf, 1)), 1);
|
||
}
|
||
x = num_squeezeChk(x);
|
||
if (TI(x,elType)==el_bit) goto bitsel;
|
||
|
||
f64* rp;
|
||
B r = m_f64arrc(&rp, x);
|
||
usz ia = a(x)->ia;
|
||
x = toF64Any(x);
|
||
orSAc_f64_f64_f64((u8*)rp, w.u, tyany_ptr(x), ia);
|
||
decG(x);
|
||
return r;
|
||
}
|
||
|
||
#include "../gen/arTables.c"
|