aboutsummaryrefslogtreecommitdiff
path: root/src/compiler/expr.zc
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/expr.zc')
-rw-r--r--src/compiler/expr.zc79
1 files changed, 67 insertions, 12 deletions
diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc
index 95ce67b..230a2d3 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);
@@ -1671,6 +1725,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);
}
@@ -1706,7 +1761,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;