diff options
Diffstat (limited to 'src/cmd/gc/walk.c')
-rw-r--r-- | src/cmd/gc/walk.c | 1189 |
1 files changed, 1012 insertions, 177 deletions
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index a4edc9062..de2105ed3 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -21,7 +21,13 @@ static NodeList* reorder3(NodeList*); static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); +static Node* sliceany(Node*, NodeList**); static void walkcompare(Node**, NodeList**); +static void walkrotate(Node**); +static void walkmul(Node**, NodeList**); +static void walkdiv(Node**, NodeList**); +static int bounded(Node*, int64); +static Mpint mpzero; // can this code branch reach the end // without an unconditional RETURN @@ -178,8 +184,8 @@ walkstmt(Node **np) dump("nottop", n); break; - case OASOP: case OAS: + case OASOP: case OAS2: case OAS2DOTTYPE: case OAS2RECV: @@ -368,9 +374,10 @@ walkexpr(Node **np, NodeList **init) NodeList *ll, *lr, *lpost; Type *t; int et; - int64 v, v1, v2, len; + int64 v; int32 lno; - Node *n, *fn; + Node *n, *fn, *n1, *n2; + Sym *sym; char buf[100], *p; n = *np; @@ -409,7 +416,7 @@ walkexpr(Node **np, NodeList **init) switch(n->op) { default: dump("walk", n); - fatal("walkexpr: switch 1 unknown op %N", n); + fatal("walkexpr: switch 1 unknown op %+hN", n); break; case OTYPE: @@ -424,14 +431,23 @@ walkexpr(Node **np, NodeList **init) case OCOM: case OREAL: case OIMAG: - case ODOT: - case ODOTPTR: case ODOTMETH: case ODOTINTER: case OIND: walkexpr(&n->left, init); goto ret; + case ODOT: + case ODOTPTR: + usefield(n); + walkexpr(&n->left, init); + goto ret; + + case OEFACE: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + goto ret; + case OITAB: walkexpr(&n->left, init); goto ret; @@ -454,19 +470,34 @@ walkexpr(Node **np, NodeList **init) case OLSH: case ORSH: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + shiftwalked: + t = n->left->type; + n->bounded = bounded(n->right, 8*t->width); + if(debug['m'] && n->etype && !isconst(n->right, CTINT)) + warn("shift bounds check elided"); + goto ret; + case OAND: - case OOR: - case OXOR: case OSUB: - case OMUL: + case OHMUL: case OLT: case OLE: case OGE: case OGT: case OADD: case OCOMPLEX: + case OLROT: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + goto ret; + + case OOR: + case OXOR: walkexpr(&n->left, init); walkexpr(&n->right, init); + walkrotate(&n); goto ret; case OEQ: @@ -505,6 +536,11 @@ walkexpr(Node **np, NodeList **init) n->addable = 1; goto ret; + case OCLOSUREVAR: + case OCFUNC: + n->addable = 1; + goto ret; + case ONAME: if(!(n->class & PHEAP) && n->class != PPARAMREF) n->addable = 1; @@ -525,10 +561,12 @@ walkexpr(Node **np, NodeList **init) if(n->list && n->list->n->op == OAS) goto ret; + /* if(n->left->op == OCLOSURE) { walkcallclosure(n, init); t = n->left->type; } + */ walkexpr(&n->left, init); walkexprlist(n->list, init); @@ -656,6 +694,31 @@ walkexpr(Node **np, NodeList **init) n->ninit = nil; r = n->rlist->n; walkexprlistsafe(n->list, init); + if(isblank(n->list->n) && !isinter(r->type)) { + strcpy(buf, "assert"); + p = buf+strlen(buf); + if(isnilinter(r->left->type)) + *p++ = 'E'; + else + *p++ = 'I'; + *p++ = '2'; + *p++ = 'T'; + *p++ = 'O'; + *p++ = 'K'; + *p = '\0'; + + fn = syslook(buf, 1); + ll = list1(typename(r->type)); + ll = list(ll, r->left); + argtype(fn, r->left->type); + n1 = nod(OCALL, fn, N); + n1->list = ll; + n = nod(OAS, n->list->next->n, n1); + typecheck(&n, Etop); + walkexpr(&n, init); + goto ret; + } + r->op = ODOTTYPE2; walkexpr(&r, init); ll = ascompatet(n->op, n->list, &r->type, 0, init); @@ -694,10 +757,22 @@ walkexpr(Node **np, NodeList **init) goto ret; case OCONVIFACE: + walkexpr(&n->left, init); + + // Optimize convT2E as a two-word copy when T is uintptr-shaped. + if(!isinter(n->left->type) && isnilinter(n->type) && + (n->left->type->width == widthptr) && + isint[simsimtype(n->left->type)]) { + l = nod(OEFACE, typename(n->left->type), n->left); + l->type = n->type; + l->typecheck = n->typecheck; + n = l; + goto ret; + } + // Build name of function: convI2E etc. // Not all names are possible // (e.g., we'll never generate convE2E or convE2I). - walkexpr(&n->left, init); strcpy(buf, "conv"); p = buf+strlen(buf); if(isnilinter(n->left->type)) @@ -719,6 +794,60 @@ walkexpr(Node **np, NodeList **init) ll = list(ll, typename(n->left->type)); if(!isnilinter(n->type)) ll = list(ll, typename(n->type)); + if(!isinter(n->left->type) && !isnilinter(n->type)){ + sym = pkglookup(smprint("%-T.%-T", n->left->type, n->type), itabpkg); + if(sym->def == N) { + l = nod(ONAME, N, N); + l->sym = sym; + l->type = ptrto(types[TUINT8]); + l->addable = 1; + l->class = PEXTERN; + l->xoffset = 0; + sym->def = l; + ggloblsym(sym, widthptr, 1, 0); + } + l = nod(OADDR, sym->def, N); + l->addable = 1; + ll = list(ll, l); + + if(n->left->type->width == widthptr && + isint[simsimtype(n->left->type)]) { + /* For pointer types, we can make a special form of optimization + * + * These statements are put onto the expression init list: + * Itab *tab = atomicloadtype(&cache); + * if(tab == nil) + * tab = typ2Itab(type, itype, &cache); + * + * The CONVIFACE expression is replaced with this: + * OEFACE{tab, ptr}; + */ + l = temp(ptrto(types[TUINT8])); + + n1 = nod(OAS, l, sym->def); + typecheck(&n1, Etop); + *init = list(*init, n1); + + fn = syslook("typ2Itab", 1); + n1 = nod(OCALL, fn, N); + n1->list = ll; + typecheck(&n1, Erv); + walkexpr(&n1, init); + + n2 = nod(OIF, N, N); + n2->ntest = nod(OEQ, l, nodnil()); + n2->nbody = list1(nod(OAS, l, n1)); + n2->likely = -1; + typecheck(&n2, Etop); + *init = list(*init, n2); + + l = nod(OEFACE, l, n->left); + l->typecheck = n->typecheck; + l->type = n->type; + n = l; + goto ret; + } + } ll = list(ll, n->left); argtype(fn, n->left->type); argtype(fn, n->type); @@ -772,7 +901,7 @@ walkexpr(Node **np, NodeList **init) * on 386, rewrite float ops into l = l op r. * everywhere, rewrite map ops into l = l op r. * everywhere, rewrite string += into l = l op r. - * everywhere, rewrite complex /= into l = l op r. + * everywhere, rewrite integer/complex /= into l = l op r. * TODO(rsc): Maybe this rewrite should be done always? */ et = n->left->type->etype; @@ -780,7 +909,8 @@ walkexpr(Node **np, NodeList **init) (thechar == '8' && isfloat[et]) || l->op == OINDEXMAP || et == TSTRING || - (iscomplex[et] && n->etype == ODIV)) { + (!isfloat[et] && n->etype == ODIV) || + n->etype == OMOD) { l = safeexpr(n->left, init); a = l; if(a->op == OINDEXMAP) { @@ -794,15 +924,24 @@ walkexpr(Node **np, NodeList **init) typecheck(&r, Etop); walkexpr(&r, init); n = r; + goto ret; } + if(n->etype == OLSH || n->etype == ORSH) + goto shiftwalked; goto ret; case OANDNOT: walkexpr(&n->left, init); - walkexpr(&n->right, init); n->op = OAND; n->right = nod(OCOM, n->right, N); typecheck(&n->right, Erv); + walkexpr(&n->right, init); + goto ret; + + case OMUL: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + walkmul(&n, init); goto ret; case ODIV: @@ -821,63 +960,80 @@ walkexpr(Node **np, NodeList **init) n = conv(n, t); goto ret; } + // Nothing to do for float divisions. + if(isfloat[et]) + goto ret; + + // Try rewriting as shifts or magic multiplies. + walkdiv(&n, init); + /* - * rewrite div and mod into function calls + * rewrite 64-bit div and mod into function calls * on 32-bit architectures. */ - if(widthptr > 4 || (et != TUINT64 && et != TINT64)) - goto ret; - if(et == TINT64) - strcpy(namebuf, "int64"); - else - strcpy(namebuf, "uint64"); - if(n->op == ODIV) - strcat(namebuf, "div"); - else - strcat(namebuf, "mod"); - n = mkcall(namebuf, n->type, init, - conv(n->left, types[et]), conv(n->right, types[et])); + switch(n->op) { + case OMOD: + case ODIV: + if(widthptr > 4 || (et != TUINT64 && et != TINT64)) + goto ret; + if(et == TINT64) + strcpy(namebuf, "int64"); + else + strcpy(namebuf, "uint64"); + if(n->op == ODIV) + strcat(namebuf, "div"); + else + strcat(namebuf, "mod"); + n = mkcall(namebuf, n->type, init, + conv(n->left, types[et]), conv(n->right, types[et])); + break; + default: + break; + } goto ret; case OINDEX: walkexpr(&n->left, init); + // save the original node for bounds checking elision. + // If it was a ODIV/OMOD walk might rewrite it. + r = n->right; walkexpr(&n->right, init); // if range of type cannot exceed static array bound, - // disable bounds check - if(isfixedarray(n->left->type)) - if(!issigned[n->right->type->etype]) - if(n->right->type->width < 4) - if((1<<(8*n->right->type->width)) <= n->left->type->bound) - n->etype = 1; - - if(isconst(n->left, CTSTR)) - if(!issigned[n->right->type->etype]) - if(n->right->type->width < 4) - if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len) - n->etype = 1; - - // check for static out of bounds - if(isconst(n->right, CTINT) && !n->etype) { - v = mpgetfix(n->right->val.u.xval); - len = 1LL<<60; - t = n->left->type; - if(isconst(n->left, CTSTR)) - len = n->left->val.u.sval->len; - if(t != T && isptr[t->etype]) - t = t->type; - if(isfixedarray(t)) - len = t->bound; - if(v < 0 || v >= (1LL<<31) || v >= len) + // disable bounds check. + if(n->bounded) + goto ret; + t = n->left->type; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) { + n->bounded = bounded(r, t->bound); + if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) + warn("index bounds check elided"); + if(smallintconst(n->right) && !n->bounded) yyerror("index out of bounds"); - else if(isconst(n->left, CTSTR)) { - // replace "abc"[2] with 'b'. - // delayed until now because "abc"[2] is not - // an ideal constant. - nodconst(n, n->type, n->left->val.u.sval->s[v]); - n->typecheck = 1; + } else if(isconst(n->left, CTSTR)) { + n->bounded = bounded(r, n->left->val.u.sval->len); + if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) + warn("index bounds check elided"); + if(smallintconst(n->right)) { + if(!n->bounded) + yyerror("index out of bounds"); + else { + // replace "abc"[2] with 'b'. + // delayed until now because "abc"[2] is not + // an ideal constant. + v = mpgetfix(n->right->val.u.xval); + nodconst(n, n->type, n->left->val.u.sval->s[v]); + n->typecheck = 1; + } } } + + if(isconst(n->right, CTINT)) + if(mpcmpfixfix(n->right->val.u.xval, &mpzero) < 0 || + mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) + yyerror("index out of bounds"); goto ret; case OINDEXMAP: @@ -895,95 +1051,29 @@ walkexpr(Node **np, NodeList **init) goto ret; case OSLICE: + if(n->right != N && n->right->left == N && n->right->right == N) { // noop + walkexpr(&n->left, init); + n = n->left; + goto ret; + } + // fallthrough case OSLICEARR: + case OSLICESTR: + if(n->right == N) // already processed + goto ret; + walkexpr(&n->left, init); - n->left = safeexpr(n->left, init); + // cgen_slice can't handle string literals as source + // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] + if((n->op == OSLICESTR && n->left->op == OLITERAL) || (n->left->op == OINDEX)) + n->left = copyexpr(n->left, n->left->type, init); + else + n->left = safeexpr(n->left, init); walkexpr(&n->right->left, init); n->right->left = safeexpr(n->right->left, init); walkexpr(&n->right->right, init); n->right->right = safeexpr(n->right->right, init); - - len = 1LL<<60; - t = n->left->type; - if(t != T && isptr[t->etype]) - t = t->type; - if(isfixedarray(t)) - len = t->bound; - - // check for static out of bounds - // NOTE: v > len not v >= len. - v1 = -1; - v2 = -1; - if(isconst(n->right->left, CTINT)) { - v1 = mpgetfix(n->right->left->val.u.xval); - if(v1 < 0 || v1 >= (1LL<<31) || v1 > len) { - yyerror("slice index out of bounds"); - v1 = -1; - } - } - if(isconst(n->right->right, CTINT)) { - v2 = mpgetfix(n->right->right->val.u.xval); - if(v2 < 0 || v2 >= (1LL<<31) || v2 > len) { - yyerror("slice index out of bounds"); - v2 = -1; - } - } - if(v1 >= 0 && v2 >= 0 && v1 > v2) - yyerror("inverted slice range"); - - if(n->op == OSLICEARR) - goto slicearray; - - // dynamic slice - // sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) - // sliceslice1(old []any, lb uint64, width uint64) (ary []any) - t = n->type; - et = n->etype; - if(n->right->left == N) - l = nodintconst(0); - else - l = conv(n->right->left, types[TUINT64]); - if(n->right->right != N) { - fn = syslook("sliceslice", 1); - argtype(fn, t->type); // any-1 - argtype(fn, t->type); // any-2 - n = mkcall1(fn, t, init, - n->left, - l, - conv(n->right->right, types[TUINT64]), - nodintconst(t->type->width)); - } else { - fn = syslook("sliceslice1", 1); - argtype(fn, t->type); // any-1 - argtype(fn, t->type); // any-2 - n = mkcall1(fn, t, init, - n->left, - l, - nodintconst(t->type->width)); - } - n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call. - goto ret; - - slicearray: - // static slice - // slicearray(old *any, uint64 nel, lb uint64, hb uint64, width uint64) (ary []any) - t = n->type; - fn = syslook("slicearray", 1); - argtype(fn, n->left->type->type); // any-1 - argtype(fn, t->type); // any-2 - if(n->right->left == N) - l = nodintconst(0); - else - l = conv(n->right->left, types[TUINT64]); - if(n->right->right == N) - r = nodintconst(n->left->type->type->bound); - else - r = conv(n->right->right, types[TUINT64]); - n = mkcall1(fn, t, init, - n->left, nodintconst(n->left->type->type->bound), - l, - r, - nodintconst(t->type->width)); + n = sliceany(n, init); // chops n->right, sets n->list goto ret; case OADDR: @@ -1031,27 +1121,34 @@ walkexpr(Node **np, NodeList **init) goto ret; } - // prepare for rewrite below if(n->etype == OEQ || n->etype == ONE) { + // prepare for rewrite below n->left = cheapexpr(n->left, init); n->right = cheapexpr(n->right, init); - } - // sys_cmpstring(s1, s2) :: 0 - r = mkcall("cmpstring", types[TINT], init, - conv(n->left, types[TSTRING]), - conv(n->right, types[TSTRING])); - r = nod(n->etype, r, nodintconst(0)); + r = mkcall("eqstring", types[TBOOL], init, + conv(n->left, types[TSTRING]), + conv(n->right, types[TSTRING])); - // quick check of len before full compare for == or != - if(n->etype == OEQ || n->etype == ONE) { - if(n->etype == OEQ) + // quick check of len before full compare for == or != + if(n->etype == OEQ) { + // len(left) == len(right) && eqstring(left, right) r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); - else + } else { + // len(left) != len(right) || !eqstring(left, right) + r = nod(ONOT, r, N); r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); + } typecheck(&r, Erv); walkexpr(&r, nil); + } else { + // sys_cmpstring(s1, s2) :: 0 + r = mkcall("cmpstring", types[TINT], init, + conv(n->left, types[TSTRING]), + conv(n->right, types[TSTRING])); + r = nod(n->etype, r, nodintconst(0)); } + typecheck(&r, Erv); if(n->type->etype != TBOOL) fatal("cmp %T", n->type); r->type = n->type; @@ -1061,25 +1158,7 @@ walkexpr(Node **np, NodeList **init) case OADDSTR: n = addstr(n, init); goto ret; - - case OSLICESTR: - // sys_slicestring(s, lb, hb) - if(n->right->left == N) - l = nodintconst(0); - else - l = conv(n->right->left, types[TINT]); - if(n->right->right) { - n = mkcall("slicestring", n->type, init, - conv(n->left, types[TSTRING]), - l, - conv(n->right->right, types[TINT])); - } else { - n = mkcall("slicestring1", n->type, init, - conv(n->left, types[TSTRING]), - l); - } - goto ret; - + case OAPPEND: if(n->isddd) { if(istype(n->type->type, TUINT8) && istype(n->list->next->n->type, TSTRING)) @@ -1177,6 +1256,9 @@ walkexpr(Node **np, NodeList **init) fn = syslook("efaceeq", 1); else fn = syslook("ifaceeq", 1); + + n->right = cheapexpr(n->right, init); + n->left = cheapexpr(n->left, init); argtype(fn, n->right->type); argtype(fn, n->left->type); r = mkcall1(fn, n->type, init, n->left, n->right); @@ -1189,7 +1271,7 @@ walkexpr(Node **np, NodeList **init) else r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r); typecheck(&r, Erv); - walkexpr(&r, nil); + walkexpr(&r, init); r->type = n->type; n = r; goto ret; @@ -1226,9 +1308,16 @@ ret: static Node* ascompatee1(int op, Node *l, Node *r, NodeList **init) { + Node *n; USED(op); + + // convas will turn map assigns into function calls, + // making it impossible for reorder3 to work. + n = nod(OAS, l, r); + if(l->op == OINDEXMAP) + return n; - return convas(nod(OAS, l, r), init); + return convas(n, init); } static NodeList* @@ -1249,12 +1338,16 @@ ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init) lr->n = safeexpr(lr->n, init); nn = nil; - for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) + for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) { + // Do not generate 'x = x' during return. See issue 4014. + if(op == ORETURN && ll->n == lr->n) + continue; nn = list(nn, ascompatee1(op, ll->n, lr->n, init)); + } // cannot happen: caller checked that lists had same length if(ll || lr) - yyerror("error in shape across %+H %O %+H", nl, op, nr); + yyerror("error in shape across %+H %O %+H / %d %d [%s]", nl, op, nr, count(nl), count(nr), curfn->nname->sym->name); return nn; } @@ -1826,13 +1919,14 @@ static int aliased(Node*, NodeList*, NodeList*); static NodeList* reorder3(NodeList *all) { - NodeList *list, *early; + NodeList *list, *early, *mapinit; Node *l; // If a needed expression may be affected by an // earlier assignment, make an early copy of that // expression and use the copy instead. early = nil; + mapinit = nil; for(list=all; list; list=list->next) { l = list->n->left; @@ -1856,8 +1950,11 @@ reorder3(NodeList *all) case ONAME: break; case OINDEX: + case OINDEXMAP: reorder3save(&l->left, all, list, &early); reorder3save(&l->right, all, list, &early); + if(l->op == OINDEXMAP) + list->n = convas(list->n, &mapinit); break; case OIND: case ODOTPTR: @@ -1868,6 +1965,7 @@ reorder3(NodeList *all) reorder3save(&list->n->right, all, list, &early); } + early = concat(mapinit, early); return concat(early, all); } @@ -2119,6 +2217,8 @@ paramstoheap(Type **argin, int out) nn = nil; for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { v = t->nname; + if(v && v->sym && v->sym->name[0] == '~') + v = N; if(v == N && out && hasdefer) { // Defer might stop a panic and show the // return values as they exist at the time of panic. @@ -2395,12 +2495,12 @@ append(Node *n, NodeList **init) l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s) nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc] - nx->etype = 1; // disable bounds check + nx->bounded = 1; l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc] for (a = n->list->next; a != nil; a = a->next) { nx = nod(OINDEX, ns, nn); // s[n] ... - nx->etype = 1; // disable bounds check + nx->bounded = 1; l = list(l, nod(OAS, nx, a->n)); // s[n] = arg if (a->next != nil) l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1 @@ -2412,6 +2512,151 @@ append(Node *n, NodeList **init) return ns; } + +// Generate frontend part for OSLICE[ARR|STR] +// +static Node* +sliceany(Node* n, NodeList **init) +{ + int bounded; + Node *src, *lb, *hb, *bound, *chk, *chk1, *chk2; + int64 lbv, hbv, bv, w; + Type *bt; + +// print("before sliceany: %+N\n", n); + + src = n->left; + lb = n->right->left; + hb = n->right->right; + + bounded = n->etype; + + if(n->op == OSLICESTR) + bound = nod(OLEN, src, N); + else + bound = nod(OCAP, src, N); + + typecheck(&bound, Erv); + walkexpr(&bound, init); // if src is an array, bound will be a const now. + + // static checks if possible + bv = 1LL<<50; + if(isconst(bound, CTINT)) { + if(!smallintconst(bound)) + yyerror("array len too large"); + else + bv = mpgetfix(bound->val.u.xval); + } + + if(isconst(hb, CTINT)) { + hbv = mpgetfix(hb->val.u.xval); + if(hbv < 0 || hbv > bv) { + yyerror("slice index out of bounds"); + hbv = -1; + } + } + if(isconst(lb, CTINT)) { + lbv = mpgetfix(lb->val.u.xval); + if(lbv < 0 || lbv > bv) { + yyerror("slice index out of bounds"); + lbv = -1; + } + if(lbv == 0) + lb = N; + } + + // dynamic checks convert all bounds to unsigned to save us the bound < 0 comparison + // generate + // if hb > bound || lb > hb { panicslice() } + chk = N; + chk1 = N; + chk2 = N; + + bt = types[simtype[TUINT]]; + if(hb != N && hb->type->width > 4) + bt = types[TUINT64]; + if(lb != N && lb->type->width > 4) + bt = types[TUINT64]; + + bound = cheapexpr(conv(bound, bt), init); + + if(hb != N) { + hb = cheapexpr(conv(hb, bt), init); + if(!bounded) + chk1 = nod(OLT, bound, hb); + } else if(n->op == OSLICEARR) { + hb = bound; + } else { + hb = nod(OLEN, src, N); + typecheck(&hb, Erv); + walkexpr(&hb, init); + hb = cheapexpr(conv(hb, bt), init); + } + + if(lb != N) { + lb = cheapexpr(conv(lb, bt), init); + if(!bounded) + chk2 = nod(OLT, hb, lb); + } + + if(chk1 != N || chk2 != N) { + chk = nod(OIF, N, N); + chk->nbody = list1(mkcall("panicslice", T, init)); + if(chk1 != N) + chk->ntest = chk1; + if(chk2 != N) { + if(chk->ntest == N) + chk->ntest = chk2; + else + chk->ntest = nod(OOROR, chk->ntest, chk2); + } + typecheck(&chk, Etop); + walkstmt(&chk); + *init = concat(*init, chk->ninit); + chk->ninit = nil; + *init = list(*init, chk); + } + + // prepare new cap, len and offs for backend cgen_slice + // cap = bound [ - lo ] + n->right = N; + n->list = nil; + if(lb == N) + bound = conv(bound, types[simtype[TUINT]]); + else + bound = nod(OSUB, conv(bound, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); + typecheck(&bound, Erv); + walkexpr(&bound, init); + n->list = list(n->list, bound); + + // len = hi [ - lo] + if(lb == N) + hb = conv(hb, types[simtype[TUINT]]); + else + hb = nod(OSUB, conv(hb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); + typecheck(&hb, Erv); + walkexpr(&hb, init); + n->list = list(n->list, hb); + + // offs = [width *] lo, but omit if zero + if(lb != N) { + if(n->op == OSLICESTR) + w = 1; + else + w = n->type->type->width; + lb = conv(lb, types[TUINTPTR]); + if(w > 1) + lb = nod(OMUL, nodintconst(w), lb); + typecheck(&lb, Erv); + walkexpr(&lb, init); + n->list = list(n->list, lb); + } + +// print("after sliceany: %+N\n", n); + + return n; +} + static Node* eqfor(Type *t) { @@ -2531,6 +2776,8 @@ walkcompare(Node **np, NodeList **init) // Struct of four or fewer fields. // Inline comparisons. for(t1=t->type; t1; t1=t1->down) { + if(isblanksym(t1->sym)) + continue; li = nod(OXDOT, l, newname(t1->sym)); ri = nod(OXDOT, r, newname(t1->sym)); a = nod(n->op, li, ri); @@ -2596,3 +2843,591 @@ hard: *np = r; return; } + +static int +samecheap(Node *a, Node *b) +{ + if(a == N || b == N || a->op != b->op) + return 0; + + switch(a->op) { + case ONAME: + return a == b; + // TODO: Could do more here, but maybe this is enough. + // It's all cheapexpr does. + } + return 0; +} + +static void +walkrotate(Node **np) +{ + int w, sl, sr, s; + Node *l, *r; + Node *n; + + n = *np; + + // Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value. + l = n->left; + r = n->right; + if((n->op != OOR && n->op != OXOR) || + (l->op != OLSH && l->op != ORSH) || + (r->op != OLSH && r->op != ORSH) || + n->type == T || issigned[n->type->etype] || + l->op == r->op) { + return; + } + + // Want same, side effect-free expression on lhs of both shifts. + if(!samecheap(l->left, r->left)) + return; + + // Constants adding to width? + w = l->type->width * 8; + if(smallintconst(l->right) && smallintconst(r->right)) { + if((sl=mpgetfix(l->right->val.u.xval)) >= 0 && (sr=mpgetfix(r->right->val.u.xval)) >= 0 && sl+sr == w) + goto yes; + return; + } + + // TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31). + return; + +yes: + // Rewrite left shift half to left rotate. + if(l->op == OLSH) + n = l; + else + n = r; + n->op = OLROT; + + // Remove rotate 0 and rotate w. + s = mpgetfix(n->right->val.u.xval); + if(s == 0 || s == w) + n = n->left; + + *np = n; + return; +} + +/* + * walkmul rewrites integer multiplication by powers of two as shifts. + */ +static void +walkmul(Node **np, NodeList **init) +{ + Node *n, *nl, *nr; + int pow, neg, w; + + n = *np; + if(!isint[n->type->etype]) + return; + + if(n->right->op == OLITERAL) { + nl = n->left; + nr = n->right; + } else if(n->left->op == OLITERAL) { + nl = n->right; + nr = n->left; + } else + return; + + neg = 0; + + // x*0 is 0 (and side effects of x). + if(mpgetfix(nr->val.u.xval) == 0) { + cheapexpr(nl, init); + nodconst(n, n->type, 0); + goto ret; + } + + // nr is a constant. + pow = powtwo(nr); + if(pow < 0) + return; + if(pow >= 1000) { + // negative power of 2, like -16 + neg = 1; + pow -= 1000; + } + + w = nl->type->width*8; + if(pow+1 >= w)// too big, shouldn't happen + return; + + nl = cheapexpr(nl, init); + + if(pow == 0) { + // x*1 is x + n = nl; + goto ret; + } + + n = nod(OLSH, nl, nodintconst(pow)); + +ret: + if(neg) + n = nod(OMINUS, n, N); + + typecheck(&n, Erv); + walkexpr(&n, init); + *np = n; +} + +/* + * walkdiv rewrites division by a constant as less expensive + * operations. + */ +static void +walkdiv(Node **np, NodeList **init) +{ + Node *n, *nl, *nr, *nc; + Node *n1, *n2, *n3, *n4; + int pow; // if >= 0, nr is 1<<pow + int s; // 1 if nr is negative. + int w; + Type *twide; + Magic m; + + n = *np; + if(n->right->op != OLITERAL) + return; + // nr is a constant. + nl = cheapexpr(n->left, init); + nr = n->right; + + // special cases of mod/div + // by a constant + w = nl->type->width*8; + s = 0; + pow = powtwo(nr); + if(pow >= 1000) { + // negative power of 2 + s = 1; + pow -= 1000; + } + + if(pow+1 >= w) { + // divisor too large. + return; + } + if(pow < 0) { + goto divbymul; + } + + switch(pow) { + case 0: + if(n->op == OMOD) { + // nl % 1 is zero. + nodconst(n, n->type, 0); + } else if(s) { + // divide by -1 + n->op = OMINUS; + n->right = N; + } else { + // divide by 1 + n = nl; + } + break; + default: + if(issigned[n->type->etype]) { + if(n->op == OMOD) { + // signed modulo 2^pow is like ANDing + // with the last pow bits, but if nl < 0, + // nl & (2^pow-1) is (nl+1)%2^pow - 1. + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], w-1); + n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. + if(pow == 1) { + typecheck(&n1, Erv); + n1 = cheapexpr(n1, init); + // n = (nl+ε)&1 -ε where ε=1 iff nl<0. + n2 = nod(OSUB, nl, n1); + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, 1); + n3 = nod(OAND, n2, nc); + n = nod(OADD, n3, n1); + } else { + // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0. + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, (1LL<<pow)-1); + n2 = nod(OAND, n1, nc); // n2 = 2^pow-1 iff nl<0. + typecheck(&n2, Erv); + n2 = cheapexpr(n2, init); + + n3 = nod(OADD, nl, n2); + n4 = nod(OAND, n3, nc); + n = nod(OSUB, n4, n2); + } + break; + } else { + // arithmetic right shift does not give the correct rounding. + // if nl >= 0, nl >> n == nl / nr + // if nl < 0, we want to add 2^n-1 first. + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], w-1); + n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. + if(pow == 1) { + // nl+1 is nl-(-1) + n->left = nod(OSUB, nl, n1); + } else { + // Do a logical right right on -1 to keep pow bits. + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], w-pow); + n2 = nod(ORSH, conv(n1, tounsigned(nl->type)), nc); + n->left = nod(OADD, nl, conv(n2, nl->type)); + } + // n = (nl + 2^pow-1) >> pow + n->op = ORSH; + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], pow); + n->right = nc; + n->typecheck = 0; + } + if(s) + n = nod(OMINUS, n, N); + break; + } + nc = nod(OXXX, N, N); + if(n->op == OMOD) { + // n = nl & (nr-1) + n->op = OAND; + nodconst(nc, nl->type, mpgetfix(nr->val.u.xval)-1); + } else { + // n = nl >> pow + n->op = ORSH; + nodconst(nc, types[simtype[TUINT]], pow); + } + n->typecheck = 0; + n->right = nc; + break; + } + goto ret; + +divbymul: + // try to do division by multiply by (2^w)/d + // see hacker's delight chapter 10 + // TODO: support 64-bit magic multiply here. + m.w = w; + if(issigned[nl->type->etype]) { + m.sd = mpgetfix(nr->val.u.xval); + smagic(&m); + } else { + m.ud = mpgetfix(nr->val.u.xval); + umagic(&m); + } + if(m.bad) + return; + + // We have a quick division method so use it + // for modulo too. + if(n->op == OMOD) + goto longmod; + + switch(simtype[nl->type->etype]) { + default: + return; + + case TUINT8: + case TUINT16: + case TUINT32: + // n1 = nl * magic >> w (HMUL) + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, m.um); + n1 = nod(OMUL, nl, nc); + typecheck(&n1, Erv); + n1->op = OHMUL; + if(m.ua) { + // Select a Go type with (at least) twice the width. + switch(simtype[nl->type->etype]) { + default: + return; + case TUINT8: + case TUINT16: + twide = types[TUINT32]; + break; + case TUINT32: + twide = types[TUINT64]; + break; + case TINT8: + case TINT16: + twide = types[TINT32]; + break; + case TINT32: + twide = types[TINT64]; + break; + } + + // add numerator (might overflow). + // n2 = (n1 + nl) + n2 = nod(OADD, conv(n1, twide), conv(nl, twide)); + + // shift by m.s + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], m.s); + n = conv(nod(ORSH, n2, nc), nl->type); + } else { + // n = n1 >> m.s + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], m.s); + n = nod(ORSH, n1, nc); + } + break; + + case TINT8: + case TINT16: + case TINT32: + // n1 = nl * magic >> w + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, m.sm); + n1 = nod(OMUL, nl, nc); + typecheck(&n1, Erv); + n1->op = OHMUL; + if(m.sm < 0) { + // add the numerator. + n1 = nod(OADD, n1, nl); + } + // shift by m.s + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], m.s); + n2 = conv(nod(ORSH, n1, nc), nl->type); + // add 1 iff n1 is negative. + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], w-1); + n3 = nod(ORSH, nl, nc); // n4 = -1 iff n1 is negative. + n = nod(OSUB, n2, n3); + // apply sign. + if(m.sd < 0) + n = nod(OMINUS, n, N); + break; + } + goto ret; + +longmod: + // rewrite as A%B = A - (A/B*B). + n1 = nod(ODIV, nl, nr); + n2 = nod(OMUL, n1, nr); + n = nod(OSUB, nl, n2); + goto ret; + +ret: + typecheck(&n, Erv); + walkexpr(&n, init); + *np = n; +} + +// return 1 if integer n must be in range [0, max), 0 otherwise +static int +bounded(Node *n, int64 max) +{ + int64 v; + int32 bits; + int sign; + + if(n->type == T || !isint[n->type->etype]) + return 0; + + sign = issigned[n->type->etype]; + bits = 8*n->type->width; + + if(smallintconst(n)) { + v = mpgetfix(n->val.u.xval); + return 0 <= v && v < max; + } + + switch(n->op) { + case OAND: + v = -1; + if(smallintconst(n->left)) { + v = mpgetfix(n->left->val.u.xval); + } else if(smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + } + if(0 <= v && v < max) + return 1; + break; + + case OMOD: + if(!sign && smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + if(0 <= v && v <= max) + return 1; + } + break; + + case ODIV: + if(!sign && smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + while(bits > 0 && v >= 2) { + bits--; + v >>= 1; + } + } + break; + + case ORSH: + if(!sign && smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + if(v > bits) + return 1; + bits -= v; + } + break; + } + + if(!sign && bits <= 62 && (1LL<<bits) <= max) + return 1; + + return 0; +} + +void +usefield(Node *n) +{ + Type *field, *l; + + if(!fieldtrack_enabled) + return; + + switch(n->op) { + default: + fatal("usefield %O", n->op); + case ODOT: + case ODOTPTR: + break; + } + + field = n->paramfld; + if(field == T) + fatal("usefield %T %S without paramfld", n->left->type, n->right->sym); + if(field->note == nil || strstr(field->note->s, "go:\"track\"") == nil) + return; + + // dedup on list + if(field->lastfn == curfn) + return; + field->lastfn = curfn; + field->outer = n->left->type; + if(isptr[field->outer->etype]) + field->outer = field->outer->type; + if(field->outer->sym == S) + yyerror("tracked field must be in named struct type"); + if(!exportname(field->sym->name)) + yyerror("tracked field must be exported (upper case)"); + + l = typ(0); + l->type = field; + l->down = curfn->paramfld; + curfn->paramfld = l; +} + +static int +candiscardlist(NodeList *l) +{ + for(; l; l=l->next) + if(!candiscard(l->n)) + return 0; + return 1; +} + +int +candiscard(Node *n) +{ + if(n == N) + return 1; + + switch(n->op) { + default: + return 0; + + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + case OADD: + case OSUB: + case OOR: + case OXOR: + case OADDSTR: + case OADDR: + case OANDAND: + case OARRAYBYTESTR: + case OARRAYRUNESTR: + case OSTRARRAYBYTE: + case OSTRARRAYRUNE: + case OCAP: + case OCMPIFACE: + case OCMPSTR: + case OCOMPLIT: + case OMAPLIT: + case OSTRUCTLIT: + case OARRAYLIT: + case OPTRLIT: + case OCONV: + case OCONVIFACE: + case OCONVNOP: + case ODOT: + case OEQ: + case ONE: + case OLT: + case OLE: + case OGT: + case OGE: + case OKEY: + case OLEN: + case OMUL: + case OLSH: + case ORSH: + case OAND: + case OANDNOT: + case ONEW: + case ONOT: + case OCOM: + case OPLUS: + case OMINUS: + case OOROR: + case OPAREN: + case ORUNESTR: + case OREAL: + case OIMAG: + case OCOMPLEX: + // Discardable as long as the subpieces are. + break; + + case ODIV: + case OMOD: + // Discardable as long as we know it's not division by zero. + if(isconst(n->right, CTINT) && mpcmpfixc(n->right->val.u.xval, 0) != 0) + break; + if(isconst(n->right, CTFLT) && mpcmpfltc(n->right->val.u.fval, 0) != 0) + break; + return 0; + + case OMAKECHAN: + case OMAKEMAP: + // Discardable as long as we know it won't fail because of a bad size. + if(isconst(n->left, CTINT) && mpcmpfixc(n->left->val.u.xval, 0) == 0) + break; + return 0; + + case OMAKESLICE: + // Difficult to tell what sizes are okay. + return 0; + } + + if(!candiscard(n->left) || + !candiscard(n->right) || + !candiscard(n->ntest) || + !candiscard(n->nincr) || + !candiscardlist(n->ninit) || + !candiscardlist(n->nbody) || + !candiscardlist(n->nelse) || + !candiscardlist(n->list) || + !candiscardlist(n->rlist)) { + return 0; + } + + return 1; +} |