blob: 193331f774d476fbb6a25fdd71741a717b6c3f05 [file] [log] [blame]
// 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;
}