|  | // 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 <u.h> | 
|  | #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; | 
|  | } |