array type support for •FFI

This commit is contained in:
dzaima 2023-03-20 16:33:22 +02:00
parent ca0c100b73
commit b0201131e2
7 changed files with 157 additions and 50 deletions

View File

@ -124,6 +124,7 @@ That is, the supported types are:
- scalars (e.g. `i8`, `u64`);
- pointers to scalars (e.g. `*i8`, `&u64`);
- conversions of either scalars, pointers to scalars, or opaque pointers (e.g. `u64:i32`, `*i64:i32`, `*:i8`, `**:c8`);
- arrays of scalars or structs (e.g. `[2]i32`, `[4]{i32,i32}`);
- structs of any of the above (except `&`-pointers) or other structs (e.g. `{*i8,*{*u32:i8,u64:i32}}`), except structs that are within `&` themselves cannot contain any pointers other than converted opaque pointers (e.g. `*{*i32,u64}`, `&{*:i32,u64}`, and `&{i32,u64}` are fine, but `&{*i32,u64}` is not);
- the `a` type, which maps to `BQNV` from [bqnffi.h](../include/bqnffi.h) (example usage in [FFI tests](../test/ffi/)).

View File

@ -30,7 +30,7 @@ BQNV bqn_call2(BQNV f, BQNV w, BQNV x);
// Evaluate BQN code in a fresh environment
BQNV bqn_eval(BQNV src); // src must be a character vector
BQNV bqn_evalCStr(const 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);`
BQNV bqn_evalCStr(const char* str); // evaluates the null-terminated UTF8-encoded str; equal to `BQNV s = bqn_makeUTF8Str(strlen(str), str); result = bqn_eval(s); bqn_free(s);`
// Read array data

View File

@ -73,7 +73,7 @@ src/
Type checks (all are safe to execute on any B object):
test tag description heap-allocated
isF64(x) F64_TAG a number no
isChr(x) C32_TAG a character no
isC32(x) C32_TAG a character no
isAtm(x) [many] !isArr(x) depends
isVal(x) [many] heap-allocated yes
isFun(x) FUN_TAG a function yes

126
src/ffi.c
View File

@ -234,7 +234,7 @@ typedef struct BQNFFIEnt {
} BQNFFIEnt;
typedef struct BQNFFIType {
struct Value;
union { u16 structSize; u16 staticAllocTotal; };
union { u16 structSize; u16 arrCount; u16 staticAllocTotal; };
u8 ty;
usz ia;
BQNFFIEnt a[];
@ -267,9 +267,11 @@ static const char* sty_names[] = {
[sty_f32]="f32", [sty_f64]="f64"
};
enum CompoundTy {
cty_ptr,
cty_repr,
cty_struct,
cty_ptr, // *... / &...
cty_repr, // something:type
cty_struct, // {...}
cty_starr, // struct-based array
cty_tlarr, // top-level array
};
static B m_bqnFFIType(BQNFFIEnt** rp, u8 ty, usz ia) {
@ -292,7 +294,7 @@ static u32 readUInt(u32** p) {
*p = c;
return r;
}
BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr) { // parse actual type
BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual type
u32* c = *src;
u32 c0 = *c++;
ffi_type rt;
@ -300,7 +302,46 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr) { // parse actual type
bool parseRepr=false, canRetype=false, mut=false;
u32 myWidth = 0; // used if parseRepr
switch (c0) {
default: thrM("FFI: Error parsing type");
default: thrF("FFI: Error parsing type: Unexpected character '%c'", c0);
case '[': {
u32 n = readUInt(&c);
if (*c++!=']') thrM("FFI: Bad array type");
if (n==0) thrM("FFI: 0-item arrays not supported");
BQNFFIEnt e = ffi_parseTypeStr(&c, top, false);
if (top) { // largely copy-pasted from `case '*': case '&':`
myWidth = sizeof(void*);
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_tlarr, 1);
rp[0] = e;
if (n>U16_MAX) thrM("FFI: 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;
rt = ffi_type_pointer;
} else { // largely copy-pasted from `case '{':`
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_starr, n+1);
for (int i = 0; i < n; i++) rp[i] = e;
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;
for (usz i = 0; i < n; i++) els[i] = &rp[i].t;
els[n] = NULL;
rt.type = FFI_TYPE_STRUCT;
TALLOC(size_t, offsets, n);
if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("FFI: Failed getting array offsets");
if (rt.size>=U16_MAX) thrM("FFI: Array too large; limit is 65534 bytes");
for (usz i = 0; i < n; i++) rp[i].offset = offsets[i];
c(BQNFFIType, ro)->structSize = rt.size;
TFREE(offsets);
}
break;
}
case 'i': case 'u': case 'f': {
u32 n = readUInt(&c);
@ -339,7 +380,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr) { // parse actual type
} else {
if (c0=='&') mut = true;
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_ptr, 1);
rp[0] = ffi_parseTypeStr(&c, true);
rp[0] = ffi_parseTypeStr(&c, true, false);
mut|= rp[0].mutates;
parseRepr = rp[0].canRetype;
@ -352,7 +393,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr) { // parse actual type
case '{': {
TSALLOC(BQNFFIEnt, es, 4);
while (true) {
BQNFFIEnt e = TSADD(es, ffi_parseTypeStr(&c, false));
BQNFFIEnt e = TSADD(es, ffi_parseTypeStr(&c, false, false));
if (e.mutates) thrM("FFI: Structs currently cannot contain mutable references");
u32 m = *c++;
if (m=='}') break;
@ -431,7 +472,7 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument
if (side) side--;
else side = 0;
t = ffi_parseTypeStr(&xp, false);
t = ffi_parseTypeStr(&xp, false, true);
// printI(arg); printf(": "); printFFIType(stdout, t.o); printf("\n");
if (xp!=xpN) thrM("FFI: Bad type descriptor");
t.onW = side;
@ -496,14 +537,18 @@ void genObj(B o, B c, bool anyMut, void* ptr) {
}
} else {
BQNFFIType* t = c(BQNFFIType, o);
if (t->ty==cty_ptr) { // *any / &any
if (t->ty==cty_ptr || t->ty==cty_tlarr) { // *any / &any
B e = t->a[0].o;
if (!isArr(c)) {
if (isC32(e)) thrF("FFI: Expected array corresponding to \"*%S\"", sty_names[o2cG(e)]);
else thrM("FFI: Expected array corresponding to *{...}");
}
usz ia = IA(c);
if (t->ty==cty_tlarr && t->arrCount!=ia) thrF("FFI: Incorrect item count of %s corresponding to \"[%s]...\"", ia, (usz)t->arrCount);
if (isC32(e)) { // *num / &num
inc(c);
B cG;
if (!isArr(c)) thrF("FFI: Expected array corresponding to \"*%S\"", sty_names[o2cG(e)]);
usz ia = IA(c);
bool mut = t->a[0].mutPtr;
switch(o2cG(e)) { default: thrF("FFI: \"*%S\" argument type NYI", sty_names[o2cG(e)]);
case sty_i8: cG = mut? taga(cpyI8Arr (c)) : toI8Any (c); break;
@ -518,13 +563,10 @@ void genObj(B o, B c, bool anyMut, void* ptr) {
ffiObjs = vec_addN(ffiObjs, cG);
*(void**)ptr = tyany_ptr(cG);
} else {
} else { // *{...} / &{...} / *[n]any
BQNFFIType* t2 = c(BQNFFIType, e);
if (t2->ty != cty_struct) thrM("FFI: Unimplemented pointer element type");
if (t2->ty!=cty_struct && t2->ty!=cty_starr) thrM("FFI: Unimplemented pointer element type");
if (!isArr(c)) thrM("FFI: Expected array corresponding to *{...}");
// *{...} / &{...}
usz ia = IA(c);
usz elSz = t2->structSize;
TALLOC(u8, dataAll, elSz*ia + sizeof(usz));
@ -569,15 +611,15 @@ void genObj(B o, B c, bool anyMut, void* ptr) {
*(void**)ptr = tyany_ptr(cG);
ffiObjs = vec_addN(ffiObjs, cG);
}
} else if (t->ty==cty_struct) {
} else if (t->ty==cty_struct || t->ty==cty_starr) {
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) thrF("FFI: Incorrect list length corresponding to %S: expected %s, got %s", t->ty==cty_struct? "a struct" : "an array", (usz)(t->ia-1), IA(c));
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);
}
} else thrM("FFI: Unimplemented type");
} else thrM("FFI: Unimplemented type (genObj)");
}
}
@ -625,7 +667,7 @@ B readAny(BQNFFIEnt e, u8* ptr) {
BQNFFIType* t = c(BQNFFIType, e.o);
if (t->ty == cty_repr) { // cty_repr, scalar:x
return readRe(t->a[0], ptr);
} else if (t->ty == cty_struct) { // {...}
} else if (t->ty==cty_struct || t->ty==cty_starr) { // {...}
return readStruct(c(BQNFFIType, e.o), ptr);
}
}
@ -651,7 +693,7 @@ B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) {
}
} else {
BQNFFIType* t2 = c(BQNFFIType, e);
assert(t2->ty == cty_struct);
assert(t2->ty==cty_struct || t2->ty==cty_starr);
u8* dataAll = c(TAlloc,f)->data;
void* dataStruct = dataAll+sizeof(usz);
@ -671,7 +713,7 @@ B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) {
bool mut = t->a[0].mutPtr;
if (!mut) return m_f64(0);
return inc(f);
} else thrM("FFI: Unimplemented type");
} else thrM("FFI: Unimplemented type (buildObj)");
}
B libffiFn_c2(B t, B w, B x) {
@ -771,6 +813,21 @@ 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;
}
static u64 calcStaticSize(BQNFFIEnt e) {
if (isC32(e.o)) { // scalar
return atomSize(e.o);
} else {
BQNFFIType* t = c(BQNFFIType, e.o);
if (t->ty==cty_ptr || t->ty==cty_tlarr) return sizeof(void*); // *any / &any / top-level [n]any
else if (t->ty==cty_struct || t->ty==cty_starr) return t->structSize; // {...}
else if (t->ty==cty_repr) { // any:any
B o2 = t->a[0].o;
if (isC32(o2)) return atomSize(o2);
if (c(BQNFFIType,o2)->ty != cty_ptr) thrM("FFI: Bad type with reinterpretation");
return sizeof(void*);
} else thrM("FFI: Unimplemented type (size calculation)");
}
}
B ffiload_c2(B t, B w, B x) {
usz xia = IA(x);
@ -797,6 +854,7 @@ B ffiload_c2(B t, B w, B x) {
goto calcRes;
}
if (t->ty==cty_struct) { size = t->structSize; goto allocRes; }
if (t->ty==cty_tlarr) thrM("FFI: Cannot return array");
thrM("FFI: Unimplemented result type");
calcRes:; size = atomSize(atomType);
@ -812,23 +870,7 @@ B ffiload_c2(B t, B w, B x) {
for (usz i = 0; i < argn; i++) {
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);
STATIC_ALLOC(e, calcStaticSize(e));
args[i+1] = e;
(e.wholeArg? one : many)[e.extra] = true;
@ -888,7 +930,7 @@ B ffiload_c2(B t, B w, B x) {
#define FFI_TYPE_FLDS(OBJ, PTR) \
BQNFFIType* t = (BQNFFIType*) x; \
BQNFFIEnt* arr=t->a; usz ia=t->ia; \
if (t->ty == cty_struct) { \
if (t->ty==cty_struct || t->ty==cty_starr) { \
ia--; \
if (arr[ia].structData!=NULL) { \
PTR(arr[ia].structData); \
@ -906,7 +948,7 @@ void ffiType_print(FILE* f, B x) {
BQNFFIType* t = c(BQNFFIType,x);
fprintf(f, "cty_%d⟨", t->ty);
usz ia = t->ia;
if (t->ty==cty_struct) ia--;
if (t->ty==cty_struct || t->ty==cty_starr) ia--;
for (usz i=0; i<ia; i++) {
if (i) fprintf(f, ", ");
printFFIType(f, t->a[i].o);

View File

@ -250,3 +250,43 @@ uint32_t arrayRefOp(U32Arr* a) {
int plusone(int x) {
return x + 1;
}
int32_t arrarg(int32_t a[3]) {
return a[0] + a[1] + a[2];
}
typedef struct {
int32_t a[1];
} ArrStruct1;
int32_t arrstruct1(ArrStruct1 a, ArrStruct1* b) {
return a.a[0] + b->a[0];
}
int32_t ptrToArr(int32_t (*a)[2]) {
return a[3][0]+a[4][1];
}
typedef struct {
int32_t a[1];
ArrStruct1 b[2];
double c[3];
} ArrStruct2;
ArrStruct2 arrstruct2(ArrStruct2 a, ArrStruct2* b) {
return (ArrStruct2){
.a = {a.a[0] + b->a[0]},
.b = {{a.b[0].a[0] + b->b[1].a[0]}, {a.b[1].a[0] + b->b[0].a[0]}},
.c = {1.0, 2.0, a.c[0] + a.c[2] + b->c[1] + b->c[2]}
};
}
void arrstruct2Inc(ArrStruct2* b) {
for (int i = 0; i < 2; i++) {
b[i].a[0]++;
b[i].b[1].a[0]++;
b[i].c[1]++;
b[i].c[2]++;
}
}

View File

@ -90,11 +90,28 @@ f ↩ @ •FFI ⟨"i32", "putchar", ">i32"⟩ ⋄ F¨ 10∾˜"text"-@
•term.Flush@
f @ •FFI "a", "bqn_makeChar", ">u64" •Show F 120169
Section "# array types"
f "lib.so"•FFI"i32""arrarg"">[3]i32" •Show F 1,2,3
f "lib.so"•FFI"i32""ptrToArr"">*[2]i32" •Show F 00, 00, 00, 1020, 34
as1 "{[1]i32}"
f "lib.so"•FFI "i32", "arrstruct1", as1, "*"as1 •Show F 10, 20
as2 "{[1]i32,[2]"as1",[3]f64}"
f "lib.so"•FFI as2, "arrstruct2", as2, "*"as2 •Out •Repr F 4 ¨12 1.22.33.4, 5 ¨1020 5.16.17.1
f "lib.so"•FFI "&", "arrstruct2Inc", ">&"as2 •Out •Repr F (50+) 10 ¨1112 131415
# 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"
# "lib.so" •FFI ""‿"printArgs"‿"i8"‿"i16:c32"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64"
# "lib.so" •FFI ""‿"testArgs"‿"i8:c16"‿"i16"‿"i32"‿"u8"‿"u16"‿"u32"‿"f32"‿"f64"
# "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
# f ↩ "lib.so" •FFI "u64"‿"ident_u64"‿">u64:i32" ⋄ •Show F 1234‿12344444
# f ↩ "lib.so" •FFI "u64"‿"ident_u64"‿">u64" ⋄ •Show F +´2⋆53‿20
# f ↩ "lib.so"•FFI"i32"‿"arrarg"‿">[3]i32" ⋄ •Show F ⟨1,2⟩
# f ↩ "lib.so"•FFI ⟨as2, "arrstruct2", as2, "*"∾as2⟩ ⋄ •Show F ⟨⟨⟨4⟩ ⋄ ⋈∘⋈¨1‿2 ⋄ 1.2‿2.3‿3.4‿4⟩, ⟨⟨⟨5⟩ ⋄ ⋈∘⋈¨10‿20 ⋄ 5.1‿6.1‿7.1⟩⟩⟩
# f ↩ "lib.so"•FFI ⟨as2, "arrstruct2", as2, "*"∾as2⟩ ⋄ •Show F ⟨⟨⟨4⟩ ⋄ ⋈∘⋈¨1‿2 ⋄ 1.2‿2.3‿3.4⟩, ⟨⟨⟨5⟩ ⋄ ⋈∘⋈¨10‿20 ⋄ 5.1‿6.1‿7.1‿5⟩⟩⟩
# f ↩ "lib.so"•FFI ⟨as2, "arrstruct2", as2, "*"∾as2⟩ ⋄ •Show F ⟨⟨⟨4⟩ ⋄ ⋈∘⋈¨1‿2 ⋄ 1.2‿2.3‿3.4⟩, ⟨⟨⟨5⟩ ⋄ ⋈∘⋈¨10‿20 ⋄ 5.1‿6.1‿7.1,5⟩⟩⟩

View File

@ -115,3 +115,10 @@ ff7fdfefefdf7bb4 ff7fdfefefdf7bb4 fefffdfff7ffbffb bffff7fffdfffeff
# self-ffi
text
'𝕩'
# array types
6
14
30
⟨⟨9⟩,⟨⟨⟨21⟩⟩,⟨⟨12⟩⟩⟩,1‿2‿17.799999999999997⟩
⟨⟨⟨11⟩,⟨⟨⟨11⟩⟩,⟨⟨13⟩⟩⟩,13‿15‿16⟩,⟨⟨61⟩,⟨⟨⟨61⟩⟩,⟨⟨63⟩⟩⟩,63‿65‿66⟩⟩