// Derived from Inferno utils/6c/txt.c // http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) // Portions Copyright © 2004,2006 Bruce Ellis // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others // Portions Copyright © 2009 The Go Authors. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include "gg.h" // TODO(rsc): Can make this bigger if we move // the text segment up higher in 6l for all GOOS. vlong unmappedzero = 4096; void clearp(Prog *p) { p->as = AEND; p->from.type = D_NONE; p->from.index = D_NONE; p->to.type = D_NONE; p->to.index = D_NONE; p->loc = pcloc; pcloc++; } /* * generate and return proc with p->as = as, * linked into program. pc is next instruction. */ Prog* prog(int as) { Prog *p; p = pc; pc = mal(sizeof(*pc)); clearp(pc); if(lineno == 0) { if(debug['K']) warn("prog: line 0"); } p->as = as; p->lineno = lineno; p->link = pc; return p; } /* * generate a branch. * t is ignored. */ Prog* gbranch(int as, Type *t) { Prog *p; p = prog(as); p->to.type = D_BRANCH; p->to.branch = P; return p; } /* * patch previous branch to jump to to. */ void patch(Prog *p, Prog *to) { if(p->to.type != D_BRANCH) fatal("patch: not a branch"); p->to.branch = to; p->to.offset = to->loc; } Prog* unpatch(Prog *p) { Prog *q; if(p->to.type != D_BRANCH) fatal("unpatch: not a branch"); q = p->to.branch; p->to.branch = P; p->to.offset = 0; return q; } /* * start a new Prog list. */ Plist* newplist(void) { Plist *pl; pl = mal(sizeof(*pl)); if(plist == nil) plist = pl; else plast->link = pl; plast = pl; pc = mal(sizeof(*pc)); clearp(pc); pl->firstpc = pc; return pl; } void clearstk(void) { Plist *pl; Prog *p1, *p2; Node sp, di, cx, con, ax; if((uint32)plast->firstpc->to.offset <= 0) return; // reestablish context for inserting code // at beginning of function. pl = plast; p1 = pl->firstpc; p2 = p1->link; pc = mal(sizeof(*pc)); clearp(pc); p1->link = pc; // zero stack frame nodreg(&sp, types[tptr], D_SP); nodreg(&di, types[tptr], D_DI); nodreg(&cx, types[TUINT64], D_CX); nodconst(&con, types[TUINT64], (uint32)p1->to.offset / widthptr); gins(ACLD, N, N); gins(AMOVQ, &sp, &di); gins(AMOVQ, &con, &cx); nodconst(&con, types[TUINT64], 0); nodreg(&ax, types[TUINT64], D_AX); gins(AMOVQ, &con, &ax); gins(AREP, N, N); gins(ASTOSQ, N, N); // continue with original code. gins(ANOP, N, N)->link = p2; pc = P; } void gused(Node *n) { gins(ANOP, n, N); // used } Prog* gjmp(Prog *to) { Prog *p; p = gbranch(AJMP, T); if(to != P) patch(p, to); return p; } void ggloblnod(Node *nam, int32 width) { Prog *p; p = gins(AGLOBL, nam, N); p->lineno = nam->lineno; p->to.sym = S; p->to.type = D_CONST; p->to.offset = width; if(nam->readonly) p->from.scale = RODATA; } void ggloblsym(Sym *s, int32 width, int dupok) { Prog *p; p = gins(AGLOBL, N, N); p->from.type = D_EXTERN; p->from.index = D_NONE; p->from.sym = s; p->to.type = D_CONST; p->to.index = D_NONE; p->to.offset = width; if(dupok) p->from.scale = DUPOK; p->from.scale |= RODATA; } int isfat(Type *t) { if(t != T) switch(t->etype) { case TSTRUCT: case TARRAY: case TSTRING: case TINTER: // maybe remove later return 1; } return 0; } /* * naddr of func generates code for address of func. * if using opcode that can take address implicitly, * call afunclit to fix up the argument. */ void afunclit(Addr *a) { if(a->type == D_ADDR && a->index == D_EXTERN) { a->type = D_EXTERN; a->index = D_NONE; } } static int resvd[] = { D_DI, // for movstring D_SI, // for movstring D_AX, // for divide D_CX, // for shift D_DX, // for divide D_SP, // for stack D_R14, // reserved for m D_R15, // reserved for u }; void ginit(void) { int i; for(i=0; ietype]; switch(et) { case TINT8: case TUINT8: case TINT16: case TUINT16: case TINT32: case TUINT32: case TINT64: case TUINT64: case TPTR32: case TPTR64: case TBOOL: if(o != N && o->op == OREGISTER) { i = o->val.u.reg; if(i >= D_AX && i <= D_R15) goto out; } for(i=D_AX; i<=D_R15; i++) if(reg[i] == 0) goto out; yyerror("out of fixed registers"); goto err; case TFLOAT32: case TFLOAT64: if(o != N && o->op == OREGISTER) { i = o->val.u.reg; if(i >= D_X0 && i <= D_X7) goto out; } for(i=D_X0; i<=D_X7; i++) if(reg[i] == 0) goto out; yyerror("out of floating registers"); goto err; case TCOMPLEX64: case TCOMPLEX128: tempname(n, t); return; } yyerror("regalloc: unknown type %T", t); err: nodreg(n, t, 0); return; out: reg[i]++; nodreg(n, t, i); } void regfree(Node *n) { int i; if(n->op == ONAME) return; if(n->op != OREGISTER && n->op != OINDREG) fatal("regfree: not a register"); i = n->val.u.reg; if(i == D_SP) return; if(i < 0 || i >= sizeof(reg)) fatal("regfree: reg out of range"); if(reg[i] <= 0) fatal("regfree: reg not allocated"); reg[i]--; } /* * initialize n to be register r of type t. */ void nodreg(Node *n, Type *t, int r) { if(t == T) fatal("nodreg: t nil"); memset(n, 0, sizeof(*n)); n->op = OREGISTER; n->addable = 1; ullmancalc(n); n->val.u.reg = r; n->type = t; } /* * initialize n to be indirect of register r; n is type t. */ void nodindreg(Node *n, Type *t, int r) { nodreg(n, t, r); n->op = OINDREG; } Node* nodarg(Type *t, int fp) { Node *n; Type *first; Iter savet; // entire argument struct, not just one arg if(t->etype == TSTRUCT && t->funarg) { n = nod(ONAME, N, N); n->sym = lookup(".args"); n->type = t; first = structfirst(&savet, &t); if(first == nil) fatal("nodarg: bad struct"); if(first->width == BADWIDTH) fatal("nodarg: offset not computed for %T", t); n->xoffset = first->width; n->addable = 1; goto fp; } if(t->etype != TFIELD) fatal("nodarg: not field %T", t); n = nod(ONAME, N, N); n->type = t->type; n->sym = t->sym; if(t->width == BADWIDTH) fatal("nodarg: offset not computed for %T", t); n->xoffset = t->width; n->addable = 1; fp: switch(fp) { case 0: // output arg n->op = OINDREG; n->val.u.reg = D_SP; break; case 1: // input arg n->class = PPARAM; break; case 2: // offset output arg fatal("shouldnt be used"); n->op = OINDREG; n->val.u.reg = D_SP; n->xoffset += types[tptr]->width; break; } return n; } /* * generate * as $c, reg */ void gconreg(int as, vlong c, int reg) { Node nr; nodreg(&nr, types[TINT64], reg); ginscon(as, c, &nr); } /* * generate * as $c, n */ void ginscon(int as, vlong c, Node *n2) { Node n1, ntmp; nodconst(&n1, types[TINT64], c); if(as != AMOVQ && (c < -1LL<<31 || c >= 1LL<<31)) { // cannot have 64-bit immediokate in ADD, etc. // instead, MOV into register first. regalloc(&ntmp, types[TINT64], N); gins(AMOVQ, &n1, &ntmp); gins(as, &ntmp, n2); regfree(&ntmp); return; } gins(as, &n1, n2); } #define CASE(a,b) (((a)<<16)|((b)<<0)) /* * Is this node a memory operand? */ int ismem(Node *n) { switch(n->op) { case OLEN: case OCAP: case OINDREG: case ONAME: case OPARAM: return 1; } return 0; } /* * set up nodes representing 2^63 */ Node bigi; Node bigf; void bignodes(void) { static int did; if(did) return; did = 1; nodconst(&bigi, types[TUINT64], 1); mpshiftfix(bigi.val.u.xval, 63); bigf = bigi; bigf.type = types[TFLOAT64]; bigf.val.ctype = CTFLT; bigf.val.u.fval = mal(sizeof *bigf.val.u.fval); mpmovefixflt(bigf.val.u.fval, bigi.val.u.xval); } /* * generate move: * t = f * hard part is conversions. */ // TODO: lost special constants for floating point. XORPD for 0.0? void gmove(Node *f, Node *t) { int a, ft, tt; Type *cvt; Node r1, r2, r3, r4, zero, one, con; Prog *p1, *p2; if(debug['M']) print("gmove %N -> %N\n", f, t); ft = simsimtype(f->type); tt = simsimtype(t->type); cvt = t->type; if(iscomplex[ft] || iscomplex[tt]) { complexmove(f, t); return; } // cannot have two memory operands if(ismem(f) && ismem(t)) goto hard; // convert constant to desired type if(f->op == OLITERAL) { convconst(&con, t->type, &f->val); f = &con; ft = tt; // so big switch will choose a simple mov // some constants can't move directly to memory. if(ismem(t)) { // float constants come from memory. if(isfloat[tt]) goto hard; // 64-bit immediates are really 32-bit sign-extended // unless moving into a register. if(isint[tt]) { if(mpcmpfixfix(con.val.u.xval, minintval[TINT32]) < 0) goto hard; if(mpcmpfixfix(con.val.u.xval, maxintval[TINT32]) > 0) goto hard; } } } // value -> value copy, only one memory operand. // figure out the instruction to use. // break out of switch for one-instruction gins. // goto rdst for "destination must be register". // goto hard for "convert to cvt type first". // otherwise handle and return. switch(CASE(ft, tt)) { default: fatal("gmove %lT -> %lT", f->type, t->type); /* * integer copy and truncate */ case CASE(TINT8, TINT8): // same size case CASE(TINT8, TUINT8): case CASE(TUINT8, TINT8): case CASE(TUINT8, TUINT8): case CASE(TINT16, TINT8): // truncate case CASE(TUINT16, TINT8): case CASE(TINT32, TINT8): case CASE(TUINT32, TINT8): case CASE(TINT64, TINT8): case CASE(TUINT64, TINT8): case CASE(TINT16, TUINT8): case CASE(TUINT16, TUINT8): case CASE(TINT32, TUINT8): case CASE(TUINT32, TUINT8): case CASE(TINT64, TUINT8): case CASE(TUINT64, TUINT8): a = AMOVB; break; case CASE(TINT16, TINT16): // same size case CASE(TINT16, TUINT16): case CASE(TUINT16, TINT16): case CASE(TUINT16, TUINT16): case CASE(TINT32, TINT16): // truncate case CASE(TUINT32, TINT16): case CASE(TINT64, TINT16): case CASE(TUINT64, TINT16): case CASE(TINT32, TUINT16): case CASE(TUINT32, TUINT16): case CASE(TINT64, TUINT16): case CASE(TUINT64, TUINT16): a = AMOVW; break; case CASE(TINT32, TINT32): // same size case CASE(TINT32, TUINT32): case CASE(TUINT32, TINT32): case CASE(TUINT32, TUINT32): case CASE(TINT64, TINT32): // truncate case CASE(TUINT64, TINT32): case CASE(TINT64, TUINT32): case CASE(TUINT64, TUINT32): a = AMOVL; break; case CASE(TINT64, TINT64): // same size case CASE(TINT64, TUINT64): case CASE(TUINT64, TINT64): case CASE(TUINT64, TUINT64): a = AMOVQ; break; /* * integer up-conversions */ case CASE(TINT8, TINT16): // sign extend int8 case CASE(TINT8, TUINT16): a = AMOVBWSX; goto rdst; case CASE(TINT8, TINT32): case CASE(TINT8, TUINT32): a = AMOVBLSX; goto rdst; case CASE(TINT8, TINT64): case CASE(TINT8, TUINT64): a = AMOVBQSX; goto rdst; case CASE(TUINT8, TINT16): // zero extend uint8 case CASE(TUINT8, TUINT16): a = AMOVBWZX; goto rdst; case CASE(TUINT8, TINT32): case CASE(TUINT8, TUINT32): a = AMOVBLZX; goto rdst; case CASE(TUINT8, TINT64): case CASE(TUINT8, TUINT64): a = AMOVBQZX; goto rdst; case CASE(TINT16, TINT32): // sign extend int16 case CASE(TINT16, TUINT32): a = AMOVWLSX; goto rdst; case CASE(TINT16, TINT64): case CASE(TINT16, TUINT64): a = AMOVWQSX; goto rdst; case CASE(TUINT16, TINT32): // zero extend uint16 case CASE(TUINT16, TUINT32): a = AMOVWLZX; goto rdst; case CASE(TUINT16, TINT64): case CASE(TUINT16, TUINT64): a = AMOVWQZX; goto rdst; case CASE(TINT32, TINT64): // sign extend int32 case CASE(TINT32, TUINT64): a = AMOVLQSX; goto rdst; case CASE(TUINT32, TINT64): // zero extend uint32 case CASE(TUINT32, TUINT64): // AMOVL into a register zeros the top of the register, // so this is not always necessary, but if we rely on AMOVL // the optimizer is almost certain to screw with us. a = AMOVLQZX; goto rdst; /* * float to integer */ case CASE(TFLOAT32, TINT32): a = ACVTTSS2SL; goto rdst; case CASE(TFLOAT64, TINT32): a = ACVTTSD2SL; goto rdst; case CASE(TFLOAT32, TINT64): a = ACVTTSS2SQ; goto rdst; case CASE(TFLOAT64, TINT64): a = ACVTTSD2SQ; goto rdst; case CASE(TFLOAT32, TINT16): case CASE(TFLOAT32, TINT8): case CASE(TFLOAT32, TUINT16): case CASE(TFLOAT32, TUINT8): case CASE(TFLOAT64, TINT16): case CASE(TFLOAT64, TINT8): case CASE(TFLOAT64, TUINT16): case CASE(TFLOAT64, TUINT8): // convert via int32. cvt = types[TINT32]; goto hard; case CASE(TFLOAT32, TUINT32): case CASE(TFLOAT64, TUINT32): // convert via int64. cvt = types[TINT64]; goto hard; case CASE(TFLOAT32, TUINT64): case CASE(TFLOAT64, TUINT64): // algorithm is: // if small enough, use native float64 -> int64 conversion. // otherwise, subtract 2^63, convert, and add it back. a = ACVTSS2SQ; if(ft == TFLOAT64) a = ACVTSD2SQ; bignodes(); regalloc(&r1, types[ft], N); regalloc(&r2, types[tt], t); regalloc(&r3, types[ft], N); regalloc(&r4, types[tt], N); gins(optoas(OAS, f->type), f, &r1); gins(optoas(OCMP, f->type), &bigf, &r1); p1 = gbranch(optoas(OLE, f->type), T); gins(a, &r1, &r2); p2 = gbranch(AJMP, T); patch(p1, pc); gins(optoas(OAS, f->type), &bigf, &r3); gins(optoas(OSUB, f->type), &r3, &r1); gins(a, &r1, &r2); gins(AMOVQ, &bigi, &r4); gins(AXORQ, &r4, &r2); patch(p2, pc); gmove(&r2, t); regfree(&r4); regfree(&r3); regfree(&r2); regfree(&r1); return; /* * integer to float */ case CASE(TINT32, TFLOAT32): a = ACVTSL2SS; goto rdst; case CASE(TINT32, TFLOAT64): a = ACVTSL2SD; goto rdst; case CASE(TINT64, TFLOAT32): a = ACVTSQ2SS; goto rdst; case CASE(TINT64, TFLOAT64): a = ACVTSQ2SD; goto rdst; case CASE(TINT16, TFLOAT32): case CASE(TINT16, TFLOAT64): case CASE(TINT8, TFLOAT32): case CASE(TINT8, TFLOAT64): case CASE(TUINT16, TFLOAT32): case CASE(TUINT16, TFLOAT64): case CASE(TUINT8, TFLOAT32): case CASE(TUINT8, TFLOAT64): // convert via int32 cvt = types[TINT32]; goto hard; case CASE(TUINT32, TFLOAT32): case CASE(TUINT32, TFLOAT64): // convert via int64. cvt = types[TINT64]; goto hard; case CASE(TUINT64, TFLOAT32): case CASE(TUINT64, TFLOAT64): // algorithm is: // if small enough, use native int64 -> uint64 conversion. // otherwise, halve (rounding to odd?), convert, and double. a = ACVTSQ2SS; if(tt == TFLOAT64) a = ACVTSQ2SD; nodconst(&zero, types[TUINT64], 0); nodconst(&one, types[TUINT64], 1); regalloc(&r1, f->type, f); regalloc(&r2, t->type, t); regalloc(&r3, f->type, N); regalloc(&r4, f->type, N); gmove(f, &r1); gins(ACMPQ, &r1, &zero); p1 = gbranch(AJLT, T); gins(a, &r1, &r2); p2 = gbranch(AJMP, T); patch(p1, pc); gmove(&r1, &r3); gins(ASHRQ, &one, &r3); gmove(&r1, &r4); gins(AANDL, &one, &r4); gins(AORQ, &r4, &r3); gins(a, &r3, &r2); gins(optoas(OADD, t->type), &r2, &r2); patch(p2, pc); gmove(&r2, t); regfree(&r4); regfree(&r3); regfree(&r2); regfree(&r1); return; /* * float to float */ case CASE(TFLOAT32, TFLOAT32): a = AMOVSS; break; case CASE(TFLOAT64, TFLOAT64): a = AMOVSD; break; case CASE(TFLOAT32, TFLOAT64): a = ACVTSS2SD; goto rdst; case CASE(TFLOAT64, TFLOAT32): a = ACVTSD2SS; goto rdst; } gins(a, f, t); return; rdst: // requires register destination regalloc(&r1, t->type, t); gins(a, f, &r1); gmove(&r1, t); regfree(&r1); return; hard: // requires register intermediate regalloc(&r1, cvt, t); gmove(f, &r1); gmove(&r1, t); regfree(&r1); return; } int samaddr(Node *f, Node *t) { if(f->op != t->op) return 0; switch(f->op) { case OREGISTER: if(f->val.u.reg != t->val.u.reg) break; return 1; } return 0; } /* * generate one instruction: * as f, t */ Prog* gins(int as, Node *f, Node *t) { // Node nod; int32 w; Prog *p; Addr af, at; // if(f != N && f->op == OINDEX) { // regalloc(&nod, ®node, Z); // v = constnode.vconst; // cgen(f->right, &nod); // constnode.vconst = v; // idx.reg = nod.reg; // regfree(&nod); // } // if(t != N && t->op == OINDEX) { // regalloc(&nod, ®node, Z); // v = constnode.vconst; // cgen(t->right, &nod); // constnode.vconst = v; // idx.reg = nod.reg; // regfree(&nod); // } switch(as) { case AMOVB: case AMOVW: case AMOVL: case AMOVQ: case AMOVSS: case AMOVSD: if(f != N && t != N && samaddr(f, t)) return nil; } memset(&af, 0, sizeof af); memset(&at, 0, sizeof at); if(f != N) naddr(f, &af, 1); if(t != N) naddr(t, &at, 1); p = prog(as); if(f != N) p->from = af; if(t != N) p->to = at; if(debug['g']) print("%P\n", p); w = 0; switch(as) { case AMOVB: w = 1; break; case AMOVW: w = 2; break; case AMOVL: w = 4; break; case AMOVQ: w = 8; break; } if(w != 0 && f != N && (af.width > w || at.width > w)) { fatal("bad width: %P (%d, %d)\n", p, af.width, at.width); } return p; } static void checkoffset(Addr *a, int canemitcode) { Prog *p; if(a->offset < unmappedzero) return; if(!canemitcode) fatal("checkoffset %#llx, cannot emit code", a->offset); // cannot rely on unmapped nil page at 0 to catch // reference with large offset. instead, emit explicit // test of 0(reg). p = gins(ATESTB, nodintconst(0), N); p->to = *a; p->to.offset = 0; } /* * generate code to compute n; * make a refer to result. */ void naddr(Node *n, Addr *a, int canemitcode) { a->scale = 0; a->index = D_NONE; a->type = D_NONE; a->gotype = S; a->node = N; if(n == N) return; switch(n->op) { default: fatal("naddr: bad %O %D", n->op, a); break; case OREGISTER: a->type = n->val.u.reg; a->sym = S; break; // case OINDEX: // case OIND: // naddr(n->left, a); // if(a->type >= D_AX && a->type <= D_DI) // a->type += D_INDIR; // else // if(a->type == D_CONST) // a->type = D_NONE+D_INDIR; // else // if(a->type == D_ADDR) { // a->type = a->index; // a->index = D_NONE; // } else // goto bad; // if(n->op == OINDEX) { // a->index = idx.reg; // a->scale = n->scale; // } // break; case OINDREG: a->type = n->val.u.reg+D_INDIR; a->sym = n->sym; a->offset = n->xoffset; checkoffset(a, canemitcode); break; case OPARAM: // n->left is PHEAP ONAME for stack parameter. // compute address of actual parameter on stack. a->etype = simtype[n->left->type->etype]; a->width = n->left->type->width; a->offset = n->xoffset; a->sym = n->left->sym; a->type = D_PARAM; break; case ONAME: a->etype = 0; a->width = 0; if(n->type != T) { a->etype = simtype[n->type->etype]; a->width = n->type->width; a->gotype = ngotype(n); } a->pun = n->pun; a->offset = n->xoffset; a->sym = n->sym; if(a->sym == S) a->sym = lookup(".noname"); if(n->method) { if(n->type != T) if(n->type->sym != S) if(n->type->sym->pkg != nil) a->sym = pkglookup(a->sym->name, n->type->sym->pkg); } switch(n->class) { default: fatal("naddr: ONAME class %S %d\n", n->sym, n->class); case PEXTERN: a->type = D_EXTERN; break; case PAUTO: a->type = D_AUTO; if (n->sym) a->node = n->orig; break; case PPARAM: case PPARAMOUT: a->type = D_PARAM; break; case PFUNC: a->index = D_EXTERN; a->type = D_ADDR; break; } break; case OLITERAL: switch(n->val.ctype) { default: fatal("naddr: const %lT", n->type); break; case CTFLT: a->type = D_FCONST; a->dval = mpgetflt(n->val.u.fval); break; case CTINT: a->sym = S; a->type = D_CONST; a->offset = mpgetfix(n->val.u.xval); break; case CTSTR: datagostring(n->val.u.sval, a); break; case CTBOOL: a->sym = S; a->type = D_CONST; a->offset = n->val.u.bval; break; case CTNIL: a->sym = S; a->type = D_CONST; a->offset = 0; break; } break; case OADDR: naddr(n->left, a, canemitcode); if(a->type >= D_INDIR) { a->type -= D_INDIR; break; } if(a->type == D_EXTERN || a->type == D_STATIC || a->type == D_AUTO || a->type == D_PARAM) if(a->index == D_NONE) { a->index = a->type; a->type = D_ADDR; break; } fatal("naddr: OADDR\n"); case OLEN: // len of string or slice naddr(n->left, a, canemitcode); if(a->type == D_CONST && a->offset == 0) break; // len(nil) a->etype = TUINT32; a->offset += Array_nel; a->width = 4; if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) checkoffset(a, canemitcode); break; case OCAP: // cap of string or slice naddr(n->left, a, canemitcode); if(a->type == D_CONST && a->offset == 0) break; // cap(nil) a->etype = TUINT32; a->offset += Array_cap; a->width = 4; if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero) checkoffset(a, canemitcode); break; // case OADD: // if(n->right->op == OLITERAL) { // v = n->right->vconst; // naddr(n->left, a, canemitcode); // } else // if(n->left->op == OLITERAL) { // v = n->left->vconst; // naddr(n->right, a, canemitcode); // } else // goto bad; // a->offset += v; // break; } } /* * return Axxx for Oxxx on type t. */ int optoas(int op, Type *t) { int a; if(t == T) fatal("optoas: t is nil"); a = AGOK; switch(CASE(op, simtype[t->etype])) { default: fatal("optoas: no entry %O-%T", op, t); break; case CASE(OADDR, TPTR32): a = ALEAL; break; case CASE(OADDR, TPTR64): a = ALEAQ; break; case CASE(OEQ, TBOOL): case CASE(OEQ, TINT8): case CASE(OEQ, TUINT8): case CASE(OEQ, TINT16): case CASE(OEQ, TUINT16): case CASE(OEQ, TINT32): case CASE(OEQ, TUINT32): case CASE(OEQ, TINT64): case CASE(OEQ, TUINT64): case CASE(OEQ, TPTR32): case CASE(OEQ, TPTR64): case CASE(OEQ, TFLOAT32): case CASE(OEQ, TFLOAT64): a = AJEQ; break; case CASE(ONE, TBOOL): case CASE(ONE, TINT8): case CASE(ONE, TUINT8): case CASE(ONE, TINT16): case CASE(ONE, TUINT16): case CASE(ONE, TINT32): case CASE(ONE, TUINT32): case CASE(ONE, TINT64): case CASE(ONE, TUINT64): case CASE(ONE, TPTR32): case CASE(ONE, TPTR64): case CASE(ONE, TFLOAT32): case CASE(ONE, TFLOAT64): a = AJNE; break; case CASE(OLT, TINT8): case CASE(OLT, TINT16): case CASE(OLT, TINT32): case CASE(OLT, TINT64): a = AJLT; break; case CASE(OLT, TUINT8): case CASE(OLT, TUINT16): case CASE(OLT, TUINT32): case CASE(OLT, TUINT64): a = AJCS; break; case CASE(OLE, TINT8): case CASE(OLE, TINT16): case CASE(OLE, TINT32): case CASE(OLE, TINT64): a = AJLE; break; case CASE(OLE, TUINT8): case CASE(OLE, TUINT16): case CASE(OLE, TUINT32): case CASE(OLE, TUINT64): a = AJLS; break; case CASE(OGT, TINT8): case CASE(OGT, TINT16): case CASE(OGT, TINT32): case CASE(OGT, TINT64): a = AJGT; break; case CASE(OGT, TUINT8): case CASE(OGT, TUINT16): case CASE(OGT, TUINT32): case CASE(OGT, TUINT64): case CASE(OLT, TFLOAT32): case CASE(OLT, TFLOAT64): a = AJHI; break; case CASE(OGE, TINT8): case CASE(OGE, TINT16): case CASE(OGE, TINT32): case CASE(OGE, TINT64): a = AJGE; break; case CASE(OGE, TUINT8): case CASE(OGE, TUINT16): case CASE(OGE, TUINT32): case CASE(OGE, TUINT64): case CASE(OLE, TFLOAT32): case CASE(OLE, TFLOAT64): a = AJCC; break; case CASE(OCMP, TBOOL): case CASE(OCMP, TINT8): case CASE(OCMP, TUINT8): a = ACMPB; break; case CASE(OCMP, TINT16): case CASE(OCMP, TUINT16): a = ACMPW; break; case CASE(OCMP, TINT32): case CASE(OCMP, TUINT32): case CASE(OCMP, TPTR32): a = ACMPL; break; case CASE(OCMP, TINT64): case CASE(OCMP, TUINT64): case CASE(OCMP, TPTR64): a = ACMPQ; break; case CASE(OCMP, TFLOAT32): a = AUCOMISS; break; case CASE(OCMP, TFLOAT64): a = AUCOMISD; break; case CASE(OAS, TBOOL): case CASE(OAS, TINT8): case CASE(OAS, TUINT8): a = AMOVB; break; case CASE(OAS, TINT16): case CASE(OAS, TUINT16): a = AMOVW; break; case CASE(OAS, TINT32): case CASE(OAS, TUINT32): case CASE(OAS, TPTR32): a = AMOVL; break; case CASE(OAS, TINT64): case CASE(OAS, TUINT64): case CASE(OAS, TPTR64): a = AMOVQ; break; case CASE(OAS, TFLOAT32): a = AMOVSS; break; case CASE(OAS, TFLOAT64): a = AMOVSD; break; case CASE(OADD, TINT8): case CASE(OADD, TUINT8): a = AADDB; break; case CASE(OADD, TINT16): case CASE(OADD, TUINT16): a = AADDW; break; case CASE(OADD, TINT32): case CASE(OADD, TUINT32): case CASE(OADD, TPTR32): a = AADDL; break; case CASE(OADD, TINT64): case CASE(OADD, TUINT64): case CASE(OADD, TPTR64): a = AADDQ; break; case CASE(OADD, TFLOAT32): a = AADDSS; break; case CASE(OADD, TFLOAT64): a = AADDSD; break; case CASE(OSUB, TINT8): case CASE(OSUB, TUINT8): a = ASUBB; break; case CASE(OSUB, TINT16): case CASE(OSUB, TUINT16): a = ASUBW; break; case CASE(OSUB, TINT32): case CASE(OSUB, TUINT32): case CASE(OSUB, TPTR32): a = ASUBL; break; case CASE(OSUB, TINT64): case CASE(OSUB, TUINT64): case CASE(OSUB, TPTR64): a = ASUBQ; break; case CASE(OSUB, TFLOAT32): a = ASUBSS; break; case CASE(OSUB, TFLOAT64): a = ASUBSD; break; case CASE(OINC, TINT8): case CASE(OINC, TUINT8): a = AINCB; break; case CASE(OINC, TINT16): case CASE(OINC, TUINT16): a = AINCW; break; case CASE(OINC, TINT32): case CASE(OINC, TUINT32): case CASE(OINC, TPTR32): a = AINCL; break; case CASE(OINC, TINT64): case CASE(OINC, TUINT64): case CASE(OINC, TPTR64): a = AINCQ; break; case CASE(ODEC, TINT8): case CASE(ODEC, TUINT8): a = ADECB; break; case CASE(ODEC, TINT16): case CASE(ODEC, TUINT16): a = ADECW; break; case CASE(ODEC, TINT32): case CASE(ODEC, TUINT32): case CASE(ODEC, TPTR32): a = ADECL; break; case CASE(ODEC, TINT64): case CASE(ODEC, TUINT64): case CASE(ODEC, TPTR64): a = ADECQ; break; case CASE(OMINUS, TINT8): case CASE(OMINUS, TUINT8): a = ANEGB; break; case CASE(OMINUS, TINT16): case CASE(OMINUS, TUINT16): a = ANEGW; break; case CASE(OMINUS, TINT32): case CASE(OMINUS, TUINT32): case CASE(OMINUS, TPTR32): a = ANEGL; break; case CASE(OMINUS, TINT64): case CASE(OMINUS, TUINT64): case CASE(OMINUS, TPTR64): a = ANEGQ; break; case CASE(OAND, TINT8): case CASE(OAND, TUINT8): a = AANDB; break; case CASE(OAND, TINT16): case CASE(OAND, TUINT16): a = AANDW; break; case CASE(OAND, TINT32): case CASE(OAND, TUINT32): case CASE(OAND, TPTR32): a = AANDL; break; case CASE(OAND, TINT64): case CASE(OAND, TUINT64): case CASE(OAND, TPTR64): a = AANDQ; break; case CASE(OOR, TINT8): case CASE(OOR, TUINT8): a = AORB; break; case CASE(OOR, TINT16): case CASE(OOR, TUINT16): a = AORW; break; case CASE(OOR, TINT32): case CASE(OOR, TUINT32): case CASE(OOR, TPTR32): a = AORL; break; case CASE(OOR, TINT64): case CASE(OOR, TUINT64): case CASE(OOR, TPTR64): a = AORQ; break; case CASE(OXOR, TINT8): case CASE(OXOR, TUINT8): a = AXORB; break; case CASE(OXOR, TINT16): case CASE(OXOR, TUINT16): a = AXORW; break; case CASE(OXOR, TINT32): case CASE(OXOR, TUINT32): case CASE(OXOR, TPTR32): a = AXORL; break; case CASE(OXOR, TINT64): case CASE(OXOR, TUINT64): case CASE(OXOR, TPTR64): a = AXORQ; break; case CASE(OLSH, TINT8): case CASE(OLSH, TUINT8): a = ASHLB; break; case CASE(OLSH, TINT16): case CASE(OLSH, TUINT16): a = ASHLW; break; case CASE(OLSH, TINT32): case CASE(OLSH, TUINT32): case CASE(OLSH, TPTR32): a = ASHLL; break; case CASE(OLSH, TINT64): case CASE(OLSH, TUINT64): case CASE(OLSH, TPTR64): a = ASHLQ; break; case CASE(ORSH, TUINT8): a = ASHRB; break; case CASE(ORSH, TUINT16): a = ASHRW; break; case CASE(ORSH, TUINT32): case CASE(ORSH, TPTR32): a = ASHRL; break; case CASE(ORSH, TUINT64): case CASE(ORSH, TPTR64): a = ASHRQ; break; case CASE(ORSH, TINT8): a = ASARB; break; case CASE(ORSH, TINT16): a = ASARW; break; case CASE(ORSH, TINT32): a = ASARL; break; case CASE(ORSH, TINT64): a = ASARQ; break; case CASE(ORRC, TINT8): case CASE(ORRC, TUINT8): a = ARCRB; break; case CASE(ORRC, TINT16): case CASE(ORRC, TUINT16): a = ARCRW; break; case CASE(ORRC, TINT32): case CASE(ORRC, TUINT32): a = ARCRL; break; case CASE(ORRC, TINT64): case CASE(ORRC, TUINT64): a = ARCRQ; break; case CASE(OHMUL, TINT8): case CASE(OMUL, TINT8): case CASE(OMUL, TUINT8): a = AIMULB; break; case CASE(OHMUL, TINT16): case CASE(OMUL, TINT16): case CASE(OMUL, TUINT16): a = AIMULW; break; case CASE(OHMUL, TINT32): case CASE(OMUL, TINT32): case CASE(OMUL, TUINT32): case CASE(OMUL, TPTR32): a = AIMULL; break; case CASE(OHMUL, TINT64): case CASE(OMUL, TINT64): case CASE(OMUL, TUINT64): case CASE(OMUL, TPTR64): a = AIMULQ; break; case CASE(OHMUL, TUINT8): a = AMULB; break; case CASE(OHMUL, TUINT16): a = AMULW; break; case CASE(OHMUL, TUINT32): case CASE(OHMUL, TPTR32): a = AMULL; break; case CASE(OHMUL, TUINT64): case CASE(OHMUL, TPTR64): a = AMULQ; break; case CASE(OMUL, TFLOAT32): a = AMULSS; break; case CASE(OMUL, TFLOAT64): a = AMULSD; break; case CASE(ODIV, TINT8): case CASE(OMOD, TINT8): a = AIDIVB; break; case CASE(ODIV, TUINT8): case CASE(OMOD, TUINT8): a = ADIVB; break; case CASE(ODIV, TINT16): case CASE(OMOD, TINT16): a = AIDIVW; break; case CASE(ODIV, TUINT16): case CASE(OMOD, TUINT16): a = ADIVW; break; case CASE(ODIV, TINT32): case CASE(OMOD, TINT32): a = AIDIVL; break; case CASE(ODIV, TUINT32): case CASE(ODIV, TPTR32): case CASE(OMOD, TUINT32): case CASE(OMOD, TPTR32): a = ADIVL; break; case CASE(ODIV, TINT64): case CASE(OMOD, TINT64): a = AIDIVQ; break; case CASE(ODIV, TUINT64): case CASE(ODIV, TPTR64): case CASE(OMOD, TUINT64): case CASE(OMOD, TPTR64): a = ADIVQ; break; case CASE(OEXTEND, TINT16): a = ACWD; break; case CASE(OEXTEND, TINT32): a = ACDQ; break; case CASE(OEXTEND, TINT64): a = ACQO; break; case CASE(ODIV, TFLOAT32): a = ADIVSS; break; case CASE(ODIV, TFLOAT64): a = ADIVSD; break; } return a; } enum { ODynam = 1<<0, OAddable = 1<<1, }; static Node clean[20]; static int cleani = 0; int xgen(Node *n, Node *a, int o) { regalloc(a, types[tptr], N); if(o & ODynam) if(n->addable) if(n->op != OINDREG) if(n->op != OREGISTER) return 1; agen(n, a); return 0; } void sudoclean(void) { if(clean[cleani-1].op != OEMPTY) regfree(&clean[cleani-1]); if(clean[cleani-2].op != OEMPTY) regfree(&clean[cleani-2]); cleani -= 2; } /* * generate code to compute address of n, * a reference to a (perhaps nested) field inside * an array or struct. * return 0 on failure, 1 on success. * on success, leaves usable address in a. * * caller is responsible for calling sudoclean * after successful sudoaddable, * to release the register used for a. */ int sudoaddable(int as, Node *n, Addr *a) { int o, i, w; int oary[10]; int64 v; Node n1, n2, n3, n4, *nn, *l, *r; Node *reg, *reg1; Prog *p1; Type *t; if(n->type == T) return 0; switch(n->op) { case OLITERAL: if(n->val.ctype != CTINT) break; v = mpgetfix(n->val.u.xval); if(v >= 32000 || v <= -32000) break; goto lit; case ODOT: case ODOTPTR: cleani += 2; reg = &clean[cleani-1]; reg1 = &clean[cleani-2]; reg->op = OEMPTY; reg1->op = OEMPTY; goto odot; case OINDEX: if(n->left->type->etype == TSTRING) return 0; goto oindex; } return 0; lit: switch(as) { default: return 0; case AADDB: case AADDW: case AADDL: case AADDQ: case ASUBB: case ASUBW: case ASUBL: case ASUBQ: case AANDB: case AANDW: case AANDL: case AANDQ: case AORB: case AORW: case AORL: case AORQ: case AXORB: case AXORW: case AXORL: case AXORQ: case AINCB: case AINCW: case AINCL: case AINCQ: case ADECB: case ADECW: case ADECL: case ADECQ: case AMOVB: case AMOVW: case AMOVL: case AMOVQ: break; } cleani += 2; reg = &clean[cleani-1]; reg1 = &clean[cleani-2]; reg->op = OEMPTY; reg1->op = OEMPTY; naddr(n, a, 1); goto yes; odot: o = dotoffset(n, oary, &nn); if(nn == N) goto no; if(nn->addable && o == 1 && oary[0] >= 0) { // directly addressable set of DOTs n1 = *nn; n1.type = n->type; n1.xoffset += oary[0]; naddr(&n1, a, 1); goto yes; } regalloc(reg, types[tptr], N); n1 = *reg; n1.op = OINDREG; if(oary[0] >= 0) { agen(nn, reg); n1.xoffset = oary[0]; } else { cgen(nn, reg); n1.xoffset = -(oary[0]+1); } for(i=1; i= 0) fatal("cant happen"); gins(AMOVQ, &n1, reg); n1.xoffset = -(oary[i]+1); } a->type = D_NONE; a->index = D_NONE; naddr(&n1, a, 1); goto yes; oindex: l = n->left; r = n->right; if(l->ullman >= UINF && r->ullman >= UINF) return 0; // set o to type of array o = 0; if(isptr[l->type->etype]) fatal("ptr ary"); if(l->type->etype != TARRAY) fatal("not ary"); if(l->type->bound < 0) o |= ODynam; w = n->type->width; if(isconst(r, CTINT)) goto oindex_const; switch(w) { default: return 0; case 1: case 2: case 4: case 8: break; } cleani += 2; reg = &clean[cleani-1]; reg1 = &clean[cleani-2]; reg->op = OEMPTY; reg1->op = OEMPTY; // load the array (reg) if(l->ullman > r->ullman) { if(xgen(l, reg, o)) o |= OAddable; } // load the index (reg1) t = types[TUINT64]; if(issigned[r->type->etype]) t = types[TINT64]; regalloc(reg1, t, N); regalloc(&n3, r->type, reg1); cgen(r, &n3); gmove(&n3, reg1); regfree(&n3); // load the array (reg) if(l->ullman <= r->ullman) { if(xgen(l, reg, o)) o |= OAddable; } if(!(o & ODynam) && l->type->width >= unmappedzero && l->op == OIND) { // cannot rely on page protections to // catch array ptr == 0, so dereference. n2 = *reg; n2.xoffset = 0; n2.op = OINDREG; n2.type = types[TUINT8]; gins(ATESTB, nodintconst(0), &n2); } // check bounds if(!debug['B'] && !n->etype) { // check bounds n4.op = OXXX; t = types[TUINT32]; if(o & ODynam) { if(o & OAddable) { n2 = *l; n2.xoffset += Array_nel; n2.type = types[TUINT32]; if(is64(r->type)) { t = types[TUINT64]; regalloc(&n4, t, N); gmove(&n2, &n4); n2 = n4; } } else { n2 = *reg; n2.xoffset = Array_nel; n2.op = OINDREG; n2.type = types[TUINT32]; if(is64(r->type)) { t = types[TUINT64]; regalloc(&n4, t, N); gmove(&n2, &n4); n2 = n4; } } } else { if(is64(r->type)) t = types[TUINT64]; nodconst(&n2, types[TUINT64], l->type->bound); } gins(optoas(OCMP, t), reg1, &n2); p1 = gbranch(optoas(OLT, t), T); if(n4.op != OXXX) regfree(&n4); ginscall(panicindex, 0); patch(p1, pc); } if(o & ODynam) { if(o & OAddable) { n2 = *l; n2.xoffset += Array_array; n2.type = types[tptr]; gmove(&n2, reg); } else { n2 = *reg; n2.op = OINDREG; n2.xoffset = Array_array; n2.type = types[tptr]; gmove(&n2, reg); } } if(o & OAddable) { naddr(reg1, a, 1); a->offset = 0; a->scale = w; a->index = a->type; a->type = reg->val.u.reg + D_INDIR; } else { naddr(reg1, a, 1); a->offset = 0; a->scale = w; a->index = a->type; a->type = reg->val.u.reg + D_INDIR; } goto yes; oindex_const: // index is constant // can check statically and // can multiply by width statically v = mpgetfix(r->val.u.xval); if(sudoaddable(as, l, a)) goto oindex_const_sudo; cleani += 2; reg = &clean[cleani-1]; reg1 = &clean[cleani-2]; reg->op = OEMPTY; reg1->op = OEMPTY; regalloc(reg, types[tptr], N); agen(l, reg); if(o & ODynam) { if(!debug['B'] && !n->etype) { n1 = *reg; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_nel; nodconst(&n2, types[TUINT64], v); gins(optoas(OCMP, types[TUINT32]), &n1, &n2); p1 = gbranch(optoas(OGT, types[TUINT32]), T); ginscall(panicindex, 0); patch(p1, pc); } n1 = *reg; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_array; gmove(&n1, reg); } n2 = *reg; n2.op = OINDREG; n2.xoffset = v*w; a->type = D_NONE; a->index = D_NONE; naddr(&n2, a, 1); goto yes; oindex_const_sudo: if((o & ODynam) == 0) { // array indexed by a constant a->offset += v*w; goto yes; } // slice indexed by a constant if(!debug['B'] && !n->etype) { a->offset += Array_nel; nodconst(&n2, types[TUINT64], v); p1 = gins(optoas(OCMP, types[TUINT32]), N, &n2); p1->from = *a; p1 = gbranch(optoas(OGT, types[TUINT32]), T); ginscall(panicindex, 0); patch(p1, pc); a->offset -= Array_nel; } a->offset += Array_array; reg = &clean[cleani-1]; if(reg->op == OEMPTY) regalloc(reg, types[tptr], N); p1 = gins(AMOVQ, N, reg); p1->from = *a; n2 = *reg; n2.op = OINDREG; n2.xoffset = v*w; a->type = D_NONE; a->index = D_NONE; naddr(&n2, a, 1); goto yes; yes: return 1; no: sudoclean(); return 0; }