diff options
Diffstat (limited to 'src/cmd/5g/cgen.c')
-rw-r--r-- | src/cmd/5g/cgen.c | 1328 |
1 files changed, 1328 insertions, 0 deletions
diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c new file mode 100644 index 000000000..6e2fbe20f --- /dev/null +++ b/src/cmd/5g/cgen.c @@ -0,0 +1,1328 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "gg.h" + +/* + * generate: + * res = n; + * simplifies and calls gmove. + */ +void +cgen(Node *n, Node *res) +{ + Node *nl, *nr, *r; + Node n1, n2, n3, f0, f1; + int a, w; + Prog *p1, *p2, *p3; + Addr addr; + + if(debug['g']) { + dump("\ncgen-n", n); + dump("cgen-res", res); + } + if(n == N || n->type == T) + goto ret; + + if(res == N || res->type == T) + fatal("cgen: res nil"); + + while(n->op == OCONVNOP) + n = n->left; + + if(n->ullman >= UINF) { + if(n->op == OINDREG) + fatal("cgen: this is going to misscompile"); + if(res->ullman >= UINF) { + tempname(&n1, n->type); + cgen(n, &n1); + cgen(&n1, res); + goto ret; + } + } + + if(isfat(n->type)) { + if(n->type->width < 0) + fatal("forgot to compute width for %T", n->type); + sgen(n, res, n->type->width); + goto ret; + } + + + // update addressability for string, slice + // can't do in walk because n->left->addable + // changes if n->left is an escaping local variable. + switch(n->op) { + case OLEN: + if(isslice(n->left->type) || istype(n->left->type, TSTRING)) + n->addable = n->left->addable; + break; + case OCAP: + if(isslice(n->left->type)) + n->addable = n->left->addable; + break; + } + + // if both are addressable, move + if(n->addable && res->addable) { + if(is64(n->type) || is64(res->type) || + n->op == OREGISTER || res->op == OREGISTER || + iscomplex[n->type->etype] || iscomplex[res->type->etype]) { + gmove(n, res); + } else { + regalloc(&n1, n->type, N); + gmove(n, &n1); + cgen(&n1, res); + regfree(&n1); + } + goto ret; + } + + // if both are not addressable, use a temporary. + if(!n->addable && !res->addable) { + // could use regalloc here sometimes, + // but have to check for ullman >= UINF. + tempname(&n1, n->type); + cgen(n, &n1); + cgen(&n1, res); + return; + } + + // if result is not addressable directly but n is, + // compute its address and then store via the address. + if(!res->addable) { + igen(res, &n1, N); + cgen(n, &n1); + regfree(&n1); + return; + } + + if(complexop(n, res)) { + complexgen(n, res); + return; + } + + // if n is sudoaddable generate addr and move + if (!is64(n->type) && !is64(res->type) && !iscomplex[n->type->etype] && !iscomplex[res->type->etype]) { + a = optoas(OAS, n->type); + if(sudoaddable(a, n, &addr, &w)) { + if (res->op != OREGISTER) { + regalloc(&n2, res->type, N); + p1 = gins(a, N, &n2); + p1->from = addr; + if(debug['g']) + print("%P [ignore previous line]\n", p1); + gmove(&n2, res); + regfree(&n2); + } else { + p1 = gins(a, N, res); + p1->from = addr; + if(debug['g']) + print("%P [ignore previous line]\n", p1); + } + sudoclean(); + goto ret; + } + } + + // otherwise, the result is addressable but n is not. + // let's do some computation. + + nl = n->left; + nr = n->right; + + if(nl != N && nl->ullman >= UINF) + if(nr != N && nr->ullman >= UINF) { + tempname(&n1, nl->type); + cgen(nl, &n1); + n2 = *n; + n2.left = &n1; + cgen(&n2, res); + goto ret; + } + + // 64-bit ops are hard on 32-bit machine. + if(is64(n->type) || is64(res->type) || n->left != N && is64(n->left->type)) { + switch(n->op) { + // math goes to cgen64. + case OMINUS: + case OCOM: + case OADD: + case OSUB: + case OMUL: + case OLSH: + case ORSH: + case OAND: + case OOR: + case OXOR: + cgen64(n, res); + return; + } + } + + if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype]) + goto flt; + switch(n->op) { + default: + dump("cgen", n); + fatal("cgen: unknown op %N", n); + break; + + case OREAL: + case OIMAG: + case OCOMPLEX: + fatal("unexpected complex"); + break; + + // these call bgen to get a bool value + case OOROR: + case OANDAND: + case OEQ: + case ONE: + case OLT: + case OLE: + case OGE: + case OGT: + case ONOT: + p1 = gbranch(AB, T); + p2 = pc; + gmove(nodbool(1), res); + p3 = gbranch(AB, T); + patch(p1, pc); + bgen(n, 1, p2); + gmove(nodbool(0), res); + patch(p3, pc); + goto ret; + + case OPLUS: + cgen(nl, res); + goto ret; + + // unary + case OCOM: + a = optoas(OXOR, nl->type); + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + nodconst(&n2, nl->type, -1); + gins(a, &n2, &n1); + gmove(&n1, res); + regfree(&n1); + goto ret; + + case OMINUS: + nodconst(&n3, nl->type, 0); + regalloc(&n2, nl->type, res); + regalloc(&n1, nl->type, N); + gmove(&n3, &n2); + cgen(nl, &n1); + gins(optoas(OSUB, nl->type), &n1, &n2); + gmove(&n2, res); + regfree(&n1); + regfree(&n2); + goto ret; + + // symmetric binary + case OAND: + case OOR: + case OXOR: + case OADD: + case OMUL: + a = optoas(n->op, nl->type); + goto sbop; + + // asymmetric binary + case OSUB: + a = optoas(n->op, nl->type); + goto abop; + + case OLSH: + case ORSH: + cgen_shift(n->op, nl, nr, res); + break; + + case OCONV: + if(eqtype(n->type, nl->type) || noconv(n->type, nl->type)) { + cgen(nl, res); + break; + } + if(nl->addable && !is64(nl->type)) { + regalloc(&n1, nl->type, res); + gmove(nl, &n1); + } else { + if(n->type->width > widthptr || is64(nl->type) || isfloat[nl->type->etype]) + tempname(&n1, nl->type); + else + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + } + if(n->type->width > widthptr || is64(n->type) || isfloat[n->type->etype]) + tempname(&n2, n->type); + else + regalloc(&n2, n->type, N); + gmove(&n1, &n2); + gmove(&n2, res); + if(n1.op == OREGISTER) + regfree(&n1); + if(n2.op == OREGISTER) + regfree(&n2); + break; + + case ODOT: + case ODOTPTR: + case OINDEX: + case OIND: + case ONAME: // PHEAP or PPARAMREF var + igen(n, &n1, res); + gmove(&n1, res); + regfree(&n1); + break; + + case OLEN: + if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { + // map has len in the first 32-bit word. + // a zero pointer means zero length + regalloc(&n1, types[tptr], res); + cgen(nl, &n1); + + nodconst(&n2, types[tptr], 0); + regalloc(&n3, n2.type, N); + gmove(&n2, &n3); + gcmp(optoas(OCMP, types[tptr]), &n1, &n3); + regfree(&n3); + p1 = gbranch(optoas(OEQ, types[tptr]), T); + + n2 = n1; + n2.op = OINDREG; + n2.type = types[TINT32]; + gmove(&n2, &n1); + + patch(p1, pc); + + gmove(&n1, res); + regfree(&n1); + break; + } + if(istype(nl->type, TSTRING) || isslice(nl->type)) { + // both slice and string have len one pointer into the struct. + igen(nl, &n1, res); + n1.op = OREGISTER; // was OINDREG + regalloc(&n2, types[TUINT32], &n1); + n1.op = OINDREG; + n1.type = types[TUINT32]; + n1.xoffset = Array_nel; + gmove(&n1, &n2); + gmove(&n2, res); + regfree(&n1); + regfree(&n2); + break; + } + fatal("cgen: OLEN: unknown type %lT", nl->type); + break; + + case OCAP: + if(istype(nl->type, TCHAN)) { + // chan has cap in the second 32-bit word. + // a zero pointer means zero length + regalloc(&n1, types[tptr], res); + cgen(nl, &n1); + + nodconst(&n2, types[tptr], 0); + regalloc(&n3, n2.type, N); + gmove(&n2, &n3); + gcmp(optoas(OCMP, types[tptr]), &n1, &n3); + regfree(&n3); + p1 = gbranch(optoas(OEQ, types[tptr]), T); + + n2 = n1; + n2.op = OINDREG; + n2.xoffset = 4; + n2.type = types[TINT32]; + gmove(&n2, &n1); + + patch(p1, pc); + + gmove(&n1, res); + regfree(&n1); + break; + } + if(isslice(nl->type)) { + regalloc(&n1, types[tptr], res); + agen(nl, &n1); + n1.op = OINDREG; + n1.type = types[TUINT32]; + n1.xoffset = Array_cap; + gmove(&n1, res); + regfree(&n1); + break; + } + fatal("cgen: OCAP: unknown type %lT", nl->type); + break; + + case OADDR: + agen(nl, res); + break; + + case OCALLMETH: + cgen_callmeth(n, 0); + cgen_callret(n, res); + break; + + case OCALLINTER: + cgen_callinter(n, res, 0); + cgen_callret(n, res); + break; + + case OCALLFUNC: + cgen_call(n, 0); + cgen_callret(n, res); + break; + + case OMOD: + case ODIV: + a = optoas(n->op, nl->type); + goto abop; + } + goto ret; + +sbop: // symmetric binary + if(nl->ullman < nr->ullman) { + r = nl; + nl = nr; + nr = r; + } + +abop: // asymmetric binary + // TODO(kaib): use fewer registers here. + if(nl->ullman >= nr->ullman) { + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + } else { + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + } + gins(a, &n2, &n1); + gmove(&n1, res); + regfree(&n1); + regfree(&n2); + goto ret; + +flt: // floating-point. + regalloc(&f0, nl->type, res); + if(nr != N) + goto flt2; + + if(n->op == OMINUS) { + nr = nodintconst(-1); + convlit(&nr, n->type); + n->op = OMUL; + goto flt2; + } + + // unary + cgen(nl, &f0); + if(n->op != OCONV && n->op != OPLUS) + gins(optoas(n->op, n->type), &f0, &f0); + gmove(&f0, res); + regfree(&f0); + goto ret; + +flt2: // binary + if(nl->ullman >= nr->ullman) { + cgen(nl, &f0); + regalloc(&f1, n->type, N); + gmove(&f0, &f1); + cgen(nr, &f0); + gins(optoas(n->op, n->type), &f0, &f1); + } else { + cgen(nr, &f0); + regalloc(&f1, n->type, N); + cgen(nl, &f1); + gins(optoas(n->op, n->type), &f0, &f1); + } + gmove(&f1, res); + regfree(&f0); + regfree(&f1); + goto ret; + +ret: + ; +} + +/* + * generate array index into res. + * n might be any size; res is 32-bit. + * returns Prog* to patch to panic call. + */ +Prog* +cgenindex(Node *n, Node *res) +{ + Node tmp, lo, hi, zero, n1, n2; + + if(!is64(n->type)) { + cgen(n, res); + return nil; + } + + tempname(&tmp, types[TINT64]); + cgen(n, &tmp); + split64(&tmp, &lo, &hi); + gmove(&lo, res); + if(debug['B']) { + splitclean(); + return nil; + } + regalloc(&n1, types[TINT32], N); + regalloc(&n2, types[TINT32], N); + nodconst(&zero, types[TINT32], 0); + gmove(&hi, &n1); + gmove(&zero, &n2); + gcmp(ACMP, &n1, &n2); + regfree(&n2); + regfree(&n1); + splitclean(); + return gbranch(ABNE, T); +} + +/* + * generate: + * res = &n; + */ +void +agen(Node *n, Node *res) +{ + Node *nl, *nr; + Node n1, n2, n3, n4, n5, tmp; + Prog *p1, *p2; + uint32 w; + uint64 v; + int r; + + if(debug['g']) { + dump("\nagen-res", res); + dump("agen-r", n); + } + if(n == N || n->type == T || res == N || res->type == T) + fatal("agen"); + + while(n->op == OCONVNOP) + n = n->left; + + if(n->addable) { + memset(&n1, 0, sizeof n1); + n1.op = OADDR; + n1.left = n; + regalloc(&n2, types[tptr], res); + gins(AMOVW, &n1, &n2); + gmove(&n2, res); + regfree(&n2); + goto ret; + } + + nl = n->left; + nr = n->right; + + switch(n->op) { + default: + fatal("agen: unknown op %N", n); + break; + + case OCALLMETH: + case OCALLFUNC: + // Release res so that it is available for cgen_call. + // Pick it up again after the call. + r = -1; + if(n->ullman >= UINF) { + if(res->op == OREGISTER || res->op == OINDREG) { + r = res->val.u.reg; + reg[r]--; + } + } + if(n->op == OCALLMETH) + cgen_callmeth(n, 0); + else + cgen_call(n, 0); + if(r >= 0) + reg[r]++; + cgen_aret(n, res); + break; + + case OCALLINTER: + cgen_callinter(n, res, 0); + cgen_aret(n, res); + break; + + case OINDEX: + p2 = nil; // to be patched to panicindex. + w = n->type->width; + if(nr->addable) { + if(!isconst(nr, CTINT)) + tempname(&tmp, types[TINT32]); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); + if(!isconst(nr, CTINT)) { + p2 = cgenindex(nr, &tmp); + regalloc(&n1, tmp.type, N); + gmove(&tmp, &n1); + } + } else + if(nl->addable) { + if(!isconst(nr, CTINT)) { + tempname(&tmp, types[TINT32]); + p2 = cgenindex(nr, &tmp); + regalloc(&n1, tmp.type, N); + gmove(&tmp, &n1); + } + if(!isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } + } else { + tempname(&tmp, types[TINT32]); + p2 = cgenindex(nr, &tmp); + nr = &tmp; + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); + regalloc(&n1, tmp.type, N); + gins(optoas(OAS, tmp.type), &tmp, &n1); + } + + // &a is in &n3 (allocated in res) + // i is in &n1 (if not constant) + // w is width + + // constant index + if(isconst(nr, CTINT)) { + if(isconst(nl, CTSTR)) + fatal("constant string constant index"); + v = mpgetfix(nr->val.u.xval); + if(isslice(nl->type) || nl->type->etype == TSTRING) { + if(!debug['B'] && !n->etype) { + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_nel; + regalloc(&n4, n1.type, N); + cgen(&n1, &n4); + nodconst(&n2, types[TUINT32], v); + regalloc(&n5, n2.type, N); + gmove(&n2, &n5); + gcmp(optoas(OCMP, types[TUINT32]), &n4, &n5); + regfree(&n4); + regfree(&n5); + p1 = gbranch(optoas(OGT, types[TUINT32]), T); + ginscall(panicindex, 0); + patch(p1, pc); + } + + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_array; + gmove(&n1, &n3); + } + + nodconst(&n2, types[tptr], v*w); + regalloc(&n4, n2.type, N); + gmove(&n2, &n4); + gins(optoas(OADD, types[tptr]), &n4, &n3); + regfree(&n4); + + gmove(&n3, res); + regfree(&n3); + break; + } + + regalloc(&n2, types[TINT32], &n1); // i + gmove(&n1, &n2); + regfree(&n1); + + if(!debug['B'] && !n->etype) { + // check bounds + regalloc(&n4, types[TUINT32], N); + if(isconst(nl, CTSTR)) { + nodconst(&n1, types[TUINT32], nl->val.u.sval->len); + gmove(&n1, &n4); + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_nel; + cgen(&n1, &n4); + } else { + nodconst(&n1, types[TUINT32], nl->type->bound); + gmove(&n1, &n4); + } + gcmp(optoas(OCMP, types[TUINT32]), &n2, &n4); + regfree(&n4); + p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(p2) + patch(p2, pc); + ginscall(panicindex, 0); + patch(p1, pc); + } + + if(isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + p1 = gins(AMOVW, N, &n3); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + p1->from.type = D_CONST; + } else + if(isslice(nl->type) || nl->type->etype == TSTRING) { + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_array; + gmove(&n1, &n3); + } + + if(w == 0) { + // nothing to do + } else if(w == 1 || w == 2 || w == 4 || w == 8) { + memset(&n4, 0, sizeof n4); + n4.op = OADDR; + n4.left = &n2; + cgen(&n4, &n3); + if (w == 1) + gins(AADD, &n2, &n3); + else if(w == 2) + gshift(AADD, &n2, SHIFT_LL, 1, &n3); + else if(w == 4) + gshift(AADD, &n2, SHIFT_LL, 2, &n3); + else if(w == 8) + gshift(AADD, &n2, SHIFT_LL, 3, &n3); + } else { + regalloc(&n4, types[TUINT32], N); + nodconst(&n1, types[TUINT32], w); + gmove(&n1, &n4); + gins(optoas(OMUL, types[TUINT32]), &n4, &n2); + gins(optoas(OADD, types[tptr]), &n2, &n3); + regfree(&n4); + gmove(&n3, res); + } + + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + break; + + case ONAME: + // should only get here with names in this func. + if(n->funcdepth > 0 && n->funcdepth != funcdepth) { + dump("bad agen", n); + fatal("agen: bad ONAME funcdepth %d != %d", + n->funcdepth, funcdepth); + } + + // should only get here for heap vars or paramref + if(!(n->class & PHEAP) && n->class != PPARAMREF) { + dump("bad agen", n); + fatal("agen: bad ONAME class %#x", n->class); + } + cgen(n->heapaddr, res); + if(n->xoffset != 0) { + nodconst(&n1, types[TINT32], n->xoffset); + regalloc(&n2, n1.type, N); + regalloc(&n3, types[TINT32], N); + gmove(&n1, &n2); + gmove(res, &n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + } + break; + + case OIND: + cgen(nl, res); + break; + + case ODOT: + agen(nl, res); + if(n->xoffset != 0) { + nodconst(&n1, types[TINT32], n->xoffset); + regalloc(&n2, n1.type, N); + regalloc(&n3, types[TINT32], N); + gmove(&n1, &n2); + gmove(res, &n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + } + break; + + case ODOTPTR: + cgen(nl, res); + if(n->xoffset != 0) { + // explicit check for nil if struct is large enough + // that we might derive too big a pointer. + if(nl->type->type->width >= unmappedzero) { + regalloc(&n1, types[tptr], N); + gmove(res, &n1); + p1 = gins(AMOVW, &n1, &n1); + p1->from.type = D_OREG; + p1->from.offset = 0; + regfree(&n1); + } + nodconst(&n1, types[TINT32], n->xoffset); + regalloc(&n2, n1.type, N); + regalloc(&n3, types[tptr], N); + gmove(&n1, &n2); + gmove(res, &n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + } + break; + } + +ret: + ; +} + +/* + * generate: + * newreg = &n; + * res = newreg + * + * on exit, a has been changed to be *newreg. + * caller must regfree(a). + */ +void +igen(Node *n, Node *a, Node *res) +{ + regalloc(a, types[tptr], res); + agen(n, a); + a->op = OINDREG; + a->type = n->type; +} + +/* + * generate: + * newreg = &n; + * + * caller must regfree(a). + */ +void +agenr(Node *n, Node *a, Node *res) +{ + regalloc(a, types[tptr], res); + agen(n, a); +} + +void +gencmp0(Node *n, Type *t, int o, Prog *to) +{ + Node n1, n2, n3; + int a; + + regalloc(&n1, t, N); + cgen(n, &n1); + a = optoas(OCMP, t); + if(a != ACMP) { + nodconst(&n2, t, 0); + regalloc(&n3, t, N); + gmove(&n2, &n3); + gcmp(a, &n1, &n3); + regfree(&n3); + } else + gins(ATST, &n1, N); + a = optoas(o, t); + patch(gbranch(a, t), to); + regfree(&n1); +} + +/* + * generate: + * if(n == true) goto to; + */ +void +bgen(Node *n, int true, Prog *to) +{ + int et, a; + Node *nl, *nr, *r; + Node n1, n2, n3, n4, tmp; + Prog *p1, *p2; + + if(debug['g']) { + dump("\nbgen", n); + } + + if(n == N) + n = nodbool(1); + + if(n->ninit != nil) + genlist(n->ninit); + + nl = n->left; + nr = n->right; + + if(n->type == T) { + convlit(&n, types[TBOOL]); + if(n->type == T) + goto ret; + } + + et = n->type->etype; + if(et != TBOOL) { + yyerror("cgen: bad type %T for %O", n->type, n->op); + patch(gins(AEND, N, N), to); + goto ret; + } + nl = N; + nr = N; + + switch(n->op) { + default: + a = ONE; + if(!true) + a = OEQ; + gencmp0(n, n->type, a, to); + goto ret; + + case OLITERAL: + // need to ask if it is bool? + if(!true == !n->val.u.bval) + patch(gbranch(AB, T), to); + goto ret; + + case OANDAND: + if(!true) + goto caseor; + + caseand: + p1 = gbranch(AB, T); + p2 = gbranch(AB, T); + patch(p1, pc); + bgen(n->left, !true, p2); + bgen(n->right, !true, p2); + p1 = gbranch(AB, T); + patch(p1, to); + patch(p2, pc); + goto ret; + + case OOROR: + if(!true) + goto caseand; + + caseor: + bgen(n->left, true, to); + bgen(n->right, true, to); + goto ret; + + case OEQ: + case ONE: + case OLT: + case OGT: + case OLE: + case OGE: + nr = n->right; + if(nr == N || nr->type == T) + goto ret; + + case ONOT: // unary + nl = n->left; + if(nl == N || nl->type == T) + goto ret; + } + + switch(n->op) { + + case ONOT: + bgen(nl, !true, to); + goto ret; + + case OEQ: + case ONE: + case OLT: + case OGT: + case OLE: + case OGE: + a = n->op; + if(!true) { + if(isfloat[nl->type->etype]) { + // brcom is not valid on floats when NaN is involved. + p1 = gbranch(AB, T); + p2 = gbranch(AB, T); + patch(p1, pc); + bgen(n, 1, p2); + patch(gbranch(AB, T), to); + patch(p2, pc); + goto ret; + } + a = brcom(a); + true = !true; + } + + // make simplest on right + if(nl->op == OLITERAL || (nl->ullman < UINF && nl->ullman < nr->ullman)) { + a = brrev(a); + r = nl; + nl = nr; + nr = r; + } + + if(isslice(nl->type)) { + // only valid to cmp darray to literal nil + if((a != OEQ && a != ONE) || nr->op != OLITERAL) { + yyerror("illegal array comparison"); + break; + } + + regalloc(&n1, types[tptr], N); + agen(nl, &n1); + n2 = n1; + n2.op = OINDREG; + n2.xoffset = Array_array; + gencmp0(&n2, types[tptr], a, to); + regfree(&n1); + break; + + a = optoas(a, types[tptr]); + regalloc(&n1, types[tptr], N); + regalloc(&n3, types[tptr], N); + regalloc(&n4, types[tptr], N); + agen(nl, &n1); + n2 = n1; + n2.op = OINDREG; + n2.xoffset = Array_array; + gmove(&n2, &n4); + nodconst(&tmp, types[tptr], 0); + gmove(&tmp, &n3); + gcmp(optoas(OCMP, types[tptr]), &n4, &n3); + patch(gbranch(a, types[tptr]), to); + regfree(&n4); + regfree(&n3); + regfree(&n1); + break; + } + + if(isinter(nl->type)) { + // front end shold only leave cmp to literal nil + if((a != OEQ && a != ONE) || nr->op != OLITERAL) { + yyerror("illegal interface comparison"); + break; + } + + regalloc(&n1, types[tptr], N); + agen(nl, &n1); + n2 = n1; + n2.op = OINDREG; + n2.xoffset = 0; + gencmp0(&n2, types[tptr], a, to); + regfree(&n1); + break; + + a = optoas(a, types[tptr]); + regalloc(&n1, types[tptr], N); + regalloc(&n3, types[tptr], N); + regalloc(&n4, types[tptr], N); + agen(nl, &n1); + n2 = n1; + n2.op = OINDREG; + n2.xoffset = 0; + gmove(&n2, &n4); + nodconst(&tmp, types[tptr], 0); + gmove(&tmp, &n3); + gcmp(optoas(OCMP, types[tptr]), &n4, &n3); + patch(gbranch(a, types[tptr]), to); + regfree(&n1); + regfree(&n3); + regfree(&n4); + break; + } + + if(iscomplex[nl->type->etype]) { + complexbool(a, nl, nr, true, to); + break; + } + + if(is64(nr->type)) { + if(!nl->addable) { + tempname(&n1, nl->type); + cgen(nl, &n1); + nl = &n1; + } + if(!nr->addable) { + tempname(&n2, nr->type); + cgen(nr, &n2); + nr = &n2; + } + cmp64(nl, nr, a, to); + break; + } + + if(nr->op == OLITERAL) { + if(nr->val.ctype == CTINT && mpgetfix(nr->val.u.xval) == 0) { + gencmp0(nl, nl->type, a, to); + break; + } + if(nr->val.ctype == CTNIL) { + gencmp0(nl, nl->type, a, to); + break; + } + } + + a = optoas(a, nr->type); + + if(nr->ullman >= UINF) { + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + + tempname(&tmp, nl->type); + gmove(&n1, &tmp); + regfree(&n1); + + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + + regalloc(&n1, nl->type, N); + cgen(&tmp, &n1); + + gcmp(optoas(OCMP, nr->type), &n1, &n2); + patch(gbranch(a, nr->type), to); + + regfree(&n1); + regfree(&n2); + break; + } + + tempname(&n3, nl->type); + cgen(nl, &n3); + + tempname(&tmp, nr->type); + cgen(nr, &tmp); + + regalloc(&n1, nl->type, N); + gmove(&n3, &n1); + + regalloc(&n2, nr->type, N); + gmove(&tmp, &n2); + + gcmp(optoas(OCMP, nr->type), &n1, &n2); + if(isfloat[nl->type->etype]) { + p1 = gbranch(ABVS, nr->type); + patch(gbranch(a, nr->type), to); + if(n->op == ONE) + patch(p1, to); + else + patch(p1, pc); + } else { + patch(gbranch(a, nr->type), to); + } + regfree(&n1); + regfree(&n2); + break; + } + goto ret; + +ret: + ; +} + +/* + * n is on stack, either local variable + * or return value from function call. + * return n's offset from SP. + */ +int32 +stkof(Node *n) +{ + Type *t; + Iter flist; + int32 off; + + switch(n->op) { + case OINDREG: + return n->xoffset; + + case ODOT: + t = n->left->type; + if(isptr[t->etype]) + break; + off = stkof(n->left); + if(off == -1000 || off == 1000) + return off; + return off + n->xoffset; + + case OINDEX: + t = n->left->type; + if(!isfixedarray(t)) + break; + off = stkof(n->left); + if(off == -1000 || off == 1000) + return off; + if(isconst(n->right, CTINT)) + return off + t->type->width * mpgetfix(n->right->val.u.xval); + return 1000; + + case OCALLMETH: + case OCALLINTER: + case OCALLFUNC: + t = n->left->type; + if(isptr[t->etype]) + t = t->type; + + t = structfirst(&flist, getoutarg(t)); + if(t != T) + return t->width + 4; // correct for LR + break; + } + + // botch - probably failing to recognize address + // arithmetic on the above. eg INDEX and DOT + return -1000; +} + +/* + * block copy: + * memmove(&res, &n, w); + * NB: character copy assumed little endian architecture + */ +void +sgen(Node *n, Node *res, int32 w) +{ + Node dst, src, tmp, nend; + int32 c, odst, osrc; + int dir, align, op; + Prog *p, *ploop; + + if(debug['g']) { + print("\nsgen w=%d\n", w); + dump("r", n); + dump("res", res); + } + if(w == 0) + return; + if(w < 0) + fatal("sgen copy %d", w); + if(n->ullman >= UINF && res->ullman >= UINF) + fatal("sgen UINF"); + if(n->type == T) + fatal("sgen: missing type"); + + // determine alignment. + // want to avoid unaligned access, so have to use + // smaller operations for less aligned types. + // for example moving [4]byte must use 4 MOVB not 1 MOVW. + align = n->type->align; + op = 0; + switch(align) { + default: + fatal("sgen: invalid alignment %d for %T", align, n->type); + case 1: + op = AMOVB; + break; + case 2: + op = AMOVH; + break; + case 4: + op = AMOVW; + break; + } + if(w%align) + fatal("sgen: unaligned size %d (align=%d) for %T", w, align, n->type); + c = w / align; + + // offset on the stack + osrc = stkof(n); + odst = stkof(res); + if(osrc != -1000 && odst != -1000 && (osrc == 1000 || odst == 1000)) { + // osrc and odst both on stack, and at least one is in + // an unknown position. Could generate code to test + // for forward/backward copy, but instead just copy + // to a temporary location first. + tempname(&tmp, n->type); + sgen(n, &tmp, w); + sgen(&tmp, res, w); + return; + } + if(osrc%align != 0 || odst%align != 0) + fatal("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align); + // if we are copying forward on the stack and + // the src and dst overlap, then reverse direction + dir = align; + if(osrc < odst && odst < osrc+w) + dir = -dir; + + regalloc(&dst, types[tptr], res); + if(n->ullman >= res->ullman) { + agen(n, &dst); // temporarily use dst + regalloc(&src, types[tptr], N); + gins(AMOVW, &dst, &src); + agen(res, &dst); + } else { + agen(res, &dst); + regalloc(&src, types[tptr], N); + agen(n, &src); + } + + regalloc(&tmp, types[TUINT32], N); + + // set up end marker + memset(&nend, 0, sizeof nend); + if(c >= 4) { + regalloc(&nend, types[TUINT32], N); + + p = gins(AMOVW, &src, &nend); + p->from.type = D_CONST; + if(dir < 0) + p->from.offset = dir; + else + p->from.offset = w; + } + + // move src and dest to the end of block if necessary + if(dir < 0) { + p = gins(AMOVW, &src, &src); + p->from.type = D_CONST; + p->from.offset = w + dir; + + p = gins(AMOVW, &dst, &dst); + p->from.type = D_CONST; + p->from.offset = w + dir; + } + + // move + if(c >= 4) { + p = gins(op, &src, &tmp); + p->from.type = D_OREG; + p->from.offset = dir; + p->scond |= C_PBIT; + ploop = p; + + p = gins(op, &tmp, &dst); + p->to.type = D_OREG; + p->to.offset = dir; + p->scond |= C_PBIT; + + p = gins(ACMP, &src, N); + raddr(&nend, p); + + patch(gbranch(ABNE, T), ploop); + regfree(&nend); + } else { + while(c-- > 0) { + p = gins(op, &src, &tmp); + p->from.type = D_OREG; + p->from.offset = dir; + p->scond |= C_PBIT; + ploop = p; + + p = gins(op, &tmp, &dst); + p->to.type = D_OREG; + p->to.offset = dir; + p->scond |= C_PBIT; + } + } + + regfree(&dst); + regfree(&src); + regfree(&tmp); +} |