diff options
Diffstat (limited to 'src/cmd/5g')
-rw-r--r-- | src/cmd/5g/Makefile | 36 | ||||
-rw-r--r-- | src/cmd/5g/cgen.c | 1328 | ||||
-rw-r--r-- | src/cmd/5g/cgen64.c | 716 | ||||
-rw-r--r-- | src/cmd/5g/doc.go | 15 | ||||
-rw-r--r-- | src/cmd/5g/galign.c | 39 | ||||
-rw-r--r-- | src/cmd/5g/gg.h | 171 | ||||
-rw-r--r-- | src/cmd/5g/ggen.c | 1030 | ||||
-rw-r--r-- | src/cmd/5g/gobj.c | 623 | ||||
-rw-r--r-- | src/cmd/5g/gsubr.c | 2010 | ||||
-rw-r--r-- | src/cmd/5g/list.c | 331 | ||||
-rw-r--r-- | src/cmd/5g/opt.h | 165 | ||||
-rw-r--r-- | src/cmd/5g/peep.c | 1521 | ||||
-rw-r--r-- | src/cmd/5g/reg.c | 1607 |
13 files changed, 9592 insertions, 0 deletions
diff --git a/src/cmd/5g/Makefile b/src/cmd/5g/Makefile new file mode 100644 index 000000000..b47014a4e --- /dev/null +++ b/src/cmd/5g/Makefile @@ -0,0 +1,36 @@ +# 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 ../../Make.inc +O:=$(HOST_O) + +TARG=5g + +HFILES=\ + ../gc/go.h\ + ../5l/5.out.h\ + gg.h\ + opt.h\ + +OFILES=\ + ../5l/enam.$O\ + cgen.$O\ + cgen64.$O\ + cplx.$O\ + galign.$O\ + ggen.$O\ + gobj.$O\ + gsubr.$O\ + list.$O\ + peep.$O\ + pgen.$O\ + reg.$O\ + +LIB=\ + ../gc/gc.a\ + +include ../../Make.ccmd + +%.$O: ../gc/%.c + $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c 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); +} diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c new file mode 100644 index 000000000..b56df765b --- /dev/null +++ b/src/cmd/5g/cgen64.c @@ -0,0 +1,716 @@ +// 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" + +/* + * attempt to generate 64-bit + * res = n + * return 1 on success, 0 if op not handled. + */ +void +cgen64(Node *n, Node *res) +{ + Node t1, t2, *l, *r; + Node lo1, lo2, hi1, hi2; + Node al, ah, bl, bh, cl, ch, s, n1, creg; + Prog *p1, *p2, *p3, *p4, *p5, *p6; + + uint64 v; + + if(res->op != OINDREG && res->op != ONAME) { + dump("n", n); + dump("res", res); + fatal("cgen64 %O of %O", n->op, res->op); + } + + l = n->left; + if(!l->addable) { + tempname(&t1, l->type); + cgen(l, &t1); + l = &t1; + } + + split64(l, &lo1, &hi1); + switch(n->op) { + default: + fatal("cgen64 %O", n->op); + + case OMINUS: + split64(res, &lo2, &hi2); + + regalloc(&t1, lo1.type, N); + regalloc(&al, lo1.type, N); + regalloc(&ah, hi1.type, N); + + gins(AMOVW, &lo1, &al); + gins(AMOVW, &hi1, &ah); + + gmove(ncon(0), &t1); + p1 = gins(ASUB, &al, &t1); + p1->scond |= C_SBIT; + gins(AMOVW, &t1, &lo2); + + gmove(ncon(0), &t1); + gins(ASBC, &ah, &t1); + gins(AMOVW, &t1, &hi2); + + regfree(&t1); + regfree(&al); + regfree(&ah); + splitclean(); + splitclean(); + return; + + case OCOM: + regalloc(&t1, lo1.type, N); + gmove(ncon(-1), &t1); + + split64(res, &lo2, &hi2); + regalloc(&n1, lo1.type, N); + + gins(AMOVW, &lo1, &n1); + gins(AEOR, &t1, &n1); + gins(AMOVW, &n1, &lo2); + + gins(AMOVW, &hi1, &n1); + gins(AEOR, &t1, &n1); + gins(AMOVW, &n1, &hi2); + + regfree(&t1); + regfree(&n1); + splitclean(); + splitclean(); + return; + + case OADD: + case OSUB: + case OMUL: + case OLSH: + case ORSH: + case OAND: + case OOR: + case OXOR: + // binary operators. + // common setup below. + break; + } + + // setup for binary operators + r = n->right; + if(r != N && !r->addable) { + tempname(&t2, r->type); + cgen(r, &t2); + r = &t2; + } + if(is64(r->type)) + split64(r, &lo2, &hi2); + + regalloc(&al, lo1.type, N); + regalloc(&ah, hi1.type, N); + + // Do op. Leave result in ah:al. + switch(n->op) { + default: + fatal("cgen64: not implemented: %N\n", n); + + case OADD: + // TODO: Constants + regalloc(&bl, types[TPTR32], N); + regalloc(&bh, types[TPTR32], N); + gins(AMOVW, &hi1, &ah); + gins(AMOVW, &lo1, &al); + gins(AMOVW, &hi2, &bh); + gins(AMOVW, &lo2, &bl); + p1 = gins(AADD, &bl, &al); + p1->scond |= C_SBIT; + gins(AADC, &bh, &ah); + regfree(&bl); + regfree(&bh); + break; + + case OSUB: + // TODO: Constants. + regalloc(&bl, types[TPTR32], N); + regalloc(&bh, types[TPTR32], N); + gins(AMOVW, &lo1, &al); + gins(AMOVW, &hi1, &ah); + gins(AMOVW, &lo2, &bl); + gins(AMOVW, &hi2, &bh); + p1 = gins(ASUB, &bl, &al); + p1->scond |= C_SBIT; + gins(ASBC, &bh, &ah); + regfree(&bl); + regfree(&bh); + break; + + case OMUL: + // TODO(kaib): this can be done with 4 regs and does not need 6 + regalloc(&bl, types[TPTR32], N); + regalloc(&bh, types[TPTR32], N); + regalloc(&cl, types[TPTR32], N); + regalloc(&ch, types[TPTR32], N); + + // load args into bh:bl and bh:bl. + gins(AMOVW, &hi1, &bh); + gins(AMOVW, &lo1, &bl); + gins(AMOVW, &hi2, &ch); + gins(AMOVW, &lo2, &cl); + + // bl * cl -> ah al + p1 = gins(AMULLU, N, N); + p1->from.type = D_REG; + p1->from.reg = bl.val.u.reg; + p1->reg = cl.val.u.reg; + p1->to.type = D_REGREG; + p1->to.reg = ah.val.u.reg; + p1->to.offset = al.val.u.reg; +//print("%P\n", p1); + + // bl * ch + ah -> ah + p1 = gins(AMULA, N, N); + p1->from.type = D_REG; + p1->from.reg = bl.val.u.reg; + p1->reg = ch.val.u.reg; + p1->to.type = D_REGREG; + p1->to.reg = ah.val.u.reg; + p1->to.offset = ah.val.u.reg; +//print("%P\n", p1); + + // bh * cl + ah -> ah + p1 = gins(AMULA, N, N); + p1->from.type = D_REG; + p1->from.reg = bh.val.u.reg; + p1->reg = cl.val.u.reg; + p1->to.type = D_REGREG; + p1->to.reg = ah.val.u.reg; + p1->to.offset = ah.val.u.reg; +//print("%P\n", p1); + + regfree(&bh); + regfree(&bl); + regfree(&ch); + regfree(&cl); + + break; + + case OLSH: + regalloc(&bl, lo1.type, N); + regalloc(&bh, hi1.type, N); + gins(AMOVW, &hi1, &bh); + gins(AMOVW, &lo1, &bl); + + if(r->op == OLITERAL) { + v = mpgetfix(r->val.u.xval); + if(v >= 64) { + // TODO(kaib): replace with gins(AMOVW, nodintconst(0), &al) + // here and below (verify it optimizes to EOR) + gins(AEOR, &al, &al); + gins(AEOR, &ah, &ah); + } else + if(v > 32) { + gins(AEOR, &al, &al); + // MOVW bl<<(v-32), ah + gshift(AMOVW, &bl, SHIFT_LL, (v-32), &ah); + } else + if(v == 32) { + gins(AEOR, &al, &al); + gins(AMOVW, &bl, &ah); + } else + if(v > 0) { + // MOVW bl<<v, al + gshift(AMOVW, &bl, SHIFT_LL, v, &al); + + // MOVW bh<<v, ah + gshift(AMOVW, &bh, SHIFT_LL, v, &ah); + + // OR bl>>(32-v), ah + gshift(AORR, &bl, SHIFT_LR, 32-v, &ah); + } else { + gins(AMOVW, &bl, &al); + gins(AMOVW, &bh, &ah); + } + goto olsh_break; + } + + regalloc(&s, types[TUINT32], N); + regalloc(&creg, types[TUINT32], N); + if (is64(r->type)) { + // shift is >= 1<<32 + split64(r, &cl, &ch); + gmove(&ch, &s); + p1 = gins(ATST, &s, N); + p6 = gbranch(ABNE, T); + gmove(&cl, &s); + splitclean(); + } else { + gmove(r, &s); + p6 = P; + } + p1 = gins(ATST, &s, N); + + // shift == 0 + p1 = gins(AMOVW, &bl, &al); + p1->scond = C_SCOND_EQ; + p1 = gins(AMOVW, &bh, &ah); + p1->scond = C_SCOND_EQ; + p2 = gbranch(ABEQ, T); + + // shift is < 32 + nodconst(&n1, types[TUINT32], 32); + gmove(&n1, &creg); + gcmp(ACMP, &s, &creg); + + // MOVW.LO bl<<s, al + p1 = gregshift(AMOVW, &bl, SHIFT_LL, &s, &al); + p1->scond = C_SCOND_LO; + + // MOVW.LO bh<<s, ah + p1 = gregshift(AMOVW, &bh, SHIFT_LL, &s, &ah); + p1->scond = C_SCOND_LO; + + // SUB.LO s, creg + p1 = gins(ASUB, &s, &creg); + p1->scond = C_SCOND_LO; + + // OR.LO bl>>creg, ah + p1 = gregshift(AORR, &bl, SHIFT_LR, &creg, &ah); + p1->scond = C_SCOND_LO; + + // BLO end + p3 = gbranch(ABLO, T); + + // shift == 32 + p1 = gins(AEOR, &al, &al); + p1->scond = C_SCOND_EQ; + p1 = gins(AMOVW, &bl, &ah); + p1->scond = C_SCOND_EQ; + p4 = gbranch(ABEQ, T); + + // shift is < 64 + nodconst(&n1, types[TUINT32], 64); + gmove(&n1, &creg); + gcmp(ACMP, &s, &creg); + + // EOR.LO al, al + p1 = gins(AEOR, &al, &al); + p1->scond = C_SCOND_LO; + + // MOVW.LO creg>>1, creg + p1 = gshift(AMOVW, &creg, SHIFT_LR, 1, &creg); + p1->scond = C_SCOND_LO; + + // SUB.LO creg, s + p1 = gins(ASUB, &creg, &s); + p1->scond = C_SCOND_LO; + + // MOVW bl<<s, ah + p1 = gregshift(AMOVW, &bl, SHIFT_LL, &s, &ah); + p1->scond = C_SCOND_LO; + + p5 = gbranch(ABLO, T); + + // shift >= 64 + if (p6 != P) patch(p6, pc); + gins(AEOR, &al, &al); + gins(AEOR, &ah, &ah); + + patch(p2, pc); + patch(p3, pc); + patch(p4, pc); + patch(p5, pc); + regfree(&s); + regfree(&creg); + +olsh_break: + regfree(&bl); + regfree(&bh); + break; + + + case ORSH: + regalloc(&bl, lo1.type, N); + regalloc(&bh, hi1.type, N); + gins(AMOVW, &hi1, &bh); + gins(AMOVW, &lo1, &bl); + + if(r->op == OLITERAL) { + v = mpgetfix(r->val.u.xval); + if(v >= 64) { + if(bh.type->etype == TINT32) { + // MOVW bh->31, al + gshift(AMOVW, &bh, SHIFT_AR, 31, &al); + + // MOVW bh->31, ah + gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); + } else { + gins(AEOR, &al, &al); + gins(AEOR, &ah, &ah); + } + } else + if(v > 32) { + if(bh.type->etype == TINT32) { + // MOVW bh->(v-32), al + gshift(AMOVW, &bh, SHIFT_AR, v-32, &al); + + // MOVW bh->31, ah + gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); + } else { + // MOVW bh>>(v-32), al + gshift(AMOVW, &bh, SHIFT_LR, v-32, &al); + gins(AEOR, &ah, &ah); + } + } else + if(v == 32) { + gins(AMOVW, &bh, &al); + if(bh.type->etype == TINT32) { + // MOVW bh->31, ah + gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); + } else { + gins(AEOR, &ah, &ah); + } + } else + if( v > 0) { + // MOVW bl>>v, al + gshift(AMOVW, &bl, SHIFT_LR, v, &al); + + // OR bh<<(32-v), al + gshift(AORR, &bh, SHIFT_LL, 32-v, &al); + + if(bh.type->etype == TINT32) { + // MOVW bh->v, ah + gshift(AMOVW, &bh, SHIFT_AR, v, &ah); + } else { + // MOVW bh>>v, ah + gshift(AMOVW, &bh, SHIFT_LR, v, &ah); + } + } else { + gins(AMOVW, &bl, &al); + gins(AMOVW, &bh, &ah); + } + goto orsh_break; + } + + regalloc(&s, types[TUINT32], N); + regalloc(&creg, types[TUINT32], N); + if(is64(r->type)) { + // shift is >= 1<<32 + split64(r, &cl, &ch); + gmove(&ch, &s); + gins(ATST, &s, N); + if(bh.type->etype == TINT32) + p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); + else + p1 = gins(AEOR, &ah, &ah); + p1->scond = C_SCOND_NE; + p6 = gbranch(ABNE, T); + gmove(&cl, &s); + splitclean(); + } else { + gmove(r, &s); + p6 = P; + } + p1 = gins(ATST, &s, N); + + // shift == 0 + p1 = gins(AMOVW, &bl, &al); + p1->scond = C_SCOND_EQ; + p1 = gins(AMOVW, &bh, &ah); + p1->scond = C_SCOND_EQ; + p2 = gbranch(ABEQ, T); + + // check if shift is < 32 + nodconst(&n1, types[TUINT32], 32); + gmove(&n1, &creg); + gcmp(ACMP, &s, &creg); + + // MOVW.LO bl>>s, al + p1 = gregshift(AMOVW, &bl, SHIFT_LR, &s, &al); + p1->scond = C_SCOND_LO; + + // SUB.LO s,creg + p1 = gins(ASUB, &s, &creg); + p1->scond = C_SCOND_LO; + + // OR.LO bh<<(32-s), al + p1 = gregshift(AORR, &bh, SHIFT_LL, &creg, &al); + p1->scond = C_SCOND_LO; + + if(bh.type->etype == TINT32) { + // MOVW bh->s, ah + p1 = gregshift(AMOVW, &bh, SHIFT_AR, &s, &ah); + } else { + // MOVW bh>>s, ah + p1 = gregshift(AMOVW, &bh, SHIFT_LR, &s, &ah); + } + p1->scond = C_SCOND_LO; + + // BLO end + p3 = gbranch(ABLO, T); + + // shift == 32 + p1 = gins(AMOVW, &bh, &al); + p1->scond = C_SCOND_EQ; + if(bh.type->etype == TINT32) + p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); + else + p1 = gins(AEOR, &ah, &ah); + p4 = gbranch(ABEQ, T); + + // check if shift is < 64 + nodconst(&n1, types[TUINT32], 64); + gmove(&n1, &creg); + gcmp(ACMP, &s, &creg); + + // MOVW.LO creg>>1, creg + p1 = gshift(AMOVW, &creg, SHIFT_LR, 1, &creg); + p1->scond = C_SCOND_LO; + + // SUB.LO creg, s + p1 = gins(ASUB, &creg, &s); + p1->scond = C_SCOND_LO; + + if(bh.type->etype == TINT32) { + // MOVW bh->(s-32), al + p1 = gregshift(AMOVW, &bh, SHIFT_AR, &s, &al); + p1->scond = C_SCOND_LO; + } else { + // MOVW bh>>(v-32), al + p1 = gregshift(AMOVW, &bh, SHIFT_LR, &s, &al); + p1->scond = C_SCOND_LO; + } + + // BLO end + p5 = gbranch(ABLO, T); + + // s >= 64 + if(p6 != P) + patch(p6, pc); + if(bh.type->etype == TINT32) { + // MOVW bh->31, al + gshift(AMOVW, &bh, SHIFT_AR, 31, &al); + } else { + gins(AEOR, &al, &al); + } + + patch(p2, pc); + patch(p3, pc); + patch(p4, pc); + patch(p5, pc); + regfree(&s); + regfree(&creg); + + +orsh_break: + regfree(&bl); + regfree(&bh); + break; + + case OXOR: + case OAND: + case OOR: + // TODO(kaib): literal optimizations + // make constant the right side (it usually is anyway). +// if(lo1.op == OLITERAL) { +// nswap(&lo1, &lo2); +// nswap(&hi1, &hi2); +// } +// if(lo2.op == OLITERAL) { +// // special cases for constants. +// lv = mpgetfix(lo2.val.u.xval); +// hv = mpgetfix(hi2.val.u.xval); +// splitclean(); // right side +// split64(res, &lo2, &hi2); +// switch(n->op) { +// case OXOR: +// gmove(&lo1, &lo2); +// gmove(&hi1, &hi2); +// switch(lv) { +// case 0: +// break; +// case 0xffffffffu: +// gins(ANOTL, N, &lo2); +// break; +// default: +// gins(AXORL, ncon(lv), &lo2); +// break; +// } +// switch(hv) { +// case 0: +// break; +// case 0xffffffffu: +// gins(ANOTL, N, &hi2); +// break; +// default: +// gins(AXORL, ncon(hv), &hi2); +// break; +// } +// break; + +// case OAND: +// switch(lv) { +// case 0: +// gins(AMOVL, ncon(0), &lo2); +// break; +// default: +// gmove(&lo1, &lo2); +// if(lv != 0xffffffffu) +// gins(AANDL, ncon(lv), &lo2); +// break; +// } +// switch(hv) { +// case 0: +// gins(AMOVL, ncon(0), &hi2); +// break; +// default: +// gmove(&hi1, &hi2); +// if(hv != 0xffffffffu) +// gins(AANDL, ncon(hv), &hi2); +// break; +// } +// break; + +// case OOR: +// switch(lv) { +// case 0: +// gmove(&lo1, &lo2); +// break; +// case 0xffffffffu: +// gins(AMOVL, ncon(0xffffffffu), &lo2); +// break; +// default: +// gmove(&lo1, &lo2); +// gins(AORL, ncon(lv), &lo2); +// break; +// } +// switch(hv) { +// case 0: +// gmove(&hi1, &hi2); +// break; +// case 0xffffffffu: +// gins(AMOVL, ncon(0xffffffffu), &hi2); +// break; +// default: +// gmove(&hi1, &hi2); +// gins(AORL, ncon(hv), &hi2); +// break; +// } +// break; +// } +// splitclean(); +// splitclean(); +// goto out; +// } + regalloc(&n1, lo1.type, N); + gins(AMOVW, &lo1, &al); + gins(AMOVW, &hi1, &ah); + gins(AMOVW, &lo2, &n1); + gins(optoas(n->op, lo1.type), &n1, &al); + gins(AMOVW, &hi2, &n1); + gins(optoas(n->op, lo1.type), &n1, &ah); + regfree(&n1); + break; + } + if(is64(r->type)) + splitclean(); + splitclean(); + + split64(res, &lo1, &hi1); + gins(AMOVW, &al, &lo1); + gins(AMOVW, &ah, &hi1); + splitclean(); + +//out: + regfree(&al); + regfree(&ah); +} + +/* + * generate comparison of nl, nr, both 64-bit. + * nl is memory; nr is constant or memory. + */ +void +cmp64(Node *nl, Node *nr, int op, Prog *to) +{ + Node lo1, hi1, lo2, hi2, r1, r2; + Prog *br; + Type *t; + + split64(nl, &lo1, &hi1); + split64(nr, &lo2, &hi2); + + // compare most significant word; + // if they differ, we're done. + t = hi1.type; + regalloc(&r1, types[TINT32], N); + regalloc(&r2, types[TINT32], N); + gins(AMOVW, &hi1, &r1); + gins(AMOVW, &hi2, &r2); + gcmp(ACMP, &r1, &r2); + regfree(&r1); + regfree(&r2); + + br = P; + switch(op) { + default: + fatal("cmp64 %O %T", op, t); + case OEQ: + // cmp hi + // bne L + // cmp lo + // beq to + // L: + br = gbranch(ABNE, T); + break; + case ONE: + // cmp hi + // bne to + // cmp lo + // bne to + patch(gbranch(ABNE, T), to); + break; + case OGE: + case OGT: + // cmp hi + // bgt to + // blt L + // cmp lo + // bge to (or bgt to) + // L: + patch(gbranch(optoas(OGT, t), T), to); + br = gbranch(optoas(OLT, t), T); + break; + case OLE: + case OLT: + // cmp hi + // blt to + // bgt L + // cmp lo + // ble to (or jlt to) + // L: + patch(gbranch(optoas(OLT, t), T), to); + br = gbranch(optoas(OGT, t), T); + break; + } + + // compare least significant word + t = lo1.type; + regalloc(&r1, types[TINT32], N); + regalloc(&r2, types[TINT32], N); + gins(AMOVW, &lo1, &r1); + gins(AMOVW, &lo2, &r2); + gcmp(ACMP, &r1, &r2); + regfree(&r1); + regfree(&r2); + + // jump again + patch(gbranch(optoas(op, t), T), to); + + // point first branch down here if appropriate + if(br != P) + patch(br, pc); + + splitclean(); + splitclean(); +} diff --git a/src/cmd/5g/doc.go b/src/cmd/5g/doc.go new file mode 100644 index 000000000..e86013bdd --- /dev/null +++ b/src/cmd/5g/doc.go @@ -0,0 +1,15 @@ +// 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. + +/* + +5g is the version of the gc compiler for the ARM. +The $GOARCH for these tools is arm. + +It reads .go files and outputs .5 files. The flags are documented in ../gc/doc.go. + +There is no instruction optimizer, so the -N flag is a no-op. + +*/ +package documentation diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c new file mode 100644 index 000000000..12766102f --- /dev/null +++ b/src/cmd/5g/galign.c @@ -0,0 +1,39 @@ +// 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" + +int thechar = '5'; +char* thestring = "arm"; + +vlong MAXWIDTH = (1LL<<32) - 1; + +/* + * go declares several platform-specific type aliases: + * int, uint, float, and uintptr + */ +Typedef typedefs[] = +{ + "int", TINT, TINT32, + "uint", TUINT, TUINT32, + "uintptr", TUINTPTR, TUINT32, + 0 +}; + +void +betypeinit(void) +{ + widthptr = 4; + + zprog.link = P; + zprog.as = AGOK; + zprog.scond = C_SCOND_NONE; + zprog.reg = NREG; + zprog.from.type = D_NONE; + zprog.from.name = D_NONE; + zprog.from.reg = NREG; + zprog.to = zprog.from; + + listinit(); +} diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h new file mode 100644 index 000000000..ce4558e21 --- /dev/null +++ b/src/cmd/5g/gg.h @@ -0,0 +1,171 @@ +// 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 <u.h> +#include <libc.h> + +#include "../gc/go.h" +#include "../5l/5.out.h" + +#ifndef EXTERN +#define EXTERN extern +#endif + +typedef struct Addr Addr; + +struct Addr +{ + int32 offset; + int32 offset2; + double dval; + Prog* branch; + char sval[NSNAME]; + + Sym* sym; + Node* node; + int width; + uchar type; + char name; + uchar reg; + char pun; + uchar etype; +}; +#define A ((Addr*)0) + +struct Prog +{ + short as; // opcode + uint32 loc; // pc offset in this func + uint32 lineno; // source line that generated this + Addr from; // src address + Addr to; // dst address + Prog* link; // next instruction in this func + void* regp; // points to enclosing Reg struct + uchar reg; // doubles as width in DATA op + uchar scond; +}; + +#define REGALLOC_R0 0 +#define REGALLOC_RMAX REGEXT +#define REGALLOC_F0 (REGALLOC_RMAX+1) +#define REGALLOC_FMAX (REGALLOC_F0 + FREGEXT) + +EXTERN Biobuf* bout; +EXTERN int32 dynloc; +EXTERN uchar reg[REGALLOC_FMAX+1]; +EXTERN int32 pcloc; // instruction counter +EXTERN Strlit emptystring; +extern char* anames[]; +EXTERN Hist* hist; +EXTERN Prog zprog; +EXTERN Node* curfn; +EXTERN Node* newproc; +EXTERN Node* deferproc; +EXTERN Node* deferreturn; +EXTERN Node* panicindex; +EXTERN Node* panicslice; +EXTERN Node* throwreturn; +EXTERN long unmappedzero; +EXTERN int maxstksize; + +/* + * gen.c + */ +void compile(Node*); +void proglist(void); +void gen(Node*); +Node* lookdot(Node*, Node*, int); +void cgen_as(Node*, Node*); +void cgen_callmeth(Node*, int); +void cgen_callinter(Node*, Node*, int); +void cgen_proc(Node*, int); +void cgen_callret(Node*, Node*); +void cgen_dcl(Node*); +int cgen_inline(Node*, Node*); +int needconvert(Type*, Type*); +void genconv(Type*, Type*); +void allocparams(void); +void checklabels(); +void ginscall(Node*, int); + +/* + * cgen + */ +void agen(Node*, Node*); +Prog* cgenindex(Node *, Node *); +void igen(Node*, Node*, Node*); +void agenr(Node *n, Node *a, Node *res); +vlong fieldoffset(Type*, Node*); +void bgen(Node*, int, Prog*); +void sgen(Node*, Node*, int32); +void gmove(Node*, Node*); +Prog* gins(int, Node*, Node*); +int samaddr(Node*, Node*); +void raddr(Node *n, Prog *p); +Prog* gcmp(int, Node*, Node*); +Prog* gshift(int as, Node *lhs, int32 stype, int32 sval, Node *rhs); +Prog * gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs); +void naddr(Node*, Addr*, int); +void cgen_aret(Node*, Node*); +void cgen_shift(int, Node*, Node*, Node*); + +/* + * cgen64.c + */ +void cmp64(Node*, Node*, int, Prog*); +void cgen64(Node*, Node*); + +/* + * gsubr.c + */ +void clearp(Prog*); +void proglist(void); +Prog* gbranch(int, Type*); +Prog* prog(int); +void gaddoffset(Node*); +void gconv(int, int); +int conv2pt(Type*); +vlong convvtox(vlong, int); +void fnparam(Type*, int, int); +Prog* gop(int, Node*, Node*, Node*); +void setconst(Addr*, vlong); +void setaddr(Addr*, Node*); +int optoas(int, Type*); +void ginit(void); +void gclean(void); +void regalloc(Node*, Type*, Node*); +void regfree(Node*); +Node* nodarg(Type*, int); +void nodreg(Node*, Type*, int); +void nodindreg(Node*, Type*, int); +void buildtxt(void); +Plist* newplist(void); +int isfat(Type*); +int dotaddable(Node*, Node*); +void sudoclean(void); +int sudoaddable(int, Node*, Addr*, int*); +void afunclit(Addr*); +void datagostring(Strlit*, Addr*); +void split64(Node*, Node*, Node*); +void splitclean(void); +Node* ncon(uint32 i); + +/* + * obj.c + */ +void datastring(char*, int, Addr*); + +/* + * list.c + */ +int Aconv(Fmt*); +int Cconv(Fmt*); +int Dconv(Fmt*); +int Mconv(Fmt*); +int Pconv(Fmt*); +int Rconv(Fmt*); +int Yconv(Fmt*); +void listinit(void); + +void zaddr(Biobuf*, Addr*, int); diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c new file mode 100644 index 000000000..3f5f47e7b --- /dev/null +++ b/src/cmd/5g/ggen.c @@ -0,0 +1,1030 @@ +// 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. + +#undef EXTERN +#define EXTERN +#include "gg.h" +#include "opt.h" + +void +defframe(Prog *ptxt) +{ + // fill in argument size + ptxt->to.type = D_CONST2; + ptxt->reg = 0; // flags + ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr); + + // fill in final stack size + if(stksize > maxstksize) + maxstksize = stksize; + ptxt->to.offset = rnd(maxstksize+maxarg, widthptr); + maxstksize = 0; +} + +// Sweep the prog list to mark any used nodes. +void +markautoused(Prog* p) +{ + for (; p; p = p->link) { + if (p->from.name == D_AUTO && p->from.node) + p->from.node->used++; + + if (p->to.name == D_AUTO && p->to.node) + p->to.node->used++; + } +} + +// Fixup instructions after compactframe has moved all autos around. +void +fixautoused(Prog* p) +{ + for (; p; p = p->link) { + if (p->from.name == D_AUTO && p->from.node) + p->from.offset += p->from.node->stkdelta; + + if (p->to.name == D_AUTO && p->to.node) + p->to.offset += p->to.node->stkdelta; + } +} + +/* + * generate: + * call f + * proc=0 normal call + * proc=1 goroutine run in new proc + * proc=2 defer call save away stack + */ +void +ginscall(Node *f, int proc) +{ + Prog *p; + Node n1, r, con; + + switch(proc) { + default: + fatal("ginscall: bad proc %d", proc); + break; + + case 0: // normal call + p = gins(ABL, N, f); + afunclit(&p->to); + break; + + case 1: // call in new proc (go) + case 2: // deferred call (defer) + regalloc(&r, types[tptr], N); + p = gins(AMOVW, N, &r); + p->from.type = D_OREG; + p->from.reg = REGSP; + + p = gins(AMOVW, &r, N); + p->to.type = D_OREG; + p->to.reg = REGSP; + p->to.offset = -12; + p->scond |= C_WBIT; + + memset(&n1, 0, sizeof n1); + n1.op = OADDR; + n1.left = f; + gins(AMOVW, &n1, &r); + + p = gins(AMOVW, &r, N); + p->to.type = D_OREG; + p->to.reg = REGSP; + p->to.offset = 8; + + nodconst(&con, types[TINT32], argsize(f->type)); + gins(AMOVW, &con, &r); + p = gins(AMOVW, &r, N); + p->to.type = D_OREG; + p->to.reg = REGSP; + p->to.offset = 4; + regfree(&r); + + if(proc == 1) + ginscall(newproc, 0); + else + ginscall(deferproc, 0); + + nodreg(&r, types[tptr], 1); + p = gins(AMOVW, N, N); + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = 12; + p->to.reg = REGSP; + p->to.type = D_REG; + + if(proc == 2) { + nodconst(&con, types[TINT32], 0); + p = gins(ACMP, &con, N); + p->reg = 0; + patch(gbranch(ABNE, T), retpc); + } + break; + } +} + +/* + * n is call to interface method. + * generate res = n. + */ +void +cgen_callinter(Node *n, Node *res, int proc) +{ + int r; + Node *i, *f; + Node tmpi, nodo, nodr, nodsp; + + i = n->left; + if(i->op != ODOTINTER) + fatal("cgen_callinter: not ODOTINTER %O", i->op); + + f = i->right; // field + if(f->op != ONAME) + fatal("cgen_callinter: not ONAME %O", f->op); + + i = i->left; // interface + + // Release res register during genlist and cgen, + // which might have their own function calls. + r = -1; + if(res != N && (res->op == OREGISTER || res->op == OINDREG)) { + r = res->val.u.reg; + reg[r]--; + } + + if(!i->addable) { + tempname(&tmpi, i->type); + cgen(i, &tmpi); + i = &tmpi; + } + + genlist(n->list); // args + if(r >= 0) + reg[r]++; + + regalloc(&nodr, types[tptr], res); + regalloc(&nodo, types[tptr], &nodr); + nodo.op = OINDREG; + + agen(i, &nodr); // REG = &inter + + nodindreg(&nodsp, types[tptr], REGSP); + nodsp.xoffset = 4; + nodo.xoffset += widthptr; + cgen(&nodo, &nodsp); // 4(SP) = 4(REG) -- i.data + + nodo.xoffset -= widthptr; + cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab + + nodo.xoffset = n->left->xoffset + 3*widthptr + 8; + cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] + + // BOTCH nodr.type = fntype; + nodr.type = n->left->type; + ginscall(&nodr, proc); + + regfree(&nodr); + regfree(&nodo); + + setmaxarg(n->left->type); +} + +/* + * generate function call; + * proc=0 normal call + * proc=1 goroutine run in new proc + * proc=2 defer call save away stack + */ +void +cgen_call(Node *n, int proc) +{ + Type *t; + Node nod, afun; + + if(n == N) + return; + + if(n->left->ullman >= UINF) { + // if name involves a fn call + // precompute the address of the fn + tempname(&afun, types[tptr]); + cgen(n->left, &afun); + } + + genlist(n->list); // assign the args + t = n->left->type; + + setmaxarg(t); + + // call tempname pointer + if(n->left->ullman >= UINF) { + regalloc(&nod, types[tptr], N); + cgen_as(&nod, &afun); + nod.type = t; + ginscall(&nod, proc); + regfree(&nod); + goto ret; + } + + // call pointer + if(n->left->op != ONAME || n->left->class != PFUNC) { + regalloc(&nod, types[tptr], N); + cgen_as(&nod, n->left); + nod.type = t; + ginscall(&nod, proc); + regfree(&nod); + goto ret; + } + + // call direct + n->left->method = 1; + ginscall(n->left, proc); + + +ret: + ; +} + +/* + * call to n has already been generated. + * generate: + * res = return value from call. + */ +void +cgen_callret(Node *n, Node *res) +{ + Node nod; + Type *fp, *t; + Iter flist; + + t = n->left->type; + if(t->etype == TPTR32 || t->etype == TPTR64) + t = t->type; + + fp = structfirst(&flist, getoutarg(t)); + if(fp == T) + fatal("cgen_callret: nil"); + + memset(&nod, 0, sizeof(nod)); + nod.op = OINDREG; + nod.val.u.reg = REGSP; + nod.addable = 1; + + nod.xoffset = fp->width + 4; // +4: saved lr at 0(SP) + nod.type = fp->type; + cgen_as(res, &nod); +} + +/* + * call to n has already been generated. + * generate: + * res = &return value from call. + */ +void +cgen_aret(Node *n, Node *res) +{ + Node nod1, nod2; + Type *fp, *t; + Iter flist; + + t = n->left->type; + if(isptr[t->etype]) + t = t->type; + + fp = structfirst(&flist, getoutarg(t)); + if(fp == T) + fatal("cgen_aret: nil"); + + memset(&nod1, 0, sizeof(nod1)); + nod1.op = OINDREG; + nod1.val.u.reg = REGSP; + nod1.addable = 1; + + nod1.xoffset = fp->width + 4; // +4: saved lr at 0(SP) + nod1.type = fp->type; + + if(res->op != OREGISTER) { + regalloc(&nod2, types[tptr], res); + agen(&nod1, &nod2); + gins(AMOVW, &nod2, res); + regfree(&nod2); + } else + agen(&nod1, res); +} + +/* + * generate return. + * n->left is assignments to return values. + */ +void +cgen_ret(Node *n) +{ + genlist(n->list); // copy out args + if(hasdefer || curfn->exit) + gjmp(retpc); + else + gins(ARET, N, N); +} + +/* + * generate += *= etc. + */ +void +cgen_asop(Node *n) +{ + Node n1, n2, n3, n4; + Node *nl, *nr; + Prog *p1; + Addr addr; + int a, w; + + nl = n->left; + nr = n->right; + + if(nr->ullman >= UINF && nl->ullman >= UINF) { + tempname(&n1, nr->type); + cgen(nr, &n1); + n2 = *n; + n2.right = &n1; + cgen_asop(&n2); + goto ret; + } + + if(!isint[nl->type->etype]) + goto hard; + if(!isint[nr->type->etype]) + goto hard; + if(is64(nl->type) || is64(nr->type)) + goto hard64; + + switch(n->etype) { + case OADD: + case OSUB: + case OXOR: + case OAND: + case OOR: + a = optoas(n->etype, nl->type); + if(nl->addable) { + regalloc(&n3, nr->type, N); + cgen(nr, &n3); + regalloc(&n2, nl->type, N); + cgen(nl, &n2); + gins(a, &n3, &n2); + cgen(&n2, nl); + regfree(&n2); + regfree(&n3); + goto ret; + } + if(nr->ullman < UINF) + if(sudoaddable(a, nl, &addr, &w)) { + w = optoas(OAS, nl->type); + regalloc(&n2, nl->type, N); + p1 = gins(w, N, &n2); + p1->from = addr; + regalloc(&n3, nr->type, N); + cgen(nr, &n3); + gins(a, &n3, &n2); + p1 = gins(w, &n2, N); + p1->to = addr; + regfree(&n2); + regfree(&n3); + sudoclean(); + goto ret; + } + } + +hard: + n2.op = 0; + n1.op = 0; + if(nr->ullman >= nl->ullman || nl->addable) { + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + nr = &n2; + } else { + tempname(&n2, nr->type); + cgen(nr, &n2); + nr = &n2; + } + if(!nl->addable) { + igen(nl, &n1, N); + nl = &n1; + } + + n3 = *n; + n3.left = nl; + n3.right = nr; + n3.op = n->etype; + + regalloc(&n4, nl->type, N); + cgen(&n3, &n4); + gmove(&n4, nl); + + if(n1.op) + regfree(&n1); + if(n2.op == OREGISTER) + regfree(&n2); + regfree(&n4); + goto ret; + +hard64: + if(nr->ullman > nl->ullman) { + tempname(&n2, nr->type); + cgen(nr, &n2); + igen(nl, &n1, N); + } else { + igen(nl, &n1, N); + tempname(&n2, nr->type); + cgen(nr, &n2); + } + + n3 = *n; + n3.left = &n1; + n3.right = &n2; + n3.op = n->etype; + + cgen(&n3, &n1); + +ret: + ; +} + +int +samereg(Node *a, Node *b) +{ + if(a->op != OREGISTER) + return 0; + if(b->op != OREGISTER) + return 0; + if(a->val.u.reg != b->val.u.reg) + return 0; + return 1; +} + +/* + * generate shift according to op, one of: + * res = nl << nr + * res = nl >> nr + */ +void +cgen_shift(int op, Node *nl, Node *nr, Node *res) +{ + Node n1, n2, n3, nt, t, lo, hi; + int w; + Prog *p1, *p2, *p3; + Type *tr; + uvlong sc; + + if(nl->type->width > 4) + fatal("cgen_shift %T", nl->type); + + w = nl->type->width * 8; + + if(nr->op == OLITERAL) { + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + sc = mpgetfix(nr->val.u.xval); + if(sc == 0) { + // nothing to do + } else if(sc >= nl->type->width*8) { + if(op == ORSH && issigned[nl->type->etype]) + gshift(AMOVW, &n1, SHIFT_AR, w, &n1); + else + gins(AEOR, &n1, &n1); + } else { + if(op == ORSH && issigned[nl->type->etype]) + gshift(AMOVW, &n1, SHIFT_AR, sc, &n1); + else if(op == ORSH) + gshift(AMOVW, &n1, SHIFT_LR, sc, &n1); + else // OLSH + gshift(AMOVW, &n1, SHIFT_LL, sc, &n1); + } + gmove(&n1, res); + regfree(&n1); + return; + } + + tr = nr->type; + if(tr->width > 4) { + tempname(&nt, nr->type); + if(nl->ullman >= nr->ullman) { + regalloc(&n2, nl->type, res); + cgen(nl, &n2); + cgen(nr, &nt); + n1 = nt; + } else { + cgen(nr, &nt); + regalloc(&n2, nl->type, res); + cgen(nl, &n2); + } + split64(&nt, &lo, &hi); + regalloc(&n1, types[TUINT32], N); + regalloc(&n3, types[TUINT32], N); + gmove(&lo, &n1); + gmove(&hi, &n3); + gins(ATST, &n3, N); + nodconst(&t, types[TUINT32], w); + p1 = gins(AMOVW, &t, &n1); + p1->scond = C_SCOND_NE; + tr = types[TUINT32]; + regfree(&n3); + } else { + if(nl->ullman >= nr->ullman) { + regalloc(&n2, nl->type, res); + cgen(nl, &n2); + regalloc(&n1, nr->type, N); + cgen(nr, &n1); + } else { + regalloc(&n1, nr->type, N); + cgen(nr, &n1); + regalloc(&n2, nl->type, res); + cgen(nl, &n2); + } + } + + // test for shift being 0 + p1 = gins(ATST, &n1, N); + p3 = gbranch(ABEQ, T); + + // test and fix up large shifts + regalloc(&n3, tr, N); + nodconst(&t, types[TUINT32], w); + gmove(&t, &n3); + gcmp(ACMP, &n1, &n3); + if(op == ORSH) { + if(issigned[nl->type->etype]) { + p1 = gshift(AMOVW, &n2, SHIFT_AR, w-1, &n2); + p2 = gregshift(AMOVW, &n2, SHIFT_AR, &n1, &n2); + } else { + p1 = gins(AEOR, &n2, &n2); + p2 = gregshift(AMOVW, &n2, SHIFT_LR, &n1, &n2); + } + p1->scond = C_SCOND_HS; + p2->scond = C_SCOND_LO; + } else { + p1 = gins(AEOR, &n2, &n2); + p2 = gregshift(AMOVW, &n2, SHIFT_LL, &n1, &n2); + p1->scond = C_SCOND_HS; + p2->scond = C_SCOND_LO; + } + regfree(&n3); + + patch(p3, pc); + gmove(&n2, res); + + regfree(&n1); + regfree(&n2); +} + +void +clearfat(Node *nl) +{ + uint32 w, c, q; + Node dst, nc, nz, end; + Prog *p, *pl; + + /* clear a fat object */ + if(debug['g']) + dump("\nclearfat", nl); + + w = nl->type->width; + c = w % 4; // bytes + q = w / 4; // quads + + regalloc(&dst, types[tptr], N); + agen(nl, &dst); + nodconst(&nc, types[TUINT32], 0); + regalloc(&nz, types[TUINT32], 0); + cgen(&nc, &nz); + + if(q >= 4) { + regalloc(&end, types[tptr], N); + p = gins(AMOVW, &dst, &end); + p->from.type = D_CONST; + p->from.offset = q*4; + + p = gins(AMOVW, &nz, &dst); + p->to.type = D_OREG; + p->to.offset = 4; + p->scond |= C_PBIT; + pl = p; + + p = gins(ACMP, &dst, N); + raddr(&end, p); + patch(gbranch(ABNE, T), pl); + + regfree(&end); + } else + while(q > 0) { + p = gins(AMOVW, &nz, &dst); + p->to.type = D_OREG; + p->to.offset = 4; + p->scond |= C_PBIT; +//print("1. %P\n", p); + q--; + } + + while(c > 0) { + p = gins(AMOVBU, &nz, &dst); + p->to.type = D_OREG; + p->to.offset = 1; + p->scond |= C_PBIT; +//print("2. %P\n", p); + c--; + } + regfree(&dst); + regfree(&nz); +} + +static int +regcmp(const void *va, const void *vb) +{ + Node *ra, *rb; + + ra = (Node*)va; + rb = (Node*)vb; + return ra->local - rb->local; +} + +static Prog* throwpc; + +// We're only going to bother inlining if we can +// convert all the arguments to 32 bits safely. Can we? +static int +fix64(NodeList *nn, int n) +{ + NodeList *l; + Node *r; + int i; + + l = nn; + for(i=0; i<n; i++) { + r = l->n->right; + if(is64(r->type) && !smallintconst(r)) { + if(r->op == OCONV) + r = r->left; + if(is64(r->type)) + return 0; + } + l = l->next; + } + return 1; +} + +void +getargs(NodeList *nn, Node *reg, int n) +{ + NodeList *l; + int i; + + throwpc = nil; + + l = nn; + for(i=0; i<n; i++) { + if(!smallintconst(l->n->right) && !isslice(l->n->right->type)) { + regalloc(reg+i, l->n->right->type, N); + cgen(l->n->right, reg+i); + } else + reg[i] = *l->n->right; + if(reg[i].local != 0) + yyerror("local used"); + reg[i].local = l->n->left->xoffset; + l = l->next; + } + qsort((void*)reg, n, sizeof(*reg), regcmp); + for(i=0; i<n; i++) + reg[i].local = 0; +} + +void +cmpandthrow(Node *nl, Node *nr) +{ + vlong cl; + Prog *p1; + int op; + Node *c, n1, n2; + + op = OLE; + if(smallintconst(nl)) { + cl = mpgetfix(nl->val.u.xval); + if(cl == 0) + return; + if(smallintconst(nr)) + return; + + // put the constant on the right + op = brrev(op); + c = nl; + nl = nr; + nr = c; + } + + n1.op = OXXX; + if(nr->op != OREGISTER) { + regalloc(&n1, types[TUINT32], N); + gmove(nr, &n1); + nr = &n1; + } + n2.op = OXXX; + if(nl->op != OREGISTER) { + regalloc(&n2, types[TUINT32], N); + gmove(nl, &n2); + nl = &n2; + } + gcmp(optoas(OCMP, types[TUINT32]), nl, nr); + if(nr == &n1) + regfree(&n1); + if(nl == &n2) + regfree(&n2); + if(throwpc == nil) { + p1 = gbranch(optoas(op, types[TUINT32]), T); + throwpc = pc; + ginscall(panicslice, 0); + patch(p1, pc); + } else { + op = brcom(op); + p1 = gbranch(optoas(op, types[TUINT32]), T); + patch(p1, throwpc); + } +} + +int +sleasy(Node *n) +{ + if(n->op != ONAME) + return 0; + if(!n->addable) + return 0; + return 1; +} + +// generate inline code for +// slicearray +// sliceslice +// arraytoslice +int +cgen_inline(Node *n, Node *res) +{ + Node nodes[5]; + Node n1, n2, n3, nres, ntemp; + vlong v; + int i, narg; + + if(n->op != OCALLFUNC) + goto no; + if(!n->left->addable) + goto no; + if(n->left->sym == S) + goto no; + if(n->left->sym->pkg != runtimepkg) + goto no; + if(strcmp(n->left->sym->name, "slicearray") == 0) + goto slicearray; + if(strcmp(n->left->sym->name, "sliceslice") == 0) { + narg = 4; + goto sliceslice; + } + if(strcmp(n->left->sym->name, "sliceslice1") == 0) { + narg = 3; + goto sliceslice; + } + goto no; + +slicearray: + if(!sleasy(res)) + goto no; + if(!fix64(n->list, 5)) + goto no; + getargs(n->list, nodes, 5); + + // if(hb[3] > nel[1]) goto throw + cmpandthrow(&nodes[3], &nodes[1]); + + // if(lb[2] > hb[3]) goto throw + cmpandthrow(&nodes[2], &nodes[3]); + + // len = hb[3] - lb[2] (destroys hb) + n2 = *res; + n2.type = types[TUINT32]; + n2.xoffset += Array_nel; + + if(smallintconst(&nodes[3]) && smallintconst(&nodes[2])) { + v = mpgetfix(nodes[3].val.u.xval) - + mpgetfix(nodes[2].val.u.xval); + nodconst(&n1, types[TUINT32], v); + gmove(&n1, &n2); + } else { + regalloc(&n1, types[TUINT32], &nodes[3]); + gmove(&nodes[3], &n1); + if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) + gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); + gmove(&n1, &n2); + regfree(&n1); + } + + // cap = nel[1] - lb[2] (destroys nel) + n2 = *res; + n2.type = types[TUINT32]; + n2.xoffset += Array_cap; + + if(smallintconst(&nodes[1]) && smallintconst(&nodes[2])) { + v = mpgetfix(nodes[1].val.u.xval) - + mpgetfix(nodes[2].val.u.xval); + nodconst(&n1, types[TUINT32], v); + gmove(&n1, &n2); + } else { + regalloc(&n1, types[TUINT32], &nodes[1]); + gmove(&nodes[1], &n1); + if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) + gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); + gmove(&n1, &n2); + regfree(&n1); + } + + // if slice could be too big, dereference to + // catch nil array pointer. + if(nodes[0].op == OREGISTER && nodes[0].type->type->width >= unmappedzero) { + n2 = nodes[0]; + n2.xoffset = 0; + n2.op = OINDREG; + n2.type = types[TUINT8]; + regalloc(&n1, types[TUINT32], N); + gins(AMOVB, &n2, &n1); + regfree(&n1); + } + + // ary = old[0] + (lb[2] * width[4]) (destroys old) + n2 = *res; + n2.type = types[tptr]; + n2.xoffset += Array_array; + + if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) { + v = mpgetfix(nodes[2].val.u.xval) * + mpgetfix(nodes[4].val.u.xval); + if(v != 0) { + nodconst(&n1, types[tptr], v); + gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); + } + } else { + regalloc(&n1, types[tptr], &nodes[2]); + gmove(&nodes[2], &n1); + if(!smallintconst(&nodes[4]) || mpgetfix(nodes[4].val.u.xval) != 1) { + regalloc(&n3, types[tptr], N); + gmove(&nodes[4], &n3); + gins(optoas(OMUL, types[tptr]), &n3, &n1); + regfree(&n3); + } + gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); + regfree(&n1); + } + gmove(&nodes[0], &n2); + + for(i=0; i<5; i++) { + if(nodes[i].op == OREGISTER) + regfree(&nodes[i]); + } + return 1; + +sliceslice: + if(!fix64(n->list, narg)) + goto no; + ntemp.op = OXXX; + if(!sleasy(n->list->n->right)) { + Node *n0; + + n0 = n->list->n->right; + tempname(&ntemp, res->type); + cgen(n0, &ntemp); + n->list->n->right = &ntemp; + getargs(n->list, nodes, narg); + n->list->n->right = n0; + } else + getargs(n->list, nodes, narg); + + nres = *res; // result + if(!sleasy(res)) { + if(ntemp.op == OXXX) + tempname(&ntemp, res->type); + nres = ntemp; + } + + if(narg == 3) { // old[lb:] + // move width to where it would be for old[lb:hb] + nodes[3] = nodes[2]; + nodes[2].op = OXXX; + + // if(lb[1] > old.nel[0]) goto throw; + n2 = nodes[0]; + n2.xoffset += Array_nel; + n2.type = types[TUINT32]; + cmpandthrow(&nodes[1], &n2); + + // ret.nel = old.nel[0]-lb[1]; + n2 = nodes[0]; + n2.type = types[TUINT32]; + n2.xoffset += Array_nel; + + regalloc(&n1, types[TUINT32], N); + gmove(&n2, &n1); + if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) + gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); + + n2 = nres; + n2.type = types[TUINT32]; + n2.xoffset += Array_nel; + gmove(&n1, &n2); + regfree(&n1); + } else { // old[lb:hb] + // if(hb[2] > old.cap[0]) goto throw; + n2 = nodes[0]; + n2.xoffset += Array_cap; + n2.type = types[TUINT32]; + cmpandthrow(&nodes[2], &n2); + + // if(lb[1] > hb[2]) goto throw; + cmpandthrow(&nodes[1], &nodes[2]); + + // ret.len = hb[2]-lb[1]; (destroys hb[2]) + n2 = nres; + n2.type = types[TUINT32]; + n2.xoffset += Array_nel; + + if(smallintconst(&nodes[2]) && smallintconst(&nodes[1])) { + v = mpgetfix(nodes[2].val.u.xval) - + mpgetfix(nodes[1].val.u.xval); + nodconst(&n1, types[TUINT32], v); + gmove(&n1, &n2); + } else { + regalloc(&n1, types[TUINT32], &nodes[2]); + gmove(&nodes[2], &n1); + if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) + gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); + gmove(&n1, &n2); + regfree(&n1); + } + } + + // ret.cap = old.cap[0]-lb[1]; (uses hb[2]) + n2 = nodes[0]; + n2.type = types[TUINT32]; + n2.xoffset += Array_cap; + + regalloc(&n1, types[TUINT32], &nodes[2]); + gmove(&n2, &n1); + if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) + gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); + + n2 = nres; + n2.type = types[TUINT32]; + n2.xoffset += Array_cap; + gmove(&n1, &n2); + regfree(&n1); + + // ret.array = old.array[0]+lb[1]*width[3]; (uses lb[1]) + n2 = nodes[0]; + n2.type = types[tptr]; + n2.xoffset += Array_array; + regalloc(&n3, types[tptr], N); + gmove(&n2, &n3); + + regalloc(&n1, types[tptr], &nodes[1]); + if(smallintconst(&nodes[1]) && smallintconst(&nodes[3])) { + gmove(&n2, &n1); + v = mpgetfix(nodes[1].val.u.xval) * + mpgetfix(nodes[3].val.u.xval); + if(v != 0) { + nodconst(&n2, types[tptr], v); + gins(optoas(OADD, types[tptr]), &n3, &n1); + } + } else { + gmove(&nodes[1], &n1); + if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) { + regalloc(&n2, types[tptr], N); + gmove(&nodes[3], &n2); + gins(optoas(OMUL, types[tptr]), &n2, &n1); + regfree(&n2); + } + gins(optoas(OADD, types[tptr]), &n3, &n1); + } + regfree(&n3); + + n2 = nres; + n2.type = types[tptr]; + n2.xoffset += Array_array; + gmove(&n1, &n2); + regfree(&n1); + + for(i=0; i<4; i++) { + if(nodes[i].op == OREGISTER) + regfree(&nodes[i]); + } + + if(!sleasy(res)) { + cgen(&nres, res); + } + return 1; + +no: + return 0; +} diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c new file mode 100644 index 000000000..27c8be67d --- /dev/null +++ b/src/cmd/5g/gobj.c @@ -0,0 +1,623 @@ +// Derived from Inferno utils/5c/swt.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/swt.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" + +void +zname(Biobuf *b, Sym *s, int t) +{ + Bputc(b, ANAME); /* as */ + Bputc(b, t); /* type */ + Bputc(b, s->sym); /* sym */ + + Bputname(b, s); +} + +void +zfile(Biobuf *b, char *p, int n) +{ + Bputc(b, ANAME); + Bputc(b, D_FILE); + Bputc(b, 1); + Bputc(b, '<'); + Bwrite(b, p, n); + Bputc(b, 0); +} + +void +zhist(Biobuf *b, int line, vlong offset) +{ + Addr a; + + Bputc(b, AHISTORY); + Bputc(b, C_SCOND_NONE); + Bputc(b, NREG); + Bputc(b, line); + Bputc(b, line>>8); + Bputc(b, line>>16); + Bputc(b, line>>24); + zaddr(b, &zprog.from, 0); + a = zprog.to; + if(offset != 0) { + a.offset = offset; + a.type = D_CONST; + } + zaddr(b, &a, 0); +} + +void +zaddr(Biobuf *b, Addr *a, int s) +{ + int32 l; + uint64 e; + int i; + char *n; + + switch(a->type) { + case D_STATIC: + case D_AUTO: + case D_EXTERN: + case D_PARAM: + // TODO(kaib): remove once everything seems to work + fatal("We should no longer generate these as types"); + + default: + Bputc(b, a->type); + Bputc(b, a->reg); + Bputc(b, s); + Bputc(b, a->name); + } + + switch(a->type) { + default: + print("unknown type %d in zaddr\n", a->type); + + case D_NONE: + case D_REG: + case D_FREG: + case D_PSR: + break; + + case D_CONST2: + l = a->offset2; + Bputc(b, l); + Bputc(b, l>>8); + Bputc(b, l>>16); + Bputc(b, l>>24); // fall through + case D_OREG: + case D_CONST: + case D_SHIFT: + case D_STATIC: + case D_AUTO: + case D_EXTERN: + case D_PARAM: + l = a->offset; + Bputc(b, l); + Bputc(b, l>>8); + Bputc(b, l>>16); + Bputc(b, l>>24); + break; + + case D_BRANCH: + if(a->branch == nil) + fatal("unpatched branch"); + a->offset = a->branch->loc; + l = a->offset; + Bputc(b, l); + Bputc(b, l>>8); + Bputc(b, l>>16); + Bputc(b, l>>24); + break; + + case D_SCONST: + n = a->sval; + for(i=0; i<NSNAME; i++) { + Bputc(b, *n); + n++; + } + break; + + case D_REGREG: + Bputc(b, a->offset); + break; + + case D_FCONST: + ieeedtod(&e, a->dval); + l = e; + Bputc(b, l); + Bputc(b, l>>8); + Bputc(b, l>>16); + Bputc(b, l>>24); + l = e >> 32; + Bputc(b, l); + Bputc(b, l>>8); + Bputc(b, l>>16); + Bputc(b, l>>24); + break; + } +} + +void +dumpfuncs(void) +{ + Plist *pl; + int sf, st, t, sym; + struct { Sym *sym; short type; } h[NSYM]; + Sym *s; + Prog *p; + + for(sym=0; sym<NSYM; sym++) { + h[sym].sym = S; + h[sym].type = 0; + } + sym = 1; + + // fix up pc + pcloc = 0; + for(pl=plist; pl!=nil; pl=pl->link) { + if(isblank(pl->name)) + continue; + for(p=pl->firstpc; p!=P; p=p->link) { + p->loc = pcloc; + if(p->as != ADATA && p->as != AGLOBL) + pcloc++; + } + } + + // put out functions + for(pl=plist; pl!=nil; pl=pl->link) { + if(isblank(pl->name)) + continue; + + if(debug['S']) { + s = S; + if(pl->name != N) + s = pl->name->sym; + print("\n--- prog list \"%S\" ---\n", s); + for(p=pl->firstpc; p!=P; p=p->link) + print("%P\n", p); + } + + for(p=pl->firstpc; p!=P; p=p->link) { + jackpot: + sf = 0; + s = p->from.sym; + while(s != S) { + sf = s->sym; + if(sf < 0 || sf >= NSYM) + sf = 0; + t = p->from.name; + if(t == D_ADDR) + t = p->from.name; + if(h[sf].type == t) + if(h[sf].sym == s) + break; + s->sym = sym; + zname(bout, s, t); + h[sym].sym = s; + h[sym].type = t; + sf = sym; + sym++; + if(sym >= NSYM) + sym = 1; + break; + } + st = 0; + s = p->to.sym; + while(s != S) { + st = s->sym; + if(st < 0 || st >= NSYM) + st = 0; + t = p->to.name; + if(t == D_ADDR) + t = p->to.name; + if(h[st].type == t) + if(h[st].sym == s) + break; + s->sym = sym; + zname(bout, s, t); + h[sym].sym = s; + h[sym].type = t; + st = sym; + sym++; + if(sym >= NSYM) + sym = 1; + if(st == sf) + goto jackpot; + break; + } + Bputc(bout, p->as); + Bputc(bout, p->scond); + Bputc(bout, p->reg); + Bputc(bout, p->lineno); + Bputc(bout, p->lineno>>8); + Bputc(bout, p->lineno>>16); + Bputc(bout, p->lineno>>24); + zaddr(bout, &p->from, sf); + zaddr(bout, &p->to, st); + } + } +} + +/* deferred DATA output */ +static Prog *strdat; +static Prog *estrdat; +static int gflag; +static Prog *savepc; + +void +data(void) +{ + gflag = debug['g']; + debug['g'] = 0; + + if(estrdat == nil) { + strdat = mal(sizeof(*pc)); + clearp(strdat); + estrdat = strdat; + } + if(savepc) + fatal("data phase error"); + savepc = pc; + pc = estrdat; +} + +void +text(void) +{ + if(!savepc) + fatal("text phase error"); + debug['g'] = gflag; + estrdat = pc; + pc = savepc; + savepc = nil; +} + +void +dumpdata(void) +{ + Prog *p; + + if(estrdat == nil) + return; + *pc = *strdat; + if(gflag) + for(p=pc; p!=estrdat; p=p->link) + print("%P\n", p); + pc = estrdat; +} + +int +dsname(Sym *sym, int off, char *t, int n) +{ + Prog *p; + + p = gins(ADATA, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.etype = TINT32; + p->from.offset = off; + p->from.reg = NREG; + p->from.sym = sym; + + p->reg = n; + + p->to.type = D_SCONST; + p->to.name = D_NONE; + p->to.reg = NREG; + p->to.offset = 0; + memmove(p->to.sval, t, n); + return off + n; +} + +/* + * make a refer to the data s, s+len + * emitting DATA if needed. + */ +void +datastring(char *s, int len, Addr *a) +{ + Sym *sym; + + sym = stringsym(s, len); + a->type = D_OREG; + a->name = D_EXTERN; + a->etype = TINT32; + a->offset = widthptr+4; // skip header + a->reg = NREG; + a->sym = sym; +} + +/* + * make a refer to the string sval, + * emitting DATA if needed. + */ +void +datagostring(Strlit *sval, Addr *a) +{ + Sym *sym; + + sym = stringsym(sval->s, sval->len); + a->type = D_OREG; + a->name = D_EXTERN; + a->etype = TINT32; + a->offset = 0; // header + a->reg = NREG; + a->sym = sym; +} + +void +gdata(Node *nam, Node *nr, int wid) +{ + Prog *p; + vlong v; + + if(wid == 8 && is64(nr->type)) { + v = mpgetfix(nr->val.u.xval); + p = gins(ADATA, nam, nodintconst(v)); + p->reg = 4; + p = gins(ADATA, nam, nodintconst(v>>32)); + p->reg = 4; + p->from.offset += 4; + return; + } + p = gins(ADATA, nam, nr); + p->reg = wid; +} + +void +gdatacomplex(Node *nam, Mpcplx *cval) +{ + Prog *p; + int w; + + w = cplxsubtype(nam->type->etype); + w = types[w]->width; + + p = gins(ADATA, nam, N); + p->reg = w; + p->to.type = D_FCONST; + p->to.dval = mpgetflt(&cval->real); + + p = gins(ADATA, nam, N); + p->reg = w; + p->from.offset += w; + p->to.type = D_FCONST; + p->to.dval = mpgetflt(&cval->imag); +} + +void +gdatastring(Node *nam, Strlit *sval) +{ + Prog *p; + Node nod1; + + p = gins(ADATA, nam, N); + datastring(sval->s, sval->len, &p->to); + p->reg = types[tptr]->width; + p->to.type = D_CONST; + p->to.etype = TINT32; +//print("%P\n", p); + + nodconst(&nod1, types[TINT32], sval->len); + p = gins(ADATA, nam, &nod1); + p->reg = types[TINT32]->width; + p->from.offset += types[tptr]->width; +} + +int +dstringptr(Sym *s, int off, char *str) +{ + Prog *p; + + off = rnd(off, widthptr); + p = gins(ADATA, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = s; + p->from.offset = off; + p->reg = widthptr; + + datastring(str, strlen(str)+1, &p->to); + p->to.type = D_CONST; + p->to.etype = TINT32; + off += widthptr; + + return off; +} + +int +dgostrlitptr(Sym *s, int off, Strlit *lit) +{ + Prog *p; + + if(lit == nil) + return duintptr(s, off, 0); + + off = rnd(off, widthptr); + p = gins(ADATA, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = s; + p->from.offset = off; + p->reg = widthptr; + datagostring(lit, &p->to); + p->to.type = D_CONST; + p->to.etype = TINT32; + off += widthptr; + + return off; +} + +int +dgostringptr(Sym *s, int off, char *str) +{ + int n; + Strlit *lit; + + if(str == nil) + return duintptr(s, off, 0); + + n = strlen(str); + lit = mal(sizeof *lit + n); + strcpy(lit->s, str); + lit->len = n; + return dgostrlitptr(s, off, lit); +} + +int +duintxx(Sym *s, int off, uint64 v, int wid) +{ + Prog *p; + + off = rnd(off, wid); + + p = gins(ADATA, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = s; + p->from.offset = off; + p->reg = wid; + p->to.type = D_CONST; + p->to.name = D_NONE; + p->to.offset = v; + off += wid; + + return off; +} + +int +dsymptr(Sym *s, int off, Sym *x, int xoff) +{ + Prog *p; + + off = rnd(off, widthptr); + + p = gins(ADATA, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = s; + p->from.offset = off; + p->reg = widthptr; + p->to.type = D_CONST; + p->to.name = D_EXTERN; + p->to.sym = x; + p->to.offset = xoff; + off += widthptr; + + return off; +} + + +void +genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface) +{ + // TODO(kaib): re-implement genembedtramp + genwrapper(rcvr, method, newnam, iface); +/* + Sym *e; + int c, d, o; + Prog *p; + Type *f; + + e = method->sym; + for(d=0; d<nelem(dotlist); d++) { + c = adddot1(e, rcvr, d, nil, 0); + if(c == 1) + goto out; + } + fatal("genembedtramp %T.%S", rcvr, method->sym); + +out: + newplist()->name = newname(newnam); + + //TEXT main·S_test2(SB),7,$0 + p = pc; + gins(ATEXT, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = newnam; + p->to.type = D_CONST2; + p->reg = 7; + p->to.offset2 = 0; + p->to.reg = NREG; +//print("1. %P\n", p); + + o = 0; + for(c=d-1; c>=0; c--) { + f = dotlist[c].field; + o += f->width; + if(!isptr[f->type->etype]) + continue; + + //MOVW o(R0), R0 + p = pc; + gins(AMOVW, N, N); + p->from.type = D_OREG; + p->from.reg = REGARG; + p->from.offset = o; + p->to.type = D_REG; + p->to.reg = REGARG; +//print("2. %P\n", p); + o = 0; + } + if(o != 0) { + //MOVW $XX(R0), R0 + p = pc; + gins(AMOVW, N, N); + p->from.type = D_CONST; + p->from.reg = REGARG; + p->from.offset = o; + p->to.type = D_REG; + p->to.reg = REGARG; +//print("3. %P\n", p); + } + + f = dotlist[0].field; + //B main·*Sub_test2(SB) + if(isptr[f->type->etype]) + f = f->type; + p = pc; + gins(AB, N, N); + p->to.type = D_OREG; + p->to.reg = NREG; + p->to.name = D_EXTERN; + p->to.sym = methodsym(method->sym, ptrto(f->type), 0); +//print("4. %P\n", p); + + pc->as = ARET; // overwrite AEND +*/ +} + +void +nopout(Prog *p) +{ + p->as = ANOP; +} diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c new file mode 100644 index 000000000..ddaf52a88 --- /dev/null +++ b/src/cmd/5g/gsubr.c @@ -0,0 +1,2010 @@ +// Derived from Inferno utils/5c/txt.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/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(kaib): Can make this bigger if we move +// the text segment up higher in 5l for all GOOS. +long unmappedzero = 4096; + +void +clearp(Prog *p) +{ + p->as = AEND; + p->reg = NREG; + p->scond = C_SCOND_NONE; + p->from.type = D_NONE; + p->from.name = D_NONE; + p->from.reg = NREG; + p->to.type = D_NONE; + p->to.name = D_NONE; + p->to.reg = NREG; + 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 *p, *p1, *p2, *p3; + Node dst, end, zero, con; + + if(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 + + // MOVW $4(SP), R1 + nodreg(&dst, types[tptr], 1); + p = gins(AMOVW, N, &dst); + p->from.type = D_CONST; + p->from.reg = REGSP; + p->from.offset = 4; + + // MOVW $n(R1), R2 + nodreg(&end, types[tptr], 2); + p = gins(AMOVW, N, &end); + p->from.type = D_CONST; + p->from.reg = 1; + p->from.offset = p1->to.offset; + + // MOVW $0, R3 + nodreg(&zero, types[TUINT32], 3); + nodconst(&con, types[TUINT32], 0); + gmove(&con, &zero); + + // L: + // MOVW.P R3, 0(R1) +4 + // CMP R1, R2 + // BNE L + p = gins(AMOVW, &zero, &dst); + p->to.type = D_OREG; + p->to.offset = 4; + p->scond |= C_PBIT; + p3 = p; + p = gins(ACMP, &dst, N); + raddr(&end, p); + patch(gbranch(ABNE, T), p3); + + // 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(AB, 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; +} + +void +ggloblsym(Sym *s, int32 width, int dupok) +{ + Prog *p; + + p = gins(AGLOBL, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = s; + p->to.type = D_CONST; + p->to.name = D_NONE; + p->to.offset = width; + if(dupok) + p->reg = DUPOK; +} + +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. + * also fix up direct register references to be D_OREG. + */ +void +afunclit(Addr *a) +{ + if(a->type == D_CONST && a->name == D_EXTERN || a->type == D_REG) { + a->type = D_OREG; + } +} + +static int resvd[] = +{ + 9, // reserved for m + 10, // reserved for g +}; + +void +ginit(void) +{ + int i; + + for(i=0; i<nelem(reg); i++) + reg[i] = 0; + for(i=0; i<nelem(resvd); i++) + reg[resvd[i]]++; +} + +void +gclean(void) +{ + int i; + + for(i=0; i<nelem(resvd); i++) + reg[resvd[i]]--; + + for(i=0; i<nelem(reg); i++) + if(reg[i]) + yyerror("reg %R left allocated\n", i); +} + +int32 +anyregalloc(void) +{ + int i, j; + + for(i=0; i<nelem(reg); i++) { + if(reg[i] == 0) + goto ok; + for(j=0; j<nelem(resvd); j++) + if(resvd[j] == i) + goto ok; + return 1; + ok:; + } + return 0; +} + +/* + * allocate register of type t, leave in n. + * if o != N, o is desired fixed register. + * caller must regfree(n). + */ +void +regalloc(Node *n, Type *t, Node *o) +{ + int i, et, fixfree, floatfree; + + if(debug['r']) { + fixfree = 0; + for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) + if(reg[i] == 0) + fixfree++; + floatfree = 0; + for(i=REGALLOC_F0; i<=REGALLOC_FMAX; i++) + if(reg[i] == 0) + floatfree++; + print("regalloc fix %d float %d\n", fixfree, floatfree); + } + + if(t == T) + fatal("regalloc: t nil"); + et = simtype[t->etype]; + if(is64(t)) + fatal("regalloc: 64 bit type %T"); + + switch(et) { + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TPTR32: + case TBOOL: + if(o != N && o->op == OREGISTER) { + i = o->val.u.reg; + if(i >= REGALLOC_R0 && i <= REGALLOC_RMAX) + goto out; + } + for(i=REGALLOC_R0; i<=REGALLOC_RMAX; 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 >= REGALLOC_F0 && i <= REGALLOC_FMAX) + goto out; + } + for(i=REGALLOC_F0; i<=REGALLOC_FMAX; i++) + if(reg[i] == 0) + goto out; + yyerror("out of floating point 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, fixfree, floatfree; + + if(debug['r']) { + fixfree = 0; + for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) + if(reg[i] == 0) + fixfree++; + floatfree = 0; + for(i=REGALLOC_F0; i<=REGALLOC_FMAX; i++) + if(reg[i] == 0) + floatfree++; + print("regalloc fix %d float %d\n", fixfree, floatfree); + } + + if(n->op == ONAME && iscomplex[n->type->etype]) + return; + if(n->op != OREGISTER && n->op != OINDREG) + fatal("regfree: not a register"); + i = n->val.u.reg; + 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) { + default: + fatal("nodarg %T %d", t, fp); + + case 0: // output arg for calling another function + n->op = OINDREG; + n->val.u.reg = REGSP; + n->xoffset += 4; + break; + + case 1: // input arg to current function + n->class = PPARAM; + break; + } + n->typecheck = 1; + return n; +} + +/* + * return constant i node. + * overwritten by next call, but useful in calls to gins. + */ +Node* +ncon(uint32 i) +{ + static Node n; + + if(n.type == T) + nodconst(&n, types[TUINT32], 0); + mpmovecfix(n.val.u.xval, i); + return &n; +} + +/* + * Is this node a memory operand? + */ +int +ismem(Node *n) +{ + switch(n->op) { + case OINDREG: + case ONAME: + case OPARAM: + return 1; + } + return 0; +} + +Node sclean[10]; +int nsclean; + +/* + * n is a 64-bit value. fill in lo and hi to refer to its 32-bit halves. + */ +void +split64(Node *n, Node *lo, Node *hi) +{ + Node n1; + int64 i; + + if(!is64(n->type)) + fatal("split64 %T", n->type); + + sclean[nsclean].op = OEMPTY; + if(nsclean >= nelem(sclean)) + fatal("split64 clean"); + nsclean++; + switch(n->op) { + default: + if(!dotaddable(n, &n1)) { + igen(n, &n1, N); + sclean[nsclean-1] = n1; + } + n = &n1; + goto common; + case ONAME: + if(n->class == PPARAMREF) { + cgen(n->heapaddr, &n1); + sclean[nsclean-1] = n1; + // fall through. + n = &n1; + } + goto common; + case OINDREG: + common: + *lo = *n; + *hi = *n; + lo->type = types[TUINT32]; + if(n->type->etype == TINT64) + hi->type = types[TINT32]; + else + hi->type = types[TUINT32]; + hi->xoffset += 4; + break; + + case OLITERAL: + convconst(&n1, n->type, &n->val); + i = mpgetfix(n1.val.u.xval); + nodconst(lo, types[TUINT32], (uint32)i); + i >>= 32; + if(n->type->etype == TINT64) + nodconst(hi, types[TINT32], (int32)i); + else + nodconst(hi, types[TUINT32], (uint32)i); + break; + } +} + +void +splitclean(void) +{ + if(nsclean <= 0) + fatal("splitclean"); + nsclean--; + if(sclean[nsclean].op != OEMPTY) + regfree(&sclean[nsclean]); +} + +#define CASE(a,b) (((a)<<16)|((b)<<0)) + +void +gmove(Node *f, Node *t) +{ + int a, ft, tt, fa, ta; + Type *cvt; + Node r1, r2, flo, fhi, tlo, thi, con; + Prog *p1; + + 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; + // except 64-bit, which always copies via registers anyway. + if(!is64(f->type) && !is64(t->type) && ismem(f) && ismem(t)) + goto hard; + + // convert constant to desired type + if(f->op == OLITERAL) { + switch(tt) { + default: + convconst(&con, t->type, &f->val); + break; + + case TINT16: + case TINT8: + convconst(&con, types[TINT32], &f->val); + regalloc(&r1, con.type, t); + gins(AMOVW, &con, &r1); + gmove(&r1, t); + regfree(&r1); + return; + + case TUINT16: + case TUINT8: + convconst(&con, types[TUINT32], &f->val); + regalloc(&r1, con.type, t); + gins(AMOVW, &con, &r1); + gmove(&r1, t); + regfree(&r1); + return; + } + + f = &con; + ft = simsimtype(con.type); + + // constants can't move directly to memory + if(ismem(t) && !is64(t->type)) 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: + goto fatal; + + /* + * integer copy and truncate + */ + case CASE(TINT8, TINT8): // same size + case CASE(TUINT8, TINT8): + case CASE(TINT16, TINT8): // truncate + case CASE(TUINT16, TINT8): + case CASE(TINT32, TINT8): + case CASE(TUINT32, TINT8): + a = AMOVB; + break; + + case CASE(TINT8, TUINT8): + case CASE(TUINT8, TUINT8): + case CASE(TINT16, TUINT8): + case CASE(TUINT16, TUINT8): + case CASE(TINT32, TUINT8): + case CASE(TUINT32, TUINT8): + a = AMOVBU; + break; + + case CASE(TINT64, TINT8): // truncate low word + case CASE(TUINT64, TINT8): + a = AMOVB; + goto trunc64; + + case CASE(TINT64, TUINT8): + case CASE(TUINT64, TUINT8): + a = AMOVBU; + goto trunc64; + + case CASE(TINT16, TINT16): // same size + case CASE(TUINT16, TINT16): + case CASE(TINT32, TINT16): // truncate + case CASE(TUINT32, TINT16): + a = AMOVH; + break; + + case CASE(TINT16, TUINT16): + case CASE(TUINT16, TUINT16): + case CASE(TINT32, TUINT16): + case CASE(TUINT32, TUINT16): + a = AMOVHU; + break; + + case CASE(TINT64, TINT16): // truncate low word + case CASE(TUINT64, TINT16): + a = AMOVH; + goto trunc64; + + case CASE(TINT64, TUINT16): + case CASE(TUINT64, TUINT16): + a = AMOVHU; + goto trunc64; + + case CASE(TINT32, TINT32): // same size + case CASE(TINT32, TUINT32): + case CASE(TUINT32, TINT32): + case CASE(TUINT32, TUINT32): + a = AMOVW; + break; + + case CASE(TINT64, TINT32): // truncate + case CASE(TUINT64, TINT32): + case CASE(TINT64, TUINT32): + case CASE(TUINT64, TUINT32): + split64(f, &flo, &fhi); + regalloc(&r1, t->type, N); + gins(AMOVW, &flo, &r1); + gins(AMOVW, &r1, t); + regfree(&r1); + splitclean(); + return; + + case CASE(TINT64, TINT64): // same size + case CASE(TINT64, TUINT64): + case CASE(TUINT64, TINT64): + case CASE(TUINT64, TUINT64): + split64(f, &flo, &fhi); + split64(t, &tlo, &thi); + regalloc(&r1, flo.type, N); + regalloc(&r2, fhi.type, N); + gins(AMOVW, &flo, &r1); + gins(AMOVW, &fhi, &r2); + gins(AMOVW, &r1, &tlo); + gins(AMOVW, &r2, &thi); + regfree(&r1); + regfree(&r2); + splitclean(); + splitclean(); + return; + + /* + * integer up-conversions + */ + case CASE(TINT8, TINT16): // sign extend int8 + case CASE(TINT8, TUINT16): + case CASE(TINT8, TINT32): + case CASE(TINT8, TUINT32): + a = AMOVB; + goto rdst; + case CASE(TINT8, TINT64): // convert via int32 + case CASE(TINT8, TUINT64): + cvt = types[TINT32]; + goto hard; + + case CASE(TUINT8, TINT16): // zero extend uint8 + case CASE(TUINT8, TUINT16): + case CASE(TUINT8, TINT32): + case CASE(TUINT8, TUINT32): + a = AMOVBU; + goto rdst; + case CASE(TUINT8, TINT64): // convert via uint32 + case CASE(TUINT8, TUINT64): + cvt = types[TUINT32]; + goto hard; + + case CASE(TINT16, TINT32): // sign extend int16 + case CASE(TINT16, TUINT32): + a = AMOVH; + goto rdst; + case CASE(TINT16, TINT64): // convert via int32 + case CASE(TINT16, TUINT64): + cvt = types[TINT32]; + goto hard; + + case CASE(TUINT16, TINT32): // zero extend uint16 + case CASE(TUINT16, TUINT32): + a = AMOVHU; + goto rdst; + case CASE(TUINT16, TINT64): // convert via uint32 + case CASE(TUINT16, TUINT64): + cvt = types[TUINT32]; + goto hard; + + case CASE(TINT32, TINT64): // sign extend int32 + case CASE(TINT32, TUINT64): + split64(t, &tlo, &thi); + regalloc(&r1, tlo.type, N); + regalloc(&r2, thi.type, N); + gmove(f, &r1); + p1 = gins(AMOVW, &r1, &r2); + p1->from.type = D_SHIFT; + p1->from.offset = 2 << 5 | 31 << 7 | r1.val.u.reg; // r1->31 + p1->from.reg = NREG; +//print("gmove: %P\n", p1); + gins(AMOVW, &r1, &tlo); + gins(AMOVW, &r2, &thi); + regfree(&r1); + regfree(&r2); + splitclean(); + return; + + case CASE(TUINT32, TINT64): // zero extend uint32 + case CASE(TUINT32, TUINT64): + split64(t, &tlo, &thi); + gmove(f, &tlo); + regalloc(&r1, thi.type, N); + gins(AMOVW, ncon(0), &r1); + gins(AMOVW, &r1, &thi); + regfree(&r1); + splitclean(); + return; + + /* + * float to integer + */ + case CASE(TFLOAT32, TINT8): + case CASE(TFLOAT32, TUINT8): + case CASE(TFLOAT32, TINT16): + case CASE(TFLOAT32, TUINT16): + case CASE(TFLOAT32, TINT32): + case CASE(TFLOAT32, TUINT32): +// case CASE(TFLOAT32, TUINT64): + + case CASE(TFLOAT64, TINT8): + case CASE(TFLOAT64, TUINT8): + case CASE(TFLOAT64, TINT16): + case CASE(TFLOAT64, TUINT16): + case CASE(TFLOAT64, TINT32): + case CASE(TFLOAT64, TUINT32): +// case CASE(TFLOAT64, TUINT64): + fa = AMOVF; + a = AMOVFW; + if(ft == TFLOAT64) { + fa = AMOVD; + a = AMOVDW; + } + ta = AMOVW; + switch(tt) { + case TINT8: + ta = AMOVB; + break; + case TUINT8: + ta = AMOVBU; + break; + case TINT16: + ta = AMOVH; + break; + case TUINT16: + ta = AMOVHU; + break; + } + + regalloc(&r1, types[ft], f); + regalloc(&r2, types[tt], t); + gins(fa, f, &r1); // load to fpu + p1 = gins(a, &r1, &r1); // convert to w + switch(tt) { + case TUINT8: + case TUINT16: + case TUINT32: + p1->scond |= C_UBIT; + } + gins(AMOVW, &r1, &r2); // copy to cpu + gins(ta, &r2, t); // store + regfree(&r1); + regfree(&r2); + return; + + /* + * integer to float + */ + case CASE(TINT8, TFLOAT32): + case CASE(TUINT8, TFLOAT32): + case CASE(TINT16, TFLOAT32): + case CASE(TUINT16, TFLOAT32): + case CASE(TINT32, TFLOAT32): + case CASE(TUINT32, TFLOAT32): + case CASE(TINT8, TFLOAT64): + case CASE(TUINT8, TFLOAT64): + case CASE(TINT16, TFLOAT64): + case CASE(TUINT16, TFLOAT64): + case CASE(TINT32, TFLOAT64): + case CASE(TUINT32, TFLOAT64): + fa = AMOVW; + switch(ft) { + case TINT8: + fa = AMOVB; + break; + case TUINT8: + fa = AMOVBU; + break; + case TINT16: + fa = AMOVH; + break; + case TUINT16: + fa = AMOVHU; + break; + } + a = AMOVWF; + ta = AMOVF; + if(tt == TFLOAT64) { + a = AMOVWD; + ta = AMOVD; + } + regalloc(&r1, types[ft], f); + regalloc(&r2, types[tt], t); + gins(fa, f, &r1); // load to cpu + gins(AMOVW, &r1, &r2); // copy to fpu + p1 = gins(a, &r2, &r2); // convert + switch(ft) { + case TUINT8: + case TUINT16: + case TUINT32: + p1->scond |= C_UBIT; + } + gins(ta, &r2, t); // store + regfree(&r1); + regfree(&r2); + return; + + case CASE(TUINT64, TFLOAT32): + case CASE(TUINT64, TFLOAT64): + fatal("gmove UINT64, TFLOAT not implemented"); + return; + + + /* + * float to float + */ + case CASE(TFLOAT32, TFLOAT32): + a = AMOVF; + break; + + case CASE(TFLOAT64, TFLOAT64): + a = AMOVD; + break; + + case CASE(TFLOAT32, TFLOAT64): + regalloc(&r1, types[TFLOAT64], t); + gins(AMOVF, f, &r1); + gins(AMOVFD, &r1, &r1); + gins(AMOVD, &r1, t); + regfree(&r1); + return; + + case CASE(TFLOAT64, TFLOAT32): + regalloc(&r1, types[TFLOAT64], t); + gins(AMOVD, f, &r1); + gins(AMOVDF, &r1, &r1); + gins(AMOVF, &r1, t); + regfree(&r1); + return; + } + + gins(a, f, t); + return; + +rdst: + // TODO(kaib): we almost always require a register dest anyway, this can probably be + // removed. + // 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; + +trunc64: + // truncate 64 bit integer + split64(f, &flo, &fhi); + regalloc(&r1, t->type, N); + gins(a, &flo, &r1); + gins(a, &r1, t); + regfree(&r1); + splitclean(); + return; + +fatal: + // should not happen + fatal("gmove %N -> %N", f, t); +} + +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 v; + Prog *p; + Addr af, at; + + if(f != N && f->op == OINDEX) { + fatal("gins OINDEX not implemented"); +// 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) { + fatal("gins OINDEX not implemented"); +// regalloc(&nod, ®node, Z); +// v = constnode.vconst; +// cgen(t->right, &nod); +// constnode.vconst = v; +// idx.reg = nod.reg; +// regfree(&nod); + } + + 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); + return p; +} + +/* + * insert n into reg slot of p + */ +void +raddr(Node *n, Prog *p) +{ + Addr a; + + naddr(n, &a, 1); + if(a.type != D_REG && a.type != D_FREG) { + if(n) + fatal("bad in raddr: %O", n->op); + else + fatal("bad in raddr: <null>"); + p->reg = NREG; + } else + p->reg = a.reg; +} + +/* generate a comparison +TODO(kaib): one of the args can actually be a small constant. relax the constraint and fix call sites. + */ +Prog* +gcmp(int as, Node *lhs, Node *rhs) +{ + Prog *p; + + if(lhs->op != OREGISTER || rhs->op != OREGISTER) + fatal("bad operands to gcmp: %O %O", lhs->op, rhs->op); + + p = gins(as, rhs, N); + raddr(lhs, p); + return p; +} + +/* generate a constant shift + * arm encodes a shift by 32 as 0, thus asking for 0 shift is illegal. +*/ +Prog* +gshift(int as, Node *lhs, int32 stype, int32 sval, Node *rhs) +{ + Prog *p; + + if(sval <= 0 || sval > 32) + fatal("bad shift value: %d", sval); + + sval = sval&0x1f; + + p = gins(as, N, rhs); + p->from.type = D_SHIFT; + p->from.offset = stype | sval<<7 | lhs->val.u.reg; + return p; +} + +/* generate a register shift +*/ +Prog * +gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs) +{ + Prog *p; + p = gins(as, N, rhs); + p->from.type = D_SHIFT; + p->from.offset = stype | reg->val.u.reg << 8 | 1<<4 | lhs->val.u.reg; + return p; +} + +static void +checkoffset(Addr *a, int canemitcode) +{ + Prog *p; + Node n1; + + if(a->offset < unmappedzero) + return; + if(!canemitcode) + fatal("checkoffset %#x, 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). + regalloc(&n1, types[TUINTPTR], N); + p = gins(AMOVW, N, &n1); + p->from = *a; + p->from.offset = 0; + regfree(&n1); +} + +/* + * generate code to compute n; + * make a refer to result. + */ +void +naddr(Node *n, Addr *a, int canemitcode) +{ + a->type = D_NONE; + a->name = D_NONE; + a->reg = NREG; + a->node = N; + a->etype = 0; + if(n == N) + return; + + switch(n->op) { + default: + fatal("naddr: bad %O %D", n->op, a); + break; + + case OREGISTER: + if(n->val.u.reg <= REGALLOC_RMAX) { + a->type = D_REG; + a->reg = n->val.u.reg; + } else { + a->type = D_FREG; + a->reg = n->val.u.reg - REGALLOC_F0; + } + a->sym = S; + break; + + case OINDEX: + case OIND: + fatal("naddr: OINDEX"); +// 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 = D_OREG; + a->reg = n->val.u.reg; + 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_OREG; + a->name = D_PARAM; + break; + + case ONAME: + a->etype = 0; + a->width = 0; + a->reg = NREG; + if(n->type != T) { + a->etype = simtype[n->type->etype]; + a->width = n->type->width; + } + 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); + } + + a->type = D_OREG; + switch(n->class) { + default: + fatal("naddr: ONAME class %S %d\n", n->sym, n->class); + case PEXTERN: + a->name = D_EXTERN; + break; + case PAUTO: + a->name = D_AUTO; + if (n->sym) + a->node = n->orig; + break; + case PPARAM: + case PPARAMOUT: + a->name = D_PARAM; + break; + case PFUNC: + a->name = D_EXTERN; + a->type = D_CONST; + 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 OLEN: + // len of string or slice + naddr(n->left, a, canemitcode); + a->etype = TINT32; + if(a->type == D_CONST && a->offset == 0) + break; // len(nil) + a->offset += Array_nel; + 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); + a->etype = TINT32; + if(a->type == D_CONST && a->offset == 0) + break; // cap(nil) + a->offset += Array_cap; + if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero) + checkoffset(a, canemitcode); + break; + + case OADDR: + naddr(n->left, a, canemitcode); + a->etype = tptr; + switch(a->type) { + case D_OREG: + a->type = D_CONST; + break; + + case D_REG: + case D_CONST: + break; + + default: + fatal("naddr: OADDR %d\n", a->type); + } + } +} + +/* + * 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 etype %T simtype %T", op, t, types[t->etype], types[simtype[t->etype]]); + break; + +/* case CASE(OADDR, TPTR32): + a = ALEAL; + break; + + case CASE(OADDR, TPTR64): + a = ALEAQ; + break; +*/ + // TODO(kaib): make sure the conditional branches work on all edge cases + 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 = ABEQ; + 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 = ABNE; + break; + + case CASE(OLT, TINT8): + case CASE(OLT, TINT16): + case CASE(OLT, TINT32): + case CASE(OLT, TINT64): + case CASE(OLT, TFLOAT32): + case CASE(OLT, TFLOAT64): + a = ABLT; + break; + + case CASE(OLT, TUINT8): + case CASE(OLT, TUINT16): + case CASE(OLT, TUINT32): + case CASE(OLT, TUINT64): + a = ABLO; + break; + + case CASE(OLE, TINT8): + case CASE(OLE, TINT16): + case CASE(OLE, TINT32): + case CASE(OLE, TINT64): + case CASE(OLE, TFLOAT32): + case CASE(OLE, TFLOAT64): + a = ABLE; + break; + + case CASE(OLE, TUINT8): + case CASE(OLE, TUINT16): + case CASE(OLE, TUINT32): + case CASE(OLE, TUINT64): + a = ABLS; + break; + + case CASE(OGT, TINT8): + case CASE(OGT, TINT16): + case CASE(OGT, TINT32): + case CASE(OGT, TINT64): + case CASE(OGT, TFLOAT32): + case CASE(OGT, TFLOAT64): + a = ABGT; + break; + + case CASE(OGT, TUINT8): + case CASE(OGT, TUINT16): + case CASE(OGT, TUINT32): + case CASE(OGT, TUINT64): + a = ABHI; + break; + + case CASE(OGE, TINT8): + case CASE(OGE, TINT16): + case CASE(OGE, TINT32): + case CASE(OGE, TINT64): + case CASE(OGE, TFLOAT32): + case CASE(OGE, TFLOAT64): + a = ABGE; + break; + + case CASE(OGE, TUINT8): + case CASE(OGE, TUINT16): + case CASE(OGE, TUINT32): + case CASE(OGE, TUINT64): + a = ABHS; + break; + + case CASE(OCMP, TBOOL): + case CASE(OCMP, TINT8): + case CASE(OCMP, TUINT8): + case CASE(OCMP, TINT16): + case CASE(OCMP, TUINT16): + case CASE(OCMP, TINT32): + case CASE(OCMP, TUINT32): + case CASE(OCMP, TPTR32): + a = ACMP; + break; + + case CASE(OCMP, TFLOAT32): + a = ACMPF; + break; + + case CASE(OCMP, TFLOAT64): + a = ACMPD; + break; + + case CASE(OAS, TBOOL): + case CASE(OAS, TINT8): + a = AMOVB; + break; + + case CASE(OAS, TUINT8): + a = AMOVBU; + break; + + case CASE(OAS, TINT16): + a = AMOVH; + break; + + case CASE(OAS, TUINT16): + a = AMOVHU; + break; + + case CASE(OAS, TINT32): + case CASE(OAS, TUINT32): + case CASE(OAS, TPTR32): + a = AMOVW; + break; + + case CASE(OAS, TFLOAT32): + a = AMOVF; + break; + + case CASE(OAS, TFLOAT64): + a = AMOVD; + break; + + case CASE(OADD, TINT8): + case CASE(OADD, TUINT8): + case CASE(OADD, TINT16): + case CASE(OADD, TUINT16): + case CASE(OADD, TINT32): + case CASE(OADD, TUINT32): + case CASE(OADD, TPTR32): + a = AADD; + break; + + case CASE(OADD, TFLOAT32): + a = AADDF; + break; + + case CASE(OADD, TFLOAT64): + a = AADDD; + break; + + case CASE(OSUB, TINT8): + case CASE(OSUB, TUINT8): + case CASE(OSUB, TINT16): + case CASE(OSUB, TUINT16): + case CASE(OSUB, TINT32): + case CASE(OSUB, TUINT32): + case CASE(OSUB, TPTR32): + a = ASUB; + break; + + case CASE(OSUB, TFLOAT32): + a = ASUBF; + break; + + case CASE(OSUB, TFLOAT64): + a = ASUBD; + break; + + case CASE(OAND, TINT8): + case CASE(OAND, TUINT8): + case CASE(OAND, TINT16): + case CASE(OAND, TUINT16): + case CASE(OAND, TINT32): + case CASE(OAND, TUINT32): + case CASE(OAND, TPTR32): + a = AAND; + break; + + case CASE(OOR, TINT8): + case CASE(OOR, TUINT8): + case CASE(OOR, TINT16): + case CASE(OOR, TUINT16): + case CASE(OOR, TINT32): + case CASE(OOR, TUINT32): + case CASE(OOR, TPTR32): + a = AORR; + break; + + case CASE(OXOR, TINT8): + case CASE(OXOR, TUINT8): + case CASE(OXOR, TINT16): + case CASE(OXOR, TUINT16): + case CASE(OXOR, TINT32): + case CASE(OXOR, TUINT32): + case CASE(OXOR, TPTR32): + a = AEOR; + break; + + case CASE(OLSH, TINT8): + case CASE(OLSH, TUINT8): + case CASE(OLSH, TINT16): + case CASE(OLSH, TUINT16): + case CASE(OLSH, TINT32): + case CASE(OLSH, TUINT32): + case CASE(OLSH, TPTR32): + a = ASLL; + break; + + case CASE(ORSH, TUINT8): + case CASE(ORSH, TUINT16): + case CASE(ORSH, TUINT32): + case CASE(ORSH, TPTR32): + a = ASRL; + break; + + case CASE(ORSH, TINT8): + case CASE(ORSH, TINT16): + case CASE(ORSH, TINT32): + a = ASRA; + break; + + case CASE(OMUL, TUINT8): + case CASE(OMUL, TUINT16): + case CASE(OMUL, TUINT32): + case CASE(OMUL, TPTR32): + a = AMULU; + break; + + case CASE(OMUL, TINT8): + case CASE(OMUL, TINT16): + case CASE(OMUL, TINT32): + a = AMUL; + break; + + case CASE(OMUL, TFLOAT32): + a = AMULF; + break; + + case CASE(OMUL, TFLOAT64): + a = AMULD; + break; + + case CASE(ODIV, TUINT8): + case CASE(ODIV, TUINT16): + case CASE(ODIV, TUINT32): + case CASE(ODIV, TPTR32): + a = ADIVU; + break; + + case CASE(ODIV, TINT8): + case CASE(ODIV, TINT16): + case CASE(ODIV, TINT32): + a = ADIV; + break; + + case CASE(OMOD, TUINT8): + case CASE(OMOD, TUINT16): + case CASE(OMOD, TUINT32): + case CASE(OMOD, TPTR32): + a = AMODU; + break; + + case CASE(OMOD, TINT8): + case CASE(OMOD, TINT16): + case CASE(OMOD, TINT32): + a = AMOD; + 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 = ADIVF; + break; + + case CASE(ODIV, TFLOAT64): + a = ADIVD; + break; + + } + return a; +} + +enum +{ + ODynam = 1<<0, + OPtrto = 1<<1, +}; + +static Node clean[20]; +static int cleani = 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; +} + +int +dotaddable(Node *n, Node *n1) +{ + int o, oary[10]; + Node *nn; + + if(n->op != ODOT) + return 0; + + o = dotoffset(n, oary, &nn); + if(nn != N && nn->addable && o == 1 && oary[0] >= 0) { + *n1 = *nn; + n1->type = n->type; + n1->xoffset += oary[0]; + return 1; + } + return 0; +} + +/* + * 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 *w) +{ + int o, i; + int oary[10]; + int64 v; + Node n1, n2, n3, n4, *nn, *l, *r; + Node *reg, *reg1; + Prog *p1, *p2; + 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; + cleani += 2; + reg = &clean[cleani-1]; + reg1 = &clean[cleani-2]; + reg->op = OEMPTY; + reg1->op = OEMPTY; + goto oindex; + } + return 0; + +lit: + switch(as) { + default: + return 0; + case AADD: case ASUB: case AAND: case AORR: case AEOR: + case AMOVB: case AMOVBU: case AMOVH: case AMOVHU: + case AMOVW: + 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<o; i++) { + if(oary[i] >= 0) + fatal("cant happen"); + gins(AMOVW, &n1, reg); + n1.xoffset = -(oary[i]+1); + } + + a->type = D_NONE; + a->name = D_NONE; + n1.type = n->type; + naddr(&n1, a, 1); + goto yes; + +oindex: + l = n->left; + r = n->right; + if(l->ullman >= UINF && r->ullman >= UINF) + goto no; + + // set o to type of array + o = 0; + if(isptr[l->type->etype]) { + o += OPtrto; + if(l->type->type->etype != TARRAY) + fatal("not ptr ary"); + if(l->type->type->bound < 0) + o += ODynam; + } else { + 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: + goto no; + case 1: + case 2: + case 4: + case 8: + break; + } + + // load the array (reg) + if(l->ullman > r->ullman) { + regalloc(reg, types[tptr], N); + if(o & OPtrto) + cgen(l, reg); + else + agen(l, reg); + } + + // load the index (reg1) + t = types[TUINT32]; + if(issigned[r->type->etype]) + t = types[TINT32]; + regalloc(reg1, t, N); + regalloc(&n3, types[TINT32], reg1); + p2 = cgenindex(r, &n3); + gmove(&n3, reg1); + regfree(&n3); + + // load the array (reg) + if(l->ullman <= r->ullman) { + regalloc(reg, types[tptr], N); + if(o & OPtrto) + cgen(l, reg); + else + agen(l, reg); + } + + // check bounds + if(!debug['B']) { + if(o & ODynam) { + n2 = *reg; + n2.op = OINDREG; + n2.type = types[tptr]; + n2.xoffset = Array_nel; + } else { + if(l->type->width >= unmappedzero && l->op == OIND) { + // cannot rely on page protections to + // catch array ptr == 0, so dereference. + n2 = *reg; + n2.op = OINDREG; + n2.type = types[TUINTPTR]; + n2.xoffset = 0; + regalloc(&n3, n2.type, N); + gins(AMOVW, &n2, &n3); + regfree(&n3); + } + nodconst(&n2, types[TUINT32], l->type->bound); + if(o & OPtrto) + nodconst(&n2, types[TUINT32], l->type->type->bound); + } + regalloc(&n3, n2.type, N); + cgen(&n2, &n3); + gcmp(optoas(OCMP, types[TUINT32]), reg1, &n3); + regfree(&n3); + p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(p2) + patch(p2, pc); + ginscall(panicindex, 0); + patch(p1, pc); + } + + if(o & ODynam) { + n2 = *reg; + n2.op = OINDREG; + n2.type = types[tptr]; + n2.xoffset = Array_array; + gmove(&n2, reg); + } + + switch(*w) { + case 1: + gins(AADD, reg1, reg); + break; + case 2: + gshift(AADD, reg1, SHIFT_LL, 1, reg); + break; + case 4: + gshift(AADD, reg1, SHIFT_LL, 2, reg); + break; + case 8: + gshift(AADD, reg1, SHIFT_LL, 3, reg); + break; + } + + naddr(reg1, a, 1); + a->type = D_OREG; + a->reg = reg->val.u.reg; + a->offset = 0; + goto yes; + +oindex_const: + // index is constant + // can check statically and + // can multiply by width statically + + regalloc(reg, types[tptr], N); + if(o & OPtrto) + cgen(l, reg); + else + agen(l, reg); + + v = mpgetfix(r->val.u.xval); + if(o & ODynam) { + + if(!debug['B'] && !n->etype) { + n1 = *reg; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_nel; + nodconst(&n2, types[TUINT32], v); + regalloc(&n3, types[TUINT32], N); + cgen(&n2, &n3); + regalloc(&n4, n1.type, N); + cgen(&n1, &n4); + gcmp(optoas(OCMP, types[TUINT32]), &n4, &n3); + regfree(&n4); + regfree(&n3); + 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->name = D_NONE; + naddr(&n2, a, 1); + goto yes; + +yes: + return 1; + +no: + sudoclean(); + return 0; +} diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c new file mode 100644 index 000000000..0c6dbbf71 --- /dev/null +++ b/src/cmd/5g/list.c @@ -0,0 +1,331 @@ +// Derived from Inferno utils/5c/list.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/list.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(kaib): make 5g/list.c congruent with 5l/list.c + +static int sconsize; +void +listinit(void) +{ + + fmtinstall('A', Aconv); // as + fmtinstall('C', Cconv); // conditional execution bit + fmtinstall('P', Pconv); // Prog* + fmtinstall('D', Dconv); // Addr* + fmtinstall('Y', Yconv); // sconst + fmtinstall('R', Rconv); // register + fmtinstall('M', Mconv); // names +} + +int +Pconv(Fmt *fp) +{ + char str[STRINGSZ], str1[STRINGSZ]; + Prog *p; + + p = va_arg(fp->args, Prog*); + sconsize = 8; + switch(p->as) { + default: + snprint(str1, sizeof(str1), "%A%C", p->as, p->scond); + if(p->reg == NREG) + snprint(str, sizeof(str), "%.4d (%L) %-7s %D,%D", + p->loc, p->lineno, str1, &p->from, &p->to); + else + if (p->from.type != D_FREG) { + snprint(str, sizeof(str), "%.4d (%L) %-7s %D,R%d,%D", + p->loc, p->lineno, str1, &p->from, p->reg, &p->to); + } else + snprint(str, sizeof(str), "%.4d (%L) %-7A%C %D,F%d,%D", + p->loc, p->lineno, p->as, p->scond, &p->from, p->reg, &p->to); + break; + + case ADATA: + snprint(str, sizeof(str), "%.4d (%L) %-7A %D/%d,%D", + p->loc, p->lineno, p->as, &p->from, p->reg, &p->to); + break; + } + return fmtstrcpy(fp, str); +} + +int +Dconv(Fmt *fp) +{ + char str[STRINGSZ]; + char *op; + Addr *a; + int i; + int32 v; + + a = va_arg(fp->args, Addr*); + if(a == A) { + sprint(str, "<nil>"); + goto conv; + } + i = a->type; + switch(i) { + + default: + sprint(str, "GOK-type(%d)", a->type); + break; + + case D_NONE: + str[0] = 0; + if(a->name != D_NONE || a->reg != NREG || a->sym != S) + sprint(str, "%M(R%d)(NONE)", a, a->reg); + break; + + case D_CONST: + if(a->reg != NREG) + sprint(str, "$%M(R%d)", a, a->reg); + else + sprint(str, "$%M", a); + break; + + case D_CONST2: + sprint(str, "$%d-%d", a->offset, a->offset2); + break; + + case D_SHIFT: + v = a->offset; + op = "<<>>->@>" + (((v>>5) & 3) << 1); + if(v & (1<<4)) + sprint(str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15); + else + sprint(str, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31); + if(a->reg != NREG) + sprint(str+strlen(str), "(R%d)", a->reg); + break; + + case D_OCONST: + sprint(str, "$*$%M", a); + if(a->reg != NREG) + sprint(str, "%M(R%d)(CONST)", a, a->reg); + break; + + case D_OREG: + if(a->reg != NREG) + sprint(str, "%M(R%d)", a, a->reg); + else + sprint(str, "%M", a); + break; + + case D_REG: + sprint(str, "R%d", a->reg); + if(a->name != D_NONE || a->sym != S) + sprint(str, "%M(R%d)(REG)", a, a->reg); + break; + + case D_REGREG: + sprint(str, "(R%d,R%d)", a->reg, (int)a->offset); + if(a->name != D_NONE || a->sym != S) + sprint(str, "%M(R%d)(REG)", a, a->reg); + break; + + case D_FREG: + sprint(str, "F%d", a->reg); + if(a->name != D_NONE || a->sym != S) + sprint(str, "%M(R%d)(REG)", a, a->reg); + break; + + case D_BRANCH: + if(a->branch == P || a->branch->loc == 0) { + if(a->sym != S) + sprint(str, "%s+%d(APC)", a->sym->name, a->offset); + else + sprint(str, "%d(APC)", a->offset); + } else + if(a->sym != S) + sprint(str, "%s+%d(APC)", a->sym->name, a->branch->loc); + else + sprint(str, "%d(APC)", a->branch->loc); + break; + + case D_FCONST: + snprint(str, sizeof(str), "$(%.17e)", a->dval); + break; + + case D_SCONST: + snprint(str, sizeof(str), "$\"%Y\"", a->sval); + break; + + // TODO(kaib): Add back +// case D_ADDR: +// a->type = a->index; +// a->index = D_NONE; +// snprint(str, sizeof(str), "$%D", a); +// a->index = a->type; +// a->type = D_ADDR; +// goto conv; + } +conv: + return fmtstrcpy(fp, str); +} + +int +Aconv(Fmt *fp) +{ + int i; + + i = va_arg(fp->args, int); + return fmtstrcpy(fp, anames[i]); +} + +char* strcond[16] = +{ + ".EQ", + ".NE", + ".HS", + ".LO", + ".MI", + ".PL", + ".VS", + ".VC", + ".HI", + ".LS", + ".GE", + ".LT", + ".GT", + ".LE", + "", + ".NV" +}; + +int +Cconv(Fmt *fp) +{ + char s[STRINGSZ]; + int c; + + c = va_arg(fp->args, int); + strcpy(s, strcond[c & C_SCOND]); + if(c & C_SBIT) + strcat(s, ".S"); + if(c & C_PBIT) + strcat(s, ".P"); + if(c & C_WBIT) + strcat(s, ".W"); + if(c & C_UBIT) /* ambiguous with FBIT */ + strcat(s, ".U"); + return fmtstrcpy(fp, s); +} + +int +Yconv(Fmt *fp) +{ + int i, c; + char str[STRINGSZ], *p, *a; + + a = va_arg(fp->args, char*); + p = str; + for(i=0; i<sconsize; i++) { + c = a[i] & 0xff; + if((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')) { + *p++ = c; + continue; + } + *p++ = '\\'; + switch(c) { + default: + if(c < 040 || c >= 0177) + break; /* not portable */ + p[-1] = c; + continue; + case 0: + *p++ = 'z'; + continue; + case '\\': + case '"': + *p++ = c; + continue; + case '\n': + *p++ = 'n'; + continue; + case '\t': + *p++ = 't'; + continue; + } + *p++ = (c>>6) + '0'; + *p++ = ((c>>3) & 7) + '0'; + *p++ = (c & 7) + '0'; + } + *p = 0; + return fmtstrcpy(fp, str); +} + +int +Rconv(Fmt *fp) +{ + int r; + char str[STRINGSZ]; + + r = va_arg(fp->args, int); + snprint(str, sizeof(str), "R%d", r); + return fmtstrcpy(fp, str); +} + +int +Mconv(Fmt *fp) +{ + char str[STRINGSZ]; + Addr *a; + + a = va_arg(fp->args, Addr*); + switch(a->name) { + default: + snprint(str, sizeof(str), "GOK-name(%d)", a->name); + break; + + case D_NONE: + snprint(str, sizeof(str), "%d", a->offset); + break; + + case D_EXTERN: + snprint(str, sizeof(str), "%S+%d(SB)", a->sym, a->offset); + break; + + case D_STATIC: + snprint(str, sizeof(str), "%S<>+%d(SB)", a->sym, a->offset); + break; + + case D_AUTO: + snprint(str, sizeof(str), "%S+%d(SP)", a->sym, a->offset); + break; + + case D_PARAM: + snprint(str, sizeof(str), "%S+%d(FP)", a->sym, a->offset); + break; + } + return fmtstrcpy(fp, str); +} diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h new file mode 100644 index 000000000..7a0070fc9 --- /dev/null +++ b/src/cmd/5g/opt.h @@ -0,0 +1,165 @@ +// Inferno utils/5c/gc.h +// http://code.google.com/p/inferno-os/source/browse/utils/5c/gc.h +// +// 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. + +#define Z N +#define Adr Addr + +#define D_HI D_NONE +#define D_LO D_NONE + +#define isregtype(t) ((t)>= D_AX && (t)<=D_R15) + +#define BLOAD(r) band(bnot(r->refbehind), r->refahead) +#define BSTORE(r) band(bnot(r->calbehind), r->calahead) +#define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z]) +#define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z]) + +#define CLOAD 5 +#define CREF 5 +#define CINF 1000 +#define LOOP 3 + +typedef struct Reg Reg; +typedef struct Rgn Rgn; + +struct Reg +{ + + Bits set; + Bits use1; + Bits use2; + + Bits refbehind; + Bits refahead; + Bits calbehind; + Bits calahead; + Bits regdiff; + Bits act; + + int32 regu; // register used bitmap + int32 rpo; // reverse post ordering + int32 active; + + uint16 loop; // x5 for every loop + uchar refset; // diagnostic generated + + Reg* p1; + Reg* p2; + Reg* p2link; + Reg* s1; + Reg* s2; + Reg* link; + Prog* prog; +}; +#define R ((Reg*)0) + +#define NRGN 600 +struct Rgn +{ + Reg* enter; + short cost; + short varno; + short regno; +}; + +EXTERN int32 exregoffset; // not set +EXTERN int32 exfregoffset; // not set +EXTERN Reg* firstr; +EXTERN Reg* lastr; +EXTERN Reg zreg; +EXTERN Reg* freer; +EXTERN Reg** rpo2r; +EXTERN Rgn region[NRGN]; +EXTERN Rgn* rgp; +EXTERN int nregion; +EXTERN int nvar; +EXTERN int32 regbits; +EXTERN int32 exregbits; +EXTERN Bits externs; +EXTERN Bits params; +EXTERN Bits consts; +EXTERN Bits addrs; +EXTERN Bits ovar; +EXTERN int change; +EXTERN int32 maxnr; +EXTERN int32* idom; + +EXTERN struct +{ + int32 ncvtreg; + int32 nspill; + int32 nreload; + int32 ndelmov; + int32 nvar; + int32 naddr; +} ostats; + +/* + * reg.c + */ +Reg* rega(void); +int rcmp(const void*, const void*); +void regopt(Prog*); +void addmove(Reg*, int, int, int); +Bits mkvar(Reg *r, Adr *a); +void prop(Reg*, Bits, Bits); +void loopit(Reg*, int32); +void synch(Reg*, Bits); +uint32 allreg(uint32, Rgn*); +void paint1(Reg*, int); +uint32 paint2(Reg*, int); +void paint3(Reg*, int, int32, int); +void addreg(Adr*, int); +void dumpit(char *str, Reg *r0); +int noreturn(Prog *p); + +/* + * peep.c + */ +void peep(void); +void excise(Reg*); +Reg* uniqp(Reg*); +Reg* uniqs(Reg*); +int regtyp(Adr*); +int anyvar(Adr*); +int subprop(Reg*); +int copyprop(Reg*); +int copy1(Adr*, Adr*, Reg*, int); +int copyu(Prog*, Adr*, Adr*); + +int copyas(Adr*, Adr*); +int copyau(Adr*, Adr*); +int copysub(Adr*, Adr*, Adr*, int); +int copysub1(Prog*, Adr*, Adr*, int); + +int32 RtoB(int); +int32 FtoB(int); +int BtoR(int32); +int BtoF(int32); diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c new file mode 100644 index 000000000..6cc93db12 --- /dev/null +++ b/src/cmd/5g/peep.c @@ -0,0 +1,1521 @@ +// Inferno utils/5c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/5g/peep.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" +#include "opt.h" + +int xtramodes(Reg*, Adr*); +int shiftprop(Reg *r); +void constprop(Adr *c1, Adr *v1, Reg *r); +void predicate(void); +int copyau1(Prog *p, Adr *v); +int isdconst(Addr *a); + +void +peep(void) +{ + Reg *r, *r1, *r2; + Prog *p, *p1; + int t; +/* + * complete R structure + */ + for(r=firstr; r!=R; r=r1) { + r1 = r->link; + if(r1 == R) + break; + p = r->prog->link; + while(p != r1->prog) + switch(p->as) { + default: + r2 = rega(); + r->link = r2; + r2->link = r1; + + r2->prog = p; + r2->p1 = r; + r->s1 = r2; + r2->s1 = r1; + r1->p1 = r2; + + r = r2; + + case ADATA: + case AGLOBL: + case ANAME: + case ASIGNAME: + p = p->link; + } + } +//dumpit("begin", firstr); + +loop1: + + t = 0; + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + case ASLL: + case ASRL: + case ASRA: + /* + * elide shift into D_SHIFT operand of subsequent instruction + */ +// if(shiftprop(r)) { +// excise(r); +// t++; +// break; +// } + break; + + case AMOVW: + case AMOVF: + case AMOVD: + if(regtyp(&p->from)) + if(p->from.type == p->to.type) + if(p->scond == C_SCOND_NONE) { + if(copyprop(r)) { + excise(r); + t++; + break; + } + if(subprop(r) && copyprop(r)) { + excise(r); + t++; + break; + } + } + break; + + if(p->scond == C_SCOND_NONE) + if(regtyp(&p->to)) + if(isdconst(&p->from)) { + constprop(&p->from, &p->to, r->s1); + } + break; + } + } + if(t) + goto loop1; + +return; + + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { +// case AEOR: +// /* +// * EOR -1,x,y => MVN x,y +// */ +// if(isdconst(&p->from) && p->from.offset == -1) { +// p->as = AMVN; +// p->from.type = D_REG; +// if(p->reg != NREG) +// p->from.reg = p->reg; +// else +// p->from.reg = p->to.reg; +// p->reg = NREG; +// } +// break; + + case AMOVH: + case AMOVHU: + case AMOVB: + case AMOVBU: + /* + * look for MOVB x,R; MOVB R,R + */ + if(p->to.type != D_REG) + break; + if(r1 == R) + break; + p1 = r1->prog; + if(p1->as != p->as) + break; + if(p1->from.type != D_REG || p1->from.reg != p->to.reg) + break; + if(p1->to.type != D_REG || p1->to.reg != p->to.reg) + break; + excise(r1); + break; + } + r1 = r->link; + } + +// for(r=firstr; r!=R; r=r->link) { +// p = r->prog; +// switch(p->as) { +// case AMOVW: +// case AMOVB: +// case AMOVBU: +// if(p->from.type == D_OREG && p->from.offset == 0) +// xtramodes(r, &p->from); +// else +// if(p->to.type == D_OREG && p->to.offset == 0) +// xtramodes(r, &p->to); +// else +// continue; +// break; +// case ACMP: +// /* +// * elide CMP $0,x if calculation of x can set condition codes +// */ +// if(isdconst(&p->from) || p->from.offset != 0) +// continue; +// r2 = r->s1; +// if(r2 == R) +// continue; +// t = r2->prog->as; +// switch(t) { +// default: +// continue; +// case ABEQ: +// case ABNE: +// case ABMI: +// case ABPL: +// break; +// case ABGE: +// t = ABPL; +// break; +// case ABLT: +// t = ABMI; +// break; +// case ABHI: +// t = ABNE; +// break; +// case ABLS: +// t = ABEQ; +// break; +// } +// r1 = r; +// do +// r1 = uniqp(r1); +// while (r1 != R && r1->prog->as == ANOP); +// if(r1 == R) +// continue; +// p1 = r1->prog; +// if(p1->to.type != D_REG) +// continue; +// if(p1->to.reg != p->reg) +// if(!(p1->as == AMOVW && p1->from.type == D_REG && p1->from.reg == p->reg)) +// continue; +// +// switch(p1->as) { +// default: +// continue; +// case AMOVW: +// if(p1->from.type != D_REG) +// continue; +// case AAND: +// case AEOR: +// case AORR: +// case ABIC: +// case AMVN: +// case ASUB: +// case ARSB: +// case AADD: +// case AADC: +// case ASBC: +// case ARSC: +// break; +// } +// p1->scond |= C_SBIT; +// r2->prog->as = t; +// excise(r); +// continue; +// } +// } + + predicate(); +} + +Reg* +uniqp(Reg *r) +{ + Reg *r1; + + r1 = r->p1; + if(r1 == R) { + r1 = r->p2; + if(r1 == R || r1->p2link != R) + return R; + } else + if(r->p2 != R) + return R; + return r1; +} + +Reg* +uniqs(Reg *r) +{ + Reg *r1; + + r1 = r->s1; + if(r1 == R) { + r1 = r->s2; + if(r1 == R) + return R; + } else + if(r->s2 != R) + return R; + return r1; +} + +int +regtyp(Adr *a) +{ + + if(a->type == D_REG) + return 1; + if(a->type == D_FREG) + return 1; + return 0; +} + +/* + * the idea is to substitute + * one register for another + * from one MOV to another + * MOV a, R0 + * ADD b, R0 / no use of R1 + * MOV R0, R1 + * would be converted to + * MOV a, R1 + * ADD b, R1 + * MOV R1, R0 + * hopefully, then the former or latter MOV + * will be eliminated by copy propagation. + */ +int +subprop(Reg *r0) +{ + Prog *p; + Adr *v1, *v2; + Reg *r; + int t; + + p = r0->prog; + v1 = &p->from; + if(!regtyp(v1)) + return 0; + v2 = &p->to; + if(!regtyp(v2)) + return 0; + for(r=uniqp(r0); r!=R; r=uniqp(r)) { + if(uniqs(r) == R) + break; + p = r->prog; + switch(p->as) { + case ABL: + return 0; + + case AMULLU: + case AMULA: + case AMVN: + return 0; + + case ACMN: + case AADD: + case ASUB: + case ASBC: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case AMULU: + case ADIV: + case ADIVU: + case AMOD: + case AMODU: + + case AADDD: + case AADDF: + case ASUBD: + case ASUBF: + case AMULD: + case AMULF: + case ADIVD: + case ADIVF: + if(p->to.type == v1->type) + if(p->to.reg == v1->reg) + if(p->scond == C_SCOND_NONE) { + if(p->reg == NREG) + p->reg = p->to.reg; + goto gotit; + } + break; + + case AMOVF: + case AMOVD: + case AMOVW: + if(p->to.type == v1->type) + if(p->to.reg == v1->reg) + if(p->scond == C_SCOND_NONE) + goto gotit; + break; + + case AMOVM: + t = 1<<v2->reg; + if((p->from.type == D_CONST && (p->from.offset&t)) || + (p->to.type == D_CONST && (p->to.offset&t))) + return 0; + break; + } + if(copyau(&p->from, v2) || + copyau1(p, v2) || + copyau(&p->to, v2)) + break; + if(copysub(&p->from, v1, v2, 0) || + copysub1(p, v1, v2, 0) || + copysub(&p->to, v1, v2, 0)) + break; + } + return 0; + +gotit: + copysub(&p->to, v1, v2, 1); + if(debug['P']) { + print("gotit: %D->%D\n%P", v1, v2, r->prog); + if(p->from.type == v2->type) + print(" excise"); + print("\n"); + } + for(r=uniqs(r); r!=r0; r=uniqs(r)) { + p = r->prog; + copysub(&p->from, v1, v2, 1); + copysub1(p, v1, v2, 1); + copysub(&p->to, v1, v2, 1); + if(debug['P']) + print("%P\n", r->prog); + } + t = v1->reg; + v1->reg = v2->reg; + v2->reg = t; + if(debug['P']) + print("%P last\n", r->prog); + return 1; +} + +/* + * The idea is to remove redundant copies. + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * use v2 return fail + * ----------------- + * v1->v2 F=0 + * (use v2 s/v2/v1/)* + * set v1 F=1 + * set v2 return success + */ +int +copyprop(Reg *r0) +{ + Prog *p; + Adr *v1, *v2; + Reg *r; + + p = r0->prog; + v1 = &p->from; + v2 = &p->to; + if(copyas(v1, v2)) + return 1; + for(r=firstr; r!=R; r=r->link) + r->active = 0; + return copy1(v1, v2, r0->s1, 0); +} + +int +copy1(Adr *v1, Adr *v2, Reg *r, int f) +{ + int t; + Prog *p; + + if(r->active) { + if(debug['P']) + print("act set; return 1\n"); + return 1; + } + r->active = 1; + if(debug['P']) + print("copy %D->%D f=%d\n", v1, v2, f); + for(; r != R; r = r->s1) { + p = r->prog; + if(debug['P']) + print("%P", p); + if(!f && uniqp(r) == R) { + f = 1; + if(debug['P']) + print("; merge; f=%d", f); + } + t = copyu(p, v2, A); + switch(t) { + case 2: /* rar, cant split */ + if(debug['P']) + print("; %Drar; return 0\n", v2); + return 0; + + case 3: /* set */ + if(debug['P']) + print("; %Dset; return 1\n", v2); + return 1; + + case 1: /* used, substitute */ + case 4: /* use and set */ + if(f) { + if(!debug['P']) + return 0; + if(t == 4) + print("; %Dused+set and f=%d; return 0\n", v2, f); + else + print("; %Dused and f=%d; return 0\n", v2, f); + return 0; + } + if(copyu(p, v2, v1)) { + if(debug['P']) + print("; sub fail; return 0\n"); + return 0; + } + if(debug['P']) + print("; sub%D/%D", v2, v1); + if(t == 4) { + if(debug['P']) + print("; %Dused+set; return 1\n", v2); + return 1; + } + break; + } + if(!f) { + t = copyu(p, v1, A); + if(!f && (t == 2 || t == 3 || t == 4)) { + f = 1; + if(debug['P']) + print("; %Dset and !f; f=%d", v1, f); + } + } + if(debug['P']) + print("\n"); + if(r->s2) + if(!copy1(v1, v2, r->s2, f)) + return 0; + } + return 1; +} + +/* + * The idea is to remove redundant constants. + * $c1->v1 + * ($c1->v2 s/$c1/v1)* + * set v1 return + * The v1->v2 should be eliminated by copy propagation. + */ +void +constprop(Adr *c1, Adr *v1, Reg *r) +{ + Prog *p; + + if(debug['P']) + print("constprop %D->%D\n", c1, v1); + for(; r != R; r = r->s1) { + p = r->prog; + if(debug['P']) + print("%P", p); + if(uniqp(r) == R) { + if(debug['P']) + print("; merge; return\n"); + return; + } + if(p->as == AMOVW && copyas(&p->from, c1)) { + if(debug['P']) + print("; sub%D/%D", &p->from, v1); + p->from = *v1; + } else if(copyu(p, v1, A) > 1) { + if(debug['P']) + print("; %Dset; return\n", v1); + return; + } + if(debug['P']) + print("\n"); + if(r->s2) + constprop(c1, v1, r->s2); + } +} + +/* + * ASLL x,y,w + * .. (not use w, not set x y w) + * AXXX w,a,b (a != w) + * .. (not use w) + * (set w) + * ----------- changed to + * .. + * AXXX (x<<y),a,b + * .. + */ +#define FAIL(msg) { if(debug['P']) print("\t%s; FAILURE\n", msg); return 0; } +int +shiftprop(Reg *r) +{ + Reg *r1; + Prog *p, *p1, *p2; + int n, o; + Adr a; + + p = r->prog; + if(p->to.type != D_REG) + FAIL("BOTCH: result not reg"); + n = p->to.reg; + a = zprog.from; + if(p->reg != NREG && p->reg != p->to.reg) { + a.type = D_REG; + a.reg = p->reg; + } + if(debug['P']) + print("shiftprop\n%P", p); + r1 = r; + for(;;) { + /* find first use of shift result; abort if shift operands or result are changed */ + r1 = uniqs(r1); + if(r1 == R) + FAIL("branch"); + if(uniqp(r1) == R) + FAIL("merge"); + p1 = r1->prog; + if(debug['P']) + print("\n%P", p1); + switch(copyu(p1, &p->to, A)) { + case 0: /* not used or set */ + if((p->from.type == D_REG && copyu(p1, &p->from, A) > 1) || + (a.type == D_REG && copyu(p1, &a, A) > 1)) + FAIL("args modified"); + continue; + case 3: /* set, not used */ + FAIL("BOTCH: noref"); + } + break; + } + /* check whether substitution can be done */ + switch(p1->as) { + default: + FAIL("non-dpi"); + case AAND: + case AEOR: + case AADD: + case AADC: + case AORR: + case ASUB: + case ASBC: + case ARSB: + case ARSC: + if(p1->reg == n || (p1->reg == NREG && p1->to.type == D_REG && p1->to.reg == n)) { + if(p1->from.type != D_REG) + FAIL("can't swap"); + p1->reg = p1->from.reg; + p1->from.reg = n; + switch(p1->as) { + case ASUB: + p1->as = ARSB; + break; + case ARSB: + p1->as = ASUB; + break; + case ASBC: + p1->as = ARSC; + break; + case ARSC: + p1->as = ASBC; + break; + } + if(debug['P']) + print("\t=>%P", p1); + } + case ABIC: + case ATST: + case ACMP: + case ACMN: + if(p1->reg == n) + FAIL("can't swap"); + if(p1->reg == NREG && p1->to.reg == n) + FAIL("shift result used twice"); +// case AMVN: + if(p1->from.type == D_SHIFT) + FAIL("shift result used in shift"); + if(p1->from.type != D_REG || p1->from.reg != n) + FAIL("BOTCH: where is it used?"); + break; + } + /* check whether shift result is used subsequently */ + p2 = p1; + if(p1->to.reg != n) + for (;;) { + r1 = uniqs(r1); + if(r1 == R) + FAIL("inconclusive"); + p1 = r1->prog; + if(debug['P']) + print("\n%P", p1); + switch(copyu(p1, &p->to, A)) { + case 0: /* not used or set */ + continue; + case 3: /* set, not used */ + break; + default:/* used */ + FAIL("reused"); + } + break; + } + + /* make the substitution */ + p2->from.type = D_SHIFT; + p2->from.reg = NREG; + o = p->reg; + if(o == NREG) + o = p->to.reg; + + switch(p->from.type){ + case D_CONST: + o |= (p->from.offset&0x1f)<<7; + break; + case D_REG: + o |= (1<<4) | (p->from.reg<<8); + break; + } + switch(p->as){ + case ASLL: + o |= 0<<5; + break; + case ASRL: + o |= 1<<5; + break; + case ASRA: + o |= 2<<5; + break; + } + p2->from.offset = o; + if(debug['P']) + print("\t=>%P\tSUCCEED\n", p2); + return 1; +} + +Reg* +findpre(Reg *r, Adr *v) +{ + Reg *r1; + + for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) { + if(uniqs(r1) != r) + return R; + switch(copyu(r1->prog, v, A)) { + case 1: /* used */ + case 2: /* read-alter-rewrite */ + return R; + case 3: /* set */ + case 4: /* set and used */ + return r1; + } + } + return R; +} + +Reg* +findinc(Reg *r, Reg *r2, Adr *v) +{ + Reg *r1; + Prog *p; + + + for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) { + if(uniqp(r1) != r) + return R; + switch(copyu(r1->prog, v, A)) { + case 0: /* not touched */ + continue; + case 4: /* set and used */ + p = r1->prog; + if(p->as == AADD) + if(isdconst(&p->from)) + if(p->from.offset > -4096 && p->from.offset < 4096) + return r1; + default: + return R; + } + } + return R; +} + +int +nochange(Reg *r, Reg *r2, Prog *p) +{ + Adr a[3]; + int i, n; + + if(r == r2) + return 1; + n = 0; + if(p->reg != NREG && p->reg != p->to.reg) { + a[n].type = D_REG; + a[n++].reg = p->reg; + } + switch(p->from.type) { + case D_SHIFT: + a[n].type = D_REG; + a[n++].reg = p->from.offset&0xf; + case D_REG: + a[n].type = D_REG; + a[n++].reg = p->from.reg; + } + if(n == 0) + return 1; + for(; r!=R && r!=r2; r=uniqs(r)) { + p = r->prog; + for(i=0; i<n; i++) + if(copyu(p, &a[i], A) > 1) + return 0; + } + return 1; +} + +int +findu1(Reg *r, Adr *v) +{ + for(; r != R; r = r->s1) { + if(r->active) + return 0; + r->active = 1; + switch(copyu(r->prog, v, A)) { + case 1: /* used */ + case 2: /* read-alter-rewrite */ + case 4: /* set and used */ + return 1; + case 3: /* set */ + return 0; + } + if(r->s2) + if (findu1(r->s2, v)) + return 1; + } + return 0; +} + +int +finduse(Reg *r, Adr *v) +{ + Reg *r1; + + for(r1=firstr; r1!=R; r1=r1->link) + r1->active = 0; + return findu1(r, v); +} + +int +xtramodes(Reg *r, Adr *a) +{ + Reg *r1, *r2, *r3; + Prog *p, *p1; + Adr v; + + p = r->prog; + if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */ + return 0; + v = *a; + v.type = D_REG; + r1 = findpre(r, &v); + if(r1 != R) { + p1 = r1->prog; + if(p1->to.type == D_REG && p1->to.reg == v.reg) + switch(p1->as) { + case AADD: + if(p1->from.type == D_REG || + (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 && + (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) || + (p1->from.type == D_CONST && + p1->from.offset > -4096 && p1->from.offset < 4096)) + if(nochange(uniqs(r1), r, p1)) { + if(a != &p->from || v.reg != p->to.reg) + if (finduse(r->s1, &v)) { + if(p1->reg == NREG || p1->reg == v.reg) + /* pre-indexing */ + p->scond |= C_WBIT; + else return 0; + } + switch (p1->from.type) { + case D_REG: + /* register offset */ + a->type = D_SHIFT; + a->offset = p1->from.reg; + break; + case D_SHIFT: + /* scaled register offset */ + a->type = D_SHIFT; + case D_CONST: + /* immediate offset */ + a->offset = p1->from.offset; + break; + } + if(p1->reg != NREG) + a->reg = p1->reg; + excise(r1); + return 1; + } + break; + case AMOVW: + if(p1->from.type == D_REG) + if((r2 = findinc(r1, r, &p1->from)) != R) { + for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3)) + ; + if(r3 == r) { + /* post-indexing */ + p1 = r2->prog; + a->reg = p1->to.reg; + a->offset = p1->from.offset; + p->scond |= C_PBIT; + if(!finduse(r, &r1->prog->to)) + excise(r1); + excise(r2); + return 1; + } + } + break; + } + } + if(a != &p->from || a->reg != p->to.reg) + if((r1 = findinc(r, R, &v)) != R) { + /* post-indexing */ + p1 = r1->prog; + a->offset = p1->from.offset; + p->scond |= C_PBIT; + excise(r1); + return 1; + } + return 0; +} + +/* + * return + * 1 if v only used (and substitute), + * 2 if read-alter-rewrite + * 3 if set + * 4 if set and used + * 0 otherwise (not touched) + */ +int +copyu(Prog *p, Adr *v, Adr *s) +{ + switch(p->as) { + + default: + print("copyu: cant find %A\n", p->as); + return 2; + + case AMOVM: + if(v->type != D_REG) + return 0; + if(p->from.type == D_CONST) { /* read reglist, read/rar */ + if(s != A) { + if(p->from.offset&(1<<v->reg)) + return 1; + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) { + if(p->scond&C_WBIT) + return 2; + return 1; + } + if(p->from.offset&(1<<v->reg)) + return 1; + } else { /* read/rar, write reglist */ + if(s != A) { + if(p->to.offset&(1<<v->reg)) + return 1; + if(copysub(&p->from, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->from, v)) { + if(p->scond&C_WBIT) + return 2; + if(p->to.offset&(1<<v->reg)) + return 4; + return 1; + } + if(p->to.offset&(1<<v->reg)) + return 3; + } + return 0; + + case ANOP: /* read,, write */ + case AMOVW: + case AMOVF: + case AMOVD: + case AMOVH: + case AMOVHU: + case AMOVB: + case AMOVBU: + case AMOVFW: + case AMOVWF: + case AMOVDW: + case AMOVWD: + case AMOVFD: + case AMOVDF: + if(p->scond&(C_WBIT|C_PBIT)) + if(v->type == D_REG) { + if(p->from.type == D_OREG || p->from.type == D_SHIFT) { + if(p->from.reg == v->reg) + return 2; + } else { + if(p->to.reg == v->reg) + return 2; + } + } + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + if(!copyas(&p->to, v)) + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyas(&p->to, v)) { + if(p->scond != C_SCOND_NONE) + return 2; + if(copyau(&p->from, v)) + return 4; + return 3; + } + if(copyau(&p->from, v)) + return 1; + if(copyau(&p->to, v)) + return 1; + return 0; + + case AMULLU: /* read, read, write, write */ + case AMULA: + case AMVN: + return 2; + + case AADD: /* read, read, write */ + case AADC: + case ASUB: + case ASBC: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case AMULU: + case ADIV: + case ADIVU: + case AMOD: + case AMODU: + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + + case ACMPF: /* read, read, */ + case ACMPD: + case ACMP: + case ACMN: + case ACASE: + case ATST: /* read,, */ + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + if(copysub1(p, v, s, 1)) + return 1; + if(!copyas(&p->to, v)) + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyas(&p->to, v)) { + if(p->scond != C_SCOND_NONE) + return 2; + if(p->reg == NREG) + p->reg = p->to.reg; + if(copyau(&p->from, v)) + return 4; + if(copyau1(p, v)) + return 4; + return 3; + } + if(copyau(&p->from, v)) + return 1; + if(copyau1(p, v)) + return 1; + if(copyau(&p->to, v)) + return 1; + return 0; + + case ABEQ: /* read, read */ + case ABNE: + case ABCS: + case ABHS: + case ABCC: + case ABLO: + case ABMI: + case ABPL: + case ABVS: + case ABVC: + case ABHI: + case ABLS: + case ABGE: + case ABLT: + case ABGT: + case ABLE: + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + return copysub1(p, v, s, 1); + } + if(copyau(&p->from, v)) + return 1; + if(copyau1(p, v)) + return 1; + return 0; + + case AB: /* funny */ + if(s != A) { + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) + return 1; + return 0; + + case ARET: /* funny */ + if(v->type == D_REG) + if(v->reg == REGRET) + return 2; + if(v->type == D_FREG) + if(v->reg == FREGRET) + return 2; + + case ABL: /* funny */ + if(v->type == D_REG) { + if(v->reg <= REGEXT && v->reg > exregoffset) + return 2; + if(v->reg == (uchar)REGARG) + return 2; + } + if(v->type == D_FREG) + if(v->reg <= FREGEXT && v->reg > exfregoffset) + return 2; + + if(s != A) { + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) + return 4; + return 3; + + case ATEXT: /* funny */ + if(v->type == D_REG) + if(v->reg == (uchar)REGARG) + return 3; + return 0; + } + return 0; +} + +/* + * direct reference, + * could be set/use depending on + * semantics + */ +int +copyas(Adr *a, Adr *v) +{ + + if(regtyp(v)) { + if(a->type == v->type) + if(a->reg == v->reg) + return 1; + } else + if(v->type == D_CONST) { /* for constprop */ + if(a->type == v->type) + if(a->name == v->name) + if(a->sym == v->sym) + if(a->reg == v->reg) + if(a->offset == v->offset) + return 1; + } + return 0; +} + +/* + * either direct or indirect + */ +int +copyau(Adr *a, Adr *v) +{ + + if(copyas(a, v)) + return 1; + if(v->type == D_REG) { + if(a->type == D_CONST && a->reg != NREG) { + if(a->reg == v->reg) + return 1; + } else + if(a->type == D_OREG) { + if(a->reg == v->reg) + return 1; + } else + if(a->type == D_REGREG) { + if(a->reg == v->reg) + return 1; + if(a->offset == v->reg) + return 1; + } else + if(a->type == D_SHIFT) { + if((a->offset&0xf) == v->reg) + return 1; + if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) + return 1; + } + } + return 0; +} + +/* + * compare v to the center + * register in p (p->reg) + * the trick is that this + * register might be D_REG + * D_FREG. there are basically + * two cases, + * ADD r,r,r + * CMP r,r, + */ +int +copyau1(Prog *p, Adr *v) +{ + + if(regtyp(v)) + if(p->reg == v->reg) { + if(p->to.type != D_NONE) { + if(v->type == p->to.type) + return 1; + return 0; + } + if(p->from.type != D_NONE) { + if(v->type == p->from.type) + return 1; + return 0; + } + print("copyau1: cant tell %P\n", p); + } + return 0; +} + +/* + * substitute s for v in a + * return failure to substitute + */ +int +copysub(Adr *a, Adr *v, Adr *s, int f) +{ + + if(f) + if(copyau(a, v)) { + if(a->type == D_SHIFT) { + if((a->offset&0xf) == v->reg) + a->offset = (a->offset&~0xf)|s->reg; + if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) + a->offset = (a->offset&~(0xf<<8))|(s->reg<<8); + } else + if(a->type == D_REGREG) { + if(a->offset == v->reg) + a->offset = s->reg; + if(a->reg == v->reg) + a->reg = s->reg; + } else + a->reg = s->reg; + } + return 0; +} + +int +copysub1(Prog *p1, Adr *v, Adr *s, int f) +{ + + if(f) + if(copyau1(p1, v)) + p1->reg = s->reg; + return 0; +} + +struct { + int opcode; + int notopcode; + int scond; + int notscond; +} predinfo[] = { + { ABEQ, ABNE, 0x0, 0x1, }, + { ABNE, ABEQ, 0x1, 0x0, }, + { ABCS, ABCC, 0x2, 0x3, }, + { ABHS, ABLO, 0x2, 0x3, }, + { ABCC, ABCS, 0x3, 0x2, }, + { ABLO, ABHS, 0x3, 0x2, }, + { ABMI, ABPL, 0x4, 0x5, }, + { ABPL, ABMI, 0x5, 0x4, }, + { ABVS, ABVC, 0x6, 0x7, }, + { ABVC, ABVS, 0x7, 0x6, }, + { ABHI, ABLS, 0x8, 0x9, }, + { ABLS, ABHI, 0x9, 0x8, }, + { ABGE, ABLT, 0xA, 0xB, }, + { ABLT, ABGE, 0xB, 0xA, }, + { ABGT, ABLE, 0xC, 0xD, }, + { ABLE, ABGT, 0xD, 0xC, }, +}; + +typedef struct { + Reg *start; + Reg *last; + Reg *end; + int len; +} Joininfo; + +enum { + Join, + Split, + End, + Branch, + Setcond, + Toolong +}; + +enum { + Falsecond, + Truecond, + Delbranch, + Keepbranch +}; + +int +isbranch(Prog *p) +{ + return (ABEQ <= p->as) && (p->as <= ABLE); +} + +int +predicable(Prog *p) +{ + switch(p->as) { + case ANOP: + case AXXX: + case ADATA: + case AGLOBL: + case AGOK: + case AHISTORY: + case ANAME: + case ASIGNAME: + case ATEXT: + case AWORD: + case ABCASE: + case ACASE: + return 0; + } + if(isbranch(p)) + return 0; + return 1; +} + +/* + * Depends on an analysis of the encodings performed by 5l. + * These seem to be all of the opcodes that lead to the "S" bit + * being set in the instruction encodings. + * + * C_SBIT may also have been set explicitly in p->scond. + */ +int +modifiescpsr(Prog *p) +{ + switch(p->as) { + case AMULLU: + case AMULA: + case AMULU: + case ADIVU: + + case ATEQ: + case ACMN: + case ATST: + case ACMP: + case AMUL: + case ADIV: + case AMOD: + case AMODU: + case ABL: + return 1; + } + if(p->scond & C_SBIT) + return 1; + return 0; +} + +/* + * Find the maximal chain of instructions starting with r which could + * be executed conditionally + */ +int +joinsplit(Reg *r, Joininfo *j) +{ + j->start = r; + j->last = r; + j->len = 0; + do { + if (r->p2 && (r->p1 || r->p2->p2link)) { + j->end = r; + return Join; + } + if (r->s1 && r->s2) { + j->end = r; + return Split; + } + j->last = r; + if (r->prog->as != ANOP) + j->len++; + if (!r->s1 && !r->s2) { + j->end = r->link; + return End; + } + if (r->s2) { + j->end = r->s2; + return Branch; + } + if (modifiescpsr(r->prog)) { + j->end = r->s1; + return Setcond; + } + r = r->s1; + } while (j->len < 4); + j->end = r; + return Toolong; +} + +Reg* +successor(Reg *r) +{ + if(r->s1) + return r->s1; + else + return r->s2; +} + +void +applypred(Reg *rstart, Joininfo *j, int cond, int branch) +{ + int pred; + Reg *r; + + if(j->len == 0) + return; + if(cond == Truecond) + pred = predinfo[rstart->prog->as - ABEQ].scond; + else + pred = predinfo[rstart->prog->as - ABEQ].notscond; + + for(r = j->start;; r = successor(r)) { + if(r->prog->as == AB) { + if(r != j->last || branch == Delbranch) + excise(r); + else { + if(cond == Truecond) + r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode; + else + r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode; + } + } + else + if(predicable(r->prog)) + r->prog->scond = (r->prog->scond&~C_SCOND)|pred; + if(r->s1 != r->link) { + r->s1 = r->link; + r->link->p1 = r; + } + if(r == j->last) + break; + } +} + +void +predicate(void) +{ + Reg *r; + int t1, t2; + Joininfo j1, j2; + + for(r=firstr; r!=R; r=r->link) { + if (isbranch(r->prog)) { + t1 = joinsplit(r->s1, &j1); + t2 = joinsplit(r->s2, &j2); + if(j1.last->link != j2.start) + continue; + if(j1.end == j2.end) + if((t1 == Branch && (t2 == Join || t2 == Setcond)) || + (t2 == Join && (t1 == Join || t1 == Setcond))) { + applypred(r, &j1, Falsecond, Delbranch); + applypred(r, &j2, Truecond, Delbranch); + excise(r); + continue; + } + if(t1 == End || t1 == Branch) { + applypred(r, &j1, Falsecond, Keepbranch); + excise(r); + continue; + } + } + } +} + +int +isdconst(Addr *a) +{ + if(a->type == D_CONST && a->reg == NREG) + return 1; + return 0; +} diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c new file mode 100644 index 000000000..2d2a6d01a --- /dev/null +++ b/src/cmd/5g/reg.c @@ -0,0 +1,1607 @@ +// Inferno utils/5c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/5g/reg.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" +#include "opt.h" + +#define NREGVAR 24 +#define REGBITS ((uint32)0xffffff) +#define P2R(p) (Reg*)(p->reg) + + void addsplits(void); + int noreturn(Prog *p); +static int first = 0; + +Reg* +rega(void) +{ + Reg *r; + + r = freer; + if(r == R) { + r = mal(sizeof(*r)); + } else + freer = r->link; + + *r = zreg; + return r; +} + +int +rcmp(const void *a1, const void *a2) +{ + Rgn *p1, *p2; + int c1, c2; + + p1 = (Rgn*)a1; + p2 = (Rgn*)a2; + c1 = p2->cost; + c2 = p1->cost; + if(c1 -= c2) + return c1; + return p2->varno - p1->varno; +} + +static void +setoutvar(void) +{ + Type *t; + Node *n; + Addr a; + Iter save; + Bits bit; + int z; + + t = structfirst(&save, getoutarg(curfn->type)); + while(t != T) { + n = nodarg(t, 1); + a = zprog.from; + naddr(n, &a, 0); + bit = mkvar(R, &a); + for(z=0; z<BITS; z++) + ovar.b[z] |= bit.b[z]; + t = structnext(&save); + } +//if(bany(b)) +//print("ovars = %Q\n", &ovar); +} + +void +excise(Reg *r) +{ + Prog *p; + + p = r->prog; + p->as = ANOP; + p->scond = zprog.scond; + p->from = zprog.from; + p->to = zprog.to; + p->reg = zprog.reg; +} + +static void +setaddrs(Bits bit) +{ + int i, n; + Var *v; + Sym *s; + + while(bany(&bit)) { + // convert each bit to a variable + i = bnum(bit); + s = var[i].sym; + n = var[i].name; + bit.b[i/32] &= ~(1L<<(i%32)); + + // disable all pieces of that variable + for(i=0; i<nvar; i++) { + v = var+i; + if(v->sym == s && v->name == n) + v->addr = 2; + } + } +} + +static char* regname[] = { + ".R0", + ".R1", + ".R2", + ".R3", + ".R4", + ".R5", + ".R6", + ".R7", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".F0", + ".F1", + ".F2", + ".F3", + ".F4", + ".F5", + ".F6", + ".F7", +}; + +void +regopt(Prog *firstp) +{ + Reg *r, *r1; + Prog *p; + int i, z, nr; + uint32 vreg; + Bits bit; + + if(first == 0) { + fmtinstall('Q', Qconv); + } + + first++; + if(debug['K']) { + if(first != 13) + return; +// debug['R'] = 2; +// debug['P'] = 2; + print("optimizing %S\n", curfn->nname->sym); + } + + // count instructions + nr = 0; + for(p=firstp; p!=P; p=p->link) + nr++; + + // if too big dont bother + if(nr >= 10000) { +// print("********** %S is too big (%d)\n", curfn->nname->sym, nr); + return; + } + + r1 = R; + firstr = R; + lastr = R; + + /* + * control flow is more complicated in generated go code + * than in generated c code. define pseudo-variables for + * registers, so we have complete register usage information. + */ + nvar = NREGVAR; + memset(var, 0, NREGVAR*sizeof var[0]); + for(i=0; i<NREGVAR; i++) + var[i].sym = lookup(regname[i]); + + regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC); + for(z=0; z<BITS; z++) { + externs.b[z] = 0; + params.b[z] = 0; + consts.b[z] = 0; + addrs.b[z] = 0; + ovar.b[z] = 0; + } + + // build list of return variables + setoutvar(); + + /* + * pass 1 + * build aux data structure + * allocate pcs + * find use and set of variables + */ + nr = 0; + for(p=firstp; p != P; p = p->link) { + switch(p->as) { + case ADATA: + case AGLOBL: + case ANAME: + case ASIGNAME: + continue; + } + r = rega(); + nr++; + if(firstr == R) { + firstr = r; + lastr = r; + } else { + lastr->link = r; + r->p1 = lastr; + lastr->s1 = r; + lastr = r; + } + r->prog = p; + p->regp = r; + + r1 = r->p1; + if(r1 != R) { + switch(r1->prog->as) { + case ARET: + case AB: + case ARFE: + r->p1 = R; + r1->s1 = R; + } + } + + /* + * left side always read + */ + bit = mkvar(r, &p->from); + for(z=0; z<BITS; z++) + r->use1.b[z] |= bit.b[z]; + + /* + * middle always read when present + */ + if(p->reg != NREG) { + if(p->from.type != D_FREG) + r->use1.b[0] |= RtoB(p->reg); + else + r->use1.b[0] |= FtoB(p->reg); + } + + /* + * right side depends on opcode + */ + bit = mkvar(r, &p->to); + if(bany(&bit)) + switch(p->as) { + default: + yyerror("reg: unknown op: %A", p->as); + break; + + /* + * right side read + */ + case ATST: + case ATEQ: + case ACMP: + case ACMN: + case ACMPD: + case ACMPF: + rightread: + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; + break; + + /* + * right side read or read+write, depending on middle + * ADD x, z => z += x + * ADD x, y, z => z = x + y + */ + case AADD: + case AAND: + case AEOR: + case ASUB: + case ARSB: + case AADC: + case ASBC: + case ARSC: + case AORR: + case ABIC: + case ASLL: + case ASRL: + case ASRA: + case AMUL: + case AMULU: + case ADIV: + case AMOD: + case AMODU: + case ADIVU: + if(p->reg != NREG) + goto rightread; + // fall through + + /* + * right side read+write + */ + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + case AMULA: + case AMULAL: + case AMULALU: + for(z=0; z<BITS; z++) { + r->use2.b[z] |= bit.b[z]; + r->set.b[z] |= bit.b[z]; + } + break; + + /* + * right side write + */ + case ANOP: + case AMOVB: + case AMOVBU: + case AMOVD: + case AMOVDF: + case AMOVDW: + case AMOVF: + case AMOVFW: + case AMOVH: + case AMOVHU: + case AMOVW: + case AMOVWD: + case AMOVWF: + case AMVN: + case AMULL: + case AMULLU: + if((p->scond & C_SCOND) != C_SCOND_NONE) + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; + for(z=0; z<BITS; z++) + r->set.b[z] |= bit.b[z]; + break; + + /* + * funny + */ + case ABL: + setaddrs(bit); + break; + } + + if(p->as == AMOVM) { + z = p->to.offset; + if(p->from.type == D_CONST) + z = p->from.offset; + for(i=0; z; i++) { + if(z&1) + regbits |= RtoB(i); + z >>= 1; + } + } + } + if(firstr == R) + return; + + for(i=0; i<nvar; i++) { + Var *v = var+i; + if(v->addr) { + bit = blsh(i); + for(z=0; z<BITS; z++) + addrs.b[z] |= bit.b[z]; + } + +// print("bit=%2d addr=%d et=%-6E w=%-2d s=%S + %lld\n", +// i, v->addr, v->etype, v->width, v->sym, v->offset); + } + + /* + * pass 2 + * turn branch references to pointers + * build back pointers + */ + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(p->to.type == D_BRANCH) { + if(p->to.branch == P) + fatal("pnil %P", p); + r1 = p->to.branch->regp; + if(r1 == R) + fatal("rnil %P", p); + if(r1 == r) { + //fatal("ref to self %P", p); + continue; + } + r->s2 = r1; + r->p2link = r1->p2; + r1->p2 = r; + } + } + if(debug['R']) { + p = firstr->prog; + print("\n%L %D\n", p->lineno, &p->from); + print(" addr = %Q\n", addrs); + } + + /* + * pass 2.5 + * find looping structure + */ + for(r = firstr; r != R; r = r->link) + r->active = 0; + change = 0; + loopit(firstr, nr); + + /* + * pass 3 + * iterate propagating usage + * back until flow graph is complete + */ +loop1: + change = 0; + for(r = firstr; r != R; r = r->link) + r->active = 0; + for(r = firstr; r != R; r = r->link) + if(r->prog->as == ARET) + prop(r, zbits, zbits); +loop11: + /* pick up unreachable code */ + i = 0; + for(r = firstr; r != R; r = r1) { + r1 = r->link; + if(r1 && r1->active && !r->active) { + prop(r, zbits, zbits); + i = 1; + } + } + if(i) + goto loop11; + if(change) + goto loop1; + + + /* + * pass 4 + * iterate propagating register/variable synchrony + * forward until graph is complete + */ +loop2: + change = 0; + for(r = firstr; r != R; r = r->link) + r->active = 0; + synch(firstr, zbits); + if(change) + goto loop2; + + addsplits(); + + if(debug['R'] > 1) { + print("\nprop structure:\n"); + for(r = firstr; r != R; r = r->link) { + print("%d:%P", r->loop, r->prog); + for(z=0; z<BITS; z++) { + bit.b[z] = r->set.b[z] | + r->refahead.b[z] | r->calahead.b[z] | + r->refbehind.b[z] | r->calbehind.b[z] | + r->use1.b[z] | r->use2.b[z]; + bit.b[z] &= ~addrs.b[z]; + } + + if(bany(&bit)) { + print("\t"); + if(bany(&r->use1)) + print(" u1=%Q", r->use1); + if(bany(&r->use2)) + print(" u2=%Q", r->use2); + if(bany(&r->set)) + print(" st=%Q", r->set); + if(bany(&r->refahead)) + print(" ra=%Q", r->refahead); + if(bany(&r->calahead)) + print(" ca=%Q", r->calahead); + if(bany(&r->refbehind)) + print(" rb=%Q", r->refbehind); + if(bany(&r->calbehind)) + print(" cb=%Q", r->calbehind); + } + print("\n"); + } + } + + /* + * pass 4.5 + * move register pseudo-variables into regu. + */ + for(r = firstr; r != R; r = r->link) { + r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; + + r->set.b[0] &= ~REGBITS; + r->use1.b[0] &= ~REGBITS; + r->use2.b[0] &= ~REGBITS; + r->refbehind.b[0] &= ~REGBITS; + r->refahead.b[0] &= ~REGBITS; + r->calbehind.b[0] &= ~REGBITS; + r->calahead.b[0] &= ~REGBITS; + r->regdiff.b[0] &= ~REGBITS; + r->act.b[0] &= ~REGBITS; + } + + /* + * pass 5 + * isolate regions + * calculate costs (paint1) + */ + r = firstr; + if(r) { + for(z=0; z<BITS; z++) + bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & + ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); + if(bany(&bit) & !r->refset) { + // should never happen - all variables are preset + if(debug['w']) + print("%L: used and not set: %Q\n", r->prog->lineno, bit); + r->refset = 1; + } + } + + for(r = firstr; r != R; r = r->link) + r->act = zbits; + rgp = region; + nregion = 0; + for(r = firstr; r != R; r = r->link) { + for(z=0; z<BITS; z++) + bit.b[z] = r->set.b[z] & + ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); + if(bany(&bit) && !r->refset) { + if(debug['w']) + print("%L: set and not used: %Q\n", r->prog->lineno, bit); + r->refset = 1; + excise(r); + } + for(z=0; z<BITS; z++) + bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); + while(bany(&bit)) { + i = bnum(bit); + rgp->enter = r; + rgp->varno = i; + change = 0; + if(debug['R'] > 1) + print("\n"); + paint1(r, i); + bit.b[i/32] &= ~(1L<<(i%32)); + if(change <= 0) { + if(debug['R']) + print("%L $%d: %Q\n", + r->prog->lineno, change, blsh(i)); + continue; + } + rgp->cost = change; + nregion++; + if(nregion >= NRGN) { + if(debug['R'] > 1) + print("too many regions\n"); + goto brk; + } + rgp++; + } + } +brk: + qsort(region, nregion, sizeof(region[0]), rcmp); + + /* + * pass 6 + * determine used registers (paint2) + * replace code (paint3) + */ + rgp = region; + for(i=0; i<nregion; i++) { + bit = blsh(rgp->varno); + vreg = paint2(rgp->enter, rgp->varno); + vreg = allreg(vreg, rgp); + if(debug['R']) { + if(rgp->regno >= NREG) + print("%L $%d F%d: %Q\n", + rgp->enter->prog->lineno, + rgp->cost, + rgp->regno-NREG, + bit); + else + print("%L $%d R%d: %Q\n", + rgp->enter->prog->lineno, + rgp->cost, + rgp->regno, + bit); + } + if(rgp->regno != 0) + paint3(rgp->enter, rgp->varno, vreg, rgp->regno); + rgp++; + } + /* + * pass 7 + * peep-hole on basic block + */ + if(!debug['R'] || debug['P']) { + peep(); + } + + /* + * last pass + * eliminate nops + * free aux structures + * adjust the stack pointer + * MOVW.W R1,-12(R13) <<- start + * MOVW R0,R1 + * MOVW R1,8(R13) + * MOVW $0,R1 + * MOVW R1,4(R13) + * BL ,runtime.newproc+0(SB) + * MOVW &ft+-32(SP),R7 <<- adjust + * MOVW &j+-40(SP),R6 <<- adjust + * MOVW autotmp_0003+-24(SP),R5 <<- adjust + * MOVW $12(R13),R13 <<- finish + */ + vreg = 0; + for(p = firstp; p != P; p = p->link) { + while(p->link != P && p->link->as == ANOP) + p->link = p->link->link; + if(p->to.type == D_BRANCH) + while(p->to.branch != P && p->to.branch->as == ANOP) + p->to.branch = p->to.branch->link; + if(p->as == AMOVW && p->to.reg == 13) { + if(p->scond & C_WBIT) { + vreg = -p->to.offset; // in adjust region +// print("%P adjusting %d\n", p, vreg); + continue; + } + if(p->from.type == D_CONST && p->to.type == D_REG) { + if(p->from.offset != vreg) + print("in and out different\n"); +// print("%P finish %d\n", p, vreg); + vreg = 0; // done adjust region + continue; + } + +// print("%P %d %d from type\n", p, p->from.type, D_CONST); +// print("%P %d %d to type\n\n", p, p->to.type, D_REG); + } + + if(p->as == AMOVW && vreg != 0) { + if(p->from.sym != S) + if(p->from.name == D_AUTO || p->from.name == D_PARAM) { + p->from.offset += vreg; +// print("%P adjusting from %d %d\n", p, vreg, p->from.type); + } + if(p->to.sym != S) + if(p->to.name == D_AUTO || p->to.name == D_PARAM) { + p->to.offset += vreg; +// print("%P adjusting to %d %d\n", p, vreg, p->from.type); + } + } + } + if(r1 != R) { + r1->link = freer; + freer = firstr; + } + +} + +void +addsplits(void) +{ + Reg *r, *r1; + int z, i; + Bits bit; + + for(r = firstr; r != R; r = r->link) { + if(r->loop > 1) + continue; + if(r->prog->as == ABL) + continue; + for(r1 = r->p2; r1 != R; r1 = r1->p2link) { + if(r1->loop <= 1) + continue; + for(z=0; z<BITS; z++) + bit.b[z] = r1->calbehind.b[z] & + (r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) & + ~(r->calahead.b[z] & addrs.b[z]); + while(bany(&bit)) { + i = bnum(bit); + bit.b[i/32] &= ~(1L << (i%32)); + } + } + } +} + +/* + * add mov b,rn + * just after r + */ +void +addmove(Reg *r, int bn, int rn, int f) +{ + Prog *p, *p1, *p2; + Adr *a; + Var *v; + + p1 = mal(sizeof(*p1)); + *p1 = zprog; + p = r->prog; + + // If there's a stack fixup coming (after BL newproc or BL deferproc), + // delay the load until after the fixup. + p2 = p->link; + if(p2 && p2->as == AMOVW && p2->from.type == D_CONST && p2->from.reg == REGSP && p2->to.reg == REGSP && p2->to.type == D_REG) + p = p2; + + p1->link = p->link; + p->link = p1; + p1->lineno = p->lineno; + + v = var + bn; + + a = &p1->to; + a->sym = v->sym; + a->name = v->name; + a->node = v->node; + a->offset = v->offset; + a->etype = v->etype; + a->type = D_OREG; + if(a->etype == TARRAY || a->sym == S) + a->type = D_CONST; + + if(v->addr) + fatal("addmove: shouldnt be doing this %A\n", a); + + switch(v->etype) { + default: + print("What is this %E\n", v->etype); + + case TINT8: + p1->as = AMOVB; + break; + case TBOOL: + case TUINT8: +//print("movbu %E %d %S\n", v->etype, bn, v->sym); + p1->as = AMOVBU; + break; + case TINT16: + p1->as = AMOVH; + break; + case TUINT16: + p1->as = AMOVHU; + break; + case TINT32: + case TUINT32: + case TPTR32: + p1->as = AMOVW; + break; + case TFLOAT32: + p1->as = AMOVF; + break; + case TFLOAT64: + p1->as = AMOVD; + break; + } + + p1->from.type = D_REG; + p1->from.reg = rn; + if(rn >= NREG) { + p1->from.type = D_FREG; + p1->from.reg = rn-NREG; + } + if(!f) { + p1->from = *a; + *a = zprog.from; + a->type = D_REG; + a->reg = rn; + if(rn >= NREG) { + a->type = D_FREG; + a->reg = rn-NREG; + } + if(v->etype == TUINT8 || v->etype == TBOOL) + p1->as = AMOVBU; + if(v->etype == TUINT16) + p1->as = AMOVHU; + } + if(debug['R']) + print("%P\t.a%P\n", p, p1); +} + +static int +overlap(int32 o1, int w1, int32 o2, int w2) +{ + int32 t1, t2; + + t1 = o1+w1; + t2 = o2+w2; + + if(!(t1 > o2 && t2 > o1)) + return 0; + + return 1; +} + +Bits +mkvar(Reg *r, Adr *a) +{ + Var *v; + int i, t, n, et, z, w, flag; + int32 o; + Bits bit; + Sym *s; + + // mark registers used + t = a->type; + n = D_NONE; + + flag = 0; + switch(t) { + default: + print("type %d %d %D\n", t, a->name, a); + goto none; + + case D_NONE: + case D_FCONST: + case D_BRANCH: + break; + + case D_CONST: + flag = 1; + goto onereg; + + case D_REGREG: + bit = zbits; + if(a->offset != NREG) + bit.b[0] |= RtoB(a->offset); + if(a->reg != NREG) + bit.b[0] |= RtoB(a->reg); + return bit; + + case D_REG: + case D_SHIFT: + onereg: + if(a->reg != NREG) { + bit = zbits; + bit.b[0] = RtoB(a->reg); + return bit; + } + break; + + case D_OREG: + if(a->reg != NREG) { + if(a == &r->prog->from) + r->use1.b[0] |= RtoB(a->reg); + else + r->use2.b[0] |= RtoB(a->reg); + if(r->prog->scond & (C_PBIT|C_WBIT)) + r->set.b[0] |= RtoB(a->reg); + } + break; + + case D_FREG: + if(a->reg != NREG) { + bit = zbits; + bit.b[0] = FtoB(a->reg); + return bit; + } + break; + } + + switch(a->name) { + default: + goto none; + + case D_EXTERN: + case D_STATIC: + case D_AUTO: + case D_PARAM: + n = a->name; + break; + } + + s = a->sym; + if(s == S) + goto none; + if(s->name[0] == '.') + goto none; + et = a->etype; + o = a->offset; + w = a->width; + + for(i=0; i<nvar; i++) { + v = var+i; + if(v->sym == s && v->name == n) { + if(v->offset == o) + if(v->etype == et) + if(v->width == w) + if(!flag) + return blsh(i); + + // if they overlap, disable both + if(overlap(v->offset, v->width, o, w)) { + v->addr = 1; + flag = 1; + } + } + } + + switch(et) { + case 0: + case TFUNC: + case TARRAY: + case TSTRING: + goto none; + } + + if(nvar >= NVAR) { + if(debug['w'] > 1 && s) + fatal("variable not optimized: %D", a); + goto none; + } + + i = nvar; + nvar++; +//print("var %d %E %D %S\n", i, et, a, s); + v = var+i; + v->sym = s; + v->offset = o; + v->name = n; +// v->gotype = a->gotype; + v->etype = et; + v->width = w; + v->addr = flag; // funny punning + v->node = a->node; + + if(debug['R']) + print("bit=%2d et=%2d w=%d+%d %S %D flag=%d\n", i, et, o, w, s, a, v->addr); + + bit = blsh(i); + if(n == D_EXTERN || n == D_STATIC) + for(z=0; z<BITS; z++) + externs.b[z] |= bit.b[z]; + if(n == D_PARAM) + for(z=0; z<BITS; z++) + params.b[z] |= bit.b[z]; + + return bit; + +none: + return zbits; +} + +void +prop(Reg *r, Bits ref, Bits cal) +{ + Reg *r1, *r2; + int z; + + for(r1 = r; r1 != R; r1 = r1->p1) { + for(z=0; z<BITS; z++) { + ref.b[z] |= r1->refahead.b[z]; + if(ref.b[z] != r1->refahead.b[z]) { + r1->refahead.b[z] = ref.b[z]; + change++; + } + cal.b[z] |= r1->calahead.b[z]; + if(cal.b[z] != r1->calahead.b[z]) { + r1->calahead.b[z] = cal.b[z]; + change++; + } + } + switch(r1->prog->as) { + case ABL: + if(noreturn(r1->prog)) + break; + for(z=0; z<BITS; z++) { + cal.b[z] |= ref.b[z] | externs.b[z]; + ref.b[z] = 0; + } + break; + + case ATEXT: + for(z=0; z<BITS; z++) { + cal.b[z] = 0; + ref.b[z] = 0; + } + break; + + case ARET: + for(z=0; z<BITS; z++) { + cal.b[z] = externs.b[z] | ovar.b[z]; + ref.b[z] = 0; + } + break; + } + for(z=0; z<BITS; z++) { + ref.b[z] = (ref.b[z] & ~r1->set.b[z]) | + r1->use1.b[z] | r1->use2.b[z]; + cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]); + r1->refbehind.b[z] = ref.b[z]; + r1->calbehind.b[z] = cal.b[z]; + } + if(r1->active) + break; + r1->active = 1; + } + for(; r != r1; r = r->p1) + for(r2 = r->p2; r2 != R; r2 = r2->p2link) + prop(r2, r->refbehind, r->calbehind); +} + +/* + * find looping structure + * + * 1) find reverse postordering + * 2) find approximate dominators, + * the actual dominators if the flow graph is reducible + * otherwise, dominators plus some other non-dominators. + * See Matthew S. Hecht and Jeffrey D. Ullman, + * "Analysis of a Simple Algorithm for Global Data Flow Problems", + * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, + * Oct. 1-3, 1973, pp. 207-217. + * 3) find all nodes with a predecessor dominated by the current node. + * such a node is a loop head. + * recursively, all preds with a greater rpo number are in the loop + */ +int32 +postorder(Reg *r, Reg **rpo2r, int32 n) +{ + Reg *r1; + + r->rpo = 1; + r1 = r->s1; + if(r1 && !r1->rpo) + n = postorder(r1, rpo2r, n); + r1 = r->s2; + if(r1 && !r1->rpo) + n = postorder(r1, rpo2r, n); + rpo2r[n] = r; + n++; + return n; +} + +int32 +rpolca(int32 *idom, int32 rpo1, int32 rpo2) +{ + int32 t; + + if(rpo1 == -1) + return rpo2; + while(rpo1 != rpo2){ + if(rpo1 > rpo2){ + t = rpo2; + rpo2 = rpo1; + rpo1 = t; + } + while(rpo1 < rpo2){ + t = idom[rpo2]; + if(t >= rpo2) + fatal("bad idom"); + rpo2 = t; + } + } + return rpo1; +} + +int +doms(int32 *idom, int32 r, int32 s) +{ + while(s > r) + s = idom[s]; + return s == r; +} + +int +loophead(int32 *idom, Reg *r) +{ + int32 src; + + src = r->rpo; + if(r->p1 != R && doms(idom, src, r->p1->rpo)) + return 1; + for(r = r->p2; r != R; r = r->p2link) + if(doms(idom, src, r->rpo)) + return 1; + return 0; +} + +void +loopmark(Reg **rpo2r, int32 head, Reg *r) +{ + if(r->rpo < head || r->active == head) + return; + r->active = head; + r->loop += LOOP; + if(r->p1 != R) + loopmark(rpo2r, head, r->p1); + for(r = r->p2; r != R; r = r->p2link) + loopmark(rpo2r, head, r); +} + +void +loopit(Reg *r, int32 nr) +{ + Reg *r1; + int32 i, d, me; + + if(nr > maxnr) { + rpo2r = mal(nr * sizeof(Reg*)); + idom = mal(nr * sizeof(int32)); + maxnr = nr; + } + d = postorder(r, rpo2r, 0); + if(d > nr) + fatal("too many reg nodes"); + nr = d; + for(i = 0; i < nr / 2; i++){ + r1 = rpo2r[i]; + rpo2r[i] = rpo2r[nr - 1 - i]; + rpo2r[nr - 1 - i] = r1; + } + for(i = 0; i < nr; i++) + rpo2r[i]->rpo = i; + + idom[0] = 0; + for(i = 0; i < nr; i++){ + r1 = rpo2r[i]; + me = r1->rpo; + d = -1; + if(r1->p1 != R && r1->p1->rpo < me) + d = r1->p1->rpo; + for(r1 = r1->p2; r1 != nil; r1 = r1->p2link) + if(r1->rpo < me) + d = rpolca(idom, d, r1->rpo); + idom[i] = d; + } + + for(i = 0; i < nr; i++){ + r1 = rpo2r[i]; + r1->loop++; + if(r1->p2 != R && loophead(idom, r1)) + loopmark(rpo2r, i, r1); + } +} + +void +synch(Reg *r, Bits dif) +{ + Reg *r1; + int z; + + for(r1 = r; r1 != R; r1 = r1->s1) { + for(z=0; z<BITS; z++) { + dif.b[z] = (dif.b[z] & + ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | + r1->set.b[z] | r1->regdiff.b[z]; + if(dif.b[z] != r1->regdiff.b[z]) { + r1->regdiff.b[z] = dif.b[z]; + change++; + } + } + if(r1->active) + break; + r1->active = 1; + for(z=0; z<BITS; z++) + dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); + if(r1->s2 != R) + synch(r1->s2, dif); + } +} + +uint32 +allreg(uint32 b, Rgn *r) +{ + Var *v; + int i; + + v = var + r->varno; + r->regno = 0; + switch(v->etype) { + + default: + fatal("unknown etype %d/%E", bitno(b), v->etype); + break; + + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT: + case TUINT: + case TUINTPTR: + case TBOOL: + case TPTR32: + i = BtoR(~b); + if(i && r->cost >= 0) { + r->regno = i; + return RtoB(i); + } + break; + + case TFLOAT32: + case TFLOAT64: + i = BtoF(~b); + if(i && r->cost >= 0) { + r->regno = i+NREG; + return FtoB(i); + } + break; + + case TINT64: + case TUINT64: + case TPTR64: + case TINTER: + case TSTRUCT: + case TARRAY: + break; + } + return 0; +} + +void +paint1(Reg *r, int bn) +{ + Reg *r1; + Prog *p; + int z; + uint32 bb; + + z = bn/32; + bb = 1L<<(bn%32); + if(r->act.b[z] & bb) + return; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + r1 = r->p1; + if(r1 == R) + break; + if(!(r1->refahead.b[z] & bb)) + break; + if(r1->act.b[z] & bb) + break; + r = r1; + } + + if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) { + change -= CLOAD * r->loop; + if(debug['R'] > 1) + print("%d%P\td %Q $%d\n", r->loop, + r->prog, blsh(bn), change); + } + for(;;) { + r->act.b[z] |= bb; + p = r->prog; + + if(r->use1.b[z] & bb) { + change += CREF * r->loop; + if(debug['R'] > 1) + print("%d%P\tu1 %Q $%d\n", r->loop, + p, blsh(bn), change); + } + + if((r->use2.b[z]|r->set.b[z]) & bb) { + change += CREF * r->loop; + if(debug['R'] > 1) + print("%d%P\tu2 %Q $%d\n", r->loop, + p, blsh(bn), change); + } + + if(STORE(r) & r->regdiff.b[z] & bb) { + change -= CLOAD * r->loop; + if(debug['R'] > 1) + print("%d%P\tst %Q $%d\n", r->loop, + p, blsh(bn), change); + } + + if(r->refbehind.b[z] & bb) + for(r1 = r->p2; r1 != R; r1 = r1->p2link) + if(r1->refahead.b[z] & bb) + paint1(r1, bn); + + if(!(r->refahead.b[z] & bb)) + break; + r1 = r->s2; + if(r1 != R) + if(r1->refbehind.b[z] & bb) + paint1(r1, bn); + r = r->s1; + if(r == R) + break; + if(r->act.b[z] & bb) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } +} + +uint32 +paint2(Reg *r, int bn) +{ + Reg *r1; + int z; + uint32 bb, vreg; + + z = bn/32; + bb = 1L << (bn%32); + vreg = regbits; + if(!(r->act.b[z] & bb)) + return vreg; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + r1 = r->p1; + if(r1 == R) + break; + if(!(r1->refahead.b[z] & bb)) + break; + if(!(r1->act.b[z] & bb)) + break; + r = r1; + } + for(;;) { + r->act.b[z] &= ~bb; + + vreg |= r->regu; + + if(r->refbehind.b[z] & bb) + for(r1 = r->p2; r1 != R; r1 = r1->p2link) + if(r1->refahead.b[z] & bb) + vreg |= paint2(r1, bn); + + if(!(r->refahead.b[z] & bb)) + break; + r1 = r->s2; + if(r1 != R) + if(r1->refbehind.b[z] & bb) + vreg |= paint2(r1, bn); + r = r->s1; + if(r == R) + break; + if(!(r->act.b[z] & bb)) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } + return vreg; +} + +void +paint3(Reg *r, int bn, int32 rb, int rn) +{ + Reg *r1; + Prog *p; + int z; + uint32 bb; + + z = bn/32; + bb = 1L << (bn%32); + if(r->act.b[z] & bb) + return; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + r1 = r->p1; + if(r1 == R) + break; + if(!(r1->refahead.b[z] & bb)) + break; + if(r1->act.b[z] & bb) + break; + r = r1; + } + + if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) + addmove(r, bn, rn, 0); + + for(;;) { + r->act.b[z] |= bb; + p = r->prog; + + if(r->use1.b[z] & bb) { + if(debug['R']) + print("%P", p); + addreg(&p->from, rn); + if(debug['R']) + print("\t.c%P\n", p); + } + if((r->use2.b[z]|r->set.b[z]) & bb) { + if(debug['R']) + print("%P", p); + addreg(&p->to, rn); + if(debug['R']) + print("\t.c%P\n", p); + } + + if(STORE(r) & r->regdiff.b[z] & bb) + addmove(r, bn, rn, 1); + r->regu |= rb; + + if(r->refbehind.b[z] & bb) + for(r1 = r->p2; r1 != R; r1 = r1->p2link) + if(r1->refahead.b[z] & bb) + paint3(r1, bn, rb, rn); + + if(!(r->refahead.b[z] & bb)) + break; + r1 = r->s2; + if(r1 != R) + if(r1->refbehind.b[z] & bb) + paint3(r1, bn, rb, rn); + r = r->s1; + if(r == R) + break; + if(r->act.b[z] & bb) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } +} + +void +addreg(Adr *a, int rn) +{ + a->sym = 0; + a->name = D_NONE; + a->type = D_REG; + a->reg = rn; + if(rn >= NREG) { + a->type = D_FREG; + a->reg = rn-NREG; + } +} + +/* + * bit reg + * 0 R0 + * 1 R1 + * ... ... + * 10 R10 + */ +int32 +RtoB(int r) +{ + if(r >= REGTMP-2) // excluded R9 and R10 for m and g + return 0; + return 1L << r; +} + +int +BtoR(int32 b) +{ + b &= 0x01fcL; // excluded R9 and R10 for m and g + if(b == 0) + return 0; + return bitno(b); +} + +/* + * bit reg + * 18 F2 + * 19 F3 + * ... ... + * 23 F7 + */ +int32 +FtoB(int f) +{ + + if(f < 2 || f > NFREG-1) + return 0; + return 1L << (f + 16); +} + +int +BtoF(int32 b) +{ + + b &= 0xfc0000L; + if(b == 0) + return 0; + return bitno(b) - 16; +} + +static Sym* symlist[10]; + +int +noreturn(Prog *p) +{ + Sym *s; + int i; + + if(symlist[0] == S) { + symlist[0] = pkglookup("panicindex", runtimepkg); + symlist[1] = pkglookup("panicslice", runtimepkg); + symlist[2] = pkglookup("throwinit", runtimepkg); + symlist[3] = pkglookup("panic", runtimepkg); + symlist[4] = pkglookup("panicwrap", runtimepkg); + } + + s = p->to.sym; + if(s == S) + return 0; + for(i=0; symlist[i]!=S; i++) + if(s == symlist[i]) + return 1; + return 0; +} + +void +dumpone(Reg *r) +{ + int z; + Bits bit; + + print("%d:%P", r->loop, r->prog); + for(z=0; z<BITS; z++) + bit.b[z] = + r->set.b[z] | + r->use1.b[z] | + r->use2.b[z] | + r->refbehind.b[z] | + r->refahead.b[z] | + r->calbehind.b[z] | + r->calahead.b[z] | + r->regdiff.b[z] | + r->act.b[z] | + 0; + if(bany(&bit)) { + print("\t"); + if(bany(&r->set)) + print(" s:%Q", r->set); + if(bany(&r->use1)) + print(" u1:%Q", r->use1); + if(bany(&r->use2)) + print(" u2:%Q", r->use2); + if(bany(&r->refbehind)) + print(" rb:%Q ", r->refbehind); + if(bany(&r->refahead)) + print(" ra:%Q ", r->refahead); + if(bany(&r->calbehind)) + print("cb:%Q ", r->calbehind); + if(bany(&r->calahead)) + print(" ca:%Q ", r->calahead); + if(bany(&r->regdiff)) + print(" d:%Q ", r->regdiff); + if(bany(&r->act)) + print(" a:%Q ", r->act); + } + print("\n"); +} + +void +dumpit(char *str, Reg *r0) +{ + Reg *r, *r1; + + print("\n%s\n", str); + for(r = r0; r != R; r = r->link) { + dumpone(r); + r1 = r->p2; + if(r1 != R) { + print(" pred:"); + for(; r1 != R; r1 = r1->p2link) + print(" %.4ud", r1->prog->loc); + print("\n"); + } +// r1 = r->s1; +// if(r1 != R) { +// print(" succ:"); +// for(; r1 != R; r1 = r1->s1) +// print(" %.4ud", r1->prog->loc); +// print("\n"); +// } + } +} |