diff options
Diffstat (limited to 'src/cmd/8g/cgen.c')
| -rw-r--r-- | src/cmd/8g/cgen.c | 897 |
1 files changed, 897 insertions, 0 deletions
diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c new file mode 100644 index 000000000..736c7925f --- /dev/null +++ b/src/cmd/8g/cgen.c @@ -0,0 +1,897 @@ +// 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 +is64(Type *t) +{ + if(t == T) + return 0; + switch(simtype[t->etype]) { + case TINT64: + case TUINT64: + case TPTR64: + return 1; + } + return 0; +} + +/* + * generate: + * res = n; + * simplifies and calls gmove. + * + * TODO: + * sudoaddable + */ +void +cgen(Node *n, Node *res) +{ + Node *nl, *nr, *r, n1, n2, rr; + Prog *p1, *p2, *p3; + int a; + + if(debug['g']) { + dump("\ncgen-n", n); + dump("cgen-res", res); + } + + if(n == N || n->type == T) + fatal("cgen: n nil"); + if(res == N || res->type == T) + fatal("cgen: res nil"); + + // function calls on both sides? introduce temporary + if(n->ullman >= UINF && res->ullman >= UINF) { + tempname(&n1, n->type); + cgen(n, &n1); + cgen(&n1, res); + return; + } + + // structs etc get handled specially + if(isfat(n->type)) { + sgen(n, res, n->type->width); + return; + } + + // if both are addressable, move + if(n->addable && res->addable) { + gmove(n, res); + return; + } + + // if both are not addressable, use a temporary. + if(!n->addable && !res->addable) { + tempalloc(&n1, n->type); + cgen(n, &n1); + cgen(&n1, res); + tempfree(&n1); + 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; + } + + // otherwise, the result is addressable but n is not. + // let's do some computation. + + // 64-bit ops are hard on 32-bit machine. + if(is64(n->type) && cgen64(n, res)) + return; + + // use ullman to pick operand to eval first. + nl = n->left; + nr = n->right; + if(nl != N && nl->ullman >= UINF) + if(nr != N && nr->ullman >= UINF) { + // both are hard + tempalloc(&n1, nr->type); + cgen(nr, &n1); + n2 = *n; + n2.right = &n1; + cgen(&n2, res); + tempfree(&n1); + return; + } + + switch(n->op) { + default: + dump("cgen", n); + fatal("cgen %O", n->op); + 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(AJMP, T); + p2 = pc; + gmove(nodbool(1), res); + p3 = gbranch(AJMP, T); + patch(p1, pc); + bgen(n, 1, p2); + gmove(nodbool(0), res); + patch(p3, pc); + return; + + case OPLUS: + cgen(nl, res); + return; + + case OMINUS: + a = optoas(n->op, nl->type); + goto uop; + + // symmetric binary + case OAND: + case OOR: + case OXOR: + case OADD: + case OMUL: + a = optoas(n->op, nl->type); + // TODO: cgen_bmul ? + goto sbop; + + // asymmetric binary + case OSUB: + a = optoas(n->op, nl->type); + goto abop; + + case OCONV: + if(eqtype(n->type, nl->type)) { + cgen(nl, res); + break; + } + tempalloc(&n1, nl->type); + cgen(nl, &n1); + gmove(&n1, res); + tempfree(&n1); + 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, TSTRING) || istype(nl->type, TMAP)) { + // both string and map have len in the first 32-bit word. + // a zero pointer means zero length + tempalloc(&n1, types[tptr]); + cgen(nl, &n1); + regalloc(&n2, types[tptr], N); + gmove(&n1, &n2); + tempfree(&n1); + n1 = n2; + + nodconst(&n2, types[tptr], 0); + gins(optoas(OCMP, types[tptr]), &n1, &n2); + 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(isslice(nl->type)) { + igen(nl, &n1, res); + n1.op = OINDREG; + n1.type = types[TUINT32]; + n1.xoffset = Array_nel; + gmove(&n1, res); + regfree(&n1); + break; + } + fatal("cgen: OLEN: unknown type %lT", nl->type); + break; + + case OCAP: + if(isslice(nl->type)) { + igen(nl, &n1, res); + 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 OCALL: + cgen_call(n, 0); + cgen_callret(n, res); + break; + + case OMOD: + case ODIV: + if(isfloat[n->type->etype]) { + a = optoas(n->op, nl->type); + goto abop; + } + cgen_div(n->op, nl, nr, res); + break; + + case OLSH: + case ORSH: + cgen_shift(n->op, nl, nr, res); + break; + } + return; + +sbop: // symmetric binary + if(nl->ullman < nr->ullman) { + r = nl; + nl = nr; + nr = r; + } + +abop: // asymmetric binary + if(nl->ullman >= nr->ullman) { + tempalloc(&n1, nl->type); + cgen(nl, &n1); + tempalloc(&n2, nr->type); + cgen(nr, &n2); + } else { + tempalloc(&n1, nl->type); + tempalloc(&n2, nr->type); + cgen(nr, &n2); + cgen(nl, &n1); + } + regalloc(&rr, res->type, N); + gmove(&n1, &rr); + gins(a, &n2, &rr); + gmove(&rr, res); + regfree(&rr); + tempfree(&n2); + tempfree(&n1); + return; + +uop: // unary + tempalloc(&n1, nl->type); + cgen(nl, &n1); + gins(a, N, &n1); + gmove(&n1, res); + tempfree(&n1); + return; +} + +/* + * address gen + * res = &n; + */ +void +agen(Node *n, Node *res) +{ + Node *nl, *nr; + Node n1, n2, n3, tmp; + Type *t; + uint32 w; + uint64 v; + Prog *p1; + + if(debug['g']) { + dump("\nagen-res", res); + dump("agen-r", n); + } + if(n == N || n->type == T || res == N || res->type == T) + fatal("agen"); + + // addressable var is easy + if(n->addable) { + regalloc(&n1, types[tptr], res); + gins(ALEAL, n, &n1); + gmove(&n1, res); + regfree(&n1); + return; + } + + // let's compute + nl = n->left; + nr = n->right; + + switch(n->op) { + default: + fatal("agen %O", n->op); + + case OCONV: + if(!eqtype(n->type, nl->type)) + fatal("agen: non-trivial OCONV"); + agen(nl, res); + break; + + case OCALLMETH: + cgen_callmeth(n, 0); + cgen_aret(n, res); + break; + + case OCALLINTER: + cgen_callinter(n, res, 0); + cgen_aret(n, res); + break; + + case OCALL: + cgen_call(n, 0); + cgen_aret(n, res); + break; + + case OINDEX: + w = n->type->width; + if(nr->addable) { + agenr(nl, &n3, res); + if(!isconst(nr, CTINT)) { + regalloc(&n1, nr->type, N); + cgen(nr, &n1); + } + } else if(nl->addable) { + if(!isconst(nr, CTINT)) { + tempalloc(&tmp, nr->type); + cgen(nr, &tmp); + regalloc(&n1, nr->type, N); + gmove(&tmp, &n1); + tempfree(&tmp); + } + regalloc(&n3, types[tptr], res); + agen(nl, &n3); + } else { + tempalloc(&tmp, nr->type); + cgen(nr, &tmp); + nr = &tmp; + agenr(nl, &n3, res); + regalloc(&n1, nr->type, N); + gins(optoas(OAS, nr->type), &tmp, &n1); + tempfree(&tmp); + } + + // &a is in &n3 (allocated in res) + // i is in &n1 (if not constant) + // w is width + + if(w == 0) + fatal("index is zero width"); + + // constant index + if(isconst(nr, CTINT)) { + v = mpgetfix(nr->val.u.xval); + if(isslice(nl->type)) { + + if(!debug['B']) { + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_nel; + nodconst(&n2, types[TUINT32], v); + gins(optoas(OCMP, types[TUINT32]), &n1, &n2); + p1 = gbranch(optoas(OGT, types[TUINT32]), T); + ginscall(throwindex, 0); + patch(p1, pc); + } + + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_array; + gmove(&n1, &n3); + } else + if(!debug['B']) { + if(v < 0) + yyerror("out of bounds on array"); + else + if(v >= nl->type->bound) + yyerror("out of bounds on array"); + } + + nodconst(&n2, types[tptr], v*w); + gins(optoas(OADD, types[tptr]), &n2, &n3); + + gmove(&n3, res); + regfree(&n3); + break; + } + + // type of the index + t = types[TUINT32]; + if(issigned[n1.type->etype]) + t = types[TINT32]; + + regalloc(&n2, t, &n1); // i + gmove(&n1, &n2); + regfree(&n1); + + if(!debug['B']) { + // check bounds + if(isslice(nl->type)) { + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_nel; + } else + nodconst(&n1, types[TUINT32], nl->type->bound); + gins(optoas(OCMP, types[TUINT32]), &n2, &n1); + p1 = gbranch(optoas(OLT, types[TUINT32]), T); + ginscall(throwindex, 0); + patch(p1, pc); + } + + if(isslice(nl->type)) { + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_array; + gmove(&n1, &n3); + } + + if(w == 1 || w == 2 || w == 4 || w == 8) { + p1 = gins(ALEAL, &n2, &n3); + p1->from.scale = w; + p1->from.index = p1->from.type; + p1->from.type = p1->to.type + D_INDIR; + } else { + nodconst(&n1, t, w); + gins(optoas(OMUL, t), &n1, &n2); + gins(optoas(OADD, types[tptr]), &n2, &n3); + 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[tptr], n->xoffset); + gins(optoas(OADD, types[tptr]), &n1, res); + } + break; + + case OIND: + cgen(nl, res); + break; + + case ODOT: + t = nl->type; + agen(nl, res); + if(n->xoffset != 0) { + nodconst(&n1, types[tptr], n->xoffset); + gins(optoas(OADD, types[tptr]), &n1, res); + } + break; + + case ODOTPTR: + t = nl->type; + if(!isptr[t->etype]) + fatal("agen: not ptr %N", n); + cgen(nl, res); + if(n->xoffset != 0) { + nodconst(&n1, types[tptr], n->xoffset); + gins(optoas(OADD, types[tptr]), &n1, res); + } + break; + } +} + +/* + * 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) +{ + Node n1; + + tempalloc(&n1, types[tptr]); + agen(n, &n1); + regalloc(a, types[tptr], res); + gins(optoas(OAS, types[tptr]), &n1, a); + tempfree(&n1); + a->op = OINDREG; + a->type = n->type; +} + +/* + * generate: + * newreg = &n; + * + * caller must regfree(a). + */ +void +agenr(Node *n, Node *a, Node *res) +{ + Node n1; + + tempalloc(&n1, types[tptr]); + agen(n, &n1); + regalloc(a, types[tptr], res); + gmove(&n1, a); + tempfree(&n1); +} + +/* + * branch gen + * if(n == true) goto to; + */ +void +bgen(Node *n, int true, Prog *to) +{ + int et, a; + Node *nl, *nr, *r; + Node n1, n2, tmp; + Prog *p1, *p2; + + if(debug['g']) { + dump("\nbgen", n); + } + + if(n == N) + n = nodbool(1); + + nl = n->left; + nr = n->right; + + if(n->type == T) { + convlit(n, types[TBOOL]); + if(n->type == T) + return; + } + + et = n->type->etype; + if(et != TBOOL) { + yyerror("cgen: bad type %T for %O", n->type, n->op); + patch(gins(AEND, N, N), to); + return; + } + nl = N; + nr = N; + + switch(n->op) { + default: + regalloc(&n1, n->type, N); + cgen(n, &n1); + nodconst(&n2, n->type, 0); + gins(optoas(OCMP, n->type), &n1, &n2); + a = AJNE; + if(!true) + a = AJEQ; + patch(gbranch(a, n->type), to); + regfree(&n1); + return; + + case OLITERAL: +// need to ask if it is bool? + if(!true == !n->val.u.bval) + patch(gbranch(AJMP, T), to); + return; + + case ONAME: + nodconst(&n1, n->type, 0); + gins(optoas(OCMP, n->type), n, &n1); + a = AJNE; + if(!true) + a = AJEQ; + patch(gbranch(a, n->type), to); + return; + + case OANDAND: + if(!true) + goto caseor; + + caseand: + p1 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T); + patch(p1, pc); + bgen(n->left, !true, p2); + bgen(n->right, !true, p2); + p1 = gbranch(AJMP, T); + patch(p1, to); + patch(p2, pc); + return; + + case OOROR: + if(!true) + goto caseand; + + caseor: + bgen(n->left, true, to); + bgen(n->right, true, to); + return; + + case OEQ: + case ONE: + case OLT: + case OGT: + case OLE: + case OGE: + nr = n->right; + if(nr == N || nr->type == T) + return; + + case ONOT: // unary + nl = n->left; + if(nl == N || nl->type == T) + return; + } + + switch(n->op) { + case ONOT: + bgen(nl, !true, to); + break; + + case OEQ: + case ONE: + case OLT: + case OGT: + case OLE: + case OGE: + a = n->op; + if(!true) + a = brcom(a); + + // make simplest on right + if(nl->op == OLITERAL || 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; + } + a = optoas(a, types[tptr]); + regalloc(&n1, types[tptr], N); + agen(nl, &n1); + n2 = n1; + n2.op = OINDREG; + n2.xoffset = Array_array; + nodconst(&tmp, types[tptr], 0); + gins(optoas(OCMP, types[tptr]), &n2, &tmp); + patch(gbranch(a, types[tptr]), to); + regfree(&n1); + break; + } + + if(is64(nr->type)) { + fatal("cmp64"); + // cmp64(nl, nr, a, to); + break; + } + + a = optoas(a, nr->type); + + if(nr->ullman >= UINF) { + regalloc(&n1, nr->type, N); + cgen(nr, &n1); + + tempname(&tmp, nr->type); + gmove(&n1, &tmp); + regfree(&n1); + + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + + regalloc(&n2, nr->type, &n2); + cgen(&tmp, &n2); + + gins(optoas(OCMP, nr->type), &n1, &n2); + patch(gbranch(a, nr->type), to); + + regfree(&n1); + regfree(&n2); + break; + } + + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + + if(smallintconst(nr)) { + gins(optoas(OCMP, nr->type), &n1, nr); + patch(gbranch(a, nr->type), to); + regfree(&n1); + break; + } + + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + + gins(optoas(OCMP, nr->type), &n1, &n2); + patch(gbranch(a, nr->type), to); + + regfree(&n1); + regfree(&n2); + break; + } +} + +/* + * 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; + + switch(n->op) { + case OINDREG: + return n->xoffset; + + case OCALLMETH: + case OCALLINTER: + case OCALL: + t = n->left->type; + if(isptr[t->etype]) + t = t->type; + + t = structfirst(&flist, getoutarg(t)); + if(t != T) + return t->width; + break; + } + + // botch - probably failing to recognize address + // arithmetic on the above. eg INDEX and DOT + return -1000; +} + +/* + * struct gen + * memmove(&res, &n, w); + */ +void +sgen(Node *n, Node *res, int w) +{ + Node nodl, nodr; + int32 c, q, odst, osrc; + + if(debug['g']) { + print("\nsgen w=%d\n", w); + dump("r", n); + dump("res", res); + } + if(w == 0) + return; + if(n->ullman >= UINF && res->ullman >= UINF) { + fatal("sgen UINF"); + } + + if(w < 0) + fatal("sgen copy %d", w); + + // offset on the stack + osrc = stkof(n); + odst = stkof(res); + + // TODO(rsc): Should these be tempalloc instead? + nodreg(&nodl, types[tptr], D_DI); + nodreg(&nodr, types[tptr], D_SI); + + if(n->ullman >= res->ullman) { + agen(n, &nodr); + agen(res, &nodl); + } else { + agen(res, &nodl); + agen(n, &nodr); + } + + c = w % 4; // bytes + q = w / 4; // doublewords + + // if we are copying forward on the stack and + // the src and dst overlap, then reverse direction + if(osrc < odst && odst < osrc+w) { + // reverse direction + gins(ASTD, N, N); // set direction flag + if(c > 0) { + gconreg(AADDL, w-1, D_SI); + gconreg(AADDL, w-1, D_DI); + + gconreg(AMOVL, c, D_CX); + gins(AREP, N, N); // repeat + gins(AMOVSB, N, N); // MOVB *(SI)-,*(DI)- + } + + if(q > 0) { + if(c > 0) { + gconreg(AADDL, -7, D_SI); + gconreg(AADDL, -7, D_DI); + } else { + gconreg(AADDL, w-8, D_SI); + gconreg(AADDL, w-8, D_DI); + } + gconreg(AMOVL, q, D_CX); + gins(AREP, N, N); // repeat + gins(AMOVSL, N, N); // MOVL *(SI)-,*(DI)- + } + // we leave with the flag clear + gins(ACLD, N, N); + } else { + // normal direction + if(q >= 4) { + gconreg(AMOVL, q, D_CX); + gins(AREP, N, N); // repeat + gins(AMOVSL, N, N); // MOVL *(SI)+,*(DI)+ + } else + while(q > 0) { + gins(AMOVSL, N, N); // MOVL *(SI)+,*(DI)+ + q--; + } + while(c > 0) { + gins(AMOVSB, N, N); // MOVB *(SI)+,*(DI)+ + c--; + } + } +} + +/* + * attempt to generate 64-bit + * res = n + * return 1 on success, 0 if op not handled. + */ +int +cgen64(Node *n, Node *res) +{ + return 0; +} |
