explicitly mark code sections where allocations aren't allowed to happen

This commit is contained in:
dzaima 2023-02-18 16:24:48 +02:00
parent c32285dba9
commit 7b57b4ae39
17 changed files with 80 additions and 37 deletions

View File

@ -248,6 +248,7 @@ usz* sh = HARR_FA(r, 4); // allocate shape for a rank 4 array. To get the result
HArr_p r = m_harrUv(10); // 10-item vector
HArr_p r = m_harrUc(10, x); // 10-item array with the same shape as x
HArr_p r = m_harrUp(10); // 10-item array without any set shape. Use the arr_shWhatever(r.c, …)
// run `NOGC_E;` after filling in the items to resume allowing allocations (not necessary if item count is 0)
// you can use withFill to add a fill element to a created array (or manually create a fillarr, see src/core/fillarr.h)

View File

@ -228,7 +228,7 @@ static NOINLINE B select_cells(usz n, B x, ur xr) {
SGet(x)
HArr_p rp = m_harrUv(cam);
for (usz i = 0; i < cam; i++) rp.a[i] = Get(x, i*csz+n);
r = rp.b;
NOGC_E; r=rp.b;
} else {
void* rp = m_tyarrv(&r, elWidth(xe), cam, el2t(xe));
void* xp = tyany_ptr(x);

View File

@ -932,6 +932,7 @@ B join_c2(B t, B w, B x) {
HArr_p r = m_harrUv(2);
r.a[0] = IGet(w,0); decG(w);
r.a[1] = IGet(x,0); decG(x);
NOGC_E;
return qWithFill(r.b, f);
}
if (c-wr > 1 || c-xr > 1) thrF("∾: Argument ranks must differ by 1 or less (%i≡=𝕨, %i≡=𝕩)", wr, xr);
@ -1115,6 +1116,7 @@ B reverse_c1(B t, B x) {
B* xp = arr_bptr(x);
if (xp!=NULL) for (usz i=0; i<n; i++) rp.a[i] = inc(xp[n-i-1]);
else { SGet(x) for (usz i=0; i<n; i++) rp.a[i] = Get(x, n-i-1); }
NOGC_E;
r = rp.b;
B xf = getFillQ(x);
decG(x);
@ -1283,6 +1285,7 @@ B transp_c1(B t, B x) {
B* x0 = xp; B* x1 = x0+w;
HArr_p rp = m_harrUp(ia);
for (usz i=0; i<w; i++) { rp.a[i*2] = inc(x0[i]); rp.a[i*2+1] = inc(x1[i]); }
NOGC_E;
r = (Arr*) rp.c;
} else {
#ifndef __BMI2__
@ -1322,6 +1325,7 @@ B transp_c1(B t, B x) {
HArr_p p = m_harrUp(ia);
for(usz y=0;y<h;y++) for(usz x=0;x<w;x++) p.a[x*h+y] = inc(xp[xi++]); // TODO inc afterwards, but don't when there's a method of freeing a HArr without freeing its elements
NOGC_E;
usz* rsh = arr_shAlloc((Arr*)p.c, xr);
if (xr==2) {

View File

@ -190,12 +190,10 @@ static B compress_grouped(u64* wp, B x, usz wia, usz wsum, u8 xt) {
rp = m_tyarrv(&r,width,wsum,xt);
} else {
ONLY_GCC(r = m_f64(0);)
xp = (u8*)arr_bptr(x);
usz ria = wsum*csz;
if (xp != NULL) {
rh = m_harrUv(ria);
rp = (u8*)rh.a;
} else {
xp = (u8*)arr_bptr(x);
if (xp == NULL) {
SLOW2("𝕨/𝕩", taga(RFLD(wp,TyArr,a)), x);
M_HARR(rp, ria) SGet(x)
for (usz i = 0; i < wia; i++) if (bitp_get(wp,i)) {
@ -203,10 +201,14 @@ static B compress_grouped(u64* wp, B x, usz wia, usz wsum, u8 xt) {
}
return withFill(HARR_FV(rp), getFillQ(x));
}
rh = m_harrUv(ria);
rp = (u8*)rh.a;
}
COMPRESS_GROUP(MEM_CPY)
if (is_B) {
for (usz i = 0; i < wsum*csz; i++) inc(((B*)rp)[i]);
NOGC_E;
r = withFill(rh.b, getFillQ(x));
a(r)->ia = wsum; // Shape-setting code at end of compress expects this
}
@ -456,8 +458,8 @@ static B compress(B w, B x, usz wia, u8 xl, u8 xt) {
#endif
break;
}
#define COMPRESS_BLOCK(T) \
usz b = bsp_max; TALLOC(i16, buf, b); \
#define COMPRESS_BLOCK_PREP(T, PREP) \
usz b = bsp_max; TALLOC(i16, buf, b); PREP; \
T* rp0=rp; \
for (usz i=0; i<wia; i+=b) { \
usz bs; \
@ -468,6 +470,7 @@ static B compress(B w, B x, usz wia, u8 xl, u8 xt) {
rp+= bs; wp+= b/64; xp+= b; \
} \
TFREE(buf)
#define COMPRESS_BLOCK(T) COMPRESS_BLOCK_PREP(T, )
#define WITH_SPARSE(W, CUTOFF, DENSE) { \
i##W *xp=tyany_ptr(x), *rp; \
if (wsum<wia/CUTOFF) { rp=m_tyarrv(&r,W/8,wsum,xt); COMPRESS_BLOCK(i##W); } \
@ -492,9 +495,9 @@ static B compress(B w, B x, usz wia, u8 xl, u8 xt) {
B xf = getFillQ(x);
B* xp = arr_bptr(x);
if (xp!=NULL) {
HArr_p rh = m_harrUv(wsum);
B *rp = rh.a; COMPRESS_BLOCK(B);
COMPRESS_BLOCK_PREP(B, HArr_p rh = m_harrUv(wsum); B *rp = rh.a;);
for (usz i=0; i<wsum; i++) inc(rh.a[i]);
NOGC_E;
r = withFill(rh.b, xf);
} else {
SLOW2("𝕨/𝕩", w, x);
@ -758,7 +761,7 @@ B slash_c2(B t, B w, B x) {
if (xlen == 0) return x;
usz s = xlen * wv;
if (xl>6 || (xl<3 && xl!=0) || TI(x,elType)==el_B) {
if (xr != 1) goto base;
if (xr!=1) goto base;
SLOW2("𝕨/𝕩", w, x);
B xf = getFillQ(x);
HArr_p r0 = m_harrUv(s);
@ -767,6 +770,7 @@ B slash_c2(B t, B w, B x) {
B cx = incBy(GetU(x, i), wv);
for (i64 j = 0; j < wv; j++) *r0.a++ = cx;
}
NOGC_E;
r = withFill(r0.b, xf);
goto decX_ret;
}

View File

@ -612,6 +612,7 @@ B reBQN_c1(B t, B x) {
ptr_dec(initBlock);
HArr_p d = m_harrUv(7); d.a[0] = m_f64(replVal); d.a[1] = scVal;
for (usz i=2; i<7; i++) d.a[i] = bi_N;
NOGC_E;
init_comp(d.a+2, prim, sys);
decG(x);
return m_nfn(reBQNDesc, d.b);
@ -1619,10 +1620,13 @@ void sysfn_init(void) {
usz i = 0;
HArr_p dsv_ns0 = m_harrUv(dsv_num); dsv_ns=dsv_ns0.b; gc_add(dsv_ns);
for (usz i = 0; i < dsv_num; i++) dsv_ns0.a[i] = m_c8vec_0(dsv_strs[i]);
NOGC_E;
HArr_p dsv_vs0 = m_harrUv(dsv_num); dsv_vs=dsv_vs0.b; gc_add(dsv_vs);
#define F(L,N,B) dsv_vs0.a[i] = inc(B); i++;
FOR_DEFAULT_SYSVALS(F)
#undef F
NOGC_E;
#if CATCH_ERRORS
lastErrMsg = bi_N;

View File

@ -59,6 +59,7 @@ B toKCells(B x, ur k) {
NOINLINE B m_caB(usz ia, B* a) {
HArr_p r = m_harrUv(ia);
for (usz i = 0; i < ia; i++) r.a[i] = a[i];
NOGC_E;
return r.b;
}

View File

@ -72,25 +72,25 @@ void harr_abandon_impl(HArr* p);
// unsafe-ish things - don't allocate/GC anything before having written to all items
#define m_harr0v(N) ({ usz n_ = (N); HArr_p r_ = m_harrUv(n_); for(usz i=0;i<n_;i++)r_.a[i]=m_f64(0); r_; })
#define m_harr0c(X) ({ B x_ = (X); usz n_ = IA(x_); HArr_p r_ = m_harrUc(x_); for(usz i=0;i<n_;i++)r_.a[i]=m_f64(0); r_; })
#define m_harr0p(N) ({ usz n_ = (N); HArr_p r_ = m_harrUp(n_); for(usz i=0;i<n_;i++)r_.a[i]=m_f64(0); r_; })
#define m_harr0v(N) ({ usz n_ = (N); HArr_p r_ = m_harrUv(n_); for(usz i=0;i<n_;i++)r_.a[i]=m_f64(0); NOGC_E; r_; })
#define m_harr0c(X) ({ B x_ = (X); usz n_ = IA(x_); HArr_p r_ = m_harrUc(x_); for(usz i=0;i<n_;i++)r_.a[i]=m_f64(0); NOGC_E; r_; })
#define m_harr0p(N) ({ usz n_ = (N); HArr_p r_ = m_harrUp(n_); for(usz i=0;i<n_;i++)r_.a[i]=m_f64(0); NOGC_E; r_; })
static HArr_p m_harrUv(usz ia) {
CHECK_IA(ia, sizeof(B));
HArr* r = m_arr(fsizeof(HArr,a,B,ia), t_harr, ia);
HArr* r = m_arr(fsizeof(HArr,a,B,ia), t_harr, ia); if(ia) NOGC_S;
arr_shVec((Arr*)r);
return harrP_parts(r);
}
static HArr_p m_harrUc(B x) { assert(isArr(x));
usz ia = IA(x);
CHECK_IA(ia, sizeof(B));
HArr* r = m_arr(fsizeof(HArr,a,B,ia), t_harr, ia);
HArr* r = m_arr(fsizeof(HArr,a,B,ia), t_harr, ia); if(ia) NOGC_S;
arr_shCopy((Arr*)r, x);
return harrP_parts(r);
}
static HArr_p m_harrUp(usz ia) {
CHECK_IA(ia, sizeof(B));
HArr* r = m_arr(fsizeof(HArr,a,B,ia), t_harr, ia);
HArr* r = m_arr(fsizeof(HArr,a,B,ia), t_harr, ia); if(ia) NOGC_S;
return harrP_parts(r);
}
@ -98,6 +98,7 @@ static B m_hunit(B x) { // consumes
HArr_p r = m_harrUp(1);
arr_shAtm((Arr*)r.c);
r.a[0] = x;
NOGC_E;
return r.b;
}
@ -109,8 +110,8 @@ static HArr* toHArr(B x) { return TY(x)==t_harr? c(HArr,x) : cpyHArr(x); }
B m_caB(usz ia, B* a);
// consumes all
static B m_hVec1(B a ) { HArr_p r = m_harrUv(1); r.a[0] = a; return r.b; }
static B m_hVec2(B a, B b ) { HArr_p r = m_harrUv(2); r.a[0] = a; r.a[1] = b; return r.b; }
static B m_hVec3(B a, B b, B c ) { HArr_p r = m_harrUv(3); r.a[0] = a; r.a[1] = b; r.a[2] = c; return r.b; }
static B m_hVec4(B a, B b, B c, B d) { HArr_p r = m_harrUv(4); r.a[0] = a; r.a[1] = b; r.a[2] = c; r.a[3] = d; return r.b; }
static B m_hVec1(B a ) { HArr_p r = m_harrUv(1); r.a[0] = a; NOGC_E; return r.b; }
static B m_hVec2(B a, B b ) { HArr_p r = m_harrUv(2); r.a[0] = a; r.a[1] = b; NOGC_E; return r.b; }
static B m_hVec3(B a, B b, B c ) { HArr_p r = m_harrUv(3); r.a[0] = a; r.a[1] = b; r.a[2] = c; NOGC_E; return r.b; }
static B m_hVec4(B a, B b, B c, B d) { HArr_p r = m_harrUv(4); r.a[0] = a; r.a[1] = b; r.a[2] = c; r.a[3] = d; NOGC_E; return r.b; }

View File

@ -10,6 +10,10 @@ bool please_tail_call_err = true;
void before_exit(void);
bool inErr;
NORETURN NOINLINE void err(char* s) {
NOGC_E;
#if MM!=0
gc_depth=1;
#endif
if (inErr) {
fputs("\nCBQN encountered fatal error during information printing of another fatal error. Exiting without printing more info.", stderr);
#ifdef DEBUG
@ -704,6 +708,8 @@ void g_iv(void* x) { ignore_bad_tag=true; B xo = tag(x, OBJ_TAG); B r = info_c
void g_pst(void) { vm_pstLive(); fflush(stdout); fflush(stderr); }
#ifdef DEBUG
bool cbqn_noAlloc;
NOINLINE void cbqn_NOGC_start() { cbqn_noAlloc=true; }
#ifdef OBJ_COUNTER
#define PRINT_ID(X) fprintf(stderr, "Object ID: "N64u"\n", (X)->uid)
#else

View File

@ -130,7 +130,7 @@ EXPORT BQNV bqn_makeF64Arr(size_t r0, const size_t* sh0, const f64* data) { usz
EXPORT BQNV bqn_makeC8Arr (size_t r0, const size_t* sh0, const u8* data) { usz ia=calcIA(r0,sh0); u8* rp; Arr* r = m_c8arrp (&rp,ia); CPYSH(r); memcpy(rp,data,ia*1); return makeX(taga(r)); }
EXPORT BQNV bqn_makeC16Arr(size_t r0, const size_t* sh0, const u16* data) { usz ia=calcIA(r0,sh0); u16* rp; Arr* r = m_c16arrp(&rp,ia); CPYSH(r); memcpy(rp,data,ia*2); return makeX(taga(r)); }
EXPORT BQNV bqn_makeC32Arr(size_t r0, const size_t* sh0, const u32* data) { usz ia=calcIA(r0,sh0); u32* rp; Arr* r = m_c32arrp(&rp,ia); CPYSH(r); memcpy(rp,data,ia*4); return makeX(taga(r)); }
EXPORT BQNV bqn_makeObjArr(size_t r0, const size_t* sh0, const BQNV* data) { usz ia=calcIA(r0,sh0); HArr_p r = m_harrUp(ia); CPYSH(r.c); copyBData(r.a,data,ia ); return makeX(r.b); }
EXPORT BQNV bqn_makeObjArr(size_t r0, const size_t* sh0, const BQNV* data) { usz ia=calcIA(r0,sh0); HArr_p r = m_harrUp(ia); copyBData(r.a,data,ia); NOGC_E; CPYSH(r.c); return makeX(r.b); }
EXPORT BQNV bqn_makeI8Vec (size_t len, const i8* data) { i8* rp; B r = m_i8arrv (&rp,len); memcpy(rp,data,len*1); return makeX(r); }
EXPORT BQNV bqn_makeI16Vec(size_t len, const i16* data) { i16* rp; B r = m_i16arrv(&rp,len); memcpy(rp,data,len*2); return makeX(r); }
@ -139,7 +139,7 @@ EXPORT BQNV bqn_makeF64Vec(size_t len, const f64* data) { f64* rp; B r = m_f64a
EXPORT BQNV bqn_makeC8Vec (size_t len, const u8* data) { u8* rp; B r = m_c8arrv (&rp,len); memcpy(rp,data,len*1); return makeX(r); }
EXPORT BQNV bqn_makeC16Vec(size_t len, const u16* data) { u16* rp; B r = m_c16arrv(&rp,len); memcpy(rp,data,len*2); return makeX(r); }
EXPORT BQNV bqn_makeC32Vec(size_t len, const u32* data) { u32* rp; B r = m_c32arrv(&rp,len); memcpy(rp,data,len*4); return makeX(r); }
EXPORT BQNV bqn_makeObjVec(size_t len, const BQNV* data) { HArr_p r = m_harrUv(len); copyBData(r.a,data,len ); return makeX(r.b); }
EXPORT BQNV bqn_makeObjVec(size_t len, const BQNV* data) { HArr_p r = m_harrUv(len); copyBData(r.a,data,len); NOGC_E;return makeX(r.b); }
EXPORT BQNV bqn_makeUTF8Str(size_t len, const char* str) { return makeX(utf8Decode(str, len)); }
typedef struct BoundFn {
@ -1008,6 +1008,7 @@ void ffi_init(void) {
a.a[4] = m_c8vec_0("write");
a.a[5] = m_c8vec_0("close");
a.a[6] = m_c8vec_0("poll");
NOGC_E;
names = a.b; gc_add(names);
forbuildDesc = registerNFn(m_c8vec_0("(function for build)"), forbuild_c1, c2_bad);
}

View File

@ -321,11 +321,17 @@ typedef struct Arr {
B VALIDATE(B x);
Value* VALIDATEP(Value* x);
#define UD assert(false);
extern bool cbqn_noAlloc;
NOINLINE void cbqn_NOGC_start(); // function to allow breakpointing
#define NOGC_S cbqn_NOGC_start()
#define NOGC_E cbqn_noAlloc=false
#else
#define assert(X) {if (!(X)) __builtin_unreachable();}
#define VALIDATE(X) (X)
#define VALIDATEP(X) (X)
#define UD __builtin_unreachable();
#define NOGC_S
#define NOGC_E
#endif
#if WARN_SLOW
void warn_slow1(char* s, B x);

View File

@ -127,7 +127,7 @@ INS B i_LST_p(B el0, i64 sz, B* cStack) { assert(sz>0);
bool allNum = isNum(el0);
r.a[sz-1] = el0;
for (i64 i = 1; i < sz; i++) if (!isNum(r.a[sz-i-1] = GSP)) allNum = false;
GS_UPD;
NOGC_E; GS_UPD;
if (allNum) return num_squeeze(r.b);
return r.b;
}
@ -136,7 +136,7 @@ INS B i_ARMO(B el0, i64 sz, B* cStack) { assert(sz>0);
HArr_p r = m_harrUv(sz);
r.a[sz-1] = el0;
for (i64 i = 1; i < sz; i++) r.a[sz-i-1] = GSP;
GS_UPD;
NOGC_E; GS_UPD;
return bqn_merge(r.b);
}
INS B i_ARMM(B el0, i64 sz, B* cStack) { assert(sz>0);
@ -144,7 +144,7 @@ INS B i_ARMM(B el0, i64 sz, B* cStack) { assert(sz>0);
HArr_p r = m_harrUv(sz); // can't use harrs as gStack isn't updated
r.a[sz-1] = el0;
for (i64 i = 1; i < sz; i++) r.a[sz-i-1] = GSP;
GS_UPD;
NOGC_E; GS_UPD;
WrappedObj* a = mm_alloc(sizeof(WrappedObj), t_arrMerge);
a->obj = r.b;
return tag(a,OBJ_TAG);
@ -388,6 +388,7 @@ static OptRes opt(u32* bc0) {
h.a[i] = inc(c.v);
RM(c.p);
}
NOGC_E;
B r = allNum? num_squeeze(h.b) : h.b;
cact = 5;
TSADD(data, r.u);

View File

@ -468,6 +468,7 @@ void load_init() { // very last init function
#endif
runtimeH.a[i] = r;
}
NOGC_E;
decG(rtObjRaw);
B* runtime = runtimeH.a;
B rtObj = runtimeH.b;

View File

@ -24,6 +24,9 @@ extern EmptyValue* mm_buckets[64];
ALLOC_FN void* mm_alloc(u64 sz, u8 type) {
assert(sz>=16);
preAlloc(sz, type);
#if DEBUG
if (cbqn_noAlloc && !gc_depth) err("allocating during noAlloc");
#endif
#if VERIFY_TAIL
i64 logAlloc = LOG2(sz + VERIFY_TAIL);
#else

View File

@ -74,6 +74,7 @@ NOINLINE B bit_sel(B b, B e0, B e1) {
HArr_p ra = m_harrUc(b);
SLOW3("bit_sel", e0, e1, b);
for (usz i = 0; i < ia; i++) ra.a[i] = bitp_get(bp,i)? e1 : e0;
NOGC_E;
u64 c1 = bit_sum(bp, ia);
u64 c0 = ia-c1;

View File

@ -242,15 +242,15 @@ DEF_G(void, copy, B, (void* a, usz ms, B x, usz xs, usz l), ms, x, x
else cpy##T##Arr_BF(xp, rp, ia, xa); \
} \
static copy_fn copy##T##Fns[] = __VA_ARGS__; \
T##Arr* cpy##T##Arr(B x) { \
usz ia = IA(x); \
MAKE; arr_shCopy(r, x); \
if (ia>0) { \
T##Arr* cpy##T##Arr(B x) { \
usz ia = IA(x); \
MAKE; arr_shCopy(r, x); \
if (ia>0) { \
copy##T##Fns[TI(x,elType)](tyany_ptr(x), XRP, ia, a(x)); \
} \
if (TY) ptr_decT(a(x)); \
else decG(x); \
return (T##Arr*)r; \
} \
if (TY) ptr_decT(a(x)); \
else decG(x); \
NOGC_E; return (T##Arr*)r; \
}
#define BIT_PUT(V) bitp_set((u64*)rp, i, o2bG(V))
#define H2T_COPY(T) copy##T##Fns[el_MAX](bxp, rp, ia, xRaw)
@ -342,6 +342,7 @@ DEF_G(void, copy, B, (void* a, usz ms, B x, usz xs, usz l), ms, x, x
if (xp!=NULL) { for (usz i=0; i<ia; i++) r.a[i] = inc(xp[i]); }
else { SGet(x) for (usz i=0; i<ia; i++) r.a[i] = Get(x, i); }
}
NOGC_E;
decG(x);
return r.c;
}

View File

@ -64,23 +64,27 @@ static void mut_init(Mut* m, u8 n) {
void mut_to(Mut* m, u8 n);
static B mut_fv(Mut* m) { assert(m->fns->elType!=el_MAX);
NOGC_E;
Arr* a = m->val;
a->sh = &a->ia;
SPRNK(a, 1);
return taga(a);
}
static B mut_fc(Mut* m, B x) { assert(m->fns->elType!=el_MAX);
NOGC_E;
Arr* a = m->val;
arr_shCopy(a, x);
return taga(a);
}
static B mut_fcd(Mut* m, B x) { assert(m->fns->elType!=el_MAX);
NOGC_E;
Arr* a = m->val;
arr_shCopy(a, x);
decG(x);
return taga(a);
}
static Arr* mut_fp(Mut* m) { assert(m->fns->elType!=el_MAX);
NOGC_E;
return m->val;
}

View File

@ -600,6 +600,7 @@ NOINLINE B v_getF(Scope* pscs[], B s) {
B* sp = harr_ptr(s);
HArr_p r = m_harrUv(ia);
for (u64 i = 0; i < ia; i++) r.a[i] = v_get(pscs, sp[i], true);
NOGC_E;
return r.b;
} else if (isExt(s)) {
Scope* sc = pscs[(u16)(s.u>>32)];
@ -777,6 +778,7 @@ B evalBC(Body* b, Scope* sc, Block* bl) { // doesn't consume
HArr_p r = m_harrUv(sz);
bool allNum = true;
for (i64 i = 0; i < sz; i++) if (!isNum(r.a[sz-i-1] = POP)) allNum = false;
NOGC_E;
if (allNum) {
GS_UPD;
ADD(num_squeeze(r.b));
@ -898,7 +900,7 @@ B evalBC(Body* b, Scope* sc, Block* bl) { // doesn't consume
assert(sz>0);
HArr_p r = m_harrUv(sz);
for (i64 i = 0; i < sz; i++) r.a[sz-i-1] = POP;
GS_UPD;
NOGC_E; GS_UPD;
ADD(bqn_merge(r.b));
break;
}
@ -907,7 +909,7 @@ B evalBC(Body* b, Scope* sc, Block* bl) { // doesn't consume
assert(sz>0);
HArr_p r = m_harrUv(sz);
for (i64 i = 0; i < sz; i++) r.a[sz-i-1] = POP;
GS_UPD;
NOGC_E; GS_UPD;
WrappedObj* a = mm_alloc(sizeof(WrappedObj), t_arrMerge);
a->obj = r.b;
ADD(tag(a,OBJ_TAG));
@ -1626,7 +1628,9 @@ void before_exit(void);
NOINLINE NORETURN void throwImpl(bool rethrow) {
// printf("gStack %p-%p:\n", gStackStart, gStack); B* c = gStack;
// while (c>gStackStart) { printI(*--c); putchar('\n'); } printf("gStack printed\n");
#if DEBUG
if (cbqn_noAlloc && !gc_depth) err("throwing during noAlloc");
#endif
if (!rethrow) envPrevHeight = envCurr-envStart + 1;
#if CATCH_ERRORS
if (cf>cfStart) { // something wants to catch errors