diff options
Diffstat (limited to 'src/compiler/expr.zc')
| -rw-r--r-- | src/compiler/expr.zc | 79 |
1 files changed, 67 insertions, 12 deletions
diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc index 3d63e4b..4c77ceb 100644 --- a/src/compiler/expr.zc +++ b/src/compiler/expr.zc @@ -678,7 +678,7 @@ int closure() { nclo++; hop = hole(); op(O_JMP); entry = here; - fn_begin(1, 0); + fn_begin(1, 0); fbnd = ctx_bnd(); lt = lt_begin(entry, "<closure>", lfl); if (declared) fty = declared; prologue(); borrow_prologue(); @@ -1321,17 +1321,49 @@ int range_jz(int ix, int src, int fld) { setlit(k, here); return 0; } +// the inline check of a fixed array T[n]: the index is on top of the stack and +// stays there. i is outside 0..n-1 exactly when i | (n - 1 - i) is negative: +// one subtract, one or and one compare, then a branch to the oob report. no +// enhancement: the x86-64 jit calls one rather than inlining it. +int fixed_chk(int n) { + int j; + lit(0); op(O_PICK); lit(n - 1); op(O_SWAP); op(O_SUB); + lit(1); op(O_PICK); op(17); + lit(0); op(O_LT); + j = hole(); op(O_JZ); + lit(n); callname(s_oob); + setlit(j, here); + return 0; +} +// the check of a pointer index: base and index are on the stack and stay there. +// the runtime knows a size only for a pointer to the start of a live heap block +// and returns at once for any other pointer. sz is the element size in words, or +// minus the element size in bytes for packed elements. +int ptr_chk(int sz) { + int k = lookup(s_bnd), j; + if (k >= 0 && skind[k] == K_ENH) { // the test is one enhancement; the call only runs to report + lit(1); op(O_PICK); lit(1); op(O_PICK); + if (sz > 0) { lit(sz); lit(1); } else { lit(0 - sz); lit(8); } + op(XOP0 + sval[k]); + j = hole(); op(O_JZ); + } + lit(1); op(O_PICK); lit(1); op(O_PICK); lit(sz); callname(s_bchk); op(O_DROP); + if (k >= 0 && skind[k] == K_ENH) setlit(j, here); + return 0; +} // suffixes after a primary, in a loop: e as T, [index], .field, ->field, // (call through a value), ++ and --. the index case is the long one. a byte -// string takes an index or a slice. a growable array is checked: through dat() -// normally, but inline when the compiler can prove or cheaply test the bound -// (o3 code with the cache variables, or an index the loop test already -// compared with the length). a plain pointer or fixed array is just base + -// index * size, and a constant index against a known length is checked at -// compile time. +// string takes an index or a slice. every index is bounds checked unless the +// function, block or unit is unbounded or the code is o3. a growable array is +// checked through dat() normally, but inline when the compiler can prove or +// cheaply test the bound (o3 code with the cache variables, or an index the +// loop test already compared with the length). a fixed array T[n] is checked +// inline against n (at compile time for a constant index, not at all when a +// loop test against a constant already proved it). a plain pointer goes +// through rt.bchk, which only knows the size of a heap block's first word. int postfix() { return postfix_tail(primary()); } int postfix_tail(int c) { - int b, n, t, et; word ft; word[8] bs; int k, ci, was, al, ix, enh, cz, cj, place, offset, temporary, call_owner, readonly; word storage, storage_mask, call_origins; + int b, n, t, et; word ft; word[8] bs; int k, ci, was, al, ix, enh, cz, cj, place, offset, temporary, call_owner, readonly, ub, bl, ic, inr; word storage, storage_mask, call_origins; while (1) { if (tk == T_ID && tv == s_as) { // e as T: same bits, another type next(); t = ptype(); rval(); if (!xaf || xaf[2] != 5) xaf = 0; vf_coerce(t, 0); @@ -1350,6 +1382,8 @@ int postfix_tail(int c) { ci = 0; if (lv && lvk == LV_LOCAL) lptr[lvi] = 1; if (o3on && lv && lvk == LV_LOCAL && o3cache[lvi]) ci = o3cache[lvi]; + ub = !fbnd || o3on || (lv && lvk == LV_LOCAL && (ltag[lvi] & 16)); // unbounded, or o3 code + bl = -1; if (lv && lvk == LV_LOCAL) bl = lvi; next(); rval(); if (was) { @@ -1377,7 +1411,11 @@ int postfix_tail(int c) { if (xconst) offset = xcval; af_index(storage, place); tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); expect(s_rk); - callname(intern("datb")); + if (ub) { // the byte address of the element, with no check + op(O_SWAP); lit(2); op(O_ADD); op(O_LD); lit(8); op(15); op(O_SWAP); + if (tbytes(et) != 1) { lit(tbytes(et)); op(15); } + op(O_ADD); + } else callname(intern("datb")); et = tpk(et); } else if (xty[0] == TY_DYN) { // a growable array: bounds-checked, through the runtime et = xty[4]; @@ -1393,7 +1431,10 @@ int postfix_tail(int c) { expect(s_rk); k = lookup(s_outside); enh = k >= 0 && skind[k] == K_ENH; - if (!mdbg && ix >= 0 && al >= 0 && proven(al, ix)) { // the loop test already compared this index with the length + if (ub) { // unbounded, or o3: just the address + if (ci && !mdbg && lookup(s_outside) >= 0) { vaddr(ci); op(O_LD); op(O_SWAP); mulsz(et); op(O_ADD); } + else addr_stk(et); + } else if (!mdbg && ix >= 0 && al >= 0 && proven(al, ix)) { // the loop test already compared this index with the length if (ci) { vaddr(ci); op(O_LD); op(O_SWAP); mulsz(et); op(O_ADD); } else addr_stk(et); } else if (ci && !mdbg && enh && ix >= 0) { @@ -1433,13 +1474,26 @@ int postfix_tail(int c) { et = telem(xty); n = 0 - 1; if (xty[0] == TY_ARR && xty[3] > 0) n = xty[3]; xconst = 0; - c = expr(); rval(); plainuse(c); + c = expr(); + ix = -1; + if (lv && lvk == LV_LOCAL) ix = lvi; + rval(); plainuse(c); + ic = xconst; if (xconst) offset = xcval; if (n < 0) af_index(storage, place); if (n >= 0 && xconst && (xcval < 0 || xcval >= n)) { serr("the index is out of range for this array"); } + inr = 0; // no check needed: the index is 0, or the analyzer knows it is inside the allocation + if (ic && xcval == 0) inr = 1; + if (ic && n < 0 && !tbytes(et) && tsz(et) == 1 && af_inrange(storage)) inr = 1; + if (bl >= 0 && n < 0 && larr[bl]) inr = 1; // an array in a frame has no heap header to read tcneed(isnum(xty) || xty == tword, "an index has to be a number", xty); expect(s_rk); + if (!ub && !inr) { + if (n >= 0) { if (!ic && !(ix >= 0 && proven_n(ix, n))) fixed_chk(n); } + else if (tbytes(et)) ptr_chk(0 - tbytes(et)); + else ptr_chk(tsz(et)); + } if (tbytes(et)) { // packed: byte address = base * 8 + index * bytes if (tbytes(et) != 1) { lit(tbytes(et)); op(15); } op(O_SWAP); lit(8); op(15); op(O_ADD); @@ -1666,6 +1720,7 @@ int bin(int minp) { if (c >= C_VAR && tracked(c - C_VAR)) rb = lroot(c - C_VAR); else rb = xbor; if (top) { ft_ir = -1; if (lv && lvk == LV_LOCAL) ft_ir = lvi; } rval(); plainuse(c); + if (top) { ft_ck = -1; if (xconst && xcval >= 0) ft_ck = xcval; } if (cmode && bname[b] == s_star && xconst && xcval >= 2 && !(xcval & (xcval - 1)) && !lf) { shifthint(opp, xcval); } @@ -1701,7 +1756,7 @@ int bin(int minp) { if (top) { ft_nops++; if (ft_nops > 1) ft_iv = -1; - else if (bname[b] == s_lt && blen[b] > 0 && il >= 0 && ft_ir >= 0 && !lf && !rf && !ls && !rs) { ft_iv = il; ft_bd = ft_ir; } + else if (bname[b] == s_lt && blen[b] > 0 && il >= 0 && (ft_ir >= 0 || ft_ck >= 0) && !lf && !rf && !ls && !rs) { ft_iv = il; ft_bd = ft_ir; } } lv = 0; lvk = LV_NONE; c = C_PLAIN; xzero = 0; xconst = 0; xlenloc = -1; xstamp = 0; xaf = 0; xvk = 0; effect_value = 0; if (isptr(xty) || xty == tword) effect_value = left_mask | right_mask; |
