| // 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 "gc.h" |
| |
| |
| int thechar = '5'; |
| char *thestring = "arm"; |
| |
| LinkArch *thelinkarch = &linkarm; |
| |
| void |
| linkarchinit(void) |
| { |
| } |
| |
| void |
| ginit(void) |
| { |
| Type *t; |
| |
| exregoffset = REGEXT; |
| exfregoffset = FREGEXT; |
| listinit(); |
| nstring = 0; |
| mnstring = 0; |
| nrathole = 0; |
| pc = 0; |
| breakpc = -1; |
| continpc = -1; |
| cases = C; |
| lastp = P; |
| tfield = types[TLONG]; |
| |
| zprog.link = P; |
| zprog.as = AGOK; |
| zprog.reg = NREG; |
| zprog.from.type = D_NONE; |
| zprog.from.name = D_NONE; |
| zprog.from.reg = NREG; |
| zprog.to = zprog.from; |
| zprog.scond = 0xE; |
| |
| regnode.op = OREGISTER; |
| regnode.class = CEXREG; |
| regnode.reg = REGTMP; |
| regnode.complex = 0; |
| regnode.addable = 11; |
| regnode.type = types[TLONG]; |
| |
| constnode.op = OCONST; |
| constnode.class = CXXX; |
| constnode.complex = 0; |
| constnode.addable = 20; |
| constnode.type = types[TLONG]; |
| |
| fconstnode.op = OCONST; |
| fconstnode.class = CXXX; |
| fconstnode.complex = 0; |
| fconstnode.addable = 20; |
| fconstnode.type = types[TDOUBLE]; |
| |
| nodsafe = new(ONAME, Z, Z); |
| nodsafe->sym = slookup(".safe"); |
| nodsafe->type = types[TINT]; |
| nodsafe->etype = types[TINT]->etype; |
| nodsafe->class = CAUTO; |
| complex(nodsafe); |
| |
| t = typ(TARRAY, types[TCHAR]); |
| symrathole = slookup(".rathole"); |
| symrathole->class = CGLOBL; |
| symrathole->type = t; |
| |
| nodrat = new(ONAME, Z, Z); |
| nodrat->sym = symrathole; |
| nodrat->type = types[TIND]; |
| nodrat->etype = TVOID; |
| nodrat->class = CGLOBL; |
| complex(nodrat); |
| nodrat->type = t; |
| |
| nodret = new(ONAME, Z, Z); |
| nodret->sym = slookup(".ret"); |
| nodret->type = types[TIND]; |
| nodret->etype = TIND; |
| nodret->class = CPARAM; |
| nodret = new(OIND, nodret, Z); |
| complex(nodret); |
| |
| com64init(); |
| |
| memset(reg, 0, sizeof(reg)); |
| } |
| |
| void |
| gclean(void) |
| { |
| int i; |
| Sym *s; |
| |
| for(i=0; i<NREG; i++) |
| if(reg[i]) |
| diag(Z, "reg %d left allocated", i); |
| for(i=NREG; i<NREG+NFREG; i++) |
| if(reg[i]) |
| diag(Z, "freg %d left allocated", i-NREG); |
| while(mnstring) |
| outstring("", 1L); |
| symstring->type->width = nstring; |
| symrathole->type->width = nrathole; |
| for(i=0; i<NHASH; i++) |
| for(s = hash[i]; s != S; s = s->link) { |
| if(s->type == T) |
| continue; |
| if(s->type->width == 0) |
| continue; |
| if(s->class != CGLOBL && s->class != CSTATIC) |
| continue; |
| if(s->type == types[TENUM]) |
| continue; |
| gpseudo(AGLOBL, s, nodconst(s->type->width)); |
| } |
| nextpc(); |
| p->as = AEND; |
| outcode(); |
| } |
| |
| void |
| nextpc(void) |
| { |
| Plist *pl; |
| |
| p = alloc(sizeof(*p)); |
| *p = zprog; |
| p->lineno = nearln; |
| pc++; |
| if(lastp == nil) { |
| pl = linknewplist(ctxt); |
| pl->firstpc = p; |
| } else |
| lastp->link = p; |
| lastp = p; |
| } |
| |
| void |
| gargs(Node *n, Node *tn1, Node *tn2) |
| { |
| int32 regs; |
| Node fnxargs[20], *fnxp; |
| |
| regs = cursafe; |
| |
| fnxp = fnxargs; |
| garg1(n, tn1, tn2, 0, &fnxp); /* compile fns to temps */ |
| |
| curarg = 0; |
| fnxp = fnxargs; |
| garg1(n, tn1, tn2, 1, &fnxp); /* compile normal args and temps */ |
| |
| cursafe = regs; |
| } |
| |
| void |
| garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp) |
| { |
| Node nod; |
| |
| if(n == Z) |
| return; |
| if(n->op == OLIST) { |
| garg1(n->left, tn1, tn2, f, fnxp); |
| garg1(n->right, tn1, tn2, f, fnxp); |
| return; |
| } |
| if(f == 0) { |
| if(n->complex >= FNX) { |
| regsalloc(*fnxp, n); |
| nod = znode; |
| nod.op = OAS; |
| nod.left = *fnxp; |
| nod.right = n; |
| nod.type = n->type; |
| cgen(&nod, Z); |
| (*fnxp)++; |
| } |
| return; |
| } |
| if(typesuv[n->type->etype]) { |
| regaalloc(tn2, n); |
| if(n->complex >= FNX) { |
| sugen(*fnxp, tn2, n->type->width); |
| (*fnxp)++; |
| } else |
| sugen(n, tn2, n->type->width); |
| return; |
| } |
| if(REGARG >= 0 && curarg == 0 && typechlp[n->type->etype]) { |
| regaalloc1(tn1, n); |
| if(n->complex >= FNX) { |
| cgen(*fnxp, tn1); |
| (*fnxp)++; |
| } else |
| cgen(n, tn1); |
| return; |
| } |
| regalloc(tn1, n, Z); |
| if(n->complex >= FNX) { |
| cgen(*fnxp, tn1); |
| (*fnxp)++; |
| } else |
| cgen(n, tn1); |
| regaalloc(tn2, n); |
| gopcode(OAS, tn1, Z, tn2); |
| regfree(tn1); |
| } |
| |
| Node* |
| nodconst(int32 v) |
| { |
| constnode.vconst = v; |
| return &constnode; |
| } |
| |
| Node* |
| nod32const(vlong v) |
| { |
| constnode.vconst = v & MASK(32); |
| return &constnode; |
| } |
| |
| Node* |
| nodfconst(double d) |
| { |
| fconstnode.fconst = d; |
| return &fconstnode; |
| } |
| |
| void |
| nodreg(Node *n, Node *nn, int reg) |
| { |
| *n = regnode; |
| n->reg = reg; |
| n->type = nn->type; |
| n->lineno = nn->lineno; |
| } |
| |
| void |
| regret(Node *n, Node *nn, Type *t, int mode) |
| { |
| int r; |
| |
| if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) { |
| r = REGRET; |
| if(typefd[nn->type->etype]) |
| r = FREGRET+NREG; |
| nodreg(n, nn, r); |
| reg[r]++; |
| return; |
| } |
| |
| if(mode == 1) { |
| // fetch returned value after call. |
| // already called gargs, so curarg is set. |
| curarg = (curarg+3) & ~3; |
| regaalloc(n, nn); |
| return; |
| } |
| |
| if(mode == 2) { |
| // store value to be returned. |
| // must compute arg offset. |
| if(t->etype != TFUNC) |
| fatal(Z, "bad regret func %T", t); |
| *n = *nn; |
| n->op = ONAME; |
| n->class = CPARAM; |
| n->sym = slookup(".ret"); |
| n->complex = nodret->complex; |
| n->xoffset = argsize(0); |
| n->addable = 20; |
| return; |
| } |
| |
| fatal(Z, "bad regret"); |
| } |
| |
| int |
| tmpreg(void) |
| { |
| int i; |
| |
| for(i=REGRET+1; i<NREG; i++) |
| if(reg[i] == 0) |
| return i; |
| diag(Z, "out of fixed registers"); |
| return 0; |
| } |
| |
| void |
| regalloc(Node *n, Node *tn, Node *o) |
| { |
| int i; |
| |
| switch(tn->type->etype) { |
| case TCHAR: |
| case TUCHAR: |
| case TSHORT: |
| case TUSHORT: |
| case TINT: |
| case TUINT: |
| case TLONG: |
| case TULONG: |
| case TIND: |
| if(o != Z && o->op == OREGISTER) { |
| i = o->reg; |
| if(i >= 0 && i < NREG) |
| goto out; |
| } |
| for(i=REGRET+1; i<=REGEXT-2; i++) |
| if(reg[i] == 0) |
| goto out; |
| diag(tn, "out of fixed registers"); |
| goto err; |
| |
| case TFLOAT: |
| case TDOUBLE: |
| case TVLONG: |
| if(o != Z && o->op == OREGISTER) { |
| i = o->reg; |
| if(i >= NREG && i < NREG+NFREG) |
| goto out; |
| } |
| for(i=NREG; i<NREG+NFREG; i++) |
| if(reg[i] == 0) |
| goto out; |
| diag(tn, "out of float registers"); |
| goto err; |
| } |
| diag(tn, "unknown type in regalloc: %T", tn->type); |
| err: |
| nodreg(n, tn, 0); |
| return; |
| out: |
| reg[i]++; |
| nodreg(n, tn, i); |
| } |
| |
| void |
| regialloc(Node *n, Node *tn, Node *o) |
| { |
| Node nod; |
| |
| nod = *tn; |
| nod.type = types[TIND]; |
| regalloc(n, &nod, o); |
| } |
| |
| void |
| regfree(Node *n) |
| { |
| int i; |
| |
| i = 0; |
| if(n->op != OREGISTER && n->op != OINDREG) |
| goto err; |
| i = n->reg; |
| if(i < 0 || i >= nelem(reg)) |
| goto err; |
| if(reg[i] <= 0) |
| goto err; |
| reg[i]--; |
| return; |
| err: |
| diag(n, "error in regfree: %d", i); |
| } |
| |
| void |
| regsalloc(Node *n, Node *nn) |
| { |
| cursafe = align(cursafe, nn->type, Aaut3, nil); |
| maxargsafe = maxround(maxargsafe, cursafe+curarg); |
| *n = *nodsafe; |
| n->xoffset = -(stkoff + cursafe); |
| n->type = nn->type; |
| n->etype = nn->type->etype; |
| n->lineno = nn->lineno; |
| } |
| |
| void |
| regaalloc1(Node *n, Node *nn) |
| { |
| if(REGARG < 0) { |
| fatal(n, "regaalloc1 and REGARG<0"); |
| return; |
| } |
| nodreg(n, nn, REGARG); |
| reg[REGARG]++; |
| curarg = align(curarg, nn->type, Aarg1, nil); |
| curarg = align(curarg, nn->type, Aarg2, nil); |
| maxargsafe = maxround(maxargsafe, cursafe+curarg); |
| } |
| |
| void |
| regaalloc(Node *n, Node *nn) |
| { |
| curarg = align(curarg, nn->type, Aarg1, nil); |
| *n = *nn; |
| n->op = OINDREG; |
| n->reg = REGSP; |
| n->xoffset = curarg + SZ_LONG; |
| n->complex = 0; |
| n->addable = 20; |
| curarg = align(curarg, nn->type, Aarg2, nil); |
| maxargsafe = maxround(maxargsafe, cursafe+curarg); |
| } |
| |
| void |
| regind(Node *n, Node *nn) |
| { |
| |
| if(n->op != OREGISTER) { |
| diag(n, "regind not OREGISTER"); |
| return; |
| } |
| n->op = OINDREG; |
| n->type = nn->type; |
| } |
| |
| void |
| raddr(Node *n, Prog *p) |
| { |
| Addr a; |
| |
| naddr(n, &a); |
| if(R0ISZERO && a.type == D_CONST && a.offset == 0) { |
| a.type = D_REG; |
| a.reg = 0; |
| } |
| if(a.type != D_REG && a.type != D_FREG) { |
| if(n) |
| diag(n, "bad in raddr: %O", n->op); |
| else |
| diag(n, "bad in raddr: <null>"); |
| p->reg = NREG; |
| } else |
| p->reg = a.reg; |
| } |
| |
| void |
| naddr(Node *n, Addr *a) |
| { |
| int32 v; |
| |
| a->type = D_NONE; |
| if(n == Z) |
| return; |
| switch(n->op) { |
| default: |
| bad: |
| diag(n, "bad in naddr: %O", n->op); |
| break; |
| |
| case OREGISTER: |
| a->type = D_REG; |
| a->sym = nil; |
| a->reg = n->reg; |
| if(a->reg >= NREG) { |
| a->type = D_FREG; |
| a->reg -= NREG; |
| } |
| break; |
| |
| case OIND: |
| naddr(n->left, a); |
| if(a->type == D_REG) { |
| a->type = D_OREG; |
| break; |
| } |
| if(a->type == D_CONST) { |
| a->type = D_OREG; |
| break; |
| } |
| goto bad; |
| |
| case OINDREG: |
| a->type = D_OREG; |
| a->sym = nil; |
| a->offset = n->xoffset; |
| a->reg = n->reg; |
| break; |
| |
| case ONAME: |
| a->etype = n->etype; |
| a->type = D_OREG; |
| a->name = D_STATIC; |
| a->sym = linksym(n->sym); |
| a->offset = n->xoffset; |
| if(n->class == CSTATIC) |
| break; |
| if(n->class == CEXTERN || n->class == CGLOBL) { |
| a->name = D_EXTERN; |
| break; |
| } |
| if(n->class == CAUTO) { |
| a->name = D_AUTO; |
| break; |
| } |
| if(n->class == CPARAM) { |
| a->name = D_PARAM; |
| break; |
| } |
| goto bad; |
| |
| case OCONST: |
| a->sym = nil; |
| a->reg = NREG; |
| if(typefd[n->type->etype]) { |
| a->type = D_FCONST; |
| a->u.dval = n->fconst; |
| } else { |
| a->type = D_CONST; |
| a->offset = n->vconst; |
| } |
| break; |
| |
| case OADDR: |
| naddr(n->left, a); |
| if(a->type == D_OREG) { |
| a->type = D_CONST; |
| break; |
| } |
| goto bad; |
| |
| case OADD: |
| if(n->left->op == OCONST) { |
| naddr(n->left, a); |
| v = a->offset; |
| naddr(n->right, a); |
| } else { |
| naddr(n->right, a); |
| v = a->offset; |
| naddr(n->left, a); |
| } |
| a->offset += v; |
| break; |
| |
| } |
| } |
| |
| void |
| fop(int as, int f1, int f2, Node *t) |
| { |
| Node nod1, nod2, nod3; |
| |
| nodreg(&nod1, t, NREG+f1); |
| nodreg(&nod2, t, NREG+f2); |
| regalloc(&nod3, t, t); |
| gopcode(as, &nod1, &nod2, &nod3); |
| gmove(&nod3, t); |
| regfree(&nod3); |
| } |
| |
| void |
| gmovm(Node *f, Node *t, int w) |
| { |
| gins(AMOVM, f, t); |
| p->scond |= C_UBIT; |
| if(w) |
| p->scond |= C_WBIT; |
| } |
| |
| void |
| gmove(Node *f, Node *t) |
| { |
| int ft, tt, a; |
| Node nod, nod1; |
| Prog *p1; |
| |
| ft = f->type->etype; |
| tt = t->type->etype; |
| |
| if(ft == TDOUBLE && f->op == OCONST) { |
| } |
| if(ft == TFLOAT && f->op == OCONST) { |
| } |
| |
| /* |
| * a load -- |
| * put it into a register then |
| * worry what to do with it. |
| */ |
| if(f->op == ONAME || f->op == OINDREG || f->op == OIND) { |
| switch(ft) { |
| default: |
| a = AMOVW; |
| break; |
| case TFLOAT: |
| a = AMOVF; |
| break; |
| case TDOUBLE: |
| a = AMOVD; |
| break; |
| case TCHAR: |
| a = AMOVBS; |
| break; |
| case TUCHAR: |
| a = AMOVBU; |
| break; |
| case TSHORT: |
| a = AMOVHS; |
| break; |
| case TUSHORT: |
| a = AMOVHU; |
| break; |
| } |
| if(typechlp[ft] && typeilp[tt]) |
| regalloc(&nod, t, t); |
| else |
| regalloc(&nod, f, t); |
| gins(a, f, &nod); |
| gmove(&nod, t); |
| regfree(&nod); |
| return; |
| } |
| |
| /* |
| * a store -- |
| * put it into a register then |
| * store it. |
| */ |
| if(t->op == ONAME || t->op == OINDREG || t->op == OIND) { |
| switch(tt) { |
| default: |
| a = AMOVW; |
| break; |
| case TUCHAR: |
| a = AMOVBU; |
| break; |
| case TCHAR: |
| a = AMOVBS; |
| break; |
| case TUSHORT: |
| a = AMOVHU; |
| break; |
| case TSHORT: |
| a = AMOVHS; |
| break; |
| case TFLOAT: |
| a = AMOVF; |
| break; |
| case TVLONG: |
| case TDOUBLE: |
| a = AMOVD; |
| break; |
| } |
| if(ft == tt) |
| regalloc(&nod, t, f); |
| else |
| regalloc(&nod, t, Z); |
| gmove(f, &nod); |
| gins(a, &nod, t); |
| regfree(&nod); |
| return; |
| } |
| |
| /* |
| * type x type cross table |
| */ |
| a = AGOK; |
| switch(ft) { |
| case TDOUBLE: |
| case TVLONG: |
| case TFLOAT: |
| switch(tt) { |
| case TDOUBLE: |
| case TVLONG: |
| a = AMOVD; |
| if(ft == TFLOAT) |
| a = AMOVFD; |
| break; |
| case TFLOAT: |
| a = AMOVDF; |
| if(ft == TFLOAT) |
| a = AMOVF; |
| break; |
| case TINT: |
| case TUINT: |
| case TLONG: |
| case TULONG: |
| case TIND: |
| a = AMOVDW; |
| if(ft == TFLOAT) |
| a = AMOVFW; |
| break; |
| case TSHORT: |
| case TUSHORT: |
| case TCHAR: |
| case TUCHAR: |
| a = AMOVDW; |
| if(ft == TFLOAT) |
| a = AMOVFW; |
| break; |
| } |
| break; |
| case TUINT: |
| case TULONG: |
| if(tt == TFLOAT || tt == TDOUBLE) { |
| // ugly and probably longer than necessary, |
| // but vfp has a single instruction for this, |
| // so hopefully it won't last long. |
| // |
| // tmp = f |
| // tmp1 = tmp & 0x80000000 |
| // tmp ^= tmp1 |
| // t = float(int32(tmp)) |
| // if(tmp1) |
| // t += 2147483648. |
| // |
| regalloc(&nod, f, Z); |
| regalloc(&nod1, f, Z); |
| gins(AMOVW, f, &nod); |
| gins(AMOVW, &nod, &nod1); |
| gins(AAND, nodconst(0x80000000), &nod1); |
| gins(AEOR, &nod1, &nod); |
| if(tt == TFLOAT) |
| gins(AMOVWF, &nod, t); |
| else |
| gins(AMOVWD, &nod, t); |
| gins(ACMP, nodconst(0), Z); |
| raddr(&nod1, p); |
| gins(ABEQ, Z, Z); |
| regfree(&nod); |
| regfree(&nod1); |
| p1 = p; |
| regalloc(&nod, t, Z); |
| gins(AMOVF, nodfconst(2147483648.), &nod); |
| gins(AADDF, &nod, t); |
| regfree(&nod); |
| patch(p1, pc); |
| return; |
| } |
| // fall through |
| |
| case TINT: |
| case TLONG: |
| case TIND: |
| switch(tt) { |
| case TDOUBLE: |
| gins(AMOVWD, f, t); |
| return; |
| case TFLOAT: |
| gins(AMOVWF, f, t); |
| return; |
| case TINT: |
| case TUINT: |
| case TLONG: |
| case TULONG: |
| case TIND: |
| case TSHORT: |
| case TUSHORT: |
| case TCHAR: |
| case TUCHAR: |
| a = AMOVW; |
| break; |
| } |
| break; |
| case TSHORT: |
| switch(tt) { |
| case TDOUBLE: |
| regalloc(&nod, f, Z); |
| gins(AMOVHS, f, &nod); |
| gins(AMOVWD, &nod, t); |
| regfree(&nod); |
| return; |
| case TFLOAT: |
| regalloc(&nod, f, Z); |
| gins(AMOVHS, f, &nod); |
| gins(AMOVWF, &nod, t); |
| regfree(&nod); |
| return; |
| case TUINT: |
| case TINT: |
| case TULONG: |
| case TLONG: |
| case TIND: |
| a = AMOVHS; |
| break; |
| case TSHORT: |
| case TUSHORT: |
| case TCHAR: |
| case TUCHAR: |
| a = AMOVW; |
| break; |
| } |
| break; |
| case TUSHORT: |
| switch(tt) { |
| case TDOUBLE: |
| regalloc(&nod, f, Z); |
| gins(AMOVHU, f, &nod); |
| gins(AMOVWD, &nod, t); |
| regfree(&nod); |
| return; |
| case TFLOAT: |
| regalloc(&nod, f, Z); |
| gins(AMOVHU, f, &nod); |
| gins(AMOVWF, &nod, t); |
| regfree(&nod); |
| return; |
| case TINT: |
| case TUINT: |
| case TLONG: |
| case TULONG: |
| case TIND: |
| a = AMOVHU; |
| break; |
| case TSHORT: |
| case TUSHORT: |
| case TCHAR: |
| case TUCHAR: |
| a = AMOVW; |
| break; |
| } |
| break; |
| case TCHAR: |
| switch(tt) { |
| case TDOUBLE: |
| regalloc(&nod, f, Z); |
| gins(AMOVBS, f, &nod); |
| gins(AMOVWD, &nod, t); |
| regfree(&nod); |
| return; |
| case TFLOAT: |
| regalloc(&nod, f, Z); |
| gins(AMOVBS, f, &nod); |
| gins(AMOVWF, &nod, t); |
| regfree(&nod); |
| return; |
| case TINT: |
| case TUINT: |
| case TLONG: |
| case TULONG: |
| case TIND: |
| case TSHORT: |
| case TUSHORT: |
| a = AMOVBS; |
| break; |
| case TCHAR: |
| case TUCHAR: |
| a = AMOVW; |
| break; |
| } |
| break; |
| case TUCHAR: |
| switch(tt) { |
| case TDOUBLE: |
| regalloc(&nod, f, Z); |
| gins(AMOVBU, f, &nod); |
| gins(AMOVWD, &nod, t); |
| regfree(&nod); |
| return; |
| case TFLOAT: |
| regalloc(&nod, f, Z); |
| gins(AMOVBU, f, &nod); |
| gins(AMOVWF, &nod, t); |
| regfree(&nod); |
| return; |
| case TINT: |
| case TUINT: |
| case TLONG: |
| case TULONG: |
| case TIND: |
| case TSHORT: |
| case TUSHORT: |
| a = AMOVBU; |
| break; |
| case TCHAR: |
| case TUCHAR: |
| a = AMOVW; |
| break; |
| } |
| break; |
| } |
| if(a == AGOK) |
| diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type); |
| if(a == AMOVW || a == AMOVF || a == AMOVD) |
| if(samaddr(f, t)) |
| return; |
| gins(a, f, t); |
| } |
| |
| void |
| gmover(Node *f, Node *t) |
| { |
| int ft, tt, a; |
| |
| ft = f->type->etype; |
| tt = t->type->etype; |
| a = AGOK; |
| if(typechlp[ft] && typechlp[tt] && ewidth[ft] >= ewidth[tt]){ |
| switch(tt){ |
| case TSHORT: |
| a = AMOVHS; |
| break; |
| case TUSHORT: |
| a = AMOVHU; |
| break; |
| case TCHAR: |
| a = AMOVBS; |
| break; |
| case TUCHAR: |
| a = AMOVBU; |
| break; |
| } |
| } |
| if(a == AGOK) |
| gmove(f, t); |
| else |
| gins(a, f, t); |
| } |
| |
| void |
| gins(int a, Node *f, Node *t) |
| { |
| |
| nextpc(); |
| p->as = a; |
| if(f != Z) |
| naddr(f, &p->from); |
| if(t != Z) |
| naddr(t, &p->to); |
| if(debug['g']) |
| print("%P\n", p); |
| } |
| |
| void |
| gopcode(int o, Node *f1, Node *f2, Node *t) |
| { |
| int a, et; |
| Addr ta; |
| |
| et = TLONG; |
| if(f1 != Z && f1->type != T) |
| et = f1->type->etype; |
| a = AGOK; |
| switch(o) { |
| case OAS: |
| gmove(f1, t); |
| return; |
| |
| case OASADD: |
| case OADD: |
| a = AADD; |
| if(et == TFLOAT) |
| a = AADDF; |
| else |
| if(et == TDOUBLE || et == TVLONG) |
| a = AADDD; |
| break; |
| |
| case OASSUB: |
| case OSUB: |
| if(f2 && f2->op == OCONST) { |
| Node *t = f1; |
| f1 = f2; |
| f2 = t; |
| a = ARSB; |
| } else |
| a = ASUB; |
| if(et == TFLOAT) |
| a = ASUBF; |
| else |
| if(et == TDOUBLE || et == TVLONG) |
| a = ASUBD; |
| break; |
| |
| case OASOR: |
| case OOR: |
| a = AORR; |
| break; |
| |
| case OASAND: |
| case OAND: |
| a = AAND; |
| break; |
| |
| case OASXOR: |
| case OXOR: |
| a = AEOR; |
| break; |
| |
| case OASLSHR: |
| case OLSHR: |
| a = ASRL; |
| break; |
| |
| case OASASHR: |
| case OASHR: |
| a = ASRA; |
| break; |
| |
| case OASASHL: |
| case OASHL: |
| a = ASLL; |
| break; |
| |
| case OFUNC: |
| a = ABL; |
| break; |
| |
| case OASMUL: |
| case OMUL: |
| a = AMUL; |
| if(et == TFLOAT) |
| a = AMULF; |
| else |
| if(et == TDOUBLE || et == TVLONG) |
| a = AMULD; |
| break; |
| |
| case OASDIV: |
| case ODIV: |
| a = ADIV; |
| if(et == TFLOAT) |
| a = ADIVF; |
| else |
| if(et == TDOUBLE || et == TVLONG) |
| a = ADIVD; |
| break; |
| |
| case OASMOD: |
| case OMOD: |
| a = AMOD; |
| break; |
| |
| case OASLMUL: |
| case OLMUL: |
| a = AMULU; |
| break; |
| |
| case OASLMOD: |
| case OLMOD: |
| a = AMODU; |
| break; |
| |
| case OASLDIV: |
| case OLDIV: |
| a = ADIVU; |
| break; |
| |
| case OCASE: |
| case OEQ: |
| case ONE: |
| case OLT: |
| case OLE: |
| case OGE: |
| case OGT: |
| case OLO: |
| case OLS: |
| case OHS: |
| case OHI: |
| a = ACMP; |
| if(et == TFLOAT) |
| a = ACMPF; |
| else |
| if(et == TDOUBLE || et == TVLONG) |
| a = ACMPD; |
| nextpc(); |
| p->as = a; |
| naddr(f1, &p->from); |
| if(a == ACMP && f1->op == OCONST && p->from.offset < 0) { |
| p->as = ACMN; |
| p->from.offset = -p->from.offset; |
| } |
| raddr(f2, p); |
| switch(o) { |
| case OEQ: |
| a = ABEQ; |
| break; |
| case ONE: |
| a = ABNE; |
| break; |
| case OLT: |
| a = ABLT; |
| break; |
| case OLE: |
| a = ABLE; |
| break; |
| case OGE: |
| a = ABGE; |
| break; |
| case OGT: |
| a = ABGT; |
| break; |
| case OLO: |
| a = ABLO; |
| break; |
| case OLS: |
| a = ABLS; |
| break; |
| case OHS: |
| a = ABHS; |
| break; |
| case OHI: |
| a = ABHI; |
| break; |
| case OCASE: |
| nextpc(); |
| p->as = ACASE; |
| p->scond = 0x9; |
| naddr(f2, &p->from); |
| a = ABHI; |
| break; |
| } |
| f1 = Z; |
| f2 = Z; |
| break; |
| } |
| if(a == AGOK) |
| diag(Z, "bad in gopcode %O", o); |
| nextpc(); |
| p->as = a; |
| if(f1 != Z) |
| naddr(f1, &p->from); |
| if(f2 != Z) { |
| naddr(f2, &ta); |
| p->reg = ta.reg; |
| } |
| if(t != Z) |
| naddr(t, &p->to); |
| if(debug['g']) |
| print("%P\n", p); |
| } |
| |
| int |
| samaddr(Node *f, Node *t) |
| { |
| |
| if(f->op != t->op) |
| return 0; |
| switch(f->op) { |
| |
| case OREGISTER: |
| if(f->reg != t->reg) |
| break; |
| return 1; |
| } |
| return 0; |
| } |
| |
| void |
| gbranch(int o) |
| { |
| int a; |
| |
| a = AGOK; |
| switch(o) { |
| case ORETURN: |
| a = ARET; |
| break; |
| case OGOTO: |
| a = AB; |
| break; |
| } |
| nextpc(); |
| if(a == AGOK) { |
| diag(Z, "bad in gbranch %O", o); |
| nextpc(); |
| } |
| p->as = a; |
| } |
| |
| void |
| patch(Prog *op, int32 pc) |
| { |
| |
| op->to.offset = pc; |
| op->to.type = D_BRANCH; |
| } |
| |
| void |
| gpseudo(int a, Sym *s, Node *n) |
| { |
| nextpc(); |
| p->as = a; |
| p->from.type = D_OREG; |
| p->from.sym = linksym(s); |
| p->from.name = D_EXTERN; |
| |
| switch(a) { |
| case ATEXT: |
| p->reg = textflag; |
| textflag = 0; |
| break; |
| case AGLOBL: |
| p->reg = s->dataflag; |
| break; |
| } |
| |
| if(s->class == CSTATIC) |
| p->from.name = D_STATIC; |
| naddr(n, &p->to); |
| if(a == ADATA || a == AGLOBL) |
| pc--; |
| } |
| |
| void |
| gpcdata(int index, int value) |
| { |
| Node n1; |
| |
| n1 = *nodconst(index); |
| gins(APCDATA, &n1, nodconst(value)); |
| } |
| |
| void |
| gprefetch(Node *n) |
| { |
| Node n1; |
| |
| regalloc(&n1, n, Z); |
| gmove(n, &n1); |
| n1.op = OINDREG; |
| gins(APLD, &n1, Z); |
| regfree(&n1); |
| } |
| |
| int |
| sconst(Node *n) |
| { |
| vlong vv; |
| |
| if(n->op == OCONST) { |
| if(!typefd[n->type->etype]) { |
| vv = n->vconst; |
| if(vv >= (vlong)(-32766) && vv < (vlong)32766) |
| return 1; |
| /* |
| * should be specialised for constant values which will |
| * fit in different instructionsl; for now, let 5l |
| * sort it out |
| */ |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| int |
| sval(int32 v) |
| { |
| int i; |
| |
| for(i=0; i<16; i++) { |
| if((v & ~0xff) == 0) |
| return 1; |
| if((~v & ~0xff) == 0) |
| return 1; |
| v = (v<<2) | ((uint32)v>>30); |
| } |
| return 0; |
| } |
| |
| int32 |
| exreg(Type *t) |
| { |
| int32 o; |
| |
| if(typechlp[t->etype]) { |
| if(exregoffset <= REGEXT-4) |
| return 0; |
| o = exregoffset; |
| exregoffset--; |
| return o; |
| } |
| if(typefd[t->etype]) { |
| if(exfregoffset <= NFREG-1) |
| return 0; |
| o = exfregoffset + NREG; |
| exfregoffset--; |
| return o; |
| } |
| return 0; |
| } |
| |
| schar ewidth[NTYPE] = |
| { |
| -1, /* [TXXX] */ |
| SZ_CHAR, /* [TCHAR] */ |
| SZ_CHAR, /* [TUCHAR] */ |
| SZ_SHORT, /* [TSHORT] */ |
| SZ_SHORT, /* [TUSHORT] */ |
| SZ_INT, /* [TINT] */ |
| SZ_INT, /* [TUINT] */ |
| SZ_LONG, /* [TLONG] */ |
| SZ_LONG, /* [TULONG] */ |
| SZ_VLONG, /* [TVLONG] */ |
| SZ_VLONG, /* [TUVLONG] */ |
| SZ_FLOAT, /* [TFLOAT] */ |
| SZ_DOUBLE, /* [TDOUBLE] */ |
| SZ_IND, /* [TIND] */ |
| 0, /* [TFUNC] */ |
| -1, /* [TARRAY] */ |
| 0, /* [TVOID] */ |
| -1, /* [TSTRUCT] */ |
| -1, /* [TUNION] */ |
| SZ_INT, /* [TENUM] */ |
| }; |
| |
| int32 ncast[NTYPE] = |
| { |
| 0, /* [TXXX] */ |
| BCHAR|BUCHAR, /* [TCHAR] */ |
| BCHAR|BUCHAR, /* [TUCHAR] */ |
| BSHORT|BUSHORT, /* [TSHORT] */ |
| BSHORT|BUSHORT, /* [TUSHORT] */ |
| BINT|BUINT|BLONG|BULONG|BIND, /* [TINT] */ |
| BINT|BUINT|BLONG|BULONG|BIND, /* [TUINT] */ |
| BINT|BUINT|BLONG|BULONG|BIND, /* [TLONG] */ |
| BINT|BUINT|BLONG|BULONG|BIND, /* [TULONG] */ |
| BVLONG|BUVLONG, /* [TVLONG] */ |
| BVLONG|BUVLONG, /* [TUVLONG] */ |
| BFLOAT, /* [TFLOAT] */ |
| BDOUBLE, /* [TDOUBLE] */ |
| BLONG|BULONG|BIND, /* [TIND] */ |
| 0, /* [TFUNC] */ |
| 0, /* [TARRAY] */ |
| 0, /* [TVOID] */ |
| BSTRUCT, /* [TSTRUCT] */ |
| BUNION, /* [TUNION] */ |
| 0, /* [TENUM] */ |
| }; |