ref: d1ad7cfaec448eca408a0226f6b956cdddeee34c
dir: /cc2/arch/qbe/cgen.c/
/* See LICENSE file for copyright and license details. */ #include <assert.h> #include <stdlib.h> #include "arch.h" #include "../../cc2.h" #include "../../../inc/sizes.h" enum lflags { FORCE = 1 << 0, LOADL = 1 << 1, LOADR = 1 << 2 }; enum sflags { ISTMP = 1, ISCONS = 2 }; static char opasmw[] = { [OADD] = ASADDW, [OSUB] = ASSUBW, [OMUL] = ASMULW, [OMOD] = ASMODW, [ODIV] = ASDIVW, [OSHL] = ASSHLW, [OSHR] = ASSHRW, [OLT] = ASLTW, [OGT] = ASGTW, [OLE] = ASLEW, [OGE] = ASGEW, [OEQ] = ASEQW, [ONE] = ASNEW, [OBAND] = ASBANDW, [OBOR] = ASBORW, [OBXOR] = ASBXORW, [OCPL] = ASCPLW }; static char opasml[] = { [OADD] = ASADDL, [OSUB] = ASSUBL, [OMUL] = ASMULL, [OMOD] = ASMODL, [ODIV] = ASDIVL, [OSHL] = ASSHLL, [OSHR] = ASSHRL, [OLT] = ASLTL, [OGT] = ASGTL, [OLE] = ASLEL, [OGE] = ASGEL, [OEQ] = ASEQL, [ONE] = ASNEL, [OBAND] = ASBANDL, [OBOR] = ASBORL, [OBXOR] = ASBXORL, [OCPL] = ASCPLL }; static char opasms[] = { [OADD] = ASADDS, [OSUB] = ASSUBS, [OMUL] = ASMULS, [OMOD] = ASMODS, [ODIV] = ASDIVS, [OSHL] = ASSHLS, [OSHR] = ASSHRS, [OLT] = ASLTS, [OGT] = ASGTS, [OLE] = ASLES, [OGE] = ASGES, [OEQ] = ASEQS, [ONE] = ASNES, [OBAND] = ASBANDS, [OBOR] = ASBORS, [OBXOR] = ASBXORS, [OCPL] = ASCPLS }; static char opasmd[] = { [OADD] = ASADDD, [OSUB] = ASSUBD, [OMUL] = ASMULD, [OMOD] = ASMODD, [ODIV] = ASDIVD, [OSHL] = ASSHLD, [OSHR] = ASSHRD, [OLT] = ASLTD, [OGT] = ASGTD, [OLE] = ASLED, [OGE] = ASGED, [OEQ] = ASEQD, [ONE] = ASNED, [OBAND] = ASBANDD, [OBOR] = ASBORD, [OBXOR] = ASBXORD, [OCPL] = ASCPLD }; static Node * tmpnode(Node *np) { Symbol *sym; sym = getsym(TMPSYM); sym->type = np->type; sym->kind = STMP; np->u.sym = sym; np->op = OTMP; np->flags |= ISTMP; return np; } /* * load() load the address passed in a child of np in a temporary * if it is not already in a temporay. It can be forced to load * using the FORCE flag */ static Node * load(Node *np, int flags) { int op; Type *tp; Node *child; child = (flags & LOADL) ? np->left : np->right; if (!child) return NULL; tp = &child->type; if ((flags & FORCE) || !(child->flags & (ISTMP|ISCONS))) { Node *new = tmpnode(newnode(OTMP)); new->type = *tp; new->left = child; switch (tp->size) { case 1: op = ASLDB; break; case 2: op = ASLDH; break; case 4: op = (tp->flags & INTF) ? ASLDW : ASLDS; break; case 8: op = (tp->flags & INTF) ? ASLDL : ASLDD; break; default: abort(); } code(op, new, child, NULL); child = new; } return (flags & LOADL) ? (np->left = child) : (np->right = child); } static Node * cast(Node *nd) { Type *ts, *td; Node *tmp, *ns; int op, disint, sisint; extern Type uint32type, int32type; ns = load(nd, LOADL); td = &nd->type; ts = &ns->type; disint = (td->flags & INTF) != 0; sisint = (ts->flags & INTF) != 0; if (disint && sisint) { if (td->size <= ts->size) return nd; assert(td->size == 4 || td->size == 8); switch (ts->size) { case 1: op = (td->size == 4) ? ASEXTBW : ASEXTBL; break; case 2: op = (td->size == 4) ? ASEXTHW : ASEXTHL; break; case 4: op = ASEXTWL; break; default: abort(); } /* * unsigned version of operations are always +1 the * signed version */ op += (td->flags & SIGNF) == 0; } else if (disint) { /* conversion from float to int */ switch (ts->size) { case 4: op = (td->size == 8) ? ASSTOL : ASSTOW; break; case 8: op = (td->size == 8) ? ASDTOL : ASDTOW; break; default: abort(); } /* TODO: Add signess */ } else if (sisint) { /* conversion from int to float */ switch (ts->size) { case 1: case 2: tmp = tmpnode(newnode(ONOP)); tmp->type = (ts->flags&SIGNF) ? int32type : uint32type; tmp->left = ns; nd->left = ns = cast(tmp); case 4: op = (td->size == 8) ? ASSWTOD : ASSWTOS; break; case 8: op = (td->size == 8) ? ASSLTOD : ASSLTOS; break; default: abort(); } /* TODO: Add signess */ } else { /* conversion from float to float */ op = (td->size == 4) ? ASEXTS : ASTRUNCD; } code(op, tmpnode(nd), ns, NULL); return nd; } static Node * call(Node *np) { int n, op; Type *tp = &np->type; Node **q, *tmp, *p, *pars[NR_FUNPARAM]; for (n = 0, p = np->right; p; p = p->right) { cgen(p->left); pars[n++] = load(p, LOADL); } switch (tp->size) { case 0: np->left = tmpnode(newnode(OTMP)); op = ASCALLW; break; case 1: op = ASCALLB; break; case 2: op = ASCALLH; break; case 4: op = (tp->flags & INTF) ? ASCALLW : ASCALLS; break; case 8: op = (tp->flags & INTF) ? ASCALLL : ASCALLD; break; default: abort(); } code(op, tmpnode(np), np->left, NULL); for (q = pars; q < &pars[n]; ++q) { op = (q == &pars[n-1]) ? ASPARE : ASPAR; p = newnode(OTMP); p->type = (*q)->type; code(op, NULL, *q, tmpnode(p)); } code(ASCALL, NULL, NULL, NULL); return np; } static Node * abbrev(Node *np) { Node *tmp; if (np->u.subop == 0) return np->right; tmp = newnode(np->u.subop); tmp->type = np->type; tmp->right = np->right; tmp->left = np->left; return np->right = cgen(tmp); } static Node * assign(Node *to, Node *from) { Type *tp = &to->type; int op; switch (tp->size) { case 1: op = ASSTB; break; case 2: op = ASSTH; break; case 4: op = (tp->flags & INTF) ? ASSTW : ASSTS; break; case 8: op = (tp->flags & INTF) ? ASSTL : ASSTD; break; default: abort(); } code(op, to, from, NULL); return from; } static Node * ternary(Node *np) { Symbol *yes, *no, *phi; Node *ifyes, *ifno, *phinode, *colon; tmpnode(np); phi = newlabel(); yes = newlabel(); no = newlabel(); ifyes = label2node(yes); ifno = label2node(no); phinode = label2node(phi); colon = np->right; cgen(np->left); code(ASBRANCH, load(np, LOADL), ifyes, ifno); setlabel(yes); cgen(colon->left); assign(np, load(colon, LOADL)); code(ASJMP, NULL, phinode, NULL); setlabel(no); cgen(colon->right); assign(np, load(colon, LOADR)); setlabel(phi); deltree(ifyes); deltree(ifno); deltree(phinode); return np; } static Node * function(void) { Symbol *p; /* allocate stack space for parameters */ for (p = locals; p && (p->type.flags & PARF) != 0; p = p->next) code(ASALLOC, label2node(p), NULL, NULL); /* allocate stack space for local variables) */ for ( ; p && p->id != TMPSYM; p = p->next) { if (p->kind != SAUTO) continue; code(ASALLOC, label2node(p), NULL, NULL); } /* store formal parameters in parameters */ for (p = locals; p; p = p->next) { if ((p->type.flags & PARF) == 0) break; code(ASFORM, label2node(p), NULL, NULL); } return NULL; } /* TODO: Fix "memory leaks" */ Node * cgen(Node *np) { Node *ifyes, *ifno, *next; Symbol *sym; Type *tp; int op, off; char *tbl; if (!np) return NULL; setlabel(np->label); if (np->op != OCALL && np->op != OASK) { np->left = cgen(np->left); np->right = cgen(np->right); } tp = &np->type; switch (np->op) { case OSTRING: abort(); case OBFUN: return function(); case OEFUN: return NULL; case OCONST: case OLABEL: np->flags |= ISCONS; case OMEM: case OAUTO: return np; case OSHR: case OMOD: case ODIV: case OLT: case OGT: case OLE: case OGE: /* * unsigned version of operations are always +1 the * signed version */ off = (tp->flags & SIGNF) == 0; goto binary; case OADD: case OSUB: case OMUL: case OSHL: case OBAND: case OBOR: case OBXOR: case OEQ: case ONE: off = 0; binary: switch (tp->size) { case 4: tbl = (tp->flags & INTF) ? opasmw : opasms; break; case 8: tbl = (tp->flags & INTF) ? opasml : opasmd; break; default: abort(); } op = tbl[np->op] + off; code(op, tmpnode(np), load(np, LOADL), load(np, LOADR)); return np; case ONOP: case OBLOOP: case OELOOP: return NULL; case OCAST: return cast(np); case OADDR: np->flags |= ISTMP; np->op = OTMP; np->u.sym = np->left->u.sym; return np; case OPTR: load(np, LOADL); load(np, LOADL|FORCE); return np->left; case OCPL: case ONEG: case OINC: case ODEC: abort(); case OASSIG: abbrev(np); return assign(np->left, load(np, LOADR)); case OCOMMA: return np->right; case OCALL: return call(np); case OFIELD: case OAND: case OOR: abort(); case OASK: return ternary(np); case OBRANCH: next = np->next; load(np, LOADL); if (!next->label) next->label = newlabel(); ifyes = label2node(np->u.sym); ifno = label2node(next->label); op = ASBRANCH; np = np->left; goto emit_jump; case OJMP: ifyes = label2node(np->u.sym); op = ASJMP; np = ifno = NULL; emit_jump: code(op, np, ifyes, ifno); deltree(ifyes); deltree(ifno); return NULL; case ORET: code(ASRET, NULL, load(np, LOADL), NULL); return NULL; case OCASE: case ODEFAULT: case OESWITCH: case OBSWITCH: default: abort(); } } /* * This is strongly influenced by * http://plan9.bell-labs.com/sys/doc/compiler.ps (/sys/doc/compiler.ps) * calculate addresability as follows * AUTO => 11 value+fp * REG => 11 reg * STATIC => 11 (value) * CONST => 11 $value * These values of addressability are not used in the code generation. * They are only used to calculate the Sethi-Ullman numbers. Since * QBE is AMD64 targered we could do a better job here, and try to * detect some of the complex addressing modes of these processors. */ Node * sethi(Node *np) { Node *lp, *rp; if (!np) return np; np->complex = 0; np->address = 0; lp = np->left; rp = np->right; switch (np->op) { case OAUTO: case OREG: case OMEM: case OCONST: np->address = 11; break; default: sethi(lp); sethi(rp); break; } if (np->address > 10) return np; if (lp) np->complex = lp->complex; if (rp) { int d = np->complex - rp->complex; if (d == 0) ++np->complex; else if (d < 0) np->complex = rp->complex; } if (np->complex == 0) ++np->complex; return np; }