#include "gc.h"

int
swcmp(const void *a1, const void *a2)
{
	C1 *p1, *p2;

	p1 = (C1*)a1;
	p2 = (C1*)a2;
	if(p1->val < p2->val)
		return -1;
	return p1->val > p2->val;
}

void
doswit(Node *n)
{
	Case *c;
	C1 *q, *iq, *iqh, *iql;
	long def, nc, i, j, isv, nh;
	Prog *hsb;
	Node *vr[2];
	int dup;

	def = 0;
	nc = 0;
	isv = 0;
	for(c = cases; c->link != C; c = c->link) {
		if(c->def) {
			if(def)
				diag(n, "more than one default in switch");
			def = c->label;
			continue;
		}
		isv |= c->isv;
		nc++;
	}
	if(typev[n->type->etype])
		isv = 1;
	else if(isv){
		warn(n, "32-bit switch expression with 64-bit case constant");
		isv = 0;
	}

	iq = alloc(nc*sizeof(C1));
	q = iq;
	for(c = cases; c->link != C; c = c->link) {
		if(c->def)
			continue;
		if(c->isv && !isv)
			continue;	/* can never match */
		q->label = c->label;
		if(isv)
			q->val = c->val;
		else
			q->val = (long)c->val;	/* cast ensures correct value for 32-bit switch on 64-bit architecture */
		q++;
	}
	qsort(iq, nc, sizeof(C1), swcmp);
	if(debug['W'])
	for(i=0; i<nc; i++)
		print("case %2ld: = %.8llux\n", i, (vlong)iq[i].val);
	dup = 0;
	for(i=0; i<nc-1; i++)
		if(iq[i].val == iq[i+1].val) {
			diag(n, "duplicate cases in switch %lld", (vlong)iq[i].val);
			dup = 1;
		}
	if(dup)
		return;
	if(def == 0) {
		def = breakpc;
		nbreak++;
	}
	if(!isv || ewidth[TIND] > ewidth[TLONG] || n->op == OREGISTER) {
		swit1(iq, nc, def, n);
		return;
	}

	/*
	 * 64-bit case on 32-bit machine:
	 * switch on high-order words, and
	 * in each of those, switch on low-order words
	 */
	if(n->op != OREGPAIR)
		fatal(n, "internal: expected register pair");
	if(thechar == '8'){	/* TO DO: need an enquiry function */
		vr[0] = n->left;	/* low */
		vr[1] = n->right;	/* high */
	}else{
		vr[0] = n->right;
		vr[1] = n->left;
	}
	vr[0]->type = types[TLONG];
	vr[1]->type = types[TLONG];
	gbranch(OGOTO);
	hsb = p;
	iqh = alloc(nc*sizeof(C1));
	iql = alloc(nc*sizeof(C1));
	nh = 0;
	for(i=0; i<nc;){
		iqh[nh].val = iq[i].val >> 32;
		q = iql;
		/* iq is sorted, so equal top halves are adjacent */
		for(j = i; j < nc; j++){
			if((iq[j].val>>32) != iqh[nh].val)
				break;
			q->val = (long)iq[j].val;
			q->label = iq[j].label;
			q++;
		}
		qsort(iql,  q-iql, sizeof(C1), swcmp);
if(0){for(int k=0; k<(q-iql); k++)print("nh=%ld k=%d h=%#llux l=%#llux lab=%ld\n", nh, k, (vlong)iqh[nh].val,  (vlong)iql[k].val, iql[k].label);}
		iqh[nh].label = pc;
		nh++;
		swit1(iql, q-iql, def, vr[0]);
		i = j;
	}
	patch(hsb, pc);
if(0){for(int k=0; k<nh; k++)print("k*=%d h=%#llux lab=%ld\n", k, (vlong)iqh[k].val,  iqh[k].label);}
	swit1(iqh, nh, def, vr[1]);
}

void
casf(void)
{
	Case *c;

	c = alloc(sizeof(*c));
	c->link = cases;
	cases = c;
}

long
outlstring(Rune *s, long n)
{
	char buf[sizeof(Rune)];
	int c, i;
	long r;

	if(suppress)
		return nstring;
	while(nstring % sizeof buf)
		outstring("", 1);
	r = nstring;
	while(n > 0) {
		c = *s++;
		if(align(0, types[TCHAR], Aarg1)) {
			for(i = sizeof buf; i > 0; c >>= 8)
				buf[--i] = c;
		} else {
			for(i = 0; i < sizeof buf; c >>= 8)
				buf[i++] = c;
		}
		outstring(buf, sizeof buf);
		n -= sizeof buf;
	}
	return r;
}

void
nullwarn(Node *l, Node *r)
{
	warn(Z, "result of operation not used");
	if(l != Z)
		cgen(l, Z);
	if(r != Z)
		cgen(r, Z);
}

void
ieeedtod(Ieee *ieee, double native)
{
	double fr, ho, f;
	int exp;

	if(native < 0) {
		ieeedtod(ieee, -native);
		ieee->h |= 0x80000000L;
		return;
	}
	if(native == 0) {
		ieee->l = 0;
		ieee->h = 0;
		return;
	}
	fr = frexp(native, &exp);
	f = 2097152L;		/* shouldnt use fp constants here */
	fr = modf(fr*f, &ho);
	ieee->h = ho;
	ieee->h &= 0xfffffL;
	ieee->h |= (exp+1022L) << 20;
	f = 65536L;
	fr = modf(fr*f, &ho);
	ieee->l = ho;
	ieee->l <<= 16;
	ieee->l |= (long)(fr*f);
}
