From 7ab13e3c5a0d27a45c165f0324f2a2faf2ba3459 Mon Sep 17 00:00:00 2001 From: dzaima Date: Mon, 5 Feb 2024 04:14:02 +0200 Subject: [PATCH] refactor BQNFFIEnt & BQNFFIType --- src/ffi.c | 375 ++++++++++++++++++++++----------------------- test/cases/ffi.bqn | 3 +- 2 files changed, 187 insertions(+), 191 deletions(-) diff --git a/src/ffi.c b/src/ffi.c index e25ca2a2..2fa60cde 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -233,36 +233,25 @@ DEF_FREE(ffiFn) { dec(((BoundFn*)x)->obj); } // ..continuing under "#if FFI" typedef struct BQNFFIEnt { + B o; union { - struct { // regular BQNFFIEnt usage - union { - B o; // usual case - TAlloc* structData; // pointer stored in last element of cty_struct - }; - #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 { - struct { u8 wholeArg; u8 resSingle; }; // ffi_parseType - u16 offset; // cty_struct, cty_starr - }; - u16 staticOffset; // only at the top level; offset into allocation - }; - - struct { // second element of a pointer holder - void* ptrh_ptr; // actual pointer - ux ptrh_stride; // stride size - }; + void* ptrh_ptr; // cty_ptrh + u64 structOffset; // cty_struct, cty_starr + struct { bool wholeArg, anyMut, resSingle; bool onW; u16 staticOffset; }; // cty_arglist, DecoratedType }; } BQNFFIEnt; typedef struct BQNFFIType { struct Value; - union { u16 structSize; u16 arrCount; u16 staticAllocTotal; }; u8 ty; - usz ia; + usz ia; // num0ber of entries in 'a', each with refcounted .o + union { + u32 staticAllocTotal; // cty_arglist + struct { u32 structSize; }; // cty_struct, cty_starr + struct { u32 arrCount; }; // cty_tlarr + struct { bool mutPtr; }; // cty_ptr + struct { u8 reType, reWidth; }; // cty_repr + u32 ptrh_stride; // cty_ptrh + }; BQNFFIEnt a[]; } BQNFFIType; @@ -337,20 +326,21 @@ static char* const sty_names[] = { [sty_f32]="f32", [sty_f64]="f64" }; enum CompoundTy { - cty_ptr, // *... / &...; .a = {{.mutPtr = startsWith("&"), .o=eltype}} - cty_tlarr, // top-level array; {{.o=eltype}}, arrCount=n - cty_struct, // {...}; .a = {{.o=el0}, {.o=el1}, ..., {.o=elLast}, {.structData = ffi_type**}} + cty_ptr, // *... / &...; .a = {{.o=eltype}}, .mutPtr = startsWith("&") + cty_tlarr, // top-level array; {{.o=eltype}}, .arrCount=n + cty_struct, // {...}; .a = {{.o=el0,structOffset=0}, {.o=el1,structOffset=n}, ..., {.o=elLast,structOffset=123}, {.o = taga(ffi_type**,OBJ_TYPE)}} cty_starr, // struct-based array; same as cty_struct - cty_repr, // something:type; {{.o=el, .t=rt, .reType=one of "icuf", .reWidth=log bit width of retype}} - cty_ptrh, // data holder for pointer objects; {{.o=eltype}, {.ptrh_ptr, .ptrh_stride}} + cty_repr, // something:type; {{.o=el}}, .reType=one of "icuf", .reWidth=log bit width of retype + cty_ptrh, // data holder for pointer objects; {{.o=eltype, .ptrh_ptr}, .ptrh_stride + cty_arglist, // top-level object holding argument & result data; {res, arg0, arg1, ...} }; -static NOINLINE B m_bqnFFIType(BQNFFIEnt** rp, u8 ty, usz ia) { +static NOINLINE B m_bqnFFIType(BQNFFIType** 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; + *rp = r; return tag(r, OBJ_TAG); } @@ -365,7 +355,34 @@ static u32 readUInt(u32** p) { *p = c; return r; } -BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual type +static ffi_type storeStructOffsets(BQNFFIType* rp, ffi_type** list, ux n) { + ffi_type rt; + rt.alignment = rt.size = 0; + rt.type = FFI_TYPE_STRUCT; + rt.elements = list; + + TALLOC(size_t, offsets, n); + if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("Type parser: Failed getting struct offsets"); + if (rt.size>=U16_MAX) thrM("Type parser: Object too large; limit is 65534 bytes"); + PLAINLOOP for (usz i = 0; i < n; i++) rp->a[i].structOffset = offsets[i]; + rp->structSize = rt.size; + TFREE(offsets); + + return rt; +} +#define ALLOC_TYPE_LIST(NELTS, NLIST) \ + ux alloc_elsSz_ = sizeof(ffi_type)*(NELTS); \ + TAlloc* ao = ARBOBJ(alloc_elsSz_ + sizeof(ffi_type*) * ((NLIST)+1)); \ + ffi_type* elts = (ffi_type*)ao->data; \ + ffi_type** list = (ffi_type**)(alloc_elsSz_ + (char*)ao->data); \ + list[NLIST] = NULL; + +typedef struct ParsedType { + B o; + ffi_type ffitype; + bool anyMutation, canRetype; +} ParsedType; +ParsedType ffi_parseType(u32** src, bool inPtr, bool top) { // parse actual type u32* c = *src; u32 c0 = *c++; ffi_type rt; @@ -380,39 +397,30 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty u32 n = readUInt(&c); if (*c++!=']') thrM("Type parser: Bad array type"); if (n==0) thrM("Type parser: 0-item arrays not supported"); - BQNFFIEnt e = ffi_parseTypeStr(&c, top, false); + ParsedType e = ffi_parseType(&c, top, false); - if (top) { // largely copy-pasted from `case '*': case '&':` + if (top) { myWidth = sizeof(void*); - BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_tlarr, 1); - rp[0] = e; + BQNFFIType* rp; ro = m_bqnFFIType(&rp, cty_tlarr, 1); + rp->a[0].o = e.o; if (n>U16_MAX) thrM("Type parser: Top-level array too large; limit is 65535 elements"); - c(BQNFFIType, ro)->arrCount = n; - mut|= rp[0].mutates; - parseRepr = rp[0].canRetype; - rp[0].mutPtr = false; + rp->arrCount = n; + mut|= e.anyMutation; + parseRepr = e.canRetype; rt = ffi_type_pointer; - } else { // largely copy-pasted from `case '{':` - BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_starr, n+1); - PLAINLOOP for (int i = 0; i < n; i++) rp[i] = e; + } else { + BQNFFIType* rp; ro = m_bqnFFIType(&rp, cty_starr, n+1); // +1 for ARBOBJ + + ALLOC_TYPE_LIST(1, n); + rp->a[n].o = tag(ao, OBJ_TAG); + *elts = e.ffitype; + PLAINLOOP for (usz i = 0; i < n; i++) { + list[i] = elts; + rp->a[i].o = e.o; + } incBy(e.o, n-1); - rp[n].structData = NULL; - TAlloc* ao = ARBOBJ(sizeof(ffi_type*) * (n+1)); - rp[n].structData = ao; - ffi_type** els = rt.elements = (ffi_type**) ao->data; - PLAINLOOP for (usz i = 0; i < n; i++) els[i] = &rp[i].t; - els[n] = NULL; - - rt.alignment = rt.size = 0; - rt.type = FFI_TYPE_STRUCT; - - TALLOC(size_t, offsets, n); - if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("Type parser: Failed getting array offsets"); - if (rt.size>=U16_MAX) thrM("Type parser: Array too large; limit is 65534 bytes"); - PLAINLOOP for (usz i = 0; i < n; i++) rp[i].offset = offsets[i]; - c(BQNFFIType, ro)->structSize = rt.size; - TFREE(offsets); + rt = storeStructOffsets(rp, list, n); } break; } @@ -453,46 +461,42 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty canRetype = inPtr; } else { if (c0=='&') mut = true; - BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_ptr, 1); - rp[0] = ffi_parseTypeStr(&c, true, false); - mut|= rp[0].mutates; - parseRepr = rp[0].canRetype; + ParsedType e = ffi_parseType(&c, true, false); - rp[0].mutPtr = c0=='&'; + BQNFFIType* rp; ro = m_bqnFFIType(&rp, cty_ptr, 1); + rp->a[0].o = e.o; + rp->mutPtr = c0=='&'; + + mut|= e.anyMutation; + parseRepr = e.canRetype; } rt = ffi_type_pointer; break; } case '{': { - TSALLOC(BQNFFIEnt, es, 4); + TSALLOC(ParsedType, es, 4); while (true) { - BQNFFIEnt e = TSADD(es, ffi_parseTypeStr(&c, false, false)); - if (e.mutates) thrM("Type parser: Structs currently cannot contain mutable references"); + ParsedType e = TSADD(es, ffi_parseType(&c, false, false)); + if (e.anyMutation) thrM("Type parser: Structs currently cannot contain mutable references"); u32 m = *c++; if (m=='}') break; if (m!=',') thrM("Type parser: Improper struct separator or end"); } usz n = TSSIZE(es); - BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_struct, n+1); - memcpy(rp, es, n*sizeof(BQNFFIEnt)); - rp[n].structData = NULL; - TAlloc* ao = ARBOBJ(sizeof(ffi_type*) * (n+1)); - rp[n].structData = ao; - ffi_type** els = rt.elements = (ffi_type**) ao->data; - PLAINLOOP for (usz i = 0; i < n; i++) els[i] = &rp[i].t; - els[n] = NULL; + BQNFFIType* rp; ro = m_bqnFFIType(&rp, cty_struct, n+1); // +1 for ARBOBJ + + ALLOC_TYPE_LIST(n, n); + rp->a[n].o = tag(ao, OBJ_TAG); + PLAINLOOP for (usz i = 0; i < n; i++) { + rp->a[i].o = es[i].o; + elts[i] = es[i].ffitype; + list[i] = &elts[i]; + } TSFREE(es); - rt.alignment = rt.size = 0; - rt.type = FFI_TYPE_STRUCT; - TALLOC(size_t, offsets, n); - if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("Type parser: Failed getting struct offsets"); - if (rt.size>=U16_MAX) thrM("Type parser: Struct too large; limit is 65534 bytes"); - PLAINLOOP for (usz i = 0; i < n; i++) rp[i].offset = offsets[i]; - c(BQNFFIType, ro)->structSize = rt.size; - TFREE(offsets); + rt = storeStructOffsets(rp, list, n); break; } } @@ -512,11 +516,13 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty // 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)}; + BQNFFIType* rp; ro = m_bqnFFIType(&rp, cty_repr, 1); + rp->reType = t; + rp->reWidth = 63-CLZ(n); + rp->a[0].o = roP; } *src = c; - return (BQNFFIEnt){.t=rt, .o=ro, .canRetype=canRetype, .extra2=mut}; + return (ParsedType){.ffitype=rt, .o=ro, .canRetype=canRetype, .anyMutation=mut}; } static NOINLINE u32* parseType_pre(B arg, ux ia) { // doesn't consume; arg can be freed immediately after TALLOC(u32, xp, ia+1); @@ -528,21 +534,26 @@ static NOINLINE void parseType_post(u32* xp0, u32* xp, usz ia) { if (xp0+ia != xp) thrM("Type parser: Garbage at end of type"); TFREE(xp0); } -BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument side & other global decorators + +typedef struct { + BQNFFIEnt ent; + ffi_type ffitype; +} DecoratedType; +DecoratedType ffi_parseDecoratedType(B arg, bool forRes) { // doesn't consume; parse argument side & other global decorators vfyStr(arg, "FFI", "type"); usz ia = IA(arg); if (ia==0) { if (!forRes) thrM("Type parser: Type was empty"); - return (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=false}; + return (DecoratedType){{.o=m_c32(sty_void), .resSingle=false}, .ffitype = ffi_type_void}; } arg = chr_squeezeChk(incG(arg)); u32* xp0 = parseType_pre(arg, ia); u32* xp = xp0; - BQNFFIEnt t; + DecoratedType t; if (forRes && xp[0]=='&' && xp[1]=='\0') { - t = (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=true}; + t = (DecoratedType){{.o=m_c32(sty_void), .resSingle=true}, .ffitype = ffi_type_void}; xp+= 1; } else { u8 side = 0; @@ -558,13 +569,11 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument if (side) side--; else side = 0; - t = ffi_parseTypeStr(&xp, false, true); - // printI(arg); printf(": "); printFFIType(stdout, t.o); printf("\n"); - - t.onW = side; - // keep .mutates - t.wholeArg = whole; - t.resSingle = false; + ParsedType e = ffi_parseType(&xp, false, true); + t = (DecoratedType) { + {.o=e.o, .onW=side, .anyMut=e.anyMutation, .wholeArg=whole, .resSingle=false}, + .ffitype = e.ffitype + }; } parseType_post(xp0, xp, ia); @@ -635,7 +644,6 @@ NOINLINE B readF32Bits(B x) { usz ia=IA(x); f32* xp=tyarr_ptr(x); f64* rp; B r=m static NOINLINE B ptrobj_checkget(B x); // doesn't consume static bool ptrty_equal(B a, B b); static B ptrh_type(B n); // returns ptrty -static BQNFFIEnt* ptrh_more(B n); static void* ptrh_ptr(B n); static NOINLINE void ty_fmt_add(B* s0, B o) { @@ -649,7 +657,7 @@ static NOINLINE void ty_fmt_add(B* s0, B o) { A8("???"); break; case cty_ptr: - ACHR(t->a[0].mutPtr? '&' : '*'); + ACHR(t->mutPtr? '&' : '*'); ty_fmt_add(&s, t->a[0].o); break; case cty_starr: @@ -665,7 +673,7 @@ static NOINLINE void ty_fmt_add(B* s0, B o) { break; case cty_repr: ty_fmt_add(&s, t->a[0].o); - AFMT(":%c%i", (u32)t->a[0].reType, (i32)(1 << t->a[0].reWidth)); + AFMT(":%c%i", (u32)t->reType, (i32)(1 << t->reWidth)); break; } } @@ -711,7 +719,7 @@ void genObj(B o, B c, bool anyMut, void* ptr) { // doesn't consume } } else { BQNFFIType* t = c(BQNFFIType, o); - if (t->ty==cty_ptr || t->ty==cty_tlarr) { // *any / &any + if (t->ty==cty_ptr || t->ty==cty_tlarr) { // *any / &any / top-level [n]any B e = t->a[0].o; if (isAtm(c)) { if (isNsp(c)) { genObj_ptr(ptr, c, e); return; } @@ -722,7 +730,7 @@ void genObj(B o, B c, bool anyMut, void* ptr) { // doesn't consume if (isC32(e)) { // *num / &num incG(c); B cG; - bool mut = t->a[0].mutPtr; + bool mut = t->ty==cty_ptr? t->mutPtr : false; switch(o2cG(e)) { default: thrF("FFI: \"*%S\" argument type not yet implemented", sty_names[o2cG(e)]); case sty_i8: ffi_checkRange(c, mut, "i8", I8_MIN, I8_MAX); cG = mut? taga(cpyI8Arr (c)) : toI8Any (c); break; case sty_i16: ffi_checkRange(c, mut, "i16", I16_MIN, I16_MAX); cG = mut? taga(cpyI16Arr(c)) : toI16Any(c); break; @@ -751,8 +759,8 @@ void genObj(B o, B c, bool anyMut, void* ptr) { // doesn't consume } } 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; + u8 reT = t->reType; + u8 reW = t->reWidth; if (isC32(o2)) { // scalar:any (incl. *:any) u8 et = o2cG(o2); bool eptr = et==sty_ptr; @@ -762,14 +770,14 @@ void genObj(B o, B c, bool anyMut, void* ptr) { // doesn't consume if (isAtm(c)) thrF("FFI: Expected array%S corresponding to %R", eptr?" or pointer object":"", ty_fmt(o)); if (IA(c) != mul) thrF("FFI: Bad array%S corresponding to %R: expected %s elements, got %s", eptr?" or pointer object":"", ty_fmt(o), (usz)mul, IA(c)); B cG = toW(reT, reW, incG(c)); - memcpy(ptr, tyany_ptr(cG), 8); // may over-read, ¯\_(ツ)_/¯ + memcpy(ptr, tyany_ptr(cG), 8); // may over-read, but CBQN-allocations allow that; may write past the end, but that's fine too? maybe? idk actually; TODO dec(cG); } else { // *scalar:any / &scalar:any BQNFFIType* t2 = c(BQNFFIType, o2); B ore = t2->a[0].o; if (isNsp(c)) { genObj_ptr(ptr, c, ore); return; } - assert(t2->ty==cty_ptr && isC32(ore)); // we shouldn't be generating anything else - bool mut = t2->a[0].mutPtr; + assert(t2->ty==cty_ptr && isC32(ore)); + bool mut = t2->mutPtr; u8 et = o2cG(ore); u8 mul = (sty_w[et]*8) >> reW; @@ -797,7 +805,7 @@ void genObj(B o, B c, bool anyMut, void* ptr) { // doesn't consume SGetU(c) for (usz i = 0; i < t->ia-1; i++) { BQNFFIEnt e = t->a[i]; - genObj(e.o, GetU(c, i), anyMut, e.offset + (u8*)ptr); + genObj(e.o, GetU(c, i), anyMut, e.structOffset + (u8*)ptr); } } else thrM("FFI: Unimplemented type (genObj)"); } @@ -808,7 +816,7 @@ B readStruct(BQNFFIType* t, u8* ptr) { usz ia = t->ia-1; M_HARR(r, ia); for (usz i = 0; i < ia; i++) { - void* c = ptr + t->a[i].offset; + void* c = ptr + t->a[i].structOffset; HARR_ADD(r, i, readAny(t->a[i].o, c)); } return HARR_FV(r); @@ -833,10 +841,10 @@ B readSimple(u8 resCType, u8* ptr) { return r; } -B readRe(BQNFFIEnt e, u8* ptr) { - u8 et = o2cG(e.o); - u8 reT = e.reType; - u8 reW = e.reWidth; +B readRe(BQNFFIType* t, u8* ptr) { + u8 et = o2cG(t->a[0].o); + u8 reT = t->reType; + u8 reW = t->reWidth; u8 etw = sty_w[et]; return makeRe(reT, reW, ptr, etw); } @@ -847,7 +855,7 @@ B readAny(B o, u8* ptr) { // doesn't consume } else { BQNFFIType* t = c(BQNFFIType, o); if (t->ty == cty_repr) { // cty_repr, scalar:x - return readRe(t->a[0], ptr); + return readRe(t, ptr); } else if (t->ty==cty_struct || t->ty==cty_starr) { // {...}, [n]... return readStruct(c(BQNFFIType, o), ptr); } @@ -859,10 +867,10 @@ B readAny(B o, u8* ptr) { // doesn't consume 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 || t->ty==cty_tlarr) { // *any / &any + if (t->ty==cty_ptr || t->ty==cty_tlarr) { // *any / &any / top-level [n]any B e = t->a[0].o; B f = objs[(*objPos)++]; - if (t->a[0].mutPtr) { + if (t->ty==cty_ptr && t->mutPtr) { if (isC32(e)) { switch(o2cG(e)) { default: UD; case sty_i8: case sty_i16: case sty_i32: case sty_f64: return inc(f); @@ -892,8 +900,7 @@ B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) { BQNFFIType* t2 = c(BQNFFIType,o2); // *scalar:any / &scalar:any assert(t2->ty == cty_ptr); B f = objs[(*objPos)++]; - if (!t2->a[0].mutPtr) return m_f64(0); - return inc(f); + return t2->mutPtr? inc(f) : m_f64(0); } else if (t->ty==cty_struct || t->ty==cty_starr) { assert(!anyMut); // Structs currently cannot contain mutable references @@ -906,7 +913,7 @@ B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) { B libffiFn_c2(B t, B w, B x) { BoundFn* bf = c(BoundFn,t); - B argObj = c(HArr,bf->obj)->a[0]; + BQNFFIType* argObj = c(BQNFFIType, c(HArr,bf->obj)->a[0]); #define PROC_ARG(ISX, L, U, S) \ Arr* L##a ONLY_GCC(=0); \ @@ -928,14 +935,14 @@ B libffiFn_c2(B t, B w, B x) { i32 idxs[2] = {0,0}; - u8* tmpAlloc = TALLOCP(u8, c(BQNFFIType,argObj)->staticAllocTotal); + u8* tmpAlloc = TALLOCP(u8, argObj->staticAllocTotal); void** argPtrs = (void**) tmpAlloc; B cifObj = c(HArr,bf->obj)->a[1]; ffi_cif* cif = (void*) c(TAlloc,cifObj)->data; usz argn = cif->nargs; - BQNFFIEnt* ents = c(BQNFFIType,argObj)->a; + BQNFFIEnt* ents = argObj->a; ffiObjsGlobal = emptyHVec(); // implicit parameter to genObj for (usz i = 0; i < argn; i++) { BQNFFIEnt e = ents[i+1]; @@ -945,14 +952,14 @@ B libffiFn_c2(B t, B w, B x) { } else { o = (e.onW? wf : xf)(e.onW?wa:xa, idxs[e.onW]++); } - genObj(e.o, o, e.extra2, tmpAlloc + e.staticOffset); + genObj(e.o, o, e.anyMut, tmpAlloc + e.staticOffset); } B ffiObjs = ffiObjsGlobal; // load the global before ffi_call to prevent issues on recursive calls for (usz i = 0; i < argn; i++) argPtrs[i] = tmpAlloc + ents[i+1].staticOffset; void* res = tmpAlloc + ents[0].staticOffset; - // for (usz i = 0; i < c(BQNFFIType,argObj)->staticAllocTotal; i++) { // simple hexdump of the allocation + // for (usz i = 0; i < argObj->staticAllocTotal; i++) { // simple hexdump of the allocation // if (!(i&15)) printf("%s%p ", i?"\n":"", (void*)(tmpAlloc+i)); // else if (!(i&3)) printf(" "); // printf("%x%x ", tmpAlloc[i]>>4, tmpAlloc[i]&15); @@ -971,7 +978,7 @@ B libffiFn_c2(B t, B w, B x) { } else { BQNFFIType* t = c(BQNFFIType, ents[0].o); if (t->ty == cty_repr) { // cty_repr, scalar:x - r = readRe(t->a[0], res); + r = readRe(t, res); } else if (t->ty == cty_struct) { // {...} r = readStruct(c(BQNFFIType, ents[0].o), res); } else if (t->ty == cty_ptr) { // *... @@ -989,16 +996,16 @@ B libffiFn_c2(B t, B w, B x) { 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; + B c = buildObj(e, e.anyMut, harr_ptr(ffiObjs), &objPos); + if (e.anyMut) 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); + B c = buildObj(e, e.anyMut, harr_ptr(ffiObjs), &objPos); + if (e.anyMut) HARR_ADDA(ra, c); } if (testBuildObj && !mutArgs) { inc(r); HARR_ABANDON(ra); } else r = HARR_FV(ra); @@ -1013,7 +1020,7 @@ B libffiFn_c1(B t, B x) { return libffiFn_c2(t, bi_N, x); } #else -BQNFFIEnt ffi_parseType(B arg, bool forRes) { +BQNFFIEnt ffi_parseDecoratedType(B arg, bool forRes) { if (!isArr(arg) || IA(arg)!=1 || IGetU(arg,0).u!=m_c32('a').u) thrM("FFI: Only \"a\" arguments & return value supported with compile flag FFI=1"); return (BQNFFIEnt){}; } @@ -1054,13 +1061,14 @@ B ffiload_c2(B t, B w, B x) { u64 staticAlloc = ffiTmpAlign(argn * sizeof(void*)); // initial alloc is the argument pointer list #define STATIC_ALLOC(O, SZ) ({ (O).staticOffset = staticAlloc; staticAlloc+= ffiTmpAlign(SZ); }) - BQNFFIEnt tRes = ffi_parseType(GetU(x,0), true); + DecoratedType tRes = ffi_parseDecoratedType(GetU(x,0), true); + BQNFFIEnt eRes = tRes.ent; { B atomType; usz size; - if (isC32(tRes.o)) { atomType = tRes.o; goto calcAtomSize; } + if (isC32(eRes.o)) { atomType = eRes.o; goto calcAtomSize; } - BQNFFIType* t = c(BQNFFIType, tRes.o); + BQNFFIType* t = c(BQNFFIType, eRes.o); if (t->ty == cty_repr) { B o2 = t->a[0].o; if (!isC32(o2)) thrM("FFI: Unimplemented result type"); @@ -1079,35 +1087,35 @@ B ffiload_c2(B t, B w, B x) { size = calcAtomSize(atomType); goto allocRes; - allocRes:; STATIC_ALLOC(tRes, size); + allocRes:; STATIC_ALLOC(eRes, size); } - #if FFI==2 - BQNFFIEnt* args; B argObj = m_bqnFFIType(&args, 255, argn+1); - args[0] = tRes; - bool whole[2]={0,0}; - i32 count[2]={0,0}; - i32 mutCount = 0; - for (usz i = 0; i < argn; i++) { - BQNFFIEnt e = ffi_parseType(GetU(x,i+2), false); - - STATIC_ALLOC(e, calcStaticSize(e.o)); - - args[i+1] = e; - if (e.wholeArg) whole[e.onW] = true; - count[e.onW]++; - if (e.mutates) mutCount++; - } - if (staticAlloc > U16_MAX-64) thrM("FFI: Static argument size too large"); - c(BQNFFIType,argObj)->staticAllocTotal = ffiTmpAlign(staticAlloc); - if (count[0]>1 && whole[0]) thrM("FFI: Multiple arguments on 𝕩 specified, some with '>'"); - if (count[1]>1 && whole[1]) thrM("FFI: Multiple arguments on 𝕨 specified, some with '>'"); - #else - i32 mutCount = 0; - 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 value is specified as \"&\", but there are %i mutated values", mutCount); + ALLOC_TYPE_LIST(argn+1, argn); + elts[argn] = tRes.ffitype; + + BQNFFIType* ap; B argObj = m_bqnFFIType(&ap, cty_arglist, argn+1); + ap->a[0] = eRes; + bool whole[2]={0,0}; + i32 count[2]={0,0}; + i32 mutCount = 0; + for (usz i = 0; i < argn; i++) { + DecoratedType tArg = ffi_parseDecoratedType(GetU(x,i+2), false); + BQNFFIEnt e = tArg.ent; + STATIC_ALLOC(e, calcStaticSize(e.o)); + + elts[i] = tArg.ffitype; + list[i] = &elts[i]; + + ap->a[i+1] = e; + if (e.wholeArg) whole[e.onW] = true; + count[e.onW]++; + if (e.anyMut) mutCount++; + } + if (staticAlloc > U16_MAX-64) thrM("FFI: Static argument size too large"); + ap->staticAllocTotal = ffiTmpAlign(staticAlloc); + if (count[0]>1 && whole[0]) thrM("FFI: Multiple arguments on 𝕩 specified, some with '>'"); + if (count[1]>1 && whole[1]) thrM("FFI: Multiple arguments on 𝕨 specified, some with '>'"); + if (eRes.resSingle && mutCount!=1) thrF("FFI: Return value is specified as \"&\", but there are %i mutated values", mutCount); char* ws = NULL; if (w.u != m_c32(0).u) { @@ -1129,21 +1137,16 @@ B ffiload_c2(B t, B w, B x) { #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; - PLAINLOOP 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); + ffi_status s = ffi_prep_cif((ffi_cif*)cif->data, FFI_DEFAULT_ABI, argn, &elts[argn], list); if (s!=FFI_OK) thrM("FFI: Error preparing call interface"); // mm_free(argsRaw) - u32 flags = tRes.resSingle<<2; - B r = m_ffiFn(foreignFnDesc, m_hvec3(argObj, tag(cif, OBJ_TAG), tag(argsRaw, OBJ_TAG)), libffiFn_c1, libffiFn_c2, TOPTR(void,flags), sym); + u32 flags = eRes.resSingle<<2; + B r = m_ffiFn(foreignFnDesc, m_hvec3(argObj, tag(cif, OBJ_TAG), tag(ao, OBJ_TAG)), libffiFn_c1, libffiFn_c2, TOPTR(void,flags), sym); c(BoundFn,r)->mutCount = mutCount; c(BoundFn,r)->wLen = whole[1]? -1 : count[1]; c(BoundFn,r)->xLen = whole[0]? -1 : count[0]; @@ -1151,24 +1154,16 @@ B ffiload_c2(B t, B w, B x) { #endif } -#define FFI_TYPE_FLDS(OBJ, PTR) \ +#define FFI_TYPE_FLDS(OBJ) \ BQNFFIType* t = (BQNFFIType*) x; \ - BQNFFIEnt* arr=t->a; usz ia=t->ia; \ - if (t->ty==cty_ptrh) { \ - ia = 1; \ - } if (t->ty==cty_struct || t->ty==cty_starr) { \ - ia--; \ - if (arr[ia].structData!=NULL) { \ - PTR(arr[ia].structData); \ - } \ - } \ + BQNFFIEnt* arr=t->a; usz ia=t->ia;\ for (usz i = 0; i < ia; i++) OBJ(arr[i].o); DEF_FREE(ffiType) { - FFI_TYPE_FLDS(dec, ptr_dec); + FFI_TYPE_FLDS(dec); } void ffiType_visit(Value* x) { - FFI_TYPE_FLDS(mm_visit, mm_visitP); + FFI_TYPE_FLDS(mm_visit); } void ffiType_print(FILE* f, B x) { BQNFFIType* t = c(BQNFFIType,x); @@ -1195,19 +1190,18 @@ STATIC_GLOBAL NFnDesc* ptrFieldDesc; // ptrh - pointer holder - BQNFFIType object of {{.o=eltype}, {.ptrh_ptr, .ptrh_stride}} // eltype of sty_void is used for an untyped pointer static B ptrh_type(B n) { return c(BQNFFIType, n)->a[0].o; } // returns ptrty -static BQNFFIEnt* ptrh_more(B n) { return &c(BQNFFIType, n)->a[1]; } -static void* ptrh_ptr(B n) { return ptrh_more(n)->ptrh_ptr; } -static ux ptrh_stride(B n) { return ptrh_more(n)->ptrh_stride; } +static void* ptrh_ptr(B n) { return c(BQNFFIType, n)->a[0].ptrh_ptr; } +static ux ptrh_stride(B n) { return c(BQNFFIType, n)->ptrh_stride; } B m_ptrobj_s(void* ptr, B o) { return m_ptrobj(ptr, o, calcMemSize(o)); } B m_ptrobj(void* ptr, B o, ux stride) { - BQNFFIEnt* op; - B obj = m_bqnFFIType(&op, cty_ptrh, 2); - op[0].o = o; - op[1].ptrh_ptr = ptr; - op[1].ptrh_stride = stride; + BQNFFIType* op; + B obj = m_bqnFFIType(&op, cty_ptrh, 1); + op->ptrh_stride = stride; + op->a[0].o = o; + op->a[0].ptrh_ptr = ptr; return m_nns(ptrobj_ns, m_nfn(ptrReadDesc, incG(obj)), @@ -1256,9 +1250,9 @@ static B ptrobjCast_c1(B t, B x) { u32* xp0 = parseType_pre(x, ia); u32* xp = xp0; decG(x); - BQNFFIEnt parsed = ffi_parseTypeStr(&xp, false, false); - B r = m_ptrobj_s(ptr, parsed.o); + ParsedType parsed = ffi_parseType(&xp, false, false); parseType_post(xp0, xp, ia); + B r = m_ptrobj_s(ptr, parsed.o); return r; } static B ptrobjField_c1(B t, B x) { @@ -1277,7 +1271,7 @@ static B ptrobjField_c1(B t, B x) { if (fld >= n) thrF(isStruct? "Cannot get field %l of a struct with %s fields" : "Cannot get pointer to element %l of an array with %s elements", fld, n); BQNFFIEnt* fldptr = &elc->a[fld]; elNew = inc(fldptr->o); - ptr+= fldptr->offset; + ptr+= fldptr->structOffset; } else { thrM("Pointer object: Field must be applied to a pointer to a struct or array"); } @@ -1356,6 +1350,7 @@ static B ptrobjSub_c1(B t, B x) { void ffi_init(void) { + assert(sizeof(ffi_type) % sizeof(ffi_type*) == 0); // assumption made by ALLOC_TYPE_LIST boundFnDesc = registerNFn(m_c8vec_0("(foreign function)"), boundFn_c1, boundFn_c2); foreignFnDesc = registerNFn(m_c8vec_0("(foreign function)"), directFn_c1, directFn_c2); ptrobj_ns = m_nnsDesc("read","write","cast","add","sub","field"); // first field must be an nfn whose object is the ptrh (needed for ptrobj_checkget) diff --git a/test/cases/ffi.bqn b/test/cases/ffi.bqn index 1d87732c..4387cef5 100644 --- a/test/cases/ffi.bqn +++ b/test/cases/ffi.bqn @@ -42,7 +42,7 @@ !"Type parser: number literal too large" % @•FFI""‿"bqn_init"‿"[999999999999]i32" !"Type parser: Top-level array too large; limit is 65535 elements" % @•FFI""‿"bqn_init"‿"[65536]i32" !"Type parser: Top-level array too large; limit is 65535 elements" % @•FFI""‿"bqn_init"‿"[4000000000]i32" -!"Type parser: Array too large; limit is 65534 bytes" % @•FFI""‿"bqn_init"‿"*[20000]i32" +!"Type parser: Object too large; limit is 65534 bytes" % @•FFI""‿"bqn_init"‿"*[20000]i32" !"FFI: Cannot return array" % @•FFI"[4]i32"‿"bqn_init" # structs @@ -210,6 +210,7 @@ !"Cannot offset an untyped pointer" % %USE WallocE {{𝕊al: ("" Al 99).Add 1}} # .Add, .Sub, .Cast +!"Type parser: Unexpected end of input" % %USE WallocE {{𝕊al: p←"{i32,*}" Al 1 ⋄ p.Cast "{"}} %USE Walloc {{𝕊al: p←"{i32,*}" Al 1 ⋄ p.Sub p.Cast "{i32,*i32}"}} %% 0 %USE Walloc {{𝕊al: p←"{i32,*}" Al 1 ⋄ p.Sub p.Add 123}} %% ¯123 %USE Walloc {{𝕊al: p←"{i32,*}" Al 1 ⋄ p.Sub p.Sub 123}} %% 123