refactor BQNFFIEnt & BQNFFIType

This commit is contained in:
dzaima 2024-02-05 04:14:02 +02:00
parent 102874c43f
commit 7ab13e3c5a
2 changed files with 187 additions and 191 deletions

375
src/ffi.c
View File

@ -233,36 +233,25 @@ DEF_FREE(ffiFn) { dec(((BoundFn*)x)->obj); }
// ..continuing under "#if FFI" // ..continuing under "#if FFI"
typedef struct BQNFFIEnt { typedef struct BQNFFIEnt {
B o;
union { union {
struct { // regular BQNFFIEnt usage void* ptrh_ptr; // cty_ptrh
union { u64 structOffset; // cty_struct, cty_starr
B o; // usual case struct { bool wholeArg, anyMut, resSingle; bool onW; u16 staticOffset; }; // cty_arglist, DecoratedType
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
};
}; };
} BQNFFIEnt; } BQNFFIEnt;
typedef struct BQNFFIType { typedef struct BQNFFIType {
struct Value; struct Value;
union { u16 structSize; u16 arrCount; u16 staticAllocTotal; };
u8 ty; 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[]; BQNFFIEnt a[];
} BQNFFIType; } BQNFFIType;
@ -337,20 +326,21 @@ static char* const sty_names[] = {
[sty_f32]="f32", [sty_f64]="f64" [sty_f32]="f32", [sty_f64]="f64"
}; };
enum CompoundTy { enum CompoundTy {
cty_ptr, // *... / &...; .a = {{.mutPtr = startsWith("&"), .o=eltype}} cty_ptr, // *... / &...; .a = {{.o=eltype}}, .mutPtr = startsWith("&")
cty_tlarr, // top-level array; {{.o=eltype}}, arrCount=n cty_tlarr, // top-level array; {{.o=eltype}}, .arrCount=n
cty_struct, // {...}; .a = {{.o=el0}, {.o=el1}, ..., {.o=elLast}, {.structData = ffi_type**}} 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_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_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_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); BQNFFIType* r = mm_alloc(fsizeof(BQNFFIType, a, BQNFFIEnt, ia), t_ffiType);
r->ty = ty; r->ty = ty;
r->ia = ia; r->ia = ia;
memset(r->a, 0, ia*sizeof(BQNFFIEnt)); memset(r->a, 0, ia*sizeof(BQNFFIEnt));
*rp = r->a; *rp = r;
return tag(r, OBJ_TAG); return tag(r, OBJ_TAG);
} }
@ -365,7 +355,34 @@ static u32 readUInt(u32** p) {
*p = c; *p = c;
return r; 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* c = *src;
u32 c0 = *c++; u32 c0 = *c++;
ffi_type rt; ffi_type rt;
@ -380,39 +397,30 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
u32 n = readUInt(&c); u32 n = readUInt(&c);
if (*c++!=']') thrM("Type parser: Bad array type"); if (*c++!=']') thrM("Type parser: Bad array type");
if (n==0) thrM("Type parser: 0-item arrays not supported"); 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*); myWidth = sizeof(void*);
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_tlarr, 1); BQNFFIType* rp; ro = m_bqnFFIType(&rp, cty_tlarr, 1);
rp[0] = e; rp->a[0].o = e.o;
if (n>U16_MAX) thrM("Type parser: Top-level array too large; limit is 65535 elements"); if (n>U16_MAX) thrM("Type parser: Top-level array too large; limit is 65535 elements");
c(BQNFFIType, ro)->arrCount = n; rp->arrCount = n;
mut|= rp[0].mutates; mut|= e.anyMutation;
parseRepr = rp[0].canRetype; parseRepr = e.canRetype;
rp[0].mutPtr = false;
rt = ffi_type_pointer; rt = ffi_type_pointer;
} else { // largely copy-pasted from `case '{':` } else {
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_starr, n+1); BQNFFIType* rp; ro = m_bqnFFIType(&rp, cty_starr, n+1); // +1 for ARBOBJ
PLAINLOOP for (int i = 0; i < n; i++) rp[i] = e;
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); incBy(e.o, n-1);
rp[n].structData = NULL;
TAlloc* ao = ARBOBJ(sizeof(ffi_type*) * (n+1)); rt = storeStructOffsets(rp, list, n);
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);
} }
break; break;
} }
@ -453,46 +461,42 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty
canRetype = inPtr; canRetype = inPtr;
} else { } else {
if (c0=='&') mut = true; if (c0=='&') mut = true;
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_ptr, 1); ParsedType e = ffi_parseType(&c, true, false);
rp[0] = ffi_parseTypeStr(&c, true, false);
mut|= rp[0].mutates;
parseRepr = rp[0].canRetype;
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; rt = ffi_type_pointer;
break; break;
} }
case '{': { case '{': {
TSALLOC(BQNFFIEnt, es, 4); TSALLOC(ParsedType, es, 4);
while (true) { while (true) {
BQNFFIEnt e = TSADD(es, ffi_parseTypeStr(&c, false, false)); ParsedType e = TSADD(es, ffi_parseType(&c, false, false));
if (e.mutates) thrM("Type parser: Structs currently cannot contain mutable references"); if (e.anyMutation) thrM("Type parser: Structs currently cannot contain mutable references");
u32 m = *c++; u32 m = *c++;
if (m=='}') break; if (m=='}') break;
if (m!=',') thrM("Type parser: Improper struct separator or end"); if (m!=',') thrM("Type parser: Improper struct separator or end");
} }
usz n = TSSIZE(es); 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)); BQNFFIType* rp; ro = m_bqnFFIType(&rp, cty_struct, n+1); // +1 for ARBOBJ
rp[n].structData = ao;
ffi_type** els = rt.elements = (ffi_type**) ao->data; ALLOC_TYPE_LIST(n, n);
PLAINLOOP for (usz i = 0; i < n; i++) els[i] = &rp[i].t; rp->a[n].o = tag(ao, OBJ_TAG);
els[n] = NULL; 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); TSFREE(es);
rt.alignment = rt.size = 0;
rt.type = FFI_TYPE_STRUCT;
TALLOC(size_t, offsets, n); rt = storeStructOffsets(rp, list, 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);
break; 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 // TODO figure out what to do with i32:i32 etc
B roP = ro; B roP = ro;
BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_repr, 1); BQNFFIType* rp; ro = m_bqnFFIType(&rp, cty_repr, 1);
rp[0] = (BQNFFIEnt){.o=roP, .t=rt, .reType=t, .reWidth=63-CLZ(n)}; rp->reType = t;
rp->reWidth = 63-CLZ(n);
rp->a[0].o = roP;
} }
*src = c; *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 static NOINLINE u32* parseType_pre(B arg, ux ia) { // doesn't consume; arg can be freed immediately after
TALLOC(u32, xp, ia+1); 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"); if (xp0+ia != xp) thrM("Type parser: Garbage at end of type");
TFREE(xp0); 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"); vfyStr(arg, "FFI", "type");
usz ia = IA(arg); usz ia = IA(arg);
if (ia==0) { if (ia==0) {
if (!forRes) thrM("Type parser: Type was empty"); 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)); arg = chr_squeezeChk(incG(arg));
u32* xp0 = parseType_pre(arg, ia); u32* xp0 = parseType_pre(arg, ia);
u32* xp = xp0; u32* xp = xp0;
BQNFFIEnt t; DecoratedType t;
if (forRes && xp[0]=='&' && xp[1]=='\0') { 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; xp+= 1;
} else { } else {
u8 side = 0; u8 side = 0;
@ -558,13 +569,11 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument
if (side) side--; if (side) side--;
else side = 0; else side = 0;
t = ffi_parseTypeStr(&xp, false, true); ParsedType e = ffi_parseType(&xp, false, true);
// printI(arg); printf(": "); printFFIType(stdout, t.o); printf("\n"); t = (DecoratedType) {
{.o=e.o, .onW=side, .anyMut=e.anyMutation, .wholeArg=whole, .resSingle=false},
t.onW = side; .ffitype = e.ffitype
// keep .mutates };
t.wholeArg = whole;
t.resSingle = false;
} }
parseType_post(xp0, xp, ia); 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 NOINLINE B ptrobj_checkget(B x); // doesn't consume
static bool ptrty_equal(B a, B b); static bool ptrty_equal(B a, B b);
static B ptrh_type(B n); // returns ptrty static B ptrh_type(B n); // returns ptrty
static BQNFFIEnt* ptrh_more(B n);
static void* ptrh_ptr(B n); static void* ptrh_ptr(B n);
static NOINLINE void ty_fmt_add(B* s0, B o) { 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("???"); A8("???");
break; break;
case cty_ptr: case cty_ptr:
ACHR(t->a[0].mutPtr? '&' : '*'); ACHR(t->mutPtr? '&' : '*');
ty_fmt_add(&s, t->a[0].o); ty_fmt_add(&s, t->a[0].o);
break; break;
case cty_starr: case cty_starr:
@ -665,7 +673,7 @@ static NOINLINE void ty_fmt_add(B* s0, B o) {
break; break;
case cty_repr: case cty_repr:
ty_fmt_add(&s, t->a[0].o); 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; break;
} }
} }
@ -711,7 +719,7 @@ void genObj(B o, B c, bool anyMut, void* ptr) { // doesn't consume
} }
} else { } else {
BQNFFIType* t = c(BQNFFIType, o); 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; B e = t->a[0].o;
if (isAtm(c)) { if (isAtm(c)) {
if (isNsp(c)) { genObj_ptr(ptr, c, e); return; } 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 if (isC32(e)) { // *num / &num
incG(c); incG(c);
B cG; 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)]); 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_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; 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 } else if (t->ty==cty_repr) { // any:any
B o2 = t->a[0].o; B o2 = t->a[0].o;
u8 reT = t->a[0].reType; u8 reT = t->reType;
u8 reW = t->a[0].reWidth; u8 reW = t->reWidth;
if (isC32(o2)) { // scalar:any (incl. *:any) if (isC32(o2)) { // scalar:any (incl. *:any)
u8 et = o2cG(o2); u8 et = o2cG(o2);
bool eptr = et==sty_ptr; 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 (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)); 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)); 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); dec(cG);
} else { // *scalar:any / &scalar:any } else { // *scalar:any / &scalar:any
BQNFFIType* t2 = c(BQNFFIType, o2); BQNFFIType* t2 = c(BQNFFIType, o2);
B ore = t2->a[0].o; B ore = t2->a[0].o;
if (isNsp(c)) { genObj_ptr(ptr, c, ore); return; } if (isNsp(c)) { genObj_ptr(ptr, c, ore); return; }
assert(t2->ty==cty_ptr && isC32(ore)); // we shouldn't be generating anything else assert(t2->ty==cty_ptr && isC32(ore));
bool mut = t2->a[0].mutPtr; bool mut = t2->mutPtr;
u8 et = o2cG(ore); u8 et = o2cG(ore);
u8 mul = (sty_w[et]*8) >> reW; 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) 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.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)"); } else thrM("FFI: Unimplemented type (genObj)");
} }
@ -808,7 +816,7 @@ B readStruct(BQNFFIType* t, u8* ptr) {
usz ia = t->ia-1; usz ia = t->ia-1;
M_HARR(r, ia); M_HARR(r, ia);
for (usz i = 0; i < ia; i++) { 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)); HARR_ADD(r, i, readAny(t->a[i].o, c));
} }
return HARR_FV(r); return HARR_FV(r);
@ -833,10 +841,10 @@ B readSimple(u8 resCType, u8* ptr) {
return r; return r;
} }
B readRe(BQNFFIEnt e, u8* ptr) { B readRe(BQNFFIType* t, u8* ptr) {
u8 et = o2cG(e.o); u8 et = o2cG(t->a[0].o);
u8 reT = e.reType; u8 reT = t->reType;
u8 reW = e.reWidth; u8 reW = t->reWidth;
u8 etw = sty_w[et]; u8 etw = sty_w[et];
return makeRe(reT, reW, ptr, etw); return makeRe(reT, reW, ptr, etw);
} }
@ -847,7 +855,7 @@ B readAny(B o, u8* ptr) { // doesn't consume
} else { } else {
BQNFFIType* t = c(BQNFFIType, o); BQNFFIType* t = c(BQNFFIType, o);
if (t->ty == cty_repr) { // cty_repr, scalar:x 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]... } else if (t->ty==cty_struct || t->ty==cty_starr) { // {...}, [n]...
return readStruct(c(BQNFFIType, o), ptr); 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) { B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) {
if (isC32(ent.o)) return m_f64(0); // scalar if (isC32(ent.o)) return m_f64(0); // scalar
BQNFFIType* t = c(BQNFFIType, ent.o); 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 e = t->a[0].o;
B f = objs[(*objPos)++]; B f = objs[(*objPos)++];
if (t->a[0].mutPtr) { if (t->ty==cty_ptr && t->mutPtr) {
if (isC32(e)) { if (isC32(e)) {
switch(o2cG(e)) { default: UD; switch(o2cG(e)) { default: UD;
case sty_i8: case sty_i16: case sty_i32: case sty_f64: return inc(f); 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 BQNFFIType* t2 = c(BQNFFIType,o2); // *scalar:any / &scalar:any
assert(t2->ty == cty_ptr); assert(t2->ty == cty_ptr);
B f = objs[(*objPos)++]; B f = objs[(*objPos)++];
if (!t2->a[0].mutPtr) return m_f64(0); return t2->mutPtr? inc(f) : m_f64(0);
return inc(f);
} else if (t->ty==cty_struct || t->ty==cty_starr) { } else if (t->ty==cty_struct || t->ty==cty_starr) {
assert(!anyMut); // Structs currently cannot contain mutable references 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) { B libffiFn_c2(B t, B w, B x) {
BoundFn* bf = c(BoundFn,t); 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) \ #define PROC_ARG(ISX, L, U, S) \
Arr* L##a ONLY_GCC(=0); \ Arr* L##a ONLY_GCC(=0); \
@ -928,14 +935,14 @@ B libffiFn_c2(B t, B w, B x) {
i32 idxs[2] = {0,0}; i32 idxs[2] = {0,0};
u8* tmpAlloc = TALLOCP(u8, c(BQNFFIType,argObj)->staticAllocTotal); u8* tmpAlloc = TALLOCP(u8, argObj->staticAllocTotal);
void** argPtrs = (void**) tmpAlloc; void** argPtrs = (void**) tmpAlloc;
B cifObj = c(HArr,bf->obj)->a[1]; B cifObj = c(HArr,bf->obj)->a[1];
ffi_cif* cif = (void*) c(TAlloc,cifObj)->data; ffi_cif* cif = (void*) c(TAlloc,cifObj)->data;
usz argn = cif->nargs; usz argn = cif->nargs;
BQNFFIEnt* ents = c(BQNFFIType,argObj)->a; BQNFFIEnt* ents = argObj->a;
ffiObjsGlobal = emptyHVec(); // implicit parameter to genObj ffiObjsGlobal = emptyHVec(); // implicit parameter to genObj
for (usz i = 0; i < argn; i++) { for (usz i = 0; i < argn; i++) {
BQNFFIEnt e = ents[i+1]; BQNFFIEnt e = ents[i+1];
@ -945,14 +952,14 @@ B libffiFn_c2(B t, B w, B x) {
} else { } else {
o = (e.onW? wf : xf)(e.onW?wa:xa, idxs[e.onW]++); 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 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; for (usz i = 0; i < argn; i++) argPtrs[i] = tmpAlloc + ents[i+1].staticOffset;
void* res = tmpAlloc + ents[0].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)); // if (!(i&15)) printf("%s%p ", i?"\n":"", (void*)(tmpAlloc+i));
// else if (!(i&3)) printf(" "); // else if (!(i&3)) printf(" ");
// printf("%x%x ", tmpAlloc[i]>>4, tmpAlloc[i]&15); // printf("%x%x ", tmpAlloc[i]>>4, tmpAlloc[i]&15);
@ -971,7 +978,7 @@ B libffiFn_c2(B t, B w, B x) {
} else { } else {
BQNFFIType* t = c(BQNFFIType, ents[0].o); BQNFFIType* t = c(BQNFFIType, ents[0].o);
if (t->ty == cty_repr) { // cty_repr, scalar:x 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) { // {...} } else if (t->ty == cty_struct) { // {...}
r = readStruct(c(BQNFFIType, ents[0].o), res); r = readStruct(c(BQNFFIType, ents[0].o), res);
} else if (t->ty == cty_ptr) { // *... } else if (t->ty == cty_ptr) { // *...
@ -989,16 +996,16 @@ B libffiFn_c2(B t, B w, B x) {
if (resSingle) { if (resSingle) {
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 c = buildObj(e, e.mutates, harr_ptr(ffiObjs), &objPos); B c = buildObj(e, e.anyMut, harr_ptr(ffiObjs), &objPos);
if (e.mutates) r = c; if (e.anyMut) r = c;
} }
} else { } else {
M_HARR(ra, mutArgs+(resVoid? 0 : 1)); M_HARR(ra, mutArgs+(resVoid? 0 : 1));
if (!resVoid) HARR_ADDA(ra, r); if (!resVoid) HARR_ADDA(ra, r);
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 c = buildObj(e, e.mutates, harr_ptr(ffiObjs), &objPos); B c = buildObj(e, e.anyMut, harr_ptr(ffiObjs), &objPos);
if (e.mutates) HARR_ADDA(ra, c); if (e.anyMut) HARR_ADDA(ra, c);
} }
if (testBuildObj && !mutArgs) { inc(r); HARR_ABANDON(ra); } if (testBuildObj && !mutArgs) { inc(r); HARR_ABANDON(ra); }
else r = HARR_FV(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 #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"); 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){}; 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 u64 staticAlloc = ffiTmpAlign(argn * sizeof(void*)); // initial alloc is the argument pointer list
#define STATIC_ALLOC(O, SZ) ({ (O).staticOffset = staticAlloc; staticAlloc+= ffiTmpAlign(SZ); }) #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; B atomType;
usz size; 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) { if (t->ty == cty_repr) {
B o2 = t->a[0].o; B o2 = t->a[0].o;
if (!isC32(o2)) thrM("FFI: Unimplemented result type"); if (!isC32(o2)) thrM("FFI: Unimplemented result type");
@ -1079,35 +1087,35 @@ B ffiload_c2(B t, B w, B x) {
size = calcAtomSize(atomType); size = calcAtomSize(atomType);
goto allocRes; goto allocRes;
allocRes:; STATIC_ALLOC(tRes, size); allocRes:; STATIC_ALLOC(eRes, size);
} }
#if FFI==2 ALLOC_TYPE_LIST(argn+1, argn);
BQNFFIEnt* args; B argObj = m_bqnFFIType(&args, 255, argn+1); elts[argn] = tRes.ffitype;
args[0] = tRes;
bool whole[2]={0,0}; BQNFFIType* ap; B argObj = m_bqnFFIType(&ap, cty_arglist, argn+1);
i32 count[2]={0,0}; ap->a[0] = eRes;
i32 mutCount = 0; bool whole[2]={0,0};
for (usz i = 0; i < argn; i++) { i32 count[2]={0,0};
BQNFFIEnt e = ffi_parseType(GetU(x,i+2), false); i32 mutCount = 0;
for (usz i = 0; i < argn; i++) {
STATIC_ALLOC(e, calcStaticSize(e.o)); DecoratedType tArg = ffi_parseDecoratedType(GetU(x,i+2), false);
BQNFFIEnt e = tArg.ent;
args[i+1] = e; STATIC_ALLOC(e, calcStaticSize(e.o));
if (e.wholeArg) whole[e.onW] = true;
count[e.onW]++; elts[i] = tArg.ffitype;
if (e.mutates) mutCount++; list[i] = &elts[i];
}
if (staticAlloc > U16_MAX-64) thrM("FFI: Static argument size too large"); ap->a[i+1] = e;
c(BQNFFIType,argObj)->staticAllocTotal = ffiTmpAlign(staticAlloc); if (e.wholeArg) whole[e.onW] = true;
if (count[0]>1 && whole[0]) thrM("FFI: Multiple arguments on 𝕩 specified, some with '>'"); count[e.onW]++;
if (count[1]>1 && whole[1]) thrM("FFI: Multiple arguments on 𝕨 specified, some with '>'"); if (e.anyMut) mutCount++;
#else }
i32 mutCount = 0; if (staticAlloc > U16_MAX-64) thrM("FFI: Static argument size too large");
for (usz i = 0; i < argn; i++) ffi_parseType(GetU(x,i+2), false); ap->staticAllocTotal = ffiTmpAlign(staticAlloc);
(void)tRes; if (count[0]>1 && whole[0]) thrM("FFI: Multiple arguments on 𝕩 specified, some with '>'");
#endif if (count[1]>1 && whole[1]) thrM("FFI: Multiple arguments on 𝕨 specified, some with '>'");
if (tRes.resSingle && mutCount!=1) thrF("FFI: Return value is specified as \"&\", but there are %i mutated values", mutCount); if (eRes.resSingle && mutCount!=1) thrF("FFI: Return value is specified as \"&\", but there are %i mutated values", mutCount);
char* ws = NULL; char* ws = NULL;
if (w.u != m_c32(0).u) { if (w.u != m_c32(0).u) {
@ -1129,21 +1137,16 @@ B ffiload_c2(B t, B w, B x) {
#if FFI==1 #if FFI==1
return m_ffiFn(foreignFnDesc, bi_N, directFn_c1, directFn_c2, sym, sym); return m_ffiFn(foreignFnDesc, bi_N, directFn_c1, directFn_c2, sym, sym);
#else #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++) { // for (usz i = 0; i < argn; i++) {
// ffi_type c = *argsRawArr[i]; // ffi_type c = *argsRawArr[i];
// printf("%zu %d %d %p\n", c.size, c.alignment, c.type, c.elements); // printf("%zu %d %d %p\n", c.size, c.alignment, c.type, c.elements);
// } // }
TAlloc* cif = ARBOBJ(sizeof(ffi_cif)); 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"); if (s!=FFI_OK) thrM("FFI: Error preparing call interface");
// mm_free(argsRaw) // mm_free(argsRaw)
u32 flags = tRes.resSingle<<2; u32 flags = eRes.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); 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)->mutCount = mutCount;
c(BoundFn,r)->wLen = whole[1]? -1 : count[1]; c(BoundFn,r)->wLen = whole[1]? -1 : count[1];
c(BoundFn,r)->xLen = whole[0]? -1 : count[0]; c(BoundFn,r)->xLen = whole[0]? -1 : count[0];
@ -1151,24 +1154,16 @@ B ffiload_c2(B t, B w, B x) {
#endif #endif
} }
#define FFI_TYPE_FLDS(OBJ, PTR) \ #define FFI_TYPE_FLDS(OBJ) \
BQNFFIType* t = (BQNFFIType*) x; \ BQNFFIType* t = (BQNFFIType*) x; \
BQNFFIEnt* arr=t->a; usz ia=t->ia; \ 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); \
} \
} \
for (usz i = 0; i < ia; i++) OBJ(arr[i].o); for (usz i = 0; i < ia; i++) OBJ(arr[i].o);
DEF_FREE(ffiType) { DEF_FREE(ffiType) {
FFI_TYPE_FLDS(dec, ptr_dec); FFI_TYPE_FLDS(dec);
} }
void ffiType_visit(Value* x) { void ffiType_visit(Value* x) {
FFI_TYPE_FLDS(mm_visit, mm_visitP); FFI_TYPE_FLDS(mm_visit);
} }
void ffiType_print(FILE* f, B x) { void ffiType_print(FILE* f, B x) {
BQNFFIType* t = c(BQNFFIType,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}} // ptrh - pointer holder - BQNFFIType object of {{.o=eltype}, {.ptrh_ptr, .ptrh_stride}}
// eltype of sty_void is used for an untyped pointer // 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 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 c(BQNFFIType, n)->a[0].ptrh_ptr; }
static void* ptrh_ptr(B n) { return ptrh_more(n)->ptrh_ptr; } static ux ptrh_stride(B n) { return c(BQNFFIType, n)->ptrh_stride; }
static ux ptrh_stride(B n) { return ptrh_more(n)->ptrh_stride; }
B m_ptrobj_s(void* ptr, B o) { B m_ptrobj_s(void* ptr, B o) {
return m_ptrobj(ptr, o, calcMemSize(o)); return m_ptrobj(ptr, o, calcMemSize(o));
} }
B m_ptrobj(void* ptr, B o, ux stride) { B m_ptrobj(void* ptr, B o, ux stride) {
BQNFFIEnt* op; BQNFFIType* op;
B obj = m_bqnFFIType(&op, cty_ptrh, 2); B obj = m_bqnFFIType(&op, cty_ptrh, 1);
op[0].o = o; op->ptrh_stride = stride;
op[1].ptrh_ptr = ptr; op->a[0].o = o;
op[1].ptrh_stride = stride; op->a[0].ptrh_ptr = ptr;
return m_nns(ptrobj_ns, return m_nns(ptrobj_ns,
m_nfn(ptrReadDesc, incG(obj)), m_nfn(ptrReadDesc, incG(obj)),
@ -1256,9 +1250,9 @@ static B ptrobjCast_c1(B t, B x) {
u32* xp0 = parseType_pre(x, ia); u32* xp0 = parseType_pre(x, ia);
u32* xp = xp0; u32* xp = xp0;
decG(x); decG(x);
BQNFFIEnt parsed = ffi_parseTypeStr(&xp, false, false); ParsedType parsed = ffi_parseType(&xp, false, false);
B r = m_ptrobj_s(ptr, parsed.o);
parseType_post(xp0, xp, ia); parseType_post(xp0, xp, ia);
B r = m_ptrobj_s(ptr, parsed.o);
return r; return r;
} }
static B ptrobjField_c1(B t, B x) { 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); 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]; BQNFFIEnt* fldptr = &elc->a[fld];
elNew = inc(fldptr->o); elNew = inc(fldptr->o);
ptr+= fldptr->offset; ptr+= fldptr->structOffset;
} else { } else {
thrM("Pointer object: Field must be applied to a pointer to a struct or array"); 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) { 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); boundFnDesc = registerNFn(m_c8vec_0("(foreign function)"), boundFn_c1, boundFn_c2);
foreignFnDesc = registerNFn(m_c8vec_0("(foreign function)"), directFn_c1, directFn_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) 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)

View File

@ -42,7 +42,7 @@
!"Type parser: number literal too large" % @•FFI"""bqn_init""[999999999999]i32" !"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""[65536]i32"
!"Type parser: Top-level array too large; limit is 65535 elements" % @•FFI"""bqn_init""[4000000000]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" !"FFI: Cannot return array" % @•FFI"[4]i32""bqn_init"
# structs # structs
@ -210,6 +210,7 @@
!"Cannot offset an untyped pointer" % %USE WallocE {{𝕊al: ("" Al 99).Add 1}} !"Cannot offset an untyped pointer" % %USE WallocE {{𝕊al: ("" Al 99).Add 1}}
# .Add, .Sub, .Cast # .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.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.Add 123}} %% ¯123
%USE Walloc {{𝕊al: p"{i32,*}" Al 1 p.Sub p.Sub 123}} %% 123 %USE Walloc {{𝕊al: p"{i32,*}" Al 1 p.Sub p.Sub 123}} %% 123