diff --git a/jsrc/cx.c b/jsrc/cx.c index f45490195..17d7374a4 100644 --- a/jsrc/cx.c +++ b/jsrc/cx.c @@ -300,7 +300,8 @@ DF2(jtxdefn){ L *sympv=SYMORIGIN; // bring into local L *ybuckptr = &sympv[LXAV0(locsym)[(US)yxbucks]]; // pointer to sym block for y, known to exist if(likely(w!=0)){ // If y given, install it & incr usecount as in assignment. Include the script index of the modification - I vtype=QCNAMED|(LOCALRA?QCRAREQD:REPSGN(AT(w))&QCRAREQD)|ATYPETOVALTYPE(AT(w)); // install QCSYMVAL flags +// obsolete I vtype=QCNAMED|(REPSGN(AT(w))&QCRAREQD)|ATYPETOVALTYPE(AT(w)); // install QCSYMVAL flags: named, with type + I vtype=unlikely(ISSPARSE(AT(w)))?QCNAMED+QCRAREQD+VALTYPESPARSE:QCNAMED+QCNOUN; // install QCSYMVAL flags: named, with type; FA needed iff sparse. Must be a noun ybuckptr->fval=MAKEFVAL(w,vtype); ybuckptr->sn=jt->currslistx; // finish the assignment, with QCSYMVAL semantics // If input is abandoned inplace and not the same as x, DO NOT increment usecount, but mark as abandoned and make not-inplace. Otherwise ra // We can handle an abandoned argument only if it is direct or recursive, since only those values can be assigned to a name @@ -319,7 +320,8 @@ DF2(jtxdefn){ if(a!=0){ L *xbuckptr = &sympv[LXAV0(locsym)[yxbucks>>16]]; // pointer to sym block for x if(!C_CRC32C&&xbuckptr==ybuckptr)xbuckptr=xbuckptr->next+sympv; - I vtype=QCNAMED|(LOCALRA?QCRAREQD:REPSGN(AT(a))&QCRAREQD)|ATYPETOVALTYPE(AT(a)); // install QCSYMVAL flags +// obsolete I vtype=QCNAMED|(LOCALRA?QCRAREQD:REPSGN(AT(a))&QCRAREQD)|ATYPETOVALTYPE(AT(a)); // install QCSYMVAL flags + I vtype=unlikely(ISSPARSE(AT(a)))?QCNAMED+QCRAREQD+VALTYPESPARSE:QCNAMED+QCNOUN; // install QCSYMVAL flags: named, with type; FA needed iff sparse xbuckptr->fval=MAKEFVAL(a,vtype); xbuckptr->sn=jt->currslistx; if(likely(a!=w)&(SGNTO0(AC(a)&(((AT(a)^AFLAG(a))&RECURSIBLE)-1))&((I)jtinplace>>JTINPLACEAX))){ AFLAGORLOCAL(a,AFKNOWNNAMED); xbuckptr->flag=LPERMANENT|LWASABANDONED; ACIPNOABAND(a); ramkrecursv(a); @@ -427,7 +429,7 @@ nextlinedebug:; dobblock: // B-block (present on every sentence in the B-block) // run the sentence - parseline(z,if((UI)jt->tnextpushp-(UI)old>TPOPSLACK*SZI)tpop(old);,t=0;); // *** run user's line *** sets tcesx to thisline/nextline; t=0 so t doesn't have to be preserved over subrt calls + parseline(z,if((UI)jt->tnextpushp-(UI)old>TPOPSLACKB*SZI)tpop(old);,t=0;); // *** run user's line *** sets tcesx to thisline/nextline; t=0 so t doesn't have to be preserved over subrt calls // if there is no error, step to next line. debug mode has set the value to use if any, or 0 to request a new line if(likely(z!=0)){bic=ic; ic-=(tcesx>>(32+TCESXTYPEX+5))+1; // advance to next sentence to be executed, which is NSI, or NSI+1 if BBLOCKEND // the sequence BBLOCKEND BBLOCKEND indicates that the second BBLOCKEND was originally an END that went to NSI which was BBLOCK, i. e. end. for an if./select. followed by BBLOCK. @@ -466,13 +468,13 @@ nextlinedebug:; // Check for assert. Since this is only for T-blocks we tolerate the test (rather than duplicating code) if(unlikely(TEQ5(tcesx,CASSERT))){ if(JT(jt,assert)){ - parseline(t,{if((UI)jt->tnextpushp-(UI)old>TPOPSLACK*SZI)if(likely((tcesx&((UI8)TCESXCECANT<<32))!=0))tpop(old);else z=gc(z,old);},); + parseline(t,{if((UI)jt->tnextpushp-(UI)old>(0)*SZI)if(likely((tcesx&((UI8)TCESXCECANT<<32))!=0))tpop(old);else z=gc(z,old);},); // 0 to force flushes during testcases if(t&&!(NOUN&AT(t)&&all1(eq(num(1),t))))t=pee(cwsent,CWTCESX2(cwsent,ic),EVASSERT,NPGpysfmtdl<<(BW-2)); // if assert., signal post-execution error if result not all 1s. if(likely(t!=0)){ // assert without error t=mtv; // An assert is an entire T-block and must clear t afterward lest t be freed before it is checked by an empty while. So we use a safe permanent value, mtv. } }else{--ic; goto nextline;} // if ignored assert, go to NSI - }else{parseline(t,{if((UI)jt->tnextpushp-(UI)old>TPOPSLACK*SZI)if(likely((tcesx&((UI8)TCESXCECANT<<32))!=0))tpop(old);else z=gc(z,old);},);} // no assert: run the line resets tcesx to thisline/nextline + }else{parseline(t,{if((UI)jt->tnextpushp-(UI)old>(TPOPSLACKT)*SZI)if(likely((tcesx&((UI8)TCESXCECANT<<32))!=0))tpop(old);else z=gc(z,old);},);} // no assert: run the line resets tcesx to thisline/nextline // this is return point from running the line if(likely(t!=0)){tic=ic,--ic; // if no error, continue on. --ic must be in bounds for a non-assert T block (there must be another control word) if(unlikely(TXOR5(tcesx,CDO)|jt->uflags.trace))goto nextlinetcesx; // next line not do.; T block extended to more than 1 line (rare). diff --git a/jsrc/j.h b/jsrc/j.h index b73903611..f64900bbc 100644 --- a/jsrc/j.h +++ b/jsrc/j.h @@ -800,14 +800,13 @@ struct jtimespec jmtfclk(void); //'fast clock'; maybe less inaccurate; intended #define NTSTACK (1LL<<(AUDITEXECRESULTS?24:14)) // number of BYTES in an allocated block of tstack - pointers to allocated blocks - allocation is bigger to leave this many bytes on boundary #define NTSTACKBLOCK 2048 // boundary for beginning of stack block -#define TPOPSLACK 1 // number of stacked blocks to allow before we tpop them (in jtxdefn) - 0 means tpop every time +#define TPOPSLACKB 1 // number of stacked blocks to allow before we tpop them (in jtxdefn) - 0 means tpop every time - for B blocks +#define TPOPSLACKT 2 // number of stacked blocks to allow before we tpop them (in jtxdefn) - 0 means tpop every time - for T blocks #define CWMAX 32766 // max # control words in an explicit defn. Must fit in signed 15-bit value because we complement it in storage #define SWMAX 32767 // max # words in a sentence #define EXPWMAX 16777215 // max # words in an explicit defn -#define LOCALRA 0 // ra() local names during lookup - must be set, but perhaps we can make it an option in explicit def - // flags for jteformat #define EMSGE 0xff // the error-code part #define EMSGNOEVM 0x200 // set to suppress moving the terse message diff --git a/jsrc/jtype.h b/jsrc/jtype.h index 8e1eac6c5..fb5ddc23a 100644 --- a/jsrc/jtype.h +++ b/jsrc/jtype.h @@ -759,8 +759,8 @@ struct AD { #define ARNAMED ((I)1<>(NAMEBYVALUEX-NAMEFLAGSX)))|((I)y&QCNOUN)){ // use value if noun or special name, or name_: if(unlikely((pt0ecam&(NAMEABANDON>>(NAMEBYVALUEX-NAMEFLAGSX))))){ // is name_:? // if name_:, go delete the name, leaving the value to be deleted later. -#if !LOCALRA // If the value is local, we must ra it and any other local pointers on the stack if(!ISFAOWED(y)&&!ACISPERM(AC(QCWORD(y)))&&!(AFLAG(QCWORD(y))&AFSENTENCEWORD)){ // if the name has been raised already, all stacked copies have been protected. If PERMANENT or from the executing sentence, needs no protecting rapos(QCWORD(y),y); // ra() the new value first so that all args to undco have been ra()d PSTK *sk; - // scan the stack to see if it is at large in the stack, setting FAOWED when it is. We don't call protectlocals because we need to protect just the one value and speed might matter + // scan the stack to see if the new value is at large in the stack, setting FAOWED when it is. We don't call protectlocals because we need to protect just the one value and speed might matter // the stacked value might have been an unflagged non-NAMED result, but after we ra() it we must call it STKNAMED because only STKNAMED values get fa()d when they leave execution for(sk=stack;sk!=stackend1;++sk){if(QCWORD(y)==QCWORD(sk->a) && !ISSTKFAOWED(sk->a)){rapos(QCWORD(y),y); sk->a=SETSTKNAMEDFAOWED(sk->a);}} // if the value we want to use is stacked already, it must be marked non-abondoned } -#endif FPSZSUFF(y=nameundco(jtinplace, QCWORD(*(volatile A*)queue), y), fa(QCWORD(y));) }else y=SYMVALTOFAOWED(y) ; // if global, mark to free later }else if(unlikely(QCPTYPE(y)==VALTYPENAMELESS)){ @@ -760,7 +758,7 @@ endname: ; if(likely((s=(L*)(I)(NAV(QCWORD(*(volatile A*)queue))->symx&~REPSGN4(SGNIF4(pt0ecam,LOCSYMFLGX+ARLCLONEDX))))!=0)){ zval=QCWORD((SYMORIGIN+(I)s)->fval); // get value of symbol in primary table. There may be no value; that's OK }else{zval=QCWORD(jtprobelocal(jt,QCWORD(*(volatile A*)queue),jt->locsyms));} - targc=LOCALRA?ACUC2:ACUC1; // since local values are not ra()d, they will have AC=1 if inplaceable. This will miss sparse values (which have been ra()d. which is OK + targc=ACUC1; // since local values are not ra()d, they will have AC=1 if inplaceable. This will miss sparse values (which have been ra()d. which is OK }else{zval=QCWORD(probequiet(QCWORD(*(volatile A*)queue))); targc=ACUC2;} // global assignment, get slot address. Global names have been ra()d and have AC=2 // to save time in the verbs (which execute more often than this assignment-parse), see if the assignment target is suitable for inplacing. Set zombieval to point to the value if so // We require flags indicate not read-only, and correct usecount: 1 if local, 2 if global since we have raised the count of this block already if it is named and to be operated on inplace; +1 if NJA to account for the mapping reference. diff --git a/jsrc/pv.c b/jsrc/pv.c index dc533882e..02565733b 100644 --- a/jsrc/pv.c +++ b/jsrc/pv.c @@ -238,7 +238,7 @@ F1(jtvtrans){PROLOG(0053);A locsyms,y,z=0;I c,i,ttabi;TA ttab[NTTAB]; RZ(y=vtokens(w)); // return AM bit0=monad I tmonad=AM(y); ttabi=c; - RZ(locsyms=stcreate(2,40,0L,0L)); AR(locsyms)&=~ARLOCALTABLE; // not necessary to set global pointers; flag table so we don't switch to locsyms during assignment + RZ(locsyms=stcreate(2,40,0L,0L)); AR(locsyms)^=ARLOCALTABLE+ARLCLONED; // not necessary to set global pointers; flag table so we don't switch to locsyms during assignment, and suppress the check for local definitions symbis(mnuvxynam[5],num(1),locsyms); if(!tmonad)symbis(mnuvxynam[4],num(1),locsyms); WITHMSGSOFF(z=jttparse(jt,y,locsyms,tmonad,0==i,ttab,&ttabi,c);) if(i&&!z)z=colon(num(4-tmonad),w); diff --git a/jsrc/s.c b/jsrc/s.c index 873af7785..1c5a7ad63 100644 --- a/jsrc/s.c +++ b/jsrc/s.c @@ -689,7 +689,8 @@ A jtprobequiet(J jt,A a){A g; R res; } -// assign symbol: assign name a in symbol table g to the value w (but g is ignored if a is a locative) +// assign symbol: assign name a in symbol table g to the value w +// g is always the current local or global symbol table (but g is ignored if a is a locative). If a is not a locative and g is a local table, // Result is 0 if error, otherwise low 2 bits are x1 = final assignment, 1x = local assignment, others garbage // flags set in jt: bit 0=this is a final assignment; bit 1 always 0 I jtsymbis(J jt,A a,A w,A g){F2PREFIP; @@ -701,7 +702,7 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP; // It is safe to do the recursive-usecount change here as local, because the value cannot have been released to any other core. Similarly for // virtuals. // Find the internal code for the name to be assigned. Do this before we take the lock. - I wt=AT(w); + I wt=AT(w); I gr=AR(g); // type of w, rank-flags for g rifv(w); // must realize any virtual if(unlikely(((wt^AFLAG(w))&RECURSIBLE)!=0)){AFLAGORLOCAL(w,wt&RECURSIBLE) wt=(I)jtra(w,wt,(A)wt);} // make the block recursive (incr children if was nonrecursive). This does not affect the usecount of w itself. @@ -711,7 +712,8 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP; C*s=1+m+NAV(a)->s; if(unlikely(anmf&NMILOC))g=locindirect(n-m-2,1+s,NAV(a)->bucketx);else g=stfindcre(n-m-2,s,NAV(a)->bucketx); }else{ // not locative assignment - if(g==jt->global){ // global assignment. +// obsolete if(g==jt->global){ // global assignment. + if(!(gr&ARLOCALTABLE+ARLCLONED)){ // global assignment, and the symbol table does not suppress the check for local names // check for non-locative global assignment to a locally-defined name. Give domain error and immediately eformat, since no one has a self for assignment // this test will usually have a positive bucketx and will not call probelocal. Unlikely that symx is present I localnexist=REPSGN(NAV(a)->bucketx|SGNIF(AR(jt->locsyms),ARNAMEADDEDX)); // 0 if bucketx nonneg (meaning name known but not locally assigned) AND no unknown name has been assigned: i. e. no local def ~0 otherwise @@ -729,18 +731,18 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP; if(unlikely((((NAV(a)->flag&NMLOC+NMILOC+NMIMPLOC)-1)&SGNIF(FAV(w)->flag2,VF2NAMELESSX))<0))valtype=VALTYPENAMELESS; // nameless & non-locative, so indicate if(unlikely(jt->glock!=0))if(likely(FAV(w)->fgh[0]!=0)){FAV(w)->flag|=VLOCK;} // fn created in locked function is also locked - if((AR(g)&ARLOCALTABLE)!=0)AR(g)|=ARHASACV; // if we assign a non-noun to a local table, note the fact so we will look them up + if(unlikely(gr&ARLOCALTABLE))AR(g)|=ARHASACV; // if we assign a non-noun to a local table, note the fact so we will look them up there } L *e; // the symbol we will use // we don't have e, look it up. // We reserve 1 symbol for the new name, in case the name is not defined. If the name is not new we won't need the symbol. // convert valtype to QCSYMVAL semantics: NAMED always, RAREQD if global table or sparse - if((AR(g)&ARLOCALTABLE)!=0){ // if assignment to a local table (which might not be jt->locsyms) + if((gr&ARLOCALTABLE)!=0){ // if assignment to a local table (which might not be jt->locsyms) I4 symx=NAV(a)->symx; // fetch the symbol slot assigned to this name (0 if none) - e=likely((SGNIF(AR(g),ARLCLONEDX)|(symx-1))>=0)?SYMORIGIN+(I)symx:probeislocal(a,g); // local symbol given and we are using the original table: use the symbol. Otherwise, look up and reserve 1 symbol + e=likely((SGNIF(gr,ARLCLONEDX)|(symx-1))>=0)?SYMORIGIN+(I)symx:probeislocal(a,g); // local symbol given and we are using the original table: use the symbol. Otherwise, look up and reserve 1 symbol g=(A)((I)jtinplace|-JTASGNWASLOCAL); // indicate local assignment (we have no lock to clear), remember final assignment - valtype|=QCNAMED|(LOCALRA?QCRAREQD:REPSGN(wt)&QCRAREQD); // enter QCSYMVAL semantics; ra needed if sparse + valtype|=QCNAMED|(REPSGN(wt)&QCRAREQD); // enter QCSYMVAL semantics; ra needed if sparse }else{ // global table SYMRESERVE(1) // before we go into lock, make sure we have a symbol to assign to C *bloombase=BLOOMBASE(g); I chainno=SYMHASH(NAV(a)->hash,AN(g)-SYMLINFOSIZE); // get addr of Bloom filter and the location we are storing to diff --git a/jsrc/va2.c b/jsrc/va2.c index f23647022..6664dfba3 100644 --- a/jsrc/va2.c +++ b/jsrc/va2.c @@ -1248,7 +1248,7 @@ DF2(jtfslashatg){A fs,gs,y,z;B b;C*av,*wv;I ak,an,ar,*as,at,m, DF2(jtatomic2){A z; F2PREFIP;ARGCHK2(a,w); UI ar=AR(a), wr=AR(w); I at=AT(a), wt=AT(w); I af; - if((ar+wr+((at|wt)&((NOUN|SPARSE)&~(B01+INT+FL))))==0){af=0; goto forcess;} // if args are both atoms, verb rank is immaterial - run as singleton + if((ar+wr+((at|wt)&((NOUN|SPARSE)&~(B01+INT+FL))))==0){af=0; goto forcess;} // if args are both INT/FL/B01 atoms, verb rank is immaterial - run as singleton A realself=FAV(self)->fgh[0]; // if rank operator, this is nonzero and points to the left arg of rank RANK2T selfranks=FAV(self)->lrr; // get left & right rank from rank/primitive self=realself?realself:self; // if this is a rank block, move to the primitive to get to the function pointers. u b. or any atomic primitive has f clear @@ -1288,7 +1288,8 @@ forcess:; // branch point for rank-0 singletons from above, always with atomic selfranks=jtranks==R2MAX?selfranks:jtranks; } // self, awr, and selfranks are needed in the retry - } + } + // not singleton, or singleton needing retry ASSERTAGREE(AS(a),AS(w),af); // outermost (or only) agreement check NOUNROLL while(1){ diff --git a/jsrc/x.c b/jsrc/x.c index 6a2b7df19..d2e4c6e7c 100644 --- a/jsrc/x.c +++ b/jsrc/x.c @@ -380,8 +380,8 @@ MN(18,7) XPRIM(VERB, jtsetpermanent, 0, VFLAGNONE,VF2NONE,RMAX,RMAX,RM // called at initialization after memory reset, to assign cocurrent_z_ and coclass_z_. The 18!:4 block is at the end of foreignA and is a read-only value I jtforeignassigninit(J jt){A nm;L *e; - RZ(nm=nfs(12,"cocurrent_z_")); symbis(nm,(A)&foreignA[(sizeof(foreignA)/sizeof(foreignA[0]))-1],0); e=probeisres(nm, *JT(jt,zpath)); e->flag|=LREADONLY; - RZ(nm=nfs(10,"coclass_z_")); symbis(nm,(A)&foreignA[(sizeof(foreignA)/sizeof(foreignA[0]))-1],0); e=probeisres(nm, *JT(jt,zpath)); e->flag|=LREADONLY; + RZ(nm=nfs(12,"cocurrent_z_")); symbis(nm,(A)&foreignA[(sizeof(foreignA)/sizeof(foreignA[0]))-1],jt->global); e=probeisres(nm, *JT(jt,zpath)); e->flag|=LREADONLY; // the assignment is in z, but we need... + RZ(nm=nfs(10,"coclass_z_")); symbis(nm,(A)&foreignA[(sizeof(foreignA)/sizeof(foreignA[0]))-1],jt->global); e=probeisres(nm, *JT(jt,zpath)); e->flag|=LREADONLY; // ... a valid global table as an arg R 1; } diff --git a/jsrc/xt.c b/jsrc/xt.c index 3e776ca13..dba79e7c8 100644 --- a/jsrc/xt.c +++ b/jsrc/xt.c @@ -285,7 +285,7 @@ foundsym:; // we found the symbol. Install its info. sym is the symbol, SYMNE A *old=jt->tnextpushp; t=qpc(); // start time // We attempt to run as if under jtxdefn, so we check ATTN and jt->jerr as a replacement for reading from the word block; and we tpop like jtxdefn - DQ(n, z=PARSERVALUE(parsea(wv,wn)); if(unlikely(!z))break; ASSERT((__atomic_load_n((S*)JT(jt,adbreakr),__ATOMIC_ACQUIRE)&3)==0,EVATTN) RE(0) if((UI)jt->tnextpushp-(UI)old>TPOPSLACK*SZI)tpop(old);); + DQ(n, z=PARSERVALUE(parsea(wv,wn)); if(unlikely(!z))break; ASSERT((__atomic_load_n((S*)JT(jt,adbreakr),__ATOMIC_ACQUIRE)&3)==0,EVATTN) RE(0) if((UI)jt->tnextpushp-(UI)old>TPOPSLACKB*SZI)tpop(old);); t=qpc()-t; // Run the sentence. No need to run as exec since the result doesn't escape. tpop like jtxdefn. no tpop on error. if(unlikely(stackallo))debz(); RZ(z); // if error, fail the timing request diff --git a/test/g4x.ijs b/test/g4x.ijs index 7524d7d69..0e84c8755 100644 --- a/test/g4x.ijs +++ b/test/g4x.ijs @@ -220,15 +220,18 @@ b =: 7!:0'' 1e6 1e6 -: (# a_:) , # a NB. deleted, stack deferred b > 2e6+7!:0'' 'value error' -: ". etx 'a + 5' + 1 -: 3 : 0 '' a =. i. 1e6 b =. 7!:0'' assert. 1e6 -: # a_: +for. i. 6 do. b end. assert. b > 2e6+7!:0'' assert. 'value error' -: ". etx 'a + 5' a =. i. 1e6 b =. 7!:0'' assert. 1e6 -: ". '# a_:' NB. not deleted inside ". +for. i. 6 do. b end. assert. b < 1e6+7!:0'' assert. 1e6 -: # a a =. i. 1e6 @@ -240,6 +243,7 @@ assert. 2e6 -: # a_: , a NB. deleted, stack deferred a =. i. 1e6 b =. 7!:0'' assert. 1e6 1e6 -: (# a_:) , , # a NB. deleted, stack deferred +for. i. 6 do. b end. assert. b > 2e6+7!:0'' assert. 'value error' -: ". etx 'a + 5' a =. 1 diff --git a/test/g600ip.ijs b/test/g600ip.ijs index c7bcd0978..969c47066 100644 --- a/test/g600ip.ijs +++ b/test/g600ip.ijs @@ -139,6 +139,7 @@ NB. Verify nothing modified in place assert. svxy -: 3!:1&.> bx;by;ix;iy;dx;dy NB. Make x inplaceable. Verify correct result tx =: 3!:2 (3!:1) bx +for. i. 3 do. tx end. assert. (9;xyzs;allopred) checkallosize 7!:2 'tx =: tx u by' assert. r -: tx [ 10 [ 'tx =: tx u by' tx =: 3!:2 (3!:1) bx diff --git a/version.txt b/version.txt index 660599178..13a23302e 100644 --- a/version.txt +++ b/version.txt @@ -1,3 +1,3 @@ // set version info for github build // a new distribution is made when the version is updated -#define jversion "9.6.0-beta26" +#define jversion "9.6.0-beta27"