diff --git a/.gitignore b/.gitignore index a0334f5f..16843744 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ perf.* /Singeli /local/ CBQNHeapDump +/test/ffi/ffiTest.o +/test/ffi/test.got +/test/ffi/lib.so diff --git a/include/bqnffi.h b/include/bqnffi.h new file mode 100644 index 00000000..f8b6736c --- /dev/null +++ b/include/bqnffi.h @@ -0,0 +1,89 @@ +#include +#include +#include + +typedef uint64_t BQNV; + +#ifdef __cplusplus +extern "C" { +#endif + +void bqn_free(BQNV v); + +double bqn_toF64 (BQNV v); // includes bqn_free(v) +uint32_t bqn_toChar(BQNV v); // includes bqn_free(v) +double bqn_readF64 (BQNV v); // doesn't include bqn_free(v) +uint32_t bqn_readChar(BQNV v); // doesn't include bqn_free(v) + +// invoke BQN function +BQNV bqn_call1(BQNV f, BQNV x); +BQNV bqn_call2(BQNV f, BQNV w, BQNV x); + +// evaluate BQN code in a fresh environment +BQNV bqn_eval(BQNV src); +BQNV bqn_evalCStr(char* str); // evaluates the null-terminated UTF8-encoded str; equal to `BQNV s = bqn_makeUTF8Str(str, strlen(str)); result = bqn_eval(s); bqn_free(s);` + + +// read array data +size_t bqn_bound(BQNV a); // aka product of shape, ×´≢a +size_t bqn_rank(BQNV a); +void bqn_shape(BQNV a, size_t* buf); // writes bqn_rank(a) items in buf +BQNV bqn_pick(BQNV a, size_t pos); + +// read all elements of `a` into the specified buffer +void bqn_readI8Arr (BQNV a, int8_t* buf); +void bqn_readI16Arr(BQNV a, int16_t* buf); +void bqn_readI32Arr(BQNV a, int32_t* buf); +void bqn_readF64Arr(BQNV a, double* buf); +void bqn_readC8Arr (BQNV a, uint8_t* buf); +void bqn_readC16Arr(BQNV a, uint16_t* buf); +void bqn_readC32Arr(BQNV a, uint32_t* buf); +void bqn_readObjArr(BQNV a, BQNV* buf); + + +// create objects +BQNV bqn_makeF64(double d); +BQNV bqn_makeChar(uint32_t c); +BQNV bqn_makeI8Arr (size_t rank, size_t* shape, int8_t* data); +BQNV bqn_makeI16Arr(size_t rank, size_t* shape, int16_t* data); +BQNV bqn_makeI32Arr(size_t rank, size_t* shape, int32_t* data); +BQNV bqn_makeF64Arr(size_t rank, size_t* shape, double* data); +BQNV bqn_makeC8Arr (size_t rank, size_t* shape, uint8_t* data); +BQNV bqn_makeC16Arr(size_t rank, size_t* shape, uint16_t* data); +BQNV bqn_makeC32Arr(size_t rank, size_t* shape, uint32_t* data); +BQNV bqn_makeObjArr(size_t rank, size_t* shape, BQNV* data); // frees the taken elements of data + +BQNV bqn_makeI8Vec (size_t len, int8_t* data); +BQNV bqn_makeI16Vec(size_t len, int16_t* data); +BQNV bqn_makeI32Vec(size_t len, int32_t* data); +BQNV bqn_makeF64Vec(size_t len, double* data); +BQNV bqn_makeC8Vec (size_t len, uint8_t* data); +BQNV bqn_makeC16Vec(size_t len, uint16_t* data); +BQNV bqn_makeC32Vec(size_t len, uint32_t* data); +BQNV bqn_makeObjVec(size_t len, BQNV* data); // frees the taken elements of data +BQNV bqn_makeUTF8Str(size_t len, char* str); + +typedef BQNV (*bqn_boundFn1)(BQNV obj, BQNV x); +typedef BQNV (*bqn_boundFn2)(BQNV obj, BQNV w, BQNV x); + +// when called, 1st arg to `f` will be `obj` +BQNV bqn_makeBoundFn1(bqn_boundFn1 f, BQNV obj); +BQNV bqn_makeBoundFn2(bqn_boundFn2 f, BQNV obj); + + +// direct (zero copy) array item access +typedef enum { elt_i8, elt_i16, elt_i32, elt_f64, elt_c8, elt_c16, elt_c32, elt_unk } BQNElType; +BQNElType bqn_directType(BQNV a); +// can only use the functions below if bqn_elType returns the corresponding type +// a valid implementation of bqn_elType would be to always return elt_unk, thus disallowing the use of direct access entirely +int8_t* bqn_directI8 (BQNV a); +int16_t* bqn_directI16(BQNV a); +int32_t* bqn_directI32(BQNV a); +double* bqn_directF64(BQNV a); +uint8_t* bqn_directC8 (BQNV a); +uint16_t* bqn_directC16(BQNV a); +uint32_t* bqn_directC32(BQNV a); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/include/syms b/include/syms new file mode 100644 index 00000000..5babdd56 --- /dev/null +++ b/include/syms @@ -0,0 +1,52 @@ +{ + bqn_free; + bqn_toF64; + bqn_toChar; + bqn_readF64; + bqn_readChar; + bqn_call1; + bqn_call2; + bqn_eval; + bqn_evalCStr; + bqn_bound; + bqn_rank; + bqn_shape; + bqn_pick; + bqn_readI8Arr; + bqn_readI16Arr; + bqn_readI32Arr; + bqn_readF64Arr; + bqn_readC8Arr; + bqn_readC16Arr; + bqn_readC32Arr; + bqn_readObjArr; + bqn_makeF64; + bqn_makeChar; + bqn_makeI8Arr; + bqn_makeI16Arr; + bqn_makeI32Arr; + bqn_makeF64Arr; + bqn_makeC8Arr; + bqn_makeC16Arr; + bqn_makeC32Arr; + bqn_makeObjArr; + bqn_makeI8Vec; + bqn_makeI16Vec; + bqn_makeI32Vec; + bqn_makeF64Vec; + bqn_makeC8Vec; + bqn_makeC16Vec; + bqn_makeC32Vec; + bqn_makeObjVec; + bqn_makeUTF8Str; + bqn_makeBoundFn1; + bqn_makeBoundFn2; + bqn_directType; + bqn_directI8; + bqn_directI16; + bqn_directI32; + bqn_directF64; + bqn_directC8; + bqn_directC16; + bqn_directC32; +}; \ No newline at end of file diff --git a/makefile b/makefile index de697fa2..9e58889f 100644 --- a/makefile +++ b/makefile @@ -37,32 +37,48 @@ c: @${MAKE} custom=1 run_incremental_0 # compiler setup -i_CC = clang -i_PIE = -no-pie -i_LD_LIBS = -lm -OUTPUT = BQN +i_CC := clang +i_PIE := -no-pie +i_LD_LIBS := -lm +i_FFI := 2 +i_singeli := 0 +OUTPUT := BQN ifeq ($(origin CC),command line) - i_CC = $(CC) - custom = 1 -endif -ifeq ($(origin PIE),command line) - i_PIE = $(PIE) - custom = 1 -endif -ifeq ($(origin LD_LIBS),command line) - i_LD_LIBS = $(LD_LIBS) + i_CC := $(CC) custom = 1 endif ifeq ($(origin singeli),command line) - i_singeli = $(singeli) + i_singeli := $(singeli) + custom = 1 +endif +ifeq ($(origin FFI),command line) + i_FFI := $(FFI) + custom = 1 +endif +ifneq ($(i_FFI),0) + i_LD_LIBS += -ldl -Wl,--dynamic-list=include/syms +endif +ifeq ($(i_FFI),2) + i_LD_LIBS += -lffi +endif +ifeq ($(origin LD_LIBS),command line) + i_LD_LIBS := $(LD_LIBS) + custom = 1 +endif +ifeq ($(origin PIE),command line) + i_PIE := $(PIE) custom = 1 endif ifeq ($(origin f),command line) custom = 1 +else + f:= endif ifeq ($(origin lf),command line) custom = 1 +else + lf:= endif ifeq ($(origin CCFLAGS),command line) custom = 1 @@ -78,14 +94,7 @@ else NOWARN = -Wno-parentheses endif -ifeq (${i_singeli}, 1) - SINGELIFLAGS = '-DSINGELI' -else - i_singeli = 0 - SINGELIFLAGS = -endif - -ALL_CC_FLAGS = -std=gnu11 -Wall -Wno-unused-function -fms-extensions $(CCFLAGS) $(f) $(i_f) $(SINGELIFLAGS) $(NOWARN) +ALL_CC_FLAGS = -std=gnu11 -Wall -Wno-unused-function -fms-extensions $(CCFLAGS) $(f) $(i_f) $(NOWARN) -DSINGELI=$(i_singeli) -DFFI=$(i_FFI) ALL_LD_FLAGS = $(LDFLAGS) $(lf) $(i_lf) $(i_PIE) $(i_LD_LIBS) ifneq (${manualJobs},1) @@ -152,7 +161,7 @@ ${bd}/%.o: src/core/%.c @echo $< | cut -c 5- @$(CC_INC) $@.d -o $@ -c $< -base: ${addprefix ${bd}/, load.o main.o rtwrap.o vm.o ns.o nfns.o} +base: ${addprefix ${bd}/, load.o main.o rtwrap.o vm.o ns.o nfns.o ffi.o} ${bd}/%.o: src/%.c @echo $< | cut -c 5- @$(CC_INC) $@.d -o $@ -c $< @@ -188,7 +197,7 @@ preSingeliBin: git submodule update --init; \ fi @echo "pre-singeli build:" - @${MAKE} i_singeli=0 singeli=0 force_build_dir=obj/presingeli f= lf= postmsg="singeli sources:" i_t=presingeli i_f='-O1 -DPRE_SINGELI' OUTPUT=obj/presingeli/BQN c + @${MAKE} i_singeli=0 singeli=0 force_build_dir=obj/presingeli f= lf= postmsg="singeli sources:" i_t=presingeli i_f='-O1 -DPRE_SINGELI' FFI=0 OUTPUT=obj/presingeli/BQN c build_singeli: ${addprefix src/singeli/gen/, cmp.c dyarith.c copy.c equal.c scan.c slash.c} diff --git a/src/builtins/sysfn.c b/src/builtins/sysfn.c index fb01d855..18544af0 100644 --- a/src/builtins/sysfn.c +++ b/src/builtins/sysfn.c @@ -239,7 +239,7 @@ B show_c1(B t, B x) { return x; } -static B vfyStr(B x, char* name, char* arg) { +B vfyStr(B x, char* name, char* arg) { if (isAtm(x) || rnk(x)!=1) thrF("%U: %U must be a character vector", name, arg); if (!elChr(TI(x,elType))) { usz ia = a(x)->ia; @@ -1096,6 +1096,10 @@ B getMathNS(void); B getPrimitives(void); static Body* file_nsGen; + +NFnDesc* ffiloadDesc; +B ffiload_c2(B t, B w, B x); + B sys_c1(B t, B x) { assert(isArr(x)); M_HARR(r, a(x)->ia) SGetU(x) @@ -1155,6 +1159,9 @@ B sys_c1(B t, B x) { else if (eqStr(c, U"fbytes")) cr = m_nfn(fBytesDesc, inc(REQ_PATH)); else if (eqStr(c, U"flines")) cr = m_nfn(fLinesDesc, inc(REQ_PATH)); else if (eqStr(c, U"import")) cr = m_nfn(importDesc, inc(REQ_PATH)); + #if FFI + else if (eqStr(c, U"ffi")) cr = m_nfn(ffiloadDesc, inc(REQ_PATH)); + #endif else if (eqStr(c, U"currenterror")) cr = inc(bi_currentError); else if (eqStr(c, U"state")) { if (q_N(comp_currArgs)) thrM("No arguments present for •state"); @@ -1191,6 +1198,7 @@ void sysfn_init() { fExistsDesc = registerNFn(m_str8l("(file).Exists"), fexists_c1, c2_bad); importDesc = registerNFn(m_str32(U"•Import"), import_c1, import_c2); reBQNDesc = registerNFn(m_str8l("(REPL)"), repl_c1, repl_c2); + ffiloadDesc = registerNFn(m_str32(U"•FFI"), c1_bad, ffiload_c2); } void sysfnPost_init() { file_nsGen = m_nnsDesc("path","at","list","bytes","chars","lines","type","exists","name","mapbytes","createdir","rename","remove"); diff --git a/src/core/stuff.c b/src/core/stuff.c index f823bb0e..57d7b0b1 100644 --- a/src/core/stuff.c +++ b/src/core/stuff.c @@ -10,17 +10,17 @@ bool please_tail_call_err = true; bool inErr; NORETURN NOINLINE void err(char* s) { if (inErr) { - printf("\nCBQN encountered fatal error during information printing of another fatal error. Exiting without printing more info.\n"); + fputs("\nCBQN encountered fatal error during information printing of another fatal error. Exiting without printing more info.", stderr); #ifdef DEBUG __builtin_trap(); #endif exit(1); } inErr = true; - puts(s); fflush(stdout); - vm_pstLive(); fflush(stdout); - print_vmStack(); fflush(stdout); - puts("CBQN interpreter entered unexpected state, exiting."); + fputs(s, stderr); fputc('\n', stderr); fflush(stderr); + vm_pstLive(); fflush(stderr); fflush(stdout); + print_vmStack(); fflush(stderr); + fputs("CBQN interpreter entered unexpected state, exiting.\n", stderr); #ifdef DEBUG __builtin_trap(); #endif @@ -132,7 +132,7 @@ void fprint(FILE* f, B x) { NUM_FMT_BUF(buf, x.f); fprintf(f, "%s", buf); } else if (isC32(x)) { - if ((u32)x.u>=32) { fprintf(f, "'"); printUTF8((u32)x.u); fprintf(f, "'"); } + if ((u32)x.u>=32) { fprintf(f, "'"); fprintUTF8(f, (u32)x.u); fprintf(f, "'"); } else if((u32)x.u>15) fprintf(f, "\\x%x", (u32)x.u); else fprintf(f, "\\x0%x", (u32)x.u); } else if (isVal(x)) { @@ -236,7 +236,7 @@ NOINLINE B do_fmt(B s, char* p, va_list a) { while (*p != 0) { c = *p++; if (c!='%') continue; if (lp!=p-1) AJOIN(fromUTF8(lp, p-1-lp)); - switch(c = *p++) { default: printf("Unknown format character '%c'", c); UD; + switch(c = *p++) { default: printf("Unknown format character '%c'", c); err(""); UD; case 'R': { B b = va_arg(a, B); if (isNum(b)) AFMT("%f", o2f(b)); @@ -831,15 +831,15 @@ void g_pst(void) { vm_pstLive(); } #ifdef DEBUG #ifdef OBJ_COUNTER - #define PRINT_ID(X) printf("Object ID: "N64u"\n", (X)->uid) + #define PRINT_ID(X) fprintf(stderr, "Object ID: "N64u"\n", (X)->uid) #else #define PRINT_ID(X) #endif NOINLINE Value* VALIDATEP(Value* x) { if (x->refc<=0 || (x->refc>>28) == 'a' || x->type==t_empty) { PRINT_ID(x); - printf("bad refcount for type %d: %d\nattempting to print: ", x->type, x->refc); fflush(stdout); - print(tag(x,OBJ_TAG)); putchar('\n'); fflush(stdout); + fprintf(stderr, "bad refcount for type %d: %d\nattempting to print: ", x->type, x->refc); fflush(stderr); + fprint(stderr, tag(x,OBJ_TAG)); fputc('\n', stderr); fflush(stderr); err(""); } if (TIv(x,isArr)) { @@ -858,15 +858,15 @@ void g_pst(void) { vm_pstLive(); } if (!isVal(x)) return x; VALIDATEP(v(x)); if(isArr(x)!=TI(x,isArr) && v(x)->type!=t_freed && v(x)->type!=t_harrPartial) { - printf("bad array tag/type: type=%d, obj=%p\n", v(x)->type, (void*)x.u); + fprintf(stderr, "bad array tag/type: type=%d, obj=%p\n", v(x)->type, (void*)x.u); PRINT_ID(v(x)); - print(x); + fprint(stderr, x); err("\n"); } return x; } NOINLINE NORETURN void assert_fail(char* expr, char* file, int line, const char fn[]) { - printf("%s:%d: %s: Assertion `%s` failed.\n", file, line, fn, expr); + fprintf(stderr, "%s:%d: %s: Assertion `%s` failed.\n", file, line, fn, expr); err(""); } #endif @@ -874,27 +874,27 @@ void g_pst(void) { vm_pstLive(); } static void warn_ln(B x) { if (isArr(x)) print_fmt("%s items, %S, shape=%H\n", a(x)->ia, eltype_repr(TI(x,elType)), x); else { - printf("atom: "); - printRaw(x = bqn_fmt(inc(x))); dec(x); - putchar('\n'); + fprintf(stderr, "atom: "); + fprintRaw(stderr, x = bqn_fmt(inc(x))); dec(x); + fputc('\n', stderr); } } void warn_slow1(char* s, B x) { if (isArr(x) && a(x)->ia<100) return; - printf("slow %s: ", s); warn_ln(x); - fflush(stdout); + fprintf(stderr, "slow %s: ", s); warn_ln(x); + fflush(stderr); } void warn_slow2(char* s, B w, B x) { if ((isArr(w)||isArr(x)) && (!isArr(w) || a(w)->ia<50) && (!isArr(x) || a(x)->ia<50)) return; - printf("slow %s:\n 𝕨: ", s); warn_ln(w); - printf(" 𝕩: "); warn_ln(x); - fflush(stdout); + fprintf(stderr, "slow %s:\n 𝕨: ", s); warn_ln(w); + fprintf(stderr, " 𝕩: "); warn_ln(x); + fflush(stderr); } void warn_slow3(char* s, B w, B x, B y) { if ((isArr(w)||isArr(x)) && (!isArr(w) || a(w)->ia<50) && (!isArr(x) || a(x)->ia<50)) return; - printf("slow %s:\n 𝕨: ", s); warn_ln(w); - printf(" 𝕩: "); warn_ln(x); - printf(" f: "); warn_ln(y); - fflush(stdout); + fprintf(stderr, "slow %s:\n 𝕨: ", s); warn_ln(w); + fprintf(stderr, " 𝕩: "); warn_ln(x); + fprintf(stderr, " f: "); warn_ln(y); + fflush(stderr); } #endif diff --git a/src/ffi.c b/src/ffi.c new file mode 100644 index 00000000..a8ad2878 --- /dev/null +++ b/src/ffi.c @@ -0,0 +1,746 @@ +#include "core.h" + +#if FFI +#include "../include/bqnffi.h" +#include "utils/utf.h" +#include "utils/cstr.h" +#include "nfns.h" +#include "utils/file.h" +#include +#if FFI==2 +#include +#include "utils/mut.h" +#endif + +// base interface defs for when GC stuff needs to be added in +static B getB(BQNV v) { + return b(v); +} +static BQNV makeX(B x) { + return x.u; +} +void bqn_free(BQNV v) { + dec(getB(v)); +} +static void freeTagged(BQNV v) { } +#define DIRECT_BQNV 1 + +double bqn_toF64 (BQNV v) { double r = o2fu(getB(v)); freeTagged(v); return r; } +uint32_t bqn_toChar(BQNV v) { uint32_t r = o2cu(getB(v)); freeTagged(v); return r; } +double bqn_readF64 (BQNV v) { return o2fu(getB(v)); } +uint32_t bqn_readChar(BQNV v) { return o2cu(getB(v)); } + +BQNV bqn_call1(BQNV f, BQNV x) { + return makeX(c1(getB(f), inc(getB(x)))); +} +BQNV bqn_call2(BQNV f, BQNV w, BQNV x) { + return makeX(c2(getB(f), inc(getB(w)), inc(getB(x)))); +} + +BQNV bqn_eval(BQNV src) { + return makeX(bqn_exec(inc(getB(src)), bi_N, bi_N)); +} +BQNV bqn_evalCStr(char* str) { + return makeX(bqn_exec(fromUTF8l(str), bi_N, bi_N)); +} + + +size_t bqn_bound(BQNV a) { return a(getB(a))->ia; } +size_t bqn_rank(BQNV a) { return rnk(getB(a)); } +void bqn_shape(BQNV a, size_t* buf) { B b = getB(a); + ur r = rnk(b); + usz* sh = a(b)->sh; + for (usz i = 0; i < r; i++) buf[i] = sh[i]; +} +BQNV bqn_pick(BQNV a, size_t pos) { + return makeX(IGet(getB(a),pos)); +} + +// TODO copy directly with some mut.h thing +void bqn_readI8Arr (BQNV a, i8* buf) { B c = toI8Any (inc(getB(a))); memcpy(buf, i8any_ptr (c), a(c)->ia * 1); dec(c); } +void bqn_readI16Arr(BQNV a, i16* buf) { B c = toI16Any(inc(getB(a))); memcpy(buf, i16any_ptr(c), a(c)->ia * 2); dec(c); } +void bqn_readI32Arr(BQNV a, i32* buf) { B c = toI32Any(inc(getB(a))); memcpy(buf, i32any_ptr(c), a(c)->ia * 4); dec(c); } +void bqn_readF64Arr(BQNV a, f64* buf) { B c = toF64Any(inc(getB(a))); memcpy(buf, f64any_ptr(c), a(c)->ia * 8); dec(c); } +void bqn_readC8Arr (BQNV a, u8* buf) { B c = toC8Any (inc(getB(a))); memcpy(buf, c8any_ptr (c), a(c)->ia * 1); dec(c); } +void bqn_readC16Arr(BQNV a, u16* buf) { B c = toC16Any(inc(getB(a))); memcpy(buf, c16any_ptr(c), a(c)->ia * 2); dec(c); } +void bqn_readC32Arr(BQNV a, u32* buf) { B c = toC32Any(inc(getB(a))); memcpy(buf, c32any_ptr(c), a(c)->ia * 4); dec(c); } +void bqn_readObjArr(BQNV a, BQNV* buf) { B b = getB(a); + usz ia = a(b)->ia; + B* p = arr_bptr(b); + if (p!=NULL) { + for (usz i = 0; i < ia; i++) buf[i] = makeX(inc(p[i])); + } else { + SGet(b) + for (usz i = 0; i < ia; i++) buf[i] = makeX(Get(b, i)); + } +} + + +BQNV bqn_makeF64(double d) { return makeX(m_f64(d)); } +BQNV bqn_makeChar(uint32_t c) { return makeX(m_c32(c)); } + + +static usz calcIA(size_t rank, size_t* shape) { + if (rank>UR_MAX) thrM("Rank too large"); + usz r = 1; + NOUNROLL for (size_t i = 0; i < rank; i++) if (mulOn(r, shape[i])) thrM("Size too large"); + return r; +} +static void copyBData(B* r, BQNV* data, usz ia) { + for (size_t i = 0; i < ia; i++) { + BQNV c = data[i]; + #if DIRECT_BQNV + r[i] = getB(c); + #else + r[i] = inc(getB(c)); + bqn_free(c); + #endif + } +} +#define CPYSH(R) usz* sh = arr_shAlloc(R, r0); \ + if (sh) NOUNROLL for (size_t i = 0; RARE(i < r0); i++) sh[i] = sh0[i]; + +BQNV bqn_makeI8Arr (size_t r0, size_t* sh0, i8* data) { usz ia=calcIA(r0,sh0); i8* rp; Arr* r = m_i8arrp (&rp,ia); CPYSH(r); memcpy(rp,data,ia*1); return makeX(taga(r)); } +BQNV bqn_makeI16Arr(size_t r0, size_t* sh0, i16* data) { usz ia=calcIA(r0,sh0); i16* rp; Arr* r = m_i16arrp(&rp,ia); CPYSH(r); memcpy(rp,data,ia*2); return makeX(taga(r)); } +BQNV bqn_makeI32Arr(size_t r0, size_t* sh0, i32* data) { usz ia=calcIA(r0,sh0); i32* rp; Arr* r = m_i32arrp(&rp,ia); CPYSH(r); memcpy(rp,data,ia*4); return makeX(taga(r)); } +BQNV bqn_makeF64Arr(size_t r0, size_t* sh0, f64* data) { usz ia=calcIA(r0,sh0); f64* rp; Arr* r = m_f64arrp(&rp,ia); CPYSH(r); memcpy(rp,data,ia*8); return makeX(taga(r)); } +BQNV bqn_makeC8Arr (size_t r0, size_t* sh0, u8* data) { usz ia=calcIA(r0,sh0); u8* rp; Arr* r = m_c8arrp (&rp,ia); CPYSH(r); memcpy(rp,data,ia*1); return makeX(taga(r)); } +BQNV bqn_makeC16Arr(size_t r0, size_t* sh0, u16* data) { usz ia=calcIA(r0,sh0); u16* rp; Arr* r = m_c16arrp(&rp,ia); CPYSH(r); memcpy(rp,data,ia*2); return makeX(taga(r)); } +BQNV bqn_makeC32Arr(size_t r0, size_t* sh0, u32* data) { usz ia=calcIA(r0,sh0); u32* rp; Arr* r = m_c32arrp(&rp,ia); CPYSH(r); memcpy(rp,data,ia*4); return makeX(taga(r)); } +BQNV bqn_makeObjArr(size_t r0, size_t* sh0, BQNV* data) { usz ia=calcIA(r0,sh0); HArr_p r = m_harrUp(ia); CPYSH((Arr*)r.c); copyBData(r.a,data,ia); return makeX(r.b); } + +BQNV bqn_makeI8Vec (size_t len, i8* data) { i8* rp; B r = m_i8arrv (&rp,len); memcpy(rp,data,len*1); return makeX(r); } +BQNV bqn_makeI16Vec(size_t len, i16* data) { i16* rp; B r = m_i16arrv(&rp,len); memcpy(rp,data,len*2); return makeX(r); } +BQNV bqn_makeI32Vec(size_t len, i32* data) { i32* rp; B r = m_i32arrv(&rp,len); memcpy(rp,data,len*4); return makeX(r); } +BQNV bqn_makeF64Vec(size_t len, f64* data) { f64* rp; B r = m_f64arrv(&rp,len); memcpy(rp,data,len*8); return makeX(r); } +BQNV bqn_makeC8Vec (size_t len, u8* data) { u8* rp; B r = m_c8arrv (&rp,len); memcpy(rp,data,len*1); return makeX(r); } +BQNV bqn_makeC16Vec(size_t len, u16* data) { u16* rp; B r = m_c16arrv(&rp,len); memcpy(rp,data,len*2); return makeX(r); } +BQNV bqn_makeC32Vec(size_t len, u32* data) { u32* rp; B r = m_c32arrv(&rp,len); memcpy(rp,data,len*4); return makeX(r); } +BQNV bqn_makeObjVec(size_t len, BQNV* data) { HArr_p r = m_harrUv(len); copyBData(r.a,data,len ); return makeX(r.b); } +BQNV bqn_makeUTF8Str(size_t len, char* str) { return makeX(fromUTF8(str, len)); } + +typedef struct BoundFn { + struct NFn; + void* w_c1; + void* w_c2; + #if FFI==2 + i32 mutCount; + #endif +} BoundFn; +NFnDesc* boundFnDesc; +NFnDesc* foreignFnDesc; + +B boundFn_c1(B t, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_boundFn1)c->w_c1)(makeX(inc(c->obj)), makeX(x))); } +B boundFn_c2(B t, B w, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_boundFn2)c->w_c2)(makeX(inc(c->obj)), makeX(w), makeX(x))); } + +typedef BQNV (*bqn_foreignFn1)(BQNV x); +typedef BQNV (*bqn_foreignFn2)(BQNV w, BQNV x); +B directFn_c1(B t, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_foreignFn1)c->w_c1)( makeX(x))); } +B directFn_c2(B t, B w, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_foreignFn2)c->w_c2)(makeX(w), makeX(x))); } + +static B m_ffiFn(NFnDesc* desc, B obj, BB2B c1, BBB2B c2, void* wc1, void* wc2) { + BoundFn* r = mm_alloc(sizeof(BoundFn), t_nfn); + nfn_lateInit((NFn*)r, desc); + r->obj = obj; + r->c1 = c1; + r->c2 = c2; + r->w_c1 = wc1; + r->w_c2 = wc2; + return tag(r, FUN_TAG); +} +BQNV bqn_makeBoundFn1(bqn_boundFn1 f, BQNV obj) { return makeX(m_ffiFn(boundFnDesc, inc(getB(obj)), boundFn_c1, c2_bad, f, NULL)); } +BQNV bqn_makeBoundFn2(bqn_boundFn2 f, BQNV obj) { return makeX(m_ffiFn(boundFnDesc, inc(getB(obj)), c1_bad, boundFn_c2, NULL, f)); } + +const static u8 typeMap[] = { + [el_bit] = elt_unk, + [el_B ] = elt_unk, + [el_i8 ] = elt_i8, [el_c8 ] = elt_c8, + [el_i16] = elt_i16, [el_c16] = elt_c16, + [el_i32] = elt_i32, [el_c32] = elt_c32, + [el_f64] = elt_f64, +}; +BQNElType bqn_directType(BQNV a) { + B b = getB(a); + if (!isArr(b)) return elt_unk; + return typeMap[TI(b,elType)]; +} +i8* bqn_directI8 (BQNV a) { return i8any_ptr (getB(a)); } +i16* bqn_directI16(BQNV a) { return i16any_ptr(getB(a)); } +i32* bqn_directI32(BQNV a) { return i32any_ptr(getB(a)); } +f64* bqn_directF64(BQNV a) { return f64any_ptr(getB(a)); } +u8* bqn_directC8 (BQNV a) { return c8any_ptr (getB(a)); } +u16* bqn_directC16(BQNV a) { return c16any_ptr(getB(a)); } +u32* bqn_directC32(BQNV a) { return c32any_ptr(getB(a)); } + +void ffiFn_visit(Value* v) { mm_visit(((BoundFn*)v)->obj); } +DEF_FREE(ffiFn) { dec(((BoundFn*)x)->obj); } + + + +typedef struct BQNFFIEnt { + B o; +#if FFI==2 + ffi_type t; +#endif + // generic ffi_parseType ffi_parseTypeStr cty_ptr cty_repr + union { u8 extra; u8 onW; u8 canRetype; u8 mutPtr; u8 reType; }; + union { u8 extra2; u8 mutates; /*mutates*/ u8 reWidth; }; + union { u8 extra3; u8 wholeArg; }; + union { u8 extra4; u8 resSingle; }; +} BQNFFIEnt; +typedef struct BQNFFIType { + struct Value; + u8 ty; + usz ia; + BQNFFIEnt a[]; +} BQNFFIType; + +B vfyStr(B x, char* name, char* arg); +static void printFFIType(FILE* f, B x) { + if (isC32(x)) fprintf(f, "%d", o2cu(x)); + else fprint(f, x); +} + +#if FFI==2 + +enum ScalarTy { + sty_void, sty_a, sty_ptr, + sty_u8, sty_u16, sty_u32, sty_u64, + sty_i8, sty_i16, sty_i32, sty_i64, + sty_f32, sty_f64 +}; +static const u8 sty_w[] = { + [sty_void]=0, [sty_a]=sizeof(BQNV), [sty_ptr]=sizeof(void*), + [sty_u8]=1, [sty_u16]=2, [sty_u32]=4, [sty_u64]=8, + [sty_i8]=1, [sty_i16]=2, [sty_i32]=4, [sty_i64]=8, + [sty_f32]=4, [sty_f64]=8 +}; +static const char* sty_names[] = { + [sty_void]="void", [sty_a]="a", [sty_ptr]="*", + [sty_u8]="u8", [sty_u16]="u16", [sty_u32]="u32", [sty_u64]="u64", + [sty_i8]="i8", [sty_i16]="i16", [sty_i32]="i32", [sty_i64]="i64", + [sty_f32]="f32", [sty_f64]="f64" +}; +enum CompoundTy { + cty_ptr, + cty_repr +}; + +static B m_bqnFFIType(BQNFFIEnt** rp, u8 ty, usz ia) { + BQNFFIType* r = mm_alloc(fsizeof(BQNFFIType, a, BQNFFIEnt, ia), t_ffiType); + r->ty = ty; + r->ia = ia; + memset(r->a, 0, ia*sizeof(BQNFFIEnt)); + *rp = r->a; + return tag(r, OBJ_TAG); +} + +static u32 readUInt(u32** p) { + u32* c = *p; + u32 r = 0; + while (*c>='0' & *c<='9') { + if (r >= U32_MAX/10 - 10) thrM("FFI: number literal too large"); + r = r*10 + *c-'0'; + c++; + } + *p = c; + return r; +} +BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr) { // parse actual type + u32* c = *src; + u32 c0 = *c++; + ffi_type rt; + B ro; + bool parseRepr=false, canRetype=false, mut=false; + u32 myWidth = 0; // used if parseRepr + switch (c0) { + default: + thrM("FFI: Error parsing type"); + + case 'i': case 'u': case 'f':; + u32 n = readUInt(&c); + if (c0=='f') { + if (n==32) rt = ffi_type_float; + else if (n==64) rt = ffi_type_double; + else thrM("FFI: Bad float width"); + ro = m_c32(n==32? sty_f32 : sty_f64); + } else { + u32 scty; + if (n== 8) { scty = c0=='i'? sty_i8 : sty_u8; rt = c0=='i'? ffi_type_sint8 : ffi_type_uint8; } + else if (n==16) { scty = c0=='i'? sty_i16 : sty_u16; rt = c0=='i'? ffi_type_sint16 : ffi_type_uint16; } + else if (n==32) { scty = c0=='i'? sty_i32 : sty_u32; rt = c0=='i'? ffi_type_sint32 : ffi_type_uint32; } + else if (n==64) { scty = c0=='i'? sty_i64 : sty_u64; rt = c0=='i'? ffi_type_sint64 : ffi_type_uint64; } + else thrM("FFI: Bad integer width"); + ro = m_c32(scty); + } + parseRepr = !inPtr; myWidth = sty_w[o2cu(ro)]; + canRetype = inPtr; + break; + + case 'a': + ro = m_c32(sty_a); + rt = ffi_type_uint64; + break; + + case '*': case '&':; + myWidth = sizeof(void*); + if (c0=='*' && (0==*c || ':'==*c)) { + ro = m_c32(sty_ptr); + parseRepr = !inPtr; + canRetype = inPtr; + } else { + if (c0=='&') mut = true; + BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_ptr, 1); + rp[0] = ffi_parseTypeStr(&c, true); + mut|= rp[0].mutates; + parseRepr = rp[0].canRetype; + + rp[0].mutPtr = c0=='&'; + } + rt = ffi_type_pointer; + break; + } + if (parseRepr && *c==':') { + c++; + u8 t = *c++; + u32 n = readUInt(&c); + if (t=='i' | t=='c') if (n!=8 & n!=16 & n!=32) { badW: thrF("Bad width in :%c%i", t, n); } + if (t=='u') if (n!=1 & n!=8 & n!=16 & n!=32) goto badW; + if (t=='f') if (n!=64) goto badW; + + if (isC32(ro) && n > myWidth*8) thrF("FFI: Representation wider than the value for \"%S:%c%i\"", sty_names[o2cu(ro)], t, n); + // TODO figure out what to do with i32:i32 etc + + B roP = ro; + BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_repr, 1); + rp[0] = (BQNFFIEnt){.o=roP, .t=rt, .reType=t, .reWidth=63-CLZ(n)}; + } + *src = c; + return (BQNFFIEnt){.t=rt, .o=ro, .canRetype=canRetype, .extra2=mut}; +} + +BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument side & other global decorators + vfyStr(arg, "FFI", "type"); + usz ia = a(arg)->ia; + if (ia==0) { + if (!forRes) thrM("FFI: Argument type empty"); + return (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=false}; + } + + MAKE_MUT(tmp, ia+1); mut_init(tmp, el_c32); MUTG_INIT(tmp); + mut_copyG(tmp, 0, arg, 0, ia); + mut_setG(tmp, ia, m_c32(0)); + u32* xp = tmp->ac32; + u32* xpN = xp + ia; + + BQNFFIEnt t; + if (xp[0]=='&' && xp[1]=='\0') { + t = (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=true}; + } else { + u8 side = 0; + bool whole = false; + while (true) { + if (*xp == U'𝕩') { if (side) thrM("FFI: Multiple occurrences of argument side specified"); side = 1; } + else if (*xp == U'𝕨') { if (side) thrM("FFI: Multiple occurrences of argument side specified"); side = 2; } + else if (*xp == U'>') { if (whole) thrM("FFI: Multiple occurrences of '>'"); whole = true; } + else break; + xp++; + } + if (forRes && side) thrM("FFI: Argument side cannot be specified for the result"); + if (side) side--; + else side = 0; + + t = ffi_parseTypeStr(&xp, false); + // print(arg); printf(": "); printFFIType(stdout, t.o); printf("\n"); + if (xp!=xpN) thrM("FFI: Bad type descriptor"); + t.onW = side; + // keep .mutates + t.wholeArg = whole; + t.resSingle = false; + } + mut_pfree(tmp, 0); + return t; +} + +TAlloc* ffiTmpObj; +u8* ffiTmpS; +u8* ffiTmpC; +u8* ffiTmpE; +static NOINLINE usz ffiTmpX(usz n) { + usz psz = ffiTmpC-ffiTmpS; + TAlloc* na = mm_alloc(sizeof(TAlloc)+psz+n, t_temp); + memcpy(na->data, ffiTmpS, psz); + + ffiTmpS = na->data; + ffiTmpC = ffiTmpS + psz; + ffiTmpE = (u8*)na + mm_size((Value*) na); + if (ffiTmpObj) mm_free((Value*)ffiTmpObj); + ffiTmpObj = na; + return psz; +} +static NOINLINE usz ffiTmpAA(usz n) { + u64 align = _Alignof(max_align_t); + n = (n+align-1) & ~(align-1); + if (ffiTmpC+n > ffiTmpE) return ffiTmpX(n); + usz r = ffiTmpC-ffiTmpS; + ffiTmpC+= n; + return r; +} + +static B ffiObjs; + +static B toW(u8 reT, u8 reW, B x) { + switch(reW) { default: UD; + case 0: return taga(toBitArr(x)); break; + case 3: return reT=='c'? toC8Any(x) : toI8Any(x); break; + case 4: return reT=='c'? toC16Any(x) : toI16Any(x); break; + case 5: return reT=='c'? toC32Any(x) : toI32Any(x); break; + case 6: return toF64Any(x); break; + } +} +static u8 reTyMapC[] = { [3]=t_c8arr, [4]=t_c16arr, [5]=t_c32arr }; +static u8 reTyMapI[] = { [3]=t_i8arr, [4]=t_i16arr, [5]=t_i32arr, [6]=t_f64arr }; +static B makeRe(u8 reT, u8 reW/*log*/, u8* src, u32 elW/*bytes*/) { + u8* dst; B r; + usz ia = (elW*8)>>reW; + if (reW) dst = m_tyarrv(&r, 1<= (1ULL<<53)) thrM("FFI: u64 argument value ≥ 2⋆53"); *(u64*)ptr = f; break; + case sty_i64: if(f!=(i64)f) thrM("FFI: i64 argument not exact"); if ((u64)((1ULL<<53)+(u64)f) >= (2ULL<<53)) thrM("FFI: i64 argument absolute value ≥ 2⋆53"); *(i64*)ptr = f; break; + case sty_f32: *(float* )ptr = f; break; + case sty_f64: *(double*)ptr = f; break; + } + } else { + BQNFFIType* t = c(BQNFFIType, ent.o); + if (t->ty==cty_ptr) { // *any / &any + pos = ffiTmpAA(sizeof(void*)); + B e = t->a[0].o; + if (!isC32(e)) thrM("FFI: Complex pointer elements NYI"); + inc(c); + B cG; + if (!isArr(c)) thrF("FFI: Expected array corresponding to \"*%S\"", sty_names[o2cu(e)]); + usz ia = a(c)->ia; + bool mut = t->a[0].mutPtr; + switch(o2cu(e)) { default: thrF("FFI: \"*%S\" argument type NYI", sty_names[o2cu(e)]); + case sty_i8: cG = mut? taga(cpyI8Arr (c)) : toI8Any (c); break; + case sty_i16: cG = mut? taga(cpyI16Arr(c)) : toI16Any(c); break; + case sty_i32: cG = mut? taga(cpyI32Arr(c)) : toI32Any(c); break; + case sty_f64: cG = mut? taga(cpyF64Arr(c)) : toF64Any(c); break; + case sty_u8: { B t=toI16Any(c); i16* tp=i16any_ptr(t); i8* gp; cG= m_i8arrv(&gp, ia); u8* np=(u8* )gp; for (usz i=0; ity==cty_repr) { // any:any + B o2 = t->a[0].o; + u8 reT = t->a[0].reType; + u8 reW = t->a[0].reWidth; + if (isC32(o2)) { // scalar:any + pos = ffiTmpAA(8); + u8 et = o2cu(o2); + u8 etw = sty_w[et]*8; + if (!isArr(c)) thrF("FFI: Expected array corresponding to \"%S:%c%i\"", sty_names[et], reT, 1<ia != etw>>reW) thrM("FFI: Bad input array length"); + B cG = toW(reT, reW, inc(c)); + memcpy(ffiTmpS+pos, tyany_ptr(cG), 8); // may over-read, ¯\_(ツ)_/¯ + dec(cG); + } else { // *scalar:any / &scalar:any + pos = ffiTmpAA(sizeof(void*)); + if (!isArr(c)) thrM("FFI: Expected array corresponding to a pointer"); + BQNFFIType* t2 = c(BQNFFIType, o2); + B ore = t2->a[0].o; + assert(t2->ty==cty_ptr && isC32(ore)); // we shouldn't be generating anything else + inc(c); + B cG; + bool mut = t->a[0].mutPtr; + if (mut) { + Arr* cGp; + switch(reW) { default: UD; + case 0: cGp = (Arr*) cpyBitArr(c); break; + case 3: cGp = reT=='c'? (Arr*) cpyC8Arr(c) : (Arr*) cpyI8Arr(c); break; + case 4: cGp = reT=='c'? (Arr*)cpyC16Arr(c) : (Arr*)cpyI16Arr(c); break; + case 5: cGp = reT=='c'? (Arr*)cpyC32Arr(c) : (Arr*)cpyI32Arr(c); break; + case 6: cGp = (Arr*) cpyF64Arr(c); break; + } + cG = taga(cGp); + } else cG = toW(reT, reW, c); + *(void**)(ffiTmpS+pos) = tyany_ptr(cG); + ffiObjs = vec_addN(ffiObjs, cG); + } + } else thrM("FFI: Unimplemented type"); + } + return pos; +} + +B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) { + if (isC32(ent.o)) return m_f64(0); // scalar + BQNFFIType* t = c(BQNFFIType, ent.o); + if (t->ty==cty_ptr) { // *any / &any + B e = t->a[0].o; + B f = objs[(*objPos)++]; + bool mut = t->a[0].mutPtr; + if (mut) { + usz ia = a(f)->ia; + switch(o2cu(e)) { default: UD; + case sty_i8: case sty_i16: case sty_i32: case sty_f64: return inc(f); + case sty_u8: { u8* tp=tyarr_ptr(f); i16* rp; B r=m_i16arrv(&rp, ia); for (usz i=0; ity==cty_repr) { // any:any + B o2 = t->a[0].o; + if (isC32(o2)) return m_f64(0); // scalar:any + // *scalar:any / &scalar:any + B f = objs[(*objPos)++]; + bool mut = t->a[0].mutPtr; + if (!mut) return m_f64(0); + return inc(f); + } else thrM("FFI: Unimplemented type"); +} + +B libffiFn_c2(B t, B w, B x) { + BoundFn* c = c(BoundFn,t); + B argObj = c(HArr,c->obj)->a[0]; + B cifObj = c(HArr,c->obj)->a[1]; + ffi_cif* cif = (void*) c(TAlloc,cifObj)->data; + void* sym = c->w_c2; + + usz argn = cif->nargs; + ffiTmpS = ffiTmpC = ffiTmpE = NULL; + ffiTmpObj = NULL; + ffiObjs = emptyHVec(); + ffiTmpX(128); + ffiTmpAA(argn * sizeof(u32)); + #define argOffs ((u32*)ffiTmpS) + + u32 flags = (u64)c->w_c1; + + Arr* wa; AS2B wf; + Arr* xa; AS2B xf; + if (flags&1) { wa=a(w); wf=TIv(wa,getU); } + if (flags&2) { xa=a(x); xf=TIv(xa,getU); } + i32 idxs[2] = {0,0}; + + BQNFFIEnt* ents = c(BQNFFIType,argObj)->a; + for (usz i = 0; i < argn; i++) { + BQNFFIEnt e = ents[i+1]; + B o = e.wholeArg? (e.onW? w : x) : (e.onW? wf : xf)(e.onW?wa:xa, idxs[e.onW]++); + argOffs[i] = genObj(e, o, e.extra2); + } + + u32 resPos; + bool simpleRes = isC32(ents[0].o); + u32 resCType; + if (simpleRes) { + resCType = o2cu(ents[0].o); + } else { + BQNFFIType* t = c(BQNFFIType, ents[0].o); + if (t->ty == cty_repr) { + B o2 = t->a[0].o; + if (!isC32(o2)) thrM("FFI: Unimplemented result type"); + resCType = o2cu(o2); + } else thrM("FFI: Unimplemented result type"); + } + resPos = ffiTmpAA(resCType==sty_a? sizeof(BQNV) : sizeof(ffi_arg)>8? sizeof(ffi_arg) : 8); + + void** argPtrs = (void**) (ffiTmpS + ffiTmpAA(argn*sizeof(void*))); // must be the very last alloc to be able to store pointers + for (usz i = 0; i < argn; i++) argPtrs[i] = ffiTmpS + argOffs[i]; + void* res = ffiTmpS + resPos; + + // for (usz i = 0; i < ffiTmpC-ffiTmpS; i++) { // simple hexdump of ffiTmp + // if (!(i&15)) printf("%s%p ", i?"\n":"", (void*)(ffiTmpS+i)); + // else if (!(i&3)) printf(" "); + // printf("%x%x ", ffiTmpS[i]>>4, ffiTmpS[i]&15); + // } + // printf("\n"); + + ffi_call(cif, sym, res, argPtrs); + + B r; + bool resVoid = false; + if (simpleRes) { + switch(resCType) { default: UD; // thrM("FFI: Unimplemented type"); + case sty_void: r = m_c32(0); resVoid = true; break; + case sty_ptr: thrM("FFI: \"*\" unimplemented"); break; + case sty_a: r = getB(*(BQNV*)res); break; + case sty_i8: r = m_i32(*( i8*)res); break; case sty_u8: r = m_i32(*( u8*)res); break; + case sty_i16: r = m_i32(*(i16*)res); break; case sty_u16: r = m_i32(*(u16*)res); break; + case sty_i32: r = m_i32(*(i32*)res); break; case sty_u32: r = m_f64(*(u32*)res); break; + case sty_i64: { i64 v = *(i64*)res; if (i64abs(v)>=(1ULL<<53)) thrM("FFI: i64 result absolute value ≥ 2⋆53"); r = m_f64(v); break; } + case sty_u64: { u64 v = *(u64*)res; if ( v >=(1ULL<<53)) thrM("FFI: u64 result ≥ 2⋆53"); r = m_f64(v); break; } + case sty_f32: r = m_f64(*(float* )res); break; + case sty_f64: r = m_f64(*(double*)res); break; + } + } else { // cty_repr, scalar:x + BQNFFIType* t = c(BQNFFIType, ents[0].o); + u8 et = o2cu(t->a[0].o); + u8 reT = t->a[0].reType; + u8 reW = t->a[0].reWidth; + u8 etw = sty_w[et]; + r = makeRe(reT, reW, res, etw); + } + mm_free((Value*)ffiTmpObj); + + i32 mutArgs = c->mutCount; + if (mutArgs) { + usz objPos = 0; + bool resSingle = flags&4; + if (resSingle) { + for (usz i = 0; i < argn; i++) { + BQNFFIEnt e = ents[i+1]; + B c = buildObj(e, e.mutates, harr_ptr(ffiObjs), &objPos); + if (e.mutates) r = c; + } + } else { + M_HARR(ra, mutArgs+(resVoid? 0 : 1)); + if (!resVoid) HARR_ADDA(ra, r); + for (usz i = 0; i < argn; i++) { + BQNFFIEnt e = ents[i+1]; + B c = buildObj(e, e.mutates, harr_ptr(ffiObjs), &objPos); + if (e.mutates) HARR_ADDA(ra, c); + } + r = HARR_FV(ra); + } + } + + dec(w); dec(x); dec(ffiObjs); + return r; +} +B libffiFn_c1(B t, B x) { return libffiFn_c2(t, bi_N, x); } + +#else + +BQNFFIEnt ffi_parseType(B arg, bool forRes) { + if (!isArr(arg) || a(arg)->ia!=1 || IGetU(arg,0).u!=m_c32('a').u) thrM("FFI: Only \"a\" arguments & return value supported with compile flag FFI=1"); + return (BQNFFIEnt){}; +} +#endif + + + + +B ffiload_c2(B t, B w, B x) { + usz xia = a(x)->ia; + if (xia<2) thrM("FFI: Function specification must have at least two items"); + usz argn = xia-2; + SGetU(x) + B name = GetU(x,1); + vfyStr(name, "FFI", "type"); + + BQNFFIEnt tRes = ffi_parseType(GetU(x,0), true); + + #if FFI==2 + BQNFFIEnt* args; B argObj = m_bqnFFIType(&args, 255, argn+1); + args[0] = tRes; + bool one [2]={0,0}; + bool many[2]={0,0}; + i32 mutCount = 0; + for (usz i = 0; i < argn; i++) { + BQNFFIEnt e = args[i+1] = ffi_parseType(GetU(x,i+2), false); + (e.wholeArg? one : many)[e.extra] = true; + if (e.mutates) mutCount++; + } + if (one[0] && many[0]) thrM("FFI: Multiple arguments for 𝕩 specified, but one has '>'"); + if (one[1] && many[1]) thrM("FFI: Multiple arguments for 𝕨 specified, but one has '>'"); + #else + for (usz i = 0; i < argn; i++) ffi_parseType(GetU(x,i+2), false); + (void)tRes; + #endif + if (tRes.resSingle && mutCount!=1) thrF("FFI: Return was \"&\", but found %i mutated variables", mutCount); + + w = path_rel(nfn_objU(t), w); + char* ws = toCStr(w); + void* dl = dlopen(ws, RTLD_NOW); + + freeCStr(ws); + dec(w); + if (dl==NULL) thrF("Failed to load: %S", dlerror()); + + char* nameStr = toCStr(name); + void* sym = dlsym(dl, nameStr); + freeCStr(nameStr); + dec(x); + + if (sym==NULL) thrF("Failed to find symbol: %S", dlerror()); + #if FFI==1 + return m_ffiFn(foreignFnDesc, bi_N, directFn_c1, directFn_c2, sym, sym); + #else + u64 sz = argn*sizeof(ffi_type*); + if (sz<16) sz = 16; + TAlloc* argsRaw = ARBOBJ(sz); + ffi_type** argsRawArr = (ffi_type**)argsRaw->data; + for (usz i = 0; i < argn; i++) argsRawArr[i] = &args[i+1].t; + // for (usz i = 0; i < argn; i++) { + // ffi_type c = *argsRawArr[i]; + // printf("%zu %d %d %p\n", c.size, c.alignment, c.type, c.elements); + // } + TAlloc* cif = ARBOBJ(sizeof(ffi_cif)); + ffi_status s = ffi_prep_cif((ffi_cif*)cif->data, FFI_DEFAULT_ABI, argn, &args[0].t, argsRawArr); + if (s!=FFI_OK) thrM("FFI: Error preparing call interface"); + // mm_free(argsRaw) + u32 flags = many[1] | many[0]<<1 | tRes.resSingle<<2; + B r = m_ffiFn(foreignFnDesc, m_hVec3(argObj, tag(cif, OBJ_TAG), tag(argsRaw, OBJ_TAG)), libffiFn_c1, libffiFn_c2, (void*)(u64)flags, sym); + c(BoundFn,r)->mutCount = mutCount; + return r; + #endif +} + +DEF_FREE(ffiType) { + usz ia = ((BQNFFIType*)x)->ia; + for (usz i = 0; i < ia; i++) dec(((BQNFFIType*)x)->a[i].o); +} +void ffiType_visit(Value* x) { + usz ia = ((BQNFFIType*)x)->ia; + for (usz i = 0; i < ia; i++) mm_visit(((BQNFFIType*)x)->a[i].o); +} +void ffiType_print(FILE* f, B x) { + BQNFFIType* t = c(BQNFFIType,x); + fprintf(f, "cty_%d⟨", t->ty); + for (usz i=0, ia=t->ia; ia[i].o); + } + fprintf(f, "⟩"); +} + +void ffi_init() { + boundFnDesc = registerNFn(m_str8l("(foreign function)"), boundFn_c1, boundFn_c2); + foreignFnDesc = registerNFn(m_str8l("(foreign function)"), directFn_c1, directFn_c2); + TIi(t_ffiType,freeO) = ffiType_freeO; + TIi(t_ffiType,freeF) = ffiType_freeF; + TIi(t_ffiType,visit) = ffiType_visit; + TIi(t_ffiType,print) = ffiType_print; +} + +#else +void ffi_init() { } +B ffiload_c2(B t, B w, B x) { thrM("CBQN was compiled without FFI"); } +#endif diff --git a/src/h.h b/src/h.h index 425a6827..dddf2069 100644 --- a/src/h.h +++ b/src/h.h @@ -39,6 +39,9 @@ #ifndef RANDSEED #define RANDSEED 0 // random seed used to make •rand (0 for using time) #endif +#ifndef FFI + #define FFI 2 +#endif // #define HEAP_VERIFY // enable usage of heapVerify() // #define ALLOC_STAT // store basic allocation statistics @@ -135,6 +138,13 @@ typedef double f64; #define N64x "%"SCNx64 #define N64d "%"SCNd64 #define N64u "%"SCNu64 +#if __clang__ + #define NOUNROLL _Pragma("clang loop unroll(disable)") _Pragma("clang loop vectorize(disable)") +#elif __GNUC__ + #define NOUNROLL _Pragma("GCC unroll 1") +#else + #define NOUNROLL +#endif #define JOIN0(A,B) A##B #define JOIN(A,B) JOIN0(A,B) @@ -212,11 +222,11 @@ typedef union B { /*22*/ F(harr ) F(fillarr ) F(i8arr ) F(i16arr ) F(i32arr ) F(c8arr ) F(c16arr ) F(c32arr ) F(f64arr ) \ /*31*/ F(bitarr) \ \ - /*32*/ F(comp) F(block) F(body) F(scope) F(scopeExt) F(blBlocks) \ - /*38*/ F(ns) F(nsDesc) F(fldAlias) F(vfyObj) F(hashmap) F(temp) F(nfn) F(nfnDesc) \ - /*46*/ F(freed) F(harrPartial) F(customObj) F(mmapH) \ + /*32*/ F(comp) F(block) F(body) F(scope) F(scopeExt) F(blBlocks) F(arbObj) F(ffiType) \ + /*40*/ F(ns) F(nsDesc) F(fldAlias) F(vfyObj) F(hashmap) F(temp) F(nfn) F(nfnDesc) \ + /*48*/ F(freed) F(harrPartial) F(customObj) F(mmapH) \ \ - /*49*/ IF_WRAP(F(funWrap) F(md1Wrap) F(md2Wrap)) + /*51*/ IF_WRAP(F(funWrap) F(md1Wrap) F(md2Wrap)) enum Type { #define F(X) t_##X, diff --git a/src/load.c b/src/load.c index 3acbb5f3..a08cd117 100644 --- a/src/load.c +++ b/src/load.c @@ -5,7 +5,7 @@ #include "ns.h" #include "builtins.h" -#define FOR_INIT(F) F(base) F(harr) F(mutF) F(fillarr) F(tyarr) F(hash) F(sfns) F(fns) F(arith) F(md1) F(md2) F(derv) F(comp) F(rtWrap) F(ns) F(nfn) F(sysfn) F(inverse) F(load) F(sysfnPost) F(dervPost) F(mmap) +#define FOR_INIT(F) F(base) F(harr) F(mutF) F(fillarr) F(tyarr) F(hash) F(sfns) F(fns) F(arith) F(md1) F(md2) F(derv) F(comp) F(rtWrap) F(ns) F(nfn) F(sysfn) F(inverse) F(load) F(sysfnPost) F(dervPost) F(ffi) F(mmap) #define F(X) void X##_init(void); FOR_INIT(F) #undef F @@ -652,6 +652,7 @@ void base_init() { // very first init function TIi(t_customObj,freeO) = customObj_freeO; TIi(t_customObj,freeF) = customObj_freeF; TIi(t_customObj,visit) = customObj_visit; + TIi(t_arbObj,visit) = noop_visit; assert((MD1_TAG>>1) == (MD2_TAG>>1)); // just to be sure it isn't changed incorrectly, `isMd` depends on this diff --git a/src/opt/mm_buddyTemplate.c b/src/opt/mm_buddyTemplate.c index 79f187a9..9d0bc247 100644 --- a/src/opt/mm_buddyTemplate.c +++ b/src/opt/mm_buddyTemplate.c @@ -19,10 +19,7 @@ u64 alSize; FORCE_INLINE void BN(splitTo)(EmptyValue* c, i64 from, i64 to, bool notEqual) { c->mmInfo = MMI(to); - #ifdef __clang__ - #pragma clang loop unroll(disable) // at least n repetitions happen with probability 2^-n, so unrolling is kind of stupid - #endif - while (from != to) { + NOUNROLL while (from != to) { from--; EmptyValue* b = (EmptyValue*) (BSZ(from) + (u8*)c); b->type = t_empty; diff --git a/src/opt/single.c b/src/opt/single.c index 96f01999..c1f81e04 100644 --- a/src/opt/single.c +++ b/src/opt/single.c @@ -29,5 +29,6 @@ #include "../ns.c" #include "../nfns.c" #include "../rtwrap.c" +#include "../ffi.c" #include "../jit/nvm.c" #include "../main.c" diff --git a/src/utils/talloc.h b/src/utils/talloc.h index e01d99bf..86a0c9b1 100644 --- a/src/utils/talloc.h +++ b/src/utils/talloc.h @@ -29,7 +29,10 @@ typedef struct TStack { #define TSFREE(N) mm_free((Value*)N##_o); #define TSUPD(N,AM) { N##_o = ts_e(N##_o, N##_e, AM); N = (void*)N##_o->data; } #define TSADD(N,X) { if (N##_o->size==N##_o->cap) TSUPD(N, 1); N[N##_o->size++] = X; } -#define TSADDA(N,P,AM) { u64 n=AM; if(N##_o->size+n>N##_o->cap) TSUPD(N, n); memcpy(N+N##_o->size,P,n*N##_e); N##_o->size+= n; } +#define TSADDA(N,P,AM) { u64 n_=(AM); if(N##_o->size+n_>N##_o->cap) TSUPD(N, n_); memcpy(N+N##_o->size,P,n_*N##_e); N##_o->size+= n_; } +#define TSADDAU(N,AM) { u64 n_=(AM); if(N##_o->size+n_>N##_o->cap) TSUPD(N, n_); N##_o->size+= n_; } #define TSFREEP(N) mm_free((void*)RFLD(N, TStack, data)); #define TSSIZE(N) (N##_o->size) TStack* ts_e(TStack* o, u32 elsz, u64 am); + +#define ARBOBJ(SZ) (TAlloc*)mm_alloc(sizeof(TAlloc)+(SZ), t_arbObj) \ No newline at end of file diff --git a/test/README.md b/test/README.md index 8ee599c1..9f6ef6ff 100644 --- a/test/README.md +++ b/test/README.md @@ -12,4 +12,5 @@ test/moreCfgs.sh path/to/mlochbaum/BQN // run "2+2" in a bunch of configurations ./BQN test/bitcpy.bqn // fuzz-test bit_cpy; requires a CBQN build with -DTEST_BITCPY ./BQN test/squeeze.bqn // fuzz-test squeezing; requires a CBQN build with -DEEQUAL_NEGZERO ./BQN test/random.bqn // various random tests +make -C test/ffi // test FFI functionality ``` \ No newline at end of file diff --git a/test/ffi/ffiTest.c b/test/ffi/ffiTest.c new file mode 100644 index 00000000..244866ba --- /dev/null +++ b/test/ffi/ffiTest.c @@ -0,0 +1,85 @@ +#include +#include +#include "../../include/bqnffi.h" + +void do_nothing() { } + +BQNV timesTen(BQNV v) { + size_t len = bqn_bound(v); + int32_t* buf = malloc(len*4); + bqn_readI32Arr(v, buf); + for (int i = 0; i < len; i++) buf[i] = buf[i] * 10; + BQNV res = bqn_makeI32Vec(len, buf); + free(buf); + bqn_free(v); + return res; +} + +BQNV add_args(BQNV t, BQNV x) { + return bqn_makeF64(bqn_toF64(t) + bqn_toF64(x)); +} +BQNV bind_add(BQNV x) { + BQNV r = bqn_makeBoundFn1(add_args, x); + bqn_free(x); + return r; +} + + + +void printArgs(int8_t i8, int16_t i16, int32_t i32, uint8_t u8, uint16_t u16, uint32_t u32, float f, double d) { + printf("args: %d %d %d %u %u %u %.18f %.18f\n", i8, i16, i32, u8, u16, u32, f, d); +} +void noopArgs(int8_t i8, int16_t i16, int32_t i32, uint8_t u8, uint16_t u16, uint32_t u32, float f, double d) { } + +void printPtrArgs(int8_t* i8, int16_t* i16, int32_t* i32, uint8_t* u8, uint16_t* u16, uint32_t* u32, float* f, double* d) { + printf("args: %d %d %d %u %u %u %.18f %.18f\n", *i8, *i16, *i32, *u8, *u16, *u32, *f, *d); +} + +float printU64s(uint64_t a, uint64_t* b) { + printf("%llx %llx %llx %llx\n", (long long)a, (long long)b[0], (long long)b[1], (long long)b[2]); + return 12345678; +} + + + +int32_t multiplyI32Ptrs(int32_t* a, int32_t* b, int32_t len) { + int32_t r = 0; + for (int i = 0; i < len; i++) { + r+= a[i]*b[i]; + } + return r; +} +float sumF32Arr(float* f, int len) { + float r = 0; + for (int i = 0; i < len; i++) r+= f[i]; + return r; +} + + +void incI32s(int32_t* a, int32_t len) { + for (int i = 0; i < len; i++) a[i]++; +} + +void incInts(int32_t* a, int16_t* b, int8_t* c) { + printf("%d %d %d\n", *a, *b, *c); + (*a)++; + (*b)++; + (*c)++; +} + +uint64_t ident_u64(uint64_t x) { return x; } +double ident_f64(double a) { return a; } + +void* pick_ptr(void** arr, int idx) { + return arr[idx]; +} +uint64_t pick_u64(uint64_t* arr, int idx) { + return arr[idx]; +} + + + + +int plusone(int x) { + return x + 1; +} diff --git a/test/ffi/makefile b/test/ffi/makefile new file mode 100644 index 00000000..37676f95 --- /dev/null +++ b/test/ffi/makefile @@ -0,0 +1,7 @@ +test: build + @../../BQN test.bqn > test.got + @diff --color -su test.expected test.got + +build: + $(CC) -O3 -g -c -fpic ffiTest.c -o ffiTest.o + $(CC) -shared -olib.so ffiTest.o \ No newline at end of file diff --git a/test/ffi/test.bqn b/test/ffi/test.bqn new file mode 100644 index 00000000..9d82309d --- /dev/null +++ b/test/ffi/test.bqn @@ -0,0 +1,71 @@ +f←@ +Section ← {•Out 𝕩∾˜@+10} + +f ↩ "lib.so" •FFI ""‿"do_nothing" ⋄ •Show F ⟨⟩ + +Section "# ""a""" +f ↩ "lib.so" •FFI "a"‿"timesTen"‿"a" ⋄ •Show F ⟨↕10⟩ +bind ← "lib.so" •FFI "a"‿"bind_add"‿">a" ⋄ g ← Bind 4 ⋄ •Show G 123 + +Section "# print args" +f ↩ "lib.so" •FFI ""‿"printArgs"‿"i8"‿"i16"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64" ⋄ •Show F ¯123‿¯12323‿¯212312312‿250‿50000‿3123456789‿π∾⋆1 +f ↩ "lib.so" •FFI ""‿ "noopArgs"‿"i8"‿"i16"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64" ⋄ •Show F ¯123‿¯12323‿¯212312312‿250‿50000‿3123456789‿π∾⋆1 +f ↩ "lib.so" •FFI ""‿"printPtrArgs"‿"*i8"‿"*i16"‿"*i32"‿"*u8"‿"*u16"‿"*u32"‿"*f32"‿"*f64" ⋄ •Show F ⥊¨¯123‿¯12323‿¯212312312‿250‿50000‿3123456789‿π∾⋆1 +f ↩ "lib.so" •FFI ""‿"printPtrArgs"‿"&i8"‿"&i16"‿"&i32"‿"&u8"‿"&u16"‿"&u32"‿"&f32"‿"&f64" ⋄ •Show F ⥊¨¯123‿¯12323‿¯212312312‿250‿50000‿3123456789‿π∾⋆1 + +f ↩ "lib.so" •FFI "f32"‿"printU64s"‿"u64:c8"‿"*u64:c8" ⋄ •Show F ⟨"hellowor", "aaaaaaaa12345678texttext"⟩ +f ↩ "lib.so" •FFI "f32"‿"printU64s"‿"u64:u1"‿"*u64:u1" ⋄ •Show F ⟨64↑∾×∘↕¨↕12, 192↑∾×∘↕¨↕21⟩ + +! 3 ≡ •Type "lib.so" •FFI ""‿"printArgs"‿"i8"‿"i16:c8"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64" +! 3 ≡ •Type "lib.so" •FFI ""‿"printArgs"‿"i8"‿"i16:c16"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64" +! 3 ≡ •Type "lib.so" •FFI ""‿"printArgs"‿"i8:c8"‿"i16"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64" + +Section "# read pointers" +f ↩ "lib.so" •FFI "i32"‿"multiplyI32Ptrs"‿"*i32"‿"*i32"‿"i32" ⋄ •Show F ⟨↕10 ⋄ •Show ⌽↕10 ⋄ •Show 10⟩ +f ↩ "lib.so" •FFI "f32"‿"sumF32Arr"‿"*f32:i32"‿"i32" ⋄ •Show F ⟨1065353216‿1073741824‿1077936128‿1082130432‿1084227584‿1086324736‿1088421888‿1090519040‿1091567616‿1092616192,10⟩ + +Section "# mutate i32*" +f ↩ "lib.so" •FFI ⟨"","incI32s","&u32", "i32"⟩ ⋄ •Show F ⟨1e8×20+↕10 ⋄ 10⟩ +f ↩ "lib.so" •FFI ⟨"","incI32s","&i32:u1", "i32"⟩ ⋄ •Show ⊑F ⟨1‿0‿1‿0‿1‿0‿1‿0‿0‿1‿0‿1‿0‿0‿1‿1‿0‿1‿1‿0‿0‿0‿0‿1‿0‿1‿0‿1‿0‿0‿1‿0‿0‿0‿1‿0‿0‿0‿1‿0‿0‿0‿1‿1‿1‿0‿1‿1‿1‿1‿1‿1‿0‿0‿1‿1‿0‿0‿1‿0‿0‿1‿0‿0, 2⟩ +f ↩ "lib.so" •FFI ⟨"","incI32s","&i32:c8", "i32"⟩ ⋄ •Show ⊑F ⟨"helloworld", 2⟩ +f ↩ "lib.so" •FFI ⟨"","incI32s",">𝕨&i32:c8",">i32"⟩ ⋄ •Show ⊑ "helloworld" F 2 + +f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8", "i32"⟩ ⋄ •Show ⊑F ⟨"helloworld", 2⟩ +f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8", "𝕨i32"⟩ ⋄ •Show ⊑ ⟨2⟩ F ⟨"helloworld"⟩ +f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8",">𝕨i32"⟩ ⋄ •Show ⊑ 2 F ⟨"helloworld"⟩ +f ↩ "lib.so" •FFI ⟨"", "incI32s", ">&i32:c8",">𝕨i32"⟩ ⋄ •Show ⊑ 2 F "helloworld" +f ↩ "lib.so" •FFI ⟨"", "incI32s",">𝕨&i32:c8", ">i32"⟩ ⋄ •Show ⊑ "helloworld" F 2 +f ↩ "lib.so" •FFI ⟨"", "incI32s", "&i32:c8", "i32"⟩ ⋄ •Show F ⟨"helloworld", 2⟩ +f ↩ "lib.so" •FFI ⟨"&","incI32s", "&i32:c8", "i32"⟩ ⋄ •Show F ⟨"helloworld", 2⟩ + +Section "# mutate i32*, i16*, i8*" +f ↩ "lib.so" •FFI ⟨"","incInts","&i32", "&i16","&i8"⟩ ⋄ •Show F ⥊¨ 10‿20‿30 +f ↩ "lib.so" •FFI ⟨"","incInts","&i32", "𝕨&i16","&i8"⟩ ⋄ •Show ⟨⥊20⟩ F ⥊¨ 10‿30 +f ↩ "lib.so" •FFI ⟨"","incInts","&i32",">𝕨&i16","&i8"⟩ ⋄ •Show ⟨20⟩ F ⥊¨ 10‿30 + +Section "# u64 tests" +f ↩ "lib.so" •FFI ⟨"u64", "ident_u64",">u64:i32"⟩ ⋄ •Show F 1234‿12 +f ↩ "lib.so" •FFI ⟨"u64", "ident_u64",">u64" ⟩ ⋄ •Show F +´2⋆52‿20 +f ↩ "lib.so" •FFI ⟨"u64:i32","ident_u64",">u64" ⟩ ⋄ •Show F 123456789123456 +f ↩ "lib.so" •FFI ⟨"u64:u1", "ident_u64",">u64:c8" ⟩ ⋄ •Show F "hellowor" + +Section "# malloc test" +f ↩ "lib.so" •FFI "*:i32"‿"malloc"‿">u64" ⋄ •Show (•internal.Info⋈≠) malloc ← F 123 +f ↩ "lib.so" •FFI ""‿"free"‿">*:i32" ⋄ F malloc + +Section "# pick item" +f ↩ "lib.so" •FFI "*:i8"‿"pick_ptr"‿">**:i8"‿">𝕨i32" ⋄ •Show @+0 F "helloworfoobarba"-@ +f ↩ "lib.so" •FFI "*:c8"‿"pick_ptr"‿">**:c8"‿">𝕨i32" ⋄ •Show 0 F "helloworfoobarba" +f ↩ "lib.so" •FFI ⟨"u64:i8","pick_u64",">*u64:i8",">𝕨i32"⟩ ⋄ •Show @+2 F "000000001234560011122100abacabad"-@ +f ↩ "lib.so" •FFI ⟨"u64:i8","pick_u64",">*u64:i8",">𝕨i32"⟩ ⋄ •Show @+3 F "000000001234560011122100abacabad"-@ +f ↩ "lib.so" •FFI ⟨"u64", "pick_u64",">*u64:i8",">𝕨i32"⟩ ⋄ •Show 1 F "000000001234560011122100"-'0' + + +# erroring: + +# "local/lib.so" •FFI ""‿"printArgs"‿"i8"‿"i16:c32"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64" +# "local/lib.so" •FFI ""‿"testArgs"‿"i8:c16"‿"i16"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64" +# "local/lib.so" •FFI ""‿"testArgs"‿"i8:c32"‿"i16"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64" + +# f ↩ "local/lib.so" •FFI "u64"‿"ident_u64"‿">u64:i32" ⋄ •Show F 1234‿12344444 +# f ↩ "local/lib.so" •FFI "u64"‿"ident_u64"‿">u64" ⋄ •Show F +´2⋆53‿20 \ No newline at end of file diff --git a/test/ffi/test.expected b/test/ffi/test.expected new file mode 100644 index 00000000..8455dc3c --- /dev/null +++ b/test/ffi/test.expected @@ -0,0 +1,61 @@ +@ + +# "a" +⟨ 0 10 20 30 40 50 60 70 80 90 ⟩ +127 + +# print args +args: -123 -12323 -212312312 250 50000 3123456789 3.141592741012573242 2.718281828459045091 +@ +@ +args: -123 -12323 -212312312 250 50000 3123456789 3.141592741012573242 2.718281828459045091 +@ +args: -123 -12323 -212312312 250 50000 3123456789 3.141592741012573242 2.718281828459045091 +⟨ ⟨ ¯123 ⟩ ⟨ ¯12323 ⟩ ⟨ ¯212312312 ⟩ ⟨ 250 ⟩ ⟨ 50000 ⟩ ⟨ 3123456789 ⟩ ⟨ 3.141592741012573 ⟩ ⟨ 2.718281828459045 ⟩ ⟩ +726f776f6c6c6568 6161616161616161 3837363534333231 7478657474786574 +12345678 +ff7fdfefefdf7bb4 ff7fdfefefdf7bb4 fefffdfff7ffbffb bffff7fffdfffeff +12345678 + +# read pointers +⟨ 9 8 7 6 5 4 3 2 1 0 ⟩ +10 +120 +55 + +# mutate i32* +⟨ ⟨ 2000000001 2100000001 2200000001 2300000001 2400000001 2500000001 2600000001 2700000001 2800000001 2900000001 ⟩ ⟩ +⟨ 0 1 1 0 1 0 1 0 0 1 0 1 0 0 1 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 1 0 1 0 1 0 0 0 1 0 0 0 1 1 1 0 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0 1 0 0 ⟩ +"iellpworld" +"iellpworld" +"iellpworld" +"iellpworld" +"iellpworld" +"iellpworld" +"iellpworld" +⟨ "iellpworld" ⟩ +"iellpworld" + +# mutate i32*, i16*, i8* +10 20 30 +⟨ ⟨ 11 ⟩ ⟨ 21 ⟩ ⟨ 31 ⟩ ⟩ +10 20 30 +⟨ ⟨ 11 ⟩ ⟨ 21 ⟩ ⟨ 31 ⟩ ⟩ +10 20 30 +⟨ ⟨ 11 ⟩ ⟨ 21 ⟩ ⟨ 31 ⟩ ⟩ + +# u64 tests +51539608786 +4503599628419072 +⟨ ¯2045800064 28744 ⟩ +⟨ 0 0 0 1 0 1 1 0 1 0 1 0 0 1 1 0 0 0 1 1 0 1 1 0 0 0 1 1 0 1 1 0 1 1 1 1 0 1 1 0 1 1 1 0 1 1 1 0 1 1 1 1 0 1 1 0 0 1 0 0 1 1 1 0 ⟩ + +# malloc test +⟨ "fff7: refc:2 type:26=i32arr alloc:128" 2 ⟩ + +# pick item +"hellowor" +"hellowor" +"11122100" +"abacabad" +6618611909121 diff --git a/test/moreCfgs.sh b/test/moreCfgs.sh index 3c6871bd..9b78a6ef 100755 --- a/test/moreCfgs.sh +++ b/test/moreCfgs.sh @@ -24,3 +24,4 @@ make f='-DNO_RT -DPRECOMP' c && ./BQN || exit make f='-DLOG_GC' c && ./BQN -p 2+2 || exit make f='-DWRITE_ASM' c && ./BQN -p 2+2 || exit make f='-DUSE_PERF' c && ./BQN -p 2+2 || exit +make f='-DUSZ_64' c && ./BQN -p 2+2 || exit diff --git a/test/x86Cfgs.sh b/test/x86Cfgs.sh index 489e8ed3..e4c35062 100755 --- a/test/x86Cfgs.sh +++ b/test/x86Cfgs.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash make f='-DDEBUG -DHEAP_VERIFY -DJIT_START=0' single-c -echo 'alljit+heapverify:' && ./BQN -M 1000 "$1/test/this.bqn" -noerr bytecode header identity literal namespace prim simple syntax token under undo unhead || exit -echo 'singeli:';make o3n-singeli && ./BQN -M 1000 "$1/test/this.bqn" || exit -echo 'singeli vfy:';make heapverifyn-singeli && ./BQN -M 1000 "$1/test/this.bqn" -noerr bytecode header identity literal namespace prim simple syntax token under undo unhead || exit -echo '32-bit:';make f='-DDEBUG -m32' single-c && ./BQN -M 1000 "$1/test/this.bqn" || exit +echo 'alljit+heapverify:' && ./BQN -M 1000 "$1/test/this.bqn" -noerr bytecode header identity literal namespace prim simple syntax token under undo unhead || exit +echo 'singeli:';make o3n-singeli && ./BQN -M 1000 "$1/test/this.bqn" || exit +echo 'singeli vfy:';make heapverifyn-singeli && ./BQN -M 1000 "$1/test/this.bqn" -noerr bytecode header identity literal namespace prim simple syntax token under undo unhead || exit +echo '32-bit:';make f='-DDEBUG -m32' single-c FFI=0 && ./BQN -M 1000 "$1/test/this.bqn" || exit