Merge pull request #21 from dzaima/ffi

FFI
This commit is contained in:
dzaima 2022-05-28 01:29:21 +03:00 committed by GitHub
commit 093958f92a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1209 additions and 64 deletions

3
.gitignore vendored
View File

@ -10,3 +10,6 @@ perf.*
/Singeli
/local/
CBQNHeapDump
/test/ffi/ffiTest.o
/test/ffi/test.got
/test/ffi/lib.so

89
include/bqnffi.h Normal file
View File

@ -0,0 +1,89 @@
#include<stddef.h>
#include<stdint.h>
#include<stdbool.h>
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

52
include/syms Normal file
View File

@ -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;
};

View File

@ -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}

View File

@ -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");

View File

@ -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

746
src/ffi.c Normal file
View File

@ -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 <dlfcn.h>
#if FFI==2
#include <ffi.h>
#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<<reW, ia, reT=='c'? reTyMapC[reW] : reTyMapI[reW]);
else { u64* d2; r = m_bitarrv(&d2, ia); dst = (u8*) d2; }
memcpy(dst, src, elW);
return r;
}
FORCE_INLINE u64 i64abs(i64 x) { return x<0?-x:x; }
usz genObj(BQNFFIEnt ent, B c, bool anyMut) {
// printFFIType(stdout,ent.o); printf(" = "); print(c); printf("\n");
usz pos;
if (isC32(ent.o)) { // scalar
u32 t = o2cu(ent.o);
pos = ffiTmpAA(t==0? sizeof(BQNV) : 8);
void* ptr = ffiTmpS+pos;
f64 f = c.f;
switch(t) { default: UD; // thrF("FFI: Unimplemented scalar type \"%S\"", sty_names[t]);
case sty_a: *(BQNV*)ptr = makeX(inc(c)); break;
case sty_ptr: thrM("FFI: \"*\" unimplemented"); break;
case sty_u8: if(f!=( u8)f) thrM("FFI: u8 argument not exact" ); *( u8*)ptr = f; break;
case sty_i8: if(f!=( i8)f) thrM("FFI: i8 argument not exact" ); *( i8*)ptr = f; break;
case sty_u16: if(f!=(u16)f) thrM("FFI: u16 argument not exact"); *(u16*)ptr = f; break;
case sty_i16: if(f!=(i16)f) thrM("FFI: i16 argument not exact"); *(i16*)ptr = f; break;
case sty_u32: if(f!=(u32)f) thrM("FFI: u32 argument not exact"); *(u32*)ptr = f; break;
case sty_i32: if(f!=(i32)f) thrM("FFI: i32 argument not exact"); *(i32*)ptr = f; break;
case sty_u64: if(f!=(u64)f) thrM("FFI: u64 argument not exact"); if ( (u64)f >= (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; i<ia; i++) np[i]=tp[i]; dec(t); break; }
case sty_u16: { B t=toI32Any(c); i32* tp=i32any_ptr(t); i16* gp; cG=m_i16arrv(&gp, ia); u16* np=(u16* )gp; for (usz i=0; i<ia; i++) np[i]=tp[i]; dec(t); break; }
case sty_u32: { B t=toF64Any(c); f64* tp=f64any_ptr(t); i32* gp; cG=m_i32arrv(&gp, ia); u32* np=(u32* )gp; for (usz i=0; i<ia; i++) np[i]=tp[i]; dec(t); break; }
case sty_f32: { B t=toF64Any(c); f64* tp=f64any_ptr(t); i32* gp; cG=m_i32arrv(&gp, ia); float* np=(float*)gp; for (usz i=0; i<ia; i++) np[i]=tp[i]; dec(t); break; }
}
*(void**)(ffiTmpS+pos) = tyany_ptr(cG);
ffiObjs = vec_addN(ffiObjs, cG);
} else if (t->ty==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<<reW);
if (a(c)->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; i<ia; i++) rp[i]=tp[i]; return r; }
case sty_u16: { u16* tp=tyarr_ptr(f); i32* rp; B r=m_i32arrv(&rp, ia); for (usz i=0; i<ia; i++) rp[i]=tp[i]; return r; }
case sty_u32: { u32* tp=tyarr_ptr(f); f64* rp; B r=m_f64arrv(&rp, ia); for (usz i=0; i<ia; i++) rp[i]=tp[i]; return r; }
case sty_f32: { float*tp=tyarr_ptr(f); f64* rp; B r=m_f64arrv(&rp, ia); for (usz i=0; i<ia; i++) rp[i]=tp[i]; return r; }
}
} else return m_f64(0);
} else if (t->ty==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; i<ia; i++) {
if (i) fprintf(f, ", ");
printFFIType(f, t->a[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

18
src/h.h
View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -29,5 +29,6 @@
#include "../ns.c"
#include "../nfns.c"
#include "../rtwrap.c"
#include "../ffi.c"
#include "../jit/nvm.c"
#include "../main.c"

View File

@ -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)

View File

@ -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
```

85
test/ffi/ffiTest.c Normal file
View File

@ -0,0 +1,85 @@
#include <stdlib.h>
#include <stdio.h>
#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;
}

7
test/ffi/makefile Normal file
View File

@ -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

71
test/ffi/test.bqn Normal file
View File

@ -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¯212312312250500003123456789π1
f "lib.so" •FFI "" "noopArgs""i8""i16""i32""u8""u16""u32""f32""f64" •Show F ¯123¯12323¯212312312250500003123456789π1
f "lib.so" •FFI """printPtrArgs""*i8""*i16""*i32""*u8""*u16""*u32""*f32""*f64" •Show F ¨¯123¯12323¯212312312250500003123456789π1
f "lib.so" •FFI """printPtrArgs""&i8""&i16""&i32""&u8""&u16""&u32""&f32""&f64" •Show F ¨¯123¯12323¯212312312250500003123456789π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 1065353216107374182410779361281082130432108422758410863247361088421888109051904010915676161092616192,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 1010101001010011011000010101001000100010001110111111001100100100, 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 ¨ 102030
f "lib.so" •FFI "","incInts","&i32", "𝕨&i16","&i8" •Show 20 F ¨ 1030
f "lib.so" •FFI "","incInts","&i32",">𝕨&i16","&i8" •Show 20 F ¨ 1030
Section "# u64 tests"
f "lib.so" •FFI "u64", "ident_u64",">u64:i32" •Show F 123412
f "lib.so" •FFI "u64", "ident_u64",">u64" •Show F +´25220
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

61
test/ffi/test.expected Normal file
View File

@ -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

View File

@ -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

View File

@ -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