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.zc75
1 files changed, 64 insertions, 11 deletions
diff --git a/src/compiler/expr.zc b/src/compiler/expr.zc
index f41109e..c76b74c 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,47 @@ 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. one "outside" enhancement against the constant length and a
+// branch to the oob report, the same shape as range_jz.
+int fixed_chk(int n) {
+ int k = lookup(s_outside), j;
+ if (k < 0 || skind[k] != K_ENH) return 0;
+ lit(0); op(O_PICK); lit(n); op(XOP0 + sval[k]);
+ 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 +1380,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 +1409,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 +1429,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 +1472,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);
@@ -1665,6 +1717,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);
}