| // Inferno utils/cc/scon.c |
| // http://code.google.com/p/inferno-os/source/browse/utils/cc/scon.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 "cc.h" |
| |
| static Node* |
| acast(Type *t, Node *n) |
| { |
| if(n->type->etype != t->etype || n->op == OBIT) { |
| n = new1(OCAST, n, Z); |
| if(nocast(n->left->type, t)) |
| *n = *n->left; |
| n->type = t; |
| } |
| return n; |
| } |
| |
| |
| void |
| evconst(Node *n) |
| { |
| Node *l, *r; |
| int et, isf; |
| vlong v; |
| double d; |
| |
| if(n == Z || n->type == T) |
| return; |
| |
| et = n->type->etype; |
| isf = typefd[et]; |
| |
| l = n->left; |
| r = n->right; |
| |
| d = 0; |
| v = 0; |
| |
| switch(n->op) { |
| default: |
| return; |
| |
| case ONEG: |
| if(isf) |
| d = -l->fconst; |
| else |
| v = -l->vconst; |
| break; |
| |
| case OCOM: |
| v = ~l->vconst; |
| break; |
| |
| case OCAST: |
| if(et == TVOID) |
| return; |
| et = l->type->etype; |
| if(isf) { |
| if(typefd[et]) |
| d = l->fconst; |
| else |
| d = l->vconst; |
| } else { |
| if(typefd[et]) |
| v = l->fconst; |
| else |
| v = convvtox(l->vconst, n->type->etype); |
| } |
| break; |
| |
| case OCONST: |
| break; |
| |
| case OADD: |
| if(isf) |
| d = l->fconst + r->fconst; |
| else { |
| v = l->vconst + r->vconst; |
| } |
| break; |
| |
| case OSUB: |
| if(isf) |
| d = l->fconst - r->fconst; |
| else |
| v = l->vconst - r->vconst; |
| break; |
| |
| case OMUL: |
| if(isf) |
| d = l->fconst * r->fconst; |
| else { |
| v = l->vconst * r->vconst; |
| } |
| break; |
| |
| case OLMUL: |
| v = (uvlong)l->vconst * (uvlong)r->vconst; |
| break; |
| |
| |
| case ODIV: |
| if(vconst(r) == 0) { |
| warn(n, "divide by zero"); |
| return; |
| } |
| if(isf) |
| d = l->fconst / r->fconst; |
| else |
| v = l->vconst / r->vconst; |
| break; |
| |
| case OLDIV: |
| if(vconst(r) == 0) { |
| warn(n, "divide by zero"); |
| return; |
| } |
| v = (uvlong)l->vconst / (uvlong)r->vconst; |
| break; |
| |
| case OMOD: |
| if(vconst(r) == 0) { |
| warn(n, "modulo by zero"); |
| return; |
| } |
| v = l->vconst % r->vconst; |
| break; |
| |
| case OLMOD: |
| if(vconst(r) == 0) { |
| warn(n, "modulo by zero"); |
| return; |
| } |
| v = (uvlong)l->vconst % (uvlong)r->vconst; |
| break; |
| |
| case OAND: |
| v = l->vconst & r->vconst; |
| break; |
| |
| case OOR: |
| v = l->vconst | r->vconst; |
| break; |
| |
| case OXOR: |
| v = l->vconst ^ r->vconst; |
| break; |
| |
| case OLSHR: |
| v = (uvlong)l->vconst >> r->vconst; |
| break; |
| |
| case OASHR: |
| v = l->vconst >> r->vconst; |
| break; |
| |
| case OASHL: |
| v = l->vconst << r->vconst; |
| break; |
| |
| case OLO: |
| v = (uvlong)l->vconst < (uvlong)r->vconst; |
| break; |
| |
| case OLT: |
| if(typefd[l->type->etype]) |
| v = l->fconst < r->fconst; |
| else |
| v = l->vconst < r->vconst; |
| break; |
| |
| case OHI: |
| v = (uvlong)l->vconst > (uvlong)r->vconst; |
| break; |
| |
| case OGT: |
| if(typefd[l->type->etype]) |
| v = l->fconst > r->fconst; |
| else |
| v = l->vconst > r->vconst; |
| break; |
| |
| case OLS: |
| v = (uvlong)l->vconst <= (uvlong)r->vconst; |
| break; |
| |
| case OLE: |
| if(typefd[l->type->etype]) |
| v = l->fconst <= r->fconst; |
| else |
| v = l->vconst <= r->vconst; |
| break; |
| |
| case OHS: |
| v = (uvlong)l->vconst >= (uvlong)r->vconst; |
| break; |
| |
| case OGE: |
| if(typefd[l->type->etype]) |
| v = l->fconst >= r->fconst; |
| else |
| v = l->vconst >= r->vconst; |
| break; |
| |
| case OEQ: |
| if(typefd[l->type->etype]) |
| v = l->fconst == r->fconst; |
| else |
| v = l->vconst == r->vconst; |
| break; |
| |
| case ONE: |
| if(typefd[l->type->etype]) |
| v = l->fconst != r->fconst; |
| else |
| v = l->vconst != r->vconst; |
| break; |
| |
| case ONOT: |
| if(typefd[l->type->etype]) |
| v = !l->fconst; |
| else |
| v = !l->vconst; |
| break; |
| |
| case OANDAND: |
| if(typefd[l->type->etype]) |
| v = l->fconst && r->fconst; |
| else |
| v = l->vconst && r->vconst; |
| break; |
| |
| case OOROR: |
| if(typefd[l->type->etype]) |
| v = l->fconst || r->fconst; |
| else |
| v = l->vconst || r->vconst; |
| break; |
| } |
| if(isf) { |
| n->fconst = d; |
| } else { |
| n->vconst = convvtox(v, n->type->etype); |
| } |
| n->oldop = n->op; |
| n->op = OCONST; |
| } |
| |
| void |
| acom(Node *n) |
| { |
| Type *t; |
| Node *l, *r; |
| int i; |
| |
| switch(n->op) |
| { |
| |
| case ONAME: |
| case OCONST: |
| case OSTRING: |
| case OINDREG: |
| case OREGISTER: |
| return; |
| |
| case ONEG: |
| l = n->left; |
| if(addo(n) && addo(l)) |
| break; |
| acom(l); |
| return; |
| |
| case OADD: |
| case OSUB: |
| case OMUL: |
| l = n->left; |
| r = n->right; |
| if(addo(n)) { |
| if(addo(r)) |
| break; |
| if(addo(l)) |
| break; |
| } |
| acom(l); |
| acom(r); |
| return; |
| |
| default: |
| l = n->left; |
| r = n->right; |
| if(l != Z) |
| acom(l); |
| if(r != Z) |
| acom(r); |
| return; |
| } |
| |
| /* bust terms out */ |
| t = n->type; |
| term[0].mult = 0; |
| term[0].node = Z; |
| nterm = 1; |
| acom1(1, n); |
| if(debug['m']) |
| for(i=0; i<nterm; i++) { |
| print("%d %3lld ", i, term[i].mult); |
| prtree1(term[i].node, 1, 0); |
| } |
| if(nterm < NTERM) |
| acom2(n, t); |
| n->type = t; |
| } |
| |
| int |
| acomcmp1(const void *a1, const void *a2) |
| { |
| vlong c1, c2; |
| Term *t1, *t2; |
| |
| t1 = (Term*)a1; |
| t2 = (Term*)a2; |
| c1 = t1->mult; |
| if(c1 < 0) |
| c1 = -c1; |
| c2 = t2->mult; |
| if(c2 < 0) |
| c2 = -c2; |
| if(c1 > c2) |
| return 1; |
| if(c1 < c2) |
| return -1; |
| c1 = 1; |
| if(t1->mult < 0) |
| c1 = 0; |
| c2 = 1; |
| if(t2->mult < 0) |
| c2 = 0; |
| if(c2 -= c1) |
| return c2; |
| if(t2 > t1) |
| return 1; |
| return -1; |
| } |
| |
| int |
| acomcmp2(const void *a1, const void *a2) |
| { |
| vlong c1, c2; |
| Term *t1, *t2; |
| |
| t1 = (Term*)a1; |
| t2 = (Term*)a2; |
| c1 = t1->mult; |
| c2 = t2->mult; |
| if(c1 > c2) |
| return 1; |
| if(c1 < c2) |
| return -1; |
| if(t2 > t1) |
| return 1; |
| return -1; |
| } |
| |
| void |
| acom2(Node *n, Type *t) |
| { |
| Node *l, *r; |
| Term trm[NTERM]; |
| int et, nt, i, j; |
| vlong c1, c2; |
| |
| /* |
| * copy into automatic |
| */ |
| c2 = 0; |
| nt = nterm; |
| for(i=0; i<nt; i++) |
| trm[i] = term[i]; |
| /* |
| * recur on subtrees |
| */ |
| j = 0; |
| for(i=1; i<nt; i++) { |
| c1 = trm[i].mult; |
| if(c1 == 0) |
| continue; |
| l = trm[i].node; |
| if(l != Z) { |
| j = 1; |
| acom(l); |
| } |
| } |
| c1 = trm[0].mult; |
| if(j == 0) { |
| n->oldop = n->op; |
| n->op = OCONST; |
| n->vconst = c1; |
| return; |
| } |
| et = t->etype; |
| |
| /* |
| * prepare constant term, |
| * combine it with an addressing term |
| */ |
| if(c1 != 0) { |
| l = new1(OCONST, Z, Z); |
| l->type = t; |
| l->vconst = c1; |
| trm[0].mult = 1; |
| for(i=1; i<nt; i++) { |
| if(trm[i].mult != 1) |
| continue; |
| r = trm[i].node; |
| if(r->op != OADDR) |
| continue; |
| r->type = t; |
| l = new1(OADD, r, l); |
| l->type = t; |
| trm[i].mult = 0; |
| break; |
| } |
| trm[0].node = l; |
| } |
| /* |
| * look for factorable terms |
| * c1*i + c1*c2*j -> c1*(i + c2*j) |
| */ |
| qsort(trm+1, nt-1, sizeof(trm[0]), acomcmp1); |
| for(i=nt-1; i>=0; i--) { |
| c1 = trm[i].mult; |
| if(c1 < 0) |
| c1 = -c1; |
| if(c1 <= 1) |
| continue; |
| for(j=i+1; j<nt; j++) { |
| c2 = trm[j].mult; |
| if(c2 < 0) |
| c2 = -c2; |
| if(c2 <= 1) |
| continue; |
| if(c2 % c1) |
| continue; |
| r = trm[j].node; |
| if(r->type->etype != et) |
| r = acast(t, r); |
| c2 = trm[j].mult/trm[i].mult; |
| if(c2 != 1 && c2 != -1) { |
| r = new1(OMUL, r, new(OCONST, Z, Z)); |
| r->type = t; |
| r->right->type = t; |
| r->right->vconst = c2; |
| } |
| l = trm[i].node; |
| if(l->type->etype != et) |
| l = acast(t, l); |
| r = new1(OADD, l, r); |
| r->type = t; |
| if(c2 == -1) |
| r->op = OSUB; |
| trm[i].node = r; |
| trm[j].mult = 0; |
| } |
| } |
| if(debug['m']) { |
| print("\n"); |
| for(i=0; i<nt; i++) { |
| print("%d %3lld ", i, trm[i].mult); |
| prtree1(trm[i].node, 1, 0); |
| } |
| } |
| |
| /* |
| * put it all back together |
| */ |
| qsort(trm+1, nt-1, sizeof(trm[0]), acomcmp2); |
| l = Z; |
| for(i=nt-1; i>=0; i--) { |
| c1 = trm[i].mult; |
| if(c1 == 0) |
| continue; |
| r = trm[i].node; |
| if(r->type->etype != et || r->op == OBIT) |
| r = acast(t, r); |
| if(c1 != 1 && c1 != -1) { |
| r = new1(OMUL, r, new(OCONST, Z, Z)); |
| r->type = t; |
| r->right->type = t; |
| if(c1 < 0) { |
| r->right->vconst = -c1; |
| c1 = -1; |
| } else { |
| r->right->vconst = c1; |
| c1 = 1; |
| } |
| } |
| if(l == Z) { |
| l = r; |
| c2 = c1; |
| continue; |
| } |
| if(c1 < 0) |
| if(c2 < 0) |
| l = new1(OADD, l, r); |
| else |
| l = new1(OSUB, l, r); |
| else |
| if(c2 < 0) { |
| l = new1(OSUB, r, l); |
| c2 = 1; |
| } else |
| l = new1(OADD, l, r); |
| l->type = t; |
| } |
| if(c2 < 0) { |
| r = new1(OCONST, 0, 0); |
| r->vconst = 0; |
| r->type = t; |
| l = new1(OSUB, r, l); |
| l->type = t; |
| } |
| *n = *l; |
| } |
| |
| void |
| acom1(vlong v, Node *n) |
| { |
| Node *l, *r; |
| |
| if(v == 0 || nterm >= NTERM) |
| return; |
| if(!addo(n)) { |
| if(n->op == OCONST) |
| if(!typefd[n->type->etype]) { |
| term[0].mult += v*n->vconst; |
| return; |
| } |
| term[nterm].mult = v; |
| term[nterm].node = n; |
| nterm++; |
| return; |
| } |
| switch(n->op) { |
| |
| case OCAST: |
| acom1(v, n->left); |
| break; |
| |
| case ONEG: |
| acom1(-v, n->left); |
| break; |
| |
| case OADD: |
| acom1(v, n->left); |
| acom1(v, n->right); |
| break; |
| |
| case OSUB: |
| acom1(v, n->left); |
| acom1(-v, n->right); |
| break; |
| |
| case OMUL: |
| l = n->left; |
| r = n->right; |
| if(l->op == OCONST) |
| if(!typefd[n->type->etype]) { |
| acom1(v*l->vconst, r); |
| break; |
| } |
| if(r->op == OCONST) |
| if(!typefd[n->type->etype]) { |
| acom1(v*r->vconst, l); |
| break; |
| } |
| break; |
| |
| default: |
| diag(n, "not addo"); |
| } |
| } |
| |
| int |
| addo(Node *n) |
| { |
| |
| if(n != Z) |
| if(!typefd[n->type->etype]) |
| if(!typev[n->type->etype] || ewidth[TVLONG] == ewidth[TIND]) |
| switch(n->op) { |
| |
| case OCAST: |
| if(nilcast(n->left->type, n->type)) |
| return 1; |
| break; |
| |
| case ONEG: |
| case OADD: |
| case OSUB: |
| return 1; |
| |
| case OMUL: |
| if(n->left->op == OCONST) |
| return 1; |
| if(n->right->op == OCONST) |
| return 1; |
| } |
| return 0; |
| } |