proper bi_optOut handling

This commit is contained in:
dzaima 2024-02-01 21:12:47 +02:00
parent 592a8312ac
commit 88f8daedbf
7 changed files with 65 additions and 31 deletions

12
src/h.h
View File

@ -176,7 +176,7 @@ typedef u8 ur;
// .FF. .111111111110nnn................................................ sNaN aka tagged aka not f64, if nnn≠0
// 7FF. 0111111111110................................................... direct value with no need of refcounting
static const u16 C32_TAG = 0b0111111111110001; // 7FF1 0111111111110001................00000000000ccccccccccccccccccccc char
static const u16 TAG_TAG = 0b0111111111110010; // 7FF2 0111111111110010................nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn special value (0=nothing, 1=undefined var, 2=bad header; 3=optimized out; 4=error?; 5=no fill)
static const u16 TAG_TAG = 0b0111111111110010; // 7FF2 0111111111110010nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn special value (see bi_N ect definitions further below)
static const u16 VAR_TAG = 0b0111111111110011; // 7FF3 0111111111110011ddddddddddddddddnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn variable reference
static const u16 EXT_TAG = 0b0111111111110100; // 7FF4 0111111111110100ddddddddddddddddnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn extended variable reference
static const u16 RAW_TAG = 0b0111111111110101; // 7FF5 0111111111110101nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn raw 48 bits of data
@ -346,11 +346,11 @@ u64 mm_heapUsed(void);
#endif
// some primitive actions
static const B bi_N = b((u64)0x7FF2000000000000ull); // tag(0,TAG_TAG); // make gcc happy
static const B bi_noVar = b((u64)0x7FF2000000000001ull); // tag(1,TAG_TAG);
static const B bi_okHdr = b((u64)0x7FF2000000000002ull); // tag(2,TAG_TAG);
static const B bi_optOut = b((u64)0x7FF2000000000003ull); // tag(3,TAG_TAG);
static const B bi_noFill = b((u64)0x7FF2000000000005ull); // tag(5,TAG_TAG);
static const B bi_N = b((u64)0x7FF2000000000000ull);
static const B bi_noVar = b((u64)0x7FF2C00000000001ull);
static const B bi_okHdr = b((u64)0x7FF2000000000002ull);
static const B bi_optOut = b((u64)0x7FF2800000000003ull);
static const B bi_noFill = b((u64)0x7FF2000000000005ull);
extern GLOBAL B bi_emptyHVec, bi_emptyIVec, bi_emptyCVec, bi_emptySVec;
#define emptyHVec() incG(bi_emptyHVec)
#define emptyIVec() incG(bi_emptyIVec)

View File

@ -164,12 +164,12 @@ INS B i_MD2C(B f,B m, B g, u32* bc) { POS_UPD; return m2_d (m,f,g); }
INS B i_TR2D(B g, B h ) { return m_atop( g,h); }
INS B i_TR3D(B f,B g, B h ) { return m_fork(f,g,h); }
INS B i_TR3O(B f,B g, B h ) { return q_N(f)? m_atop(g,h) : m_fork(f,g,h); }
INS B i_NOVAR(u32* bc, B* cStack) {
POS_UPD; GS_UPD; thrM("Reading variable before its defined");
INS B i_BADREAD(u32* bc, B* cStack, B var) {
POS_UPD; GS_UPD; v_tagError(var, 0);
}
INS B i_EXTO(u32 p, Scope* sc, u32* bc, B* cStack) {
B l = sc->ext->vars[p];
if(l.u==bi_noVar.u) { POS_UPD; GS_UPD; thrM("Reading variable before its defined"); }
if(isTag(l)) { POS_UPD; GS_UPD; v_tagError(l, false); }
return inc(l);
}
INS B i_EXTU(u32 p, Scope* sc) {
@ -225,8 +225,8 @@ INS B i_SETMi(B f, B x, Scope* sc, u32 p, u32* bc) { POS_UPD; B r = c2(f,v_getI(
INS B i_SETCi(B f, Scope* sc, u32 p, u32* bc) { POS_UPD; B r = c1(f,v_getI(sc, p, true) ); dec(f); v_setI(sc, p, inc(r), true, false); return r; }
INS void i_SETNv(B x, Scope* sc, u32 p ) { v_setI(sc, p, x, false, false); }
INS void i_SETUv(B x, Scope* sc, u32 p, u32* bc) { POS_UPD; v_setI(sc, p, x, true, false); }
INS void i_SETMv(B f, B x, Scope* sc, u32 p, u32* bc) { POS_UPD; B r = c2(f,v_getI(sc, p, false),x); dec(f); v_setI(sc, p, r, true, false); }
INS void i_SETCv(B f, Scope* sc, u32 p, u32* bc) { POS_UPD; B r = c1(f,v_getI(sc, p, false) ); dec(f); v_setI(sc, p, r, true, false); }
INS void i_SETMv(B f, B x, Scope* sc, u32 p, u32* bc) { POS_UPD; B r = c2(f,v_getI(sc, p, true),x); dec(f); v_setI(sc, p, r, true, false); }
INS void i_SETCv(B f, Scope* sc, u32 p, u32* bc) { POS_UPD; B r = c1(f,v_getI(sc, p, true) ); dec(f); v_setI(sc, p, r, true, false); }
INS B i_FLDG(B ns, u32 p, Scope* sc) {
if (!isNsp(ns)) thrM("Trying to read a field from non-namespace");
B r = inc(ns_getU(ns, p));
@ -647,7 +647,7 @@ Nvm_res m_nvm(Body* body) {
#define LSC(R,D) { if(D) MOV8rmo(R,R_SP,VAR8(pscs,D)); else MOV(R,r_SC); } // TODO return r_SC directly without a pointless mov
#define INCV(R) INC4mo(R, offsetof(Value,refc)); // ADD4mi(R_A3, 1); CCALL(i_INC);
#ifdef __BMI2__ // TODO move to runtime detection maybe
#define INCB(R,T,U) IMM(T,0xfffffffffffffull);ADD(T,R);IMM(U,0x7fffffffffffeull);CMP(T,U);{J1(cA,lI);MOVi1l(U,0x30);BZHI(U,R,U);INCV(U);LBL1(lI);}
#define INCB(R,T,U) IMM(T,0xfffffffffffffull);ADD(T,R);IMM(U,0x7fffffffffffeull);CMP(T,U);{J1(cA,lI);MOVi1l(U,48);BZHI(U,R,U);INCV(U);LBL1(lI);}
#else
#define INCB(R,T,U) IMM(T,0xfffffffffffffull);ADD(T,R);IMM(U,0x7fffffffffffeull);CMP(T,U);{J1(cA,lI);IMM(U,0xffffffffffffull);AND(U,R);INCV(U);LBL1(lI);}
#endif
@ -698,8 +698,8 @@ Nvm_res m_nvm(Body* body) {
case EXTM: TOPs; { u64 d=*bc++; u64 p=*bc++; IMM(R_RES, tagu64((u64)d<<32 | (u32)p, EXT_TAG).u); } break;
case VARO: TOPs; { u64 d=*bc++; u64 p=*bc++; LSC(R_A1,d);
MOV8rmo(R_RES,R_A1,p*8+offsetof(Scope,vars)); // read variable
MOV(R_A2,R_RES); SHR8i(R_A2, 47); CMP4i(R_A2, v_bad17_read); { J1(cNE,lN); IMM(R_A0,off); MOV(R_A2,R_RES); INV(1,1,i_BADREAD); LBL1(lN); } // check for error
INCB(R_RES,R_A2,R_A3); // increment refcount if one's needed
if (d) { IMM(R_A2, bi_noVar.u); CMP(R_A2,R_RES); J1(cNE,lN); IMM(R_A0,off); INV(1,1,i_NOVAR); LBL1(lN); } // check for error
} break;
case VARU: TOPs; { u64 d=*bc++; u64 p=*bc++;
LSC(R_A1,d); MOV8rmo(R_RES,R_A1,p*8+offsetof(Scope,vars)); // read variable

View File

@ -197,7 +197,10 @@ ASMI(XOR, Reg o, Reg i) { ASMS; REX8(o,i); ASM1(0x31); MRMr(o,i); ASME; }
ASMI(AND, Reg o, Reg i) { ASMS; REX8(o,i); ASM1(0x21); MRMr(o,i); ASME; }
ASMI(XOR4, Reg o, Reg i) { ASMS; REX4(o,i); ASM1(0x31); MRMr(o,i); ASME; }
ASMI(CMP, Reg o, Reg i) { ASMS; REX8(o,i); ASM1(0x39); MRMr(o,i); ASME; }
ASMI(CMP, Reg o, Reg i) { ASMS; REX8(o,i); ASM1(0x39); MRMr(o,i); ASME; }
ASMI(CMP2i,Reg o, i16 imm) { ASMS; REX2(o,0); ASM1(0x81); MRM1(o,0xF8); ASM2(imm); ASME; }
ASMI(CMP4i,Reg o, i32 imm) { ASMS; REX4(o,0); ASM1(0x81); MRM1(o,0xF8); ASM4(imm); ASME; }
ASMI(ADDi, Reg o, i32 imm) { if(!imm) return; ASMS; REX8(o,0); if(imm==(i8)imm) { ASM1(0x83); MRM1(o,0xc0); ASM1(imm); } else { ASM1(0x81); MRM1(o,0xc0); ASM4(imm); } ASME; }
ASMI(SUBi, Reg o, i32 imm) { if(!imm) return; ASMS; REX8(o,0); if(imm==(i8)imm) { ASM1(0x83); MRM1(o,0xe8); ASM1(imm); } else { ASM1(0x81); MRM1(o,0xe8); ASM4(imm); } ASME; }

View File

@ -824,7 +824,16 @@ void base_init() { // very first init function
TIi(t_customObj,visit) = customObj_visit;
TIi(t_arbObj,visit) = noop_visit;
assert((MD1_TAG>>1) == (MD2_TAG>>1)); // just to be sure it isn't changed incorrectly, `isMd` depends on this
// making sure that expected properties of constants hold
assert((MD1_TAG>>1) == (MD2_TAG>>1)); // dependencies: isMd
// dependencies: v_tagCheck & the JIT equivalent
assert(v_bad17_read == (bi_noVar.u >>47));
assert(v_bad17_read == (bi_optOut.u>>47));
assert(v_bad17_read != (bi_N.u >>47));
assert(v_bad18_write == (bi_noVar.u >>46));
assert(v_bad18_write != (bi_optOut.u>>46));
assert(v_bad18_write != (bi_N.u >>46));
#define FA(N,X) bi_##N = m_bfn(N##_c1, N##_c2, pf_##N);
#define FM(N,X) bi_##N = m_bfn(N##_c1, c2_bad, pf_##N);

View File

@ -537,6 +537,12 @@ FORCE_INLINE bool v_merge(Scope* pscs[], B s, B x, bool upd, bool hdr) {
return true;
}
NOINLINE NORETURN void v_tagError(B x, bool write) {
char* act = write? "Assignment: Attempting to modify" : "Attempting to read";
if (x.u == bi_noVar.u) thrF("%S variable which is not yet defined", act);
if (x.u == bi_optOut.u) thrF("%S variable which isn't available due to incomplete or aborted F↩", act);
fatal("Unexpected v_tagError argument");
}
NOINLINE void v_setF(Scope* pscs[], B s, B x, bool upd) {
if (isArr(s)) { VTY(s, t_harr);
B* sp = harr_ptr(s);
@ -571,7 +577,7 @@ NOINLINE void v_setF(Scope* pscs[], B s, B x, bool upd) {
Scope* sc = pscs[V_DEPTH(s)];
B prev = sc->ext->vars[V_POS(s)];
if (upd) {
if (prev.u==bi_noVar.u) thrM("↩: Updating undefined variable");
if (v_checkBadWrite(prev)) v_tagError(prev, true);
dec(prev);
} else dec(prev);
sc->ext->vars[V_POS(s)] = inc(x);
@ -621,7 +627,7 @@ NOINLINE B v_getF(Scope* pscs[], B s) {
} else if (isExt(s)) {
Scope* sc = pscs[V_DEPTH(s)];
B r = sc->ext->vars[V_POS(s)];
if (r.u==bi_noVar.u) thrM("↩: Reading variable that hasn't been set");
if (v_checkBadRead(r)) v_tagError(r, false);
sc->ext->vars[V_POS(s)] = bi_optOut;
return r;
} else {
@ -823,7 +829,7 @@ B evalBC(Body* b, Scope* sc, Block* bl) { // doesn't consume
}
case VARO: { u32 d = *bc++; u32 p = *bc++;
B l = pscs[d]->vars[p];
if(l.u==bi_noVar.u) { POS_UPD; thrM("Reading variable before its defined"); }
if(v_checkBadRead(l)) { POS_UPD; v_tagError(l, false); }
ADD(inc(l));
break;
}
@ -840,7 +846,7 @@ B evalBC(Body* b, Scope* sc, Block* bl) { // doesn't consume
}
case EXTO: { u32 d = *bc++; u32 p = *bc++;
B l = pscs[d]->ext->vars[p];
if(l.u==bi_noVar.u) { POS_UPD; thrM("Reading variable before its defined"); }
if(v_checkBadRead(l)) { POS_UPD; v_tagError(l, false); }
ADD(inc(l));
break;
}

View File

@ -279,10 +279,16 @@ typedef struct WrappedObj {
#define V_POS(X) ((u32)((X).u))
#define V_DEPTH(X) ((u16)((X).u>>32))
static u32 v_bad17_read = (TAG_TAG<<1) | 1;
static u32 v_bad18_write = (TAG_TAG<<2) | 3;
SHOULD_INLINE bool v_checkBadRead(B x) { return (x.u >> 47) == v_bad17_read; }
SHOULD_INLINE bool v_checkBadWrite(B x) { return (x.u >> 46) == v_bad18_write; }
NOINLINE NORETURN void v_tagError(B x, bool write);
NOINLINE B v_getF(Scope* pscs[], B s); // doesn't consume
FORCE_INLINE B v_getI(Scope* sc, u32 p, bool chk) {
B r = sc->vars[p];
if (chk && r.u==bi_noVar.u) thrM("↩: Reading variable that hasn't been set");
if (chk && v_checkBadRead(r)) v_tagError(r, false);
sc->vars[p] = bi_optOut;
return r;
}
@ -296,7 +302,7 @@ NOINLINE bool v_sethF(Scope* pscs[], B s, B x); // doesn't consume
FORCE_INLINE void v_setI(Scope* sc, u32 p, B x, bool upd, bool chk) { // consumes x
if (upd) {
B prev = sc->vars[p];
if (chk && prev.u==bi_noVar.u) thrM("↩: Updating variable that hasn't been set");
if (chk && v_checkBadWrite(prev)) v_tagError(prev, true);
sc->vars[p] = x;
dec(prev);
} else {

View File

@ -1,3 +1,5 @@
%DEF jiteq _jiteq {r (1𝔽){𝕊: 0•CurrentError@}¨ 4<𝕩 "TEST FAIL: all results weren't equal" ! 1=r {0e: !e; 1e: e}r}
# body usage
a"zero""one" !aa{𝕩?1𝕨;0𝕨}¨01
!"No header matched argument" % { 𝕊ab: a+b ; d𝕊abc: a+b+c} 3
@ -29,13 +31,13 @@ a←"zero"‿"one" ⋄ !a≡a⊸{𝕩?1⊑𝕨;0⊑𝕨}¨0‿1
!"No dyadic F˜⁼ undo header found for this 2-modifier block" % 1 +{𝔽𝔾𝕩}+˜ 1
# assignment
!"Reading variable before its defined" % {a} a1
!"↩: Reading variable that hasn't been set" % {a10} a2
!"↩: Updating variable that hasn't been set" % {a10} a2
!"↩: Reading variable that hasn't been set" % {a (10)˙} a2
!"↩: Reading variable that hasn't been set" % a1 {ab2} b2
!"↩: Updating variable that hasn't been set" % a1 {ab2} b2
!"↩: Reading variable that hasn't been set" % a1 {ab (10)˙} b2
!"Attempting to read variable which is not yet defined" % {a} a1
!"Attempting to read variable which is not yet defined" % {a10} a2
!"Assignment: Attempting to modify variable which is not yet defined" % {a10} a2
!"Attempting to read variable which is not yet defined" % {a (10)˙} a2
!"Attempting to read variable which is not yet defined" % a1 {ab2} b2
!"Assignment: Attempting to modify variable which is not yet defined" % a1 {ab2} b2
!"Attempting to read variable which is not yet defined" % a1 {ab (10)˙} b2
!"Assignment: Mismatched shape for spread assignment" % ab1
!"Assignment: Mismatched shape for spread assignment" % ab123
!"Assignment: Mismatched shape for spread assignment" % a<1
@ -43,9 +45,17 @@ a←"zero"‿"one" ⋄ !a≡a⊸{𝕩?1⊑𝕨;0⊑𝕨}¨0‿1
!"Assignment: Mismatched shape for spread assignment" % 1
%%
# mid-update assignment; precise behavior here isn't particularly important, but it should at least be consistent
a2 a {𝕊: a"foo" {𝕊:a!0}•CurrentError @} %% "foo"
!"Reading variable that was optimized out by F↩ after error" % a"foo" ! "foo"{𝕊:a!@}•CurrentError @ •Type a
# mid-update variable behavior
%USE jiteq {𝕊: a"foo" {𝕊:a!@}•CurrentError @ 1} _jiteq @ %% 1
!"Attempting to read variable which isn't available due to incomplete or aborted F↩" % %USE jiteq {𝕊: a"foo" ! "foo" {𝕊:a!@}•CurrentError @ a 1} _jiteq @
!"Attempting to read variable which isn't available due to incomplete or aborted F↩" % %USE jiteq {𝕊: a"foo" a {𝕊: a} 1} _jiteq @
!"Attempting to read variable which isn't available due to incomplete or aborted F↩" % %USE jiteq {𝕊: a1 a {𝕊: a } 1} _jiteq @
# not-yet-defined variable usage
!"Attempting to read variable which is not yet defined" % %USE jiteq {𝕊: {a } a1} _jiteq @
!"Attempting to read variable which is not yet defined" % %USE jiteq {𝕊: {a 1} a1} _jiteq @
!"Attempting to read variable which is not yet defined" % %USE jiteq {𝕊: {a 1} a1} _jiteq @
!"Assignment: Attempting to modify variable which is not yet defined" % %USE jiteq {𝕊: {aa12 1} a1} _jiteq @
# namespaces
a,b•BQN"{x⇐1‿2⋄a⇐3‿4⋄y←5‿6⋄b⇐7‿8}" ab %% 34 78