| // 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: | 
 | 		if(l->type->width != sizeof(uvlong)) | 
 | 			v = ((uvlong)l->vconst & 0xffffffffULL) >> r->vconst; | 
 | 		else | 
 | 			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; | 
 | } |