calculate •FFI allocation layout ahead-of-time
This commit is contained in:
parent
2dac64560f
commit
2a17d87572
165
src/ffi.c
165
src/ffi.c
@ -217,10 +217,11 @@ typedef struct BQNFFIEnt {
|
|||||||
struct { u8 wholeArg; u8 resSingle; }; // ffi_parseType
|
struct { u8 wholeArg; u8 resSingle; }; // ffi_parseType
|
||||||
u16 offset; // cty_struct
|
u16 offset; // cty_struct
|
||||||
};
|
};
|
||||||
|
u16 staticOffset; // only at the top level; offset into allocation
|
||||||
} BQNFFIEnt;
|
} BQNFFIEnt;
|
||||||
typedef struct BQNFFIType {
|
typedef struct BQNFFIType {
|
||||||
struct Value;
|
struct Value;
|
||||||
union { u16 structSize; };
|
union { u16 structSize; u16 staticAllocTotal; };
|
||||||
u8 ty;
|
u8 ty;
|
||||||
usz ia;
|
usz ia;
|
||||||
BQNFFIEnt a[];
|
BQNFFIEnt a[];
|
||||||
@ -428,29 +429,10 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
TAlloc* ffiTmpObj;
|
static usz ffiTmpAlign(usz n) {
|
||||||
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);
|
u64 align = _Alignof(max_align_t);
|
||||||
n = (n+align-1) & ~(align-1);
|
n = (n+align-1) & ~(align-1);
|
||||||
if (ffiTmpC+n > ffiTmpE) return ffiTmpX(n);
|
return n;
|
||||||
usz r = ffiTmpC-ffiTmpS;
|
|
||||||
ffiTmpC+= n;
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static B ffiObjs;
|
static B ffiObjs;
|
||||||
@ -478,13 +460,10 @@ static B makeRe(u8 reT, u8 reW/*log*/, u8* src, u32 elW/*bytes*/) {
|
|||||||
FORCE_INLINE u64 i64abs(i64 x) { return x<0?-x:x; }
|
FORCE_INLINE u64 i64abs(i64 x) { return x<0?-x:x; }
|
||||||
|
|
||||||
|
|
||||||
usz genObj(BQNFFIEnt ent, B c, bool anyMut, void* ptr) {
|
void genObj(B o, B c, bool anyMut, void* ptr) {
|
||||||
// printFFIType(stdout,ent.o); printf(" = "); print(c); printf("\n");
|
// printFFIType(stdout,ent.o); printf(" = "); print(c); printf("\n");
|
||||||
usz pos;
|
if (isC32(o)) { // scalar
|
||||||
#define ALLOC(N) if (ptr==NULL) { pos=ffiTmpAA(N); ptr=ffiTmpS+pos; } // TODO make top-level pos statically allocated so that there's no need for the conditionals
|
u32 t = o2cG(o);
|
||||||
if (isC32(ent.o)) { // scalar
|
|
||||||
u32 t = o2cG(ent.o);
|
|
||||||
ALLOC(t==0? sizeof(BQNV) : 8);
|
|
||||||
f64 f = c.f;
|
f64 f = c.f;
|
||||||
switch(t) { default: UD; // thrF("FFI: Unimplemented scalar type \"%S\"", sty_names[t]);
|
switch(t) { default: UD; // thrF("FFI: Unimplemented scalar type \"%S\"", sty_names[t]);
|
||||||
case sty_a: *(BQNV*)ptr = makeX(inc(c)); break;
|
case sty_a: *(BQNV*)ptr = makeX(inc(c)); break;
|
||||||
@ -501,9 +480,8 @@ usz genObj(BQNFFIEnt ent, B c, bool anyMut, void* ptr) {
|
|||||||
case sty_f64: *(double*)ptr = f; break;
|
case sty_f64: *(double*)ptr = f; break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
BQNFFIType* t = c(BQNFFIType, ent.o);
|
BQNFFIType* t = c(BQNFFIType, o);
|
||||||
if (t->ty==cty_ptr) { // *any / &any
|
if (t->ty==cty_ptr) { // *any / &any
|
||||||
ALLOC(sizeof(void*));
|
|
||||||
|
|
||||||
B e = t->a[0].o;
|
B e = t->a[0].o;
|
||||||
if (isC32(e)) { // *num / &num
|
if (isC32(e)) { // *num / &num
|
||||||
@ -538,7 +516,7 @@ usz genObj(BQNFFIEnt ent, B c, bool anyMut, void* ptr) {
|
|||||||
void* dataStruct = dataAll+sizeof(usz);
|
void* dataStruct = dataAll+sizeof(usz);
|
||||||
*((usz*)dataAll) = ia;
|
*((usz*)dataAll) = ia;
|
||||||
SGetU(c)
|
SGetU(c)
|
||||||
for (usz i = 0; i < ia; i++) genObj(t->a[0], GetU(c, i), anyMut, dataStruct + elSz*i);
|
for (usz i = 0; i < ia; i++) genObj(t->a[0].o, GetU(c, i), anyMut, dataStruct + elSz*i);
|
||||||
*(void**)ptr = dataStruct;
|
*(void**)ptr = dataStruct;
|
||||||
ffiObjs = vec_addN(ffiObjs, tag(TOBJ(dataAll), OBJ_TAG));
|
ffiObjs = vec_addN(ffiObjs, tag(TOBJ(dataAll), OBJ_TAG));
|
||||||
}
|
}
|
||||||
@ -547,7 +525,6 @@ usz genObj(BQNFFIEnt ent, B c, bool anyMut, void* ptr) {
|
|||||||
u8 reT = t->a[0].reType;
|
u8 reT = t->a[0].reType;
|
||||||
u8 reW = t->a[0].reWidth;
|
u8 reW = t->a[0].reWidth;
|
||||||
if (isC32(o2)) { // scalar:any
|
if (isC32(o2)) { // scalar:any
|
||||||
ALLOC(8);
|
|
||||||
u8 et = o2cG(o2);
|
u8 et = o2cG(o2);
|
||||||
u8 etw = sty_w[et]*8;
|
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 (!isArr(c)) thrF("FFI: Expected array corresponding to \"%S:%c%i\"", sty_names[et], reT, 1<<reW);
|
||||||
@ -556,7 +533,6 @@ usz genObj(BQNFFIEnt ent, B c, bool anyMut, void* ptr) {
|
|||||||
memcpy(ptr, tyany_ptr(cG), 8); // may over-read, ¯\_(ツ)_/¯
|
memcpy(ptr, tyany_ptr(cG), 8); // may over-read, ¯\_(ツ)_/¯
|
||||||
dec(cG);
|
dec(cG);
|
||||||
} else { // *scalar:any / &scalar:any
|
} else { // *scalar:any / &scalar:any
|
||||||
ALLOC(sizeof(void*));
|
|
||||||
if (!isArr(c)) thrM("FFI: Expected array corresponding to a pointer");
|
if (!isArr(c)) thrM("FFI: Expected array corresponding to a pointer");
|
||||||
BQNFFIType* t2 = c(BQNFFIType, o2);
|
BQNFFIType* t2 = c(BQNFFIType, o2);
|
||||||
B ore = t2->a[0].o;
|
B ore = t2->a[0].o;
|
||||||
@ -579,17 +555,15 @@ usz genObj(BQNFFIEnt ent, B c, bool anyMut, void* ptr) {
|
|||||||
ffiObjs = vec_addN(ffiObjs, cG);
|
ffiObjs = vec_addN(ffiObjs, cG);
|
||||||
}
|
}
|
||||||
} else if (t->ty==cty_struct) {
|
} else if (t->ty==cty_struct) {
|
||||||
ALLOC(t->structSize);
|
|
||||||
if (!isArr(c)) thrM("FFI: Expected array corresponding to a struct");
|
if (!isArr(c)) thrM("FFI: Expected array corresponding to a struct");
|
||||||
if (IA(c)!=t->ia-1) thrM("FFI: Incorrect list length corresponding to a struct");
|
if (IA(c)!=t->ia-1) thrM("FFI: Incorrect list length corresponding to a struct");
|
||||||
SGetU(c)
|
SGetU(c)
|
||||||
for (usz i = 0; i < t->ia-1; i++) {
|
for (usz i = 0; i < t->ia-1; i++) {
|
||||||
BQNFFIEnt e = t->a[i];
|
BQNFFIEnt e = t->a[i];
|
||||||
genObj(e, GetU(c, i), anyMut, e.offset + (u8*)ptr);
|
genObj(e.o, GetU(c, i), anyMut, e.offset + (u8*)ptr);
|
||||||
}
|
}
|
||||||
} else thrM("FFI: Unimplemented type");
|
} else thrM("FFI: Unimplemented type");
|
||||||
}
|
}
|
||||||
return pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
B readAny(BQNFFIEnt e, u8* ptr);
|
B readAny(BQNFFIEnt e, u8* ptr);
|
||||||
@ -686,21 +660,12 @@ B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
B libffiFn_c2(B t, B w, B x) {
|
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();
|
ffiObjs = emptyHVec();
|
||||||
ffiTmpX(128);
|
|
||||||
ffiTmpAA(argn * sizeof(u32));
|
|
||||||
#define argOffs ((u32*)ffiTmpS)
|
|
||||||
|
|
||||||
u32 flags = (u64)c->w_c1;
|
BoundFn* bf = c(BoundFn,t);
|
||||||
|
B argObj = c(HArr,bf->obj)->a[0];
|
||||||
|
|
||||||
|
u32 flags = (u64)bf->w_c1;
|
||||||
|
|
||||||
Arr* wa; AS2B wf;
|
Arr* wa; AS2B wf;
|
||||||
Arr* xa; AS2B xf;
|
Arr* xa; AS2B xf;
|
||||||
@ -708,53 +673,37 @@ B libffiFn_c2(B t, B w, B x) {
|
|||||||
if (flags&2) { xa=a(x); xf=TIv(xa,getU); }
|
if (flags&2) { xa=a(x); xf=TIv(xa,getU); }
|
||||||
i32 idxs[2] = {0,0};
|
i32 idxs[2] = {0,0};
|
||||||
|
|
||||||
|
u8* tmpAlloc = TALLOCP(u8, c(BQNFFIType,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 = c(BQNFFIType,argObj)->a;
|
||||||
for (usz i = 0; i < argn; i++) {
|
for (usz i = 0; i < argn; i++) {
|
||||||
BQNFFIEnt e = ents[i+1];
|
BQNFFIEnt e = ents[i+1];
|
||||||
B o = e.wholeArg? (e.onW? w : x) : (e.onW? wf : xf)(e.onW?wa:xa, idxs[e.onW]++);
|
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, NULL);
|
genObj(e.o, o, e.extra2, tmpAlloc + e.staticOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool simpleRes = isC32(ents[0].o);
|
for (usz i = 0; i < argn; i++) argPtrs[i] = tmpAlloc + ents[i+1].staticOffset;
|
||||||
u32 resCType;
|
void* res = tmpAlloc + ents[0].staticOffset;
|
||||||
usz resSize;
|
|
||||||
if (simpleRes) {
|
|
||||||
resCType = o2cG(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 = o2cG(o2);
|
|
||||||
} else if (t->ty == cty_struct) {
|
|
||||||
resSize = t->structSize;
|
|
||||||
goto resSize_alreadySet;
|
|
||||||
} else {
|
|
||||||
thrM("FFI: Unimplemented result type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resSize = resCType==sty_a? sizeof(BQNV) : sizeof(ffi_arg)>8? sizeof(ffi_arg) : 8;
|
// for (usz i = 0; i < c(BQNFFIType,argObj)->staticAllocTotal; i++) { // simple hexdump of the allocation
|
||||||
|
// if (!(i&15)) printf("%s%p ", i?"\n":"", (void*)(tmpAlloc+i));
|
||||||
resSize_alreadySet:;
|
|
||||||
usz resPos = ffiTmpAA(resSize);
|
|
||||||
|
|
||||||
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(" ");
|
// else if (!(i&3)) printf(" ");
|
||||||
// printf("%x%x ", ffiTmpS[i]>>4, ffiTmpS[i]&15);
|
// printf("%x%x ", tmpAlloc[i]>>4, tmpAlloc[i]&15);
|
||||||
// }
|
// }
|
||||||
// printf("\n");
|
// printf("\n");
|
||||||
|
|
||||||
|
void* sym = bf->w_c2;
|
||||||
ffi_call(cif, sym, res, argPtrs);
|
ffi_call(cif, sym, res, argPtrs);
|
||||||
|
|
||||||
B r;
|
B r;
|
||||||
bool resVoid = false;
|
bool resVoid = false;
|
||||||
if (simpleRes) {
|
if (isC32(ents[0].o)) {
|
||||||
|
u32 resCType = o2cG(ents[0].o);
|
||||||
r = readSimple(resCType, res);
|
r = readSimple(resCType, res);
|
||||||
resVoid = resCType==sty_void;
|
resVoid = resCType==sty_void;
|
||||||
} else {
|
} else {
|
||||||
@ -765,9 +714,9 @@ B libffiFn_c2(B t, B w, B x) {
|
|||||||
r = readStruct(c(BQNFFIType, ents[0].o), res);
|
r = readStruct(c(BQNFFIType, ents[0].o), res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mm_free((Value*)ffiTmpObj);
|
TFREE(tmpAlloc);
|
||||||
|
|
||||||
i32 mutArgs = c->mutCount;
|
i32 mutArgs = bf->mutCount;
|
||||||
if (mutArgs) {
|
if (mutArgs) {
|
||||||
usz objPos = 0;
|
usz objPos = 0;
|
||||||
bool resSingle = flags&4;
|
bool resSingle = flags&4;
|
||||||
@ -804,6 +753,9 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static u64 atomSize(B chr) {
|
||||||
|
return o2cG(chr)==sty_a? sizeof(BQNV) : sizeof(ffi_arg)>8? sizeof(ffi_arg) : 8;
|
||||||
|
}
|
||||||
|
|
||||||
B ffiload_c2(B t, B w, B x) {
|
B ffiload_c2(B t, B w, B x) {
|
||||||
usz xia = IA(x);
|
usz xia = IA(x);
|
||||||
@ -813,7 +765,28 @@ B ffiload_c2(B t, B w, B x) {
|
|||||||
B name = GetU(x,1);
|
B name = GetU(x,1);
|
||||||
vfyStr(name, "FFI", "type");
|
vfyStr(name, "FFI", "type");
|
||||||
|
|
||||||
|
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);
|
BQNFFIEnt tRes = ffi_parseType(GetU(x,0), true);
|
||||||
|
{
|
||||||
|
B atomType;
|
||||||
|
usz size;
|
||||||
|
if (isC32(tRes.o)) { atomType = tRes.o; goto calcRes; }
|
||||||
|
|
||||||
|
BQNFFIType* t = c(BQNFFIType, tRes.o);
|
||||||
|
if (t->ty == cty_repr) {
|
||||||
|
B o2 = t->a[0].o;
|
||||||
|
if (!isC32(o2)) thrM("FFI: Unimplemented result type");
|
||||||
|
atomType = o2;
|
||||||
|
goto calcRes;
|
||||||
|
}
|
||||||
|
if (t->ty == cty_struct) { size = t->structSize; goto allocRes; }
|
||||||
|
thrM("FFI: Unimplemented result type");
|
||||||
|
|
||||||
|
calcRes:; size = atomSize(atomType);
|
||||||
|
allocRes:; STATIC_ALLOC(tRes, size);
|
||||||
|
}
|
||||||
|
|
||||||
#if FFI==2
|
#if FFI==2
|
||||||
BQNFFIEnt* args; B argObj = m_bqnFFIType(&args, 255, argn+1);
|
BQNFFIEnt* args; B argObj = m_bqnFFIType(&args, 255, argn+1);
|
||||||
@ -822,10 +795,32 @@ B ffiload_c2(B t, B w, B x) {
|
|||||||
bool many[2]={0,0};
|
bool many[2]={0,0};
|
||||||
i32 mutCount = 0;
|
i32 mutCount = 0;
|
||||||
for (usz i = 0; i < argn; i++) {
|
for (usz i = 0; i < argn; i++) {
|
||||||
BQNFFIEnt e = args[i+1] = ffi_parseType(GetU(x,i+2), false);
|
BQNFFIEnt e = ffi_parseType(GetU(x,i+2), false);
|
||||||
|
|
||||||
|
u16 size;
|
||||||
|
if (isC32(e.o)) { // scalar
|
||||||
|
size = atomSize(e.o);
|
||||||
|
} else {
|
||||||
|
BQNFFIType* t = c(BQNFFIType, e.o);
|
||||||
|
if (t->ty==cty_ptr) size = sizeof(void*); // *any / &any
|
||||||
|
else if (t->ty==cty_struct) size = t->structSize; // {...}
|
||||||
|
else if (t->ty==cty_repr) { // any:any
|
||||||
|
B o2 = t->a[0].o;
|
||||||
|
if (isC32(o2)) size = atomSize(o2);
|
||||||
|
else {
|
||||||
|
if (c(BQNFFIType,o2)->ty != cty_ptr) thrM("FFI: Bad type with reinterpretation");
|
||||||
|
size = sizeof(void*);
|
||||||
|
}
|
||||||
|
} else thrM("FFI: Unimplemented type");
|
||||||
|
}
|
||||||
|
STATIC_ALLOC(e, size);
|
||||||
|
|
||||||
|
args[i+1] = e;
|
||||||
(e.wholeArg? one : many)[e.extra] = true;
|
(e.wholeArg? one : many)[e.extra] = true;
|
||||||
if (e.mutates) mutCount++;
|
if (e.mutates) mutCount++;
|
||||||
}
|
}
|
||||||
|
if (staticAlloc > U16_MAX-64) thrM("FFI: Static argument size too large");
|
||||||
|
c(BQNFFIType,argObj)->staticAllocTotal = ffiTmpAlign(staticAlloc);
|
||||||
if (one[0] && many[0]) thrM("FFI: Multiple arguments for 𝕩 specified, but one has '>'");
|
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 '>'");
|
if (one[1] && many[1]) thrM("FFI: Multiple arguments for 𝕨 specified, but one has '>'");
|
||||||
#else
|
#else
|
||||||
@ -833,7 +828,7 @@ B ffiload_c2(B t, B w, B x) {
|
|||||||
for (usz i = 0; i < argn; i++) ffi_parseType(GetU(x,i+2), false);
|
for (usz i = 0; i < argn; i++) ffi_parseType(GetU(x,i+2), false);
|
||||||
(void)tRes;
|
(void)tRes;
|
||||||
#endif
|
#endif
|
||||||
if (tRes.resSingle && mutCount!=1) thrF("FFI: Return was \"&\", but found %i mutated variables", mutCount);
|
if (tRes.resSingle && mutCount!=1) thrF("FFI: Return was \"&\", but found %i mutated values", mutCount);
|
||||||
|
|
||||||
char* ws = NULL;
|
char* ws = NULL;
|
||||||
if (w.u != m_c32(0).u) {
|
if (w.u != m_c32(0).u) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user