/*	$Id: local2.c,v 1.61 2015/02/07 08:47:54 ragge Exp $	*/
/*
 * Copyright (c) 2008 Michael Shalayeff
 * Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

# include "pass2.h"
# include <ctype.h>
# include <string.h>

static int stkpos;

void
deflab(int label)
{
	printf(LABFMT ":\n", label);
}

static int regoff[MAXREGS];
static TWORD ftype;
char *rbyte[], *rshort[], *rlong[];
static int needframe;

/*
 * Print out the prolog assembler.
 * addto and regoff are already calculated.
 */
static void
prtprolog(struct interpass_prolog *ipp, int addto)
{
	int i;

	printf("\tpushq %%rbp\n");
	printf("\tmovq %%rsp,%%rbp\n");
	addto = (addto+15) & ~15; /* 16-byte aligned */
	if (addto)
		printf("\tsubq $%d,%%rsp\n", addto);

	/* save permanent registers */
	for (i = 0; i < MAXREGS; i++)
		if (TESTBIT(ipp->ipp_regs, i))
			printf("\tmovq %s,-%d(%s)\n",
			    rnames[i], regoff[i], rnames[FPREG]);
}

/*
 * calculate stack size and offsets
 */
static int
offcalc(struct interpass_prolog *ipp)
{
	int i, addto;

	addto = p2maxautooff;
	if (addto >= AUTOINIT/SZCHAR)
		addto -= AUTOINIT/SZCHAR;
	for (i = 0; i < MAXREGS; i++)
		if (TESTBIT(ipp->ipp_regs, i)) {
			addto += SZLONG/SZCHAR;
			regoff[i] = addto;
		}
	return addto;
}

/*
 * Traverse a tree to check if we need to emit a frame at all.
 * We emit it if:
 * - any function call
 * - rsp or rbp referenced
 * Return 1 if frame is needed, 0 otherwise.
 */
static int
chkf(NODE *p)
{
	int o = p->n_op;

	if ((o == REG || o == OREG) && (regno(p) == RBP || regno(p) == RSP))
		return 1;
	if (callop(o))
		return 1;
	if (optype(o) == UTYPE)
		return chkf(p->n_left);
	else if (optype(o) == BITYPE)
		return chkf(p->n_left) || chkf(p->n_right);
	return 0;
}

static int
chkframe(struct interpass_prolog *ipp)
{
	struct interpass *ip;

	DLIST_FOREACH(ip, &ipp->ipp_ip, qelem) {
		if (ip->type == IP_EPILOG)
			break;
		if (ip->type == IP_NODE) {
			if (chkf(ip->ip_node))
				return 1;
		}
	}
	return 0;
}

void
prologue(struct interpass_prolog *ipp)
{
	int addto;

	ftype = ipp->ipp_type;

	if (xdeljumps)
		needframe = chkframe(ipp);
	else
		needframe = 1;

#ifdef LANG_F77
	if (ipp->ipp_vis)
		printf("	.globl %s\n", ipp->ipp_name);
	printf("	.align 16\n");
	printf("%s:\n", ipp->ipp_name);
#endif
	/*
	 * We here know what register to save and how much to 
	 * add to the stack.
	 */
	addto = offcalc(ipp);
	if (addto)
		needframe = 1;
	if (needframe)
		prtprolog(ipp, addto);
}

void
eoftn(struct interpass_prolog *ipp)
{
	int i;

	if (ipp->ipp_ip.ip_lbl == 0)
		return; /* no code needs to be generated */

	if (needframe) {
		/* return from function code */
		for (i = 0; i < MAXREGS; i++)
			if (TESTBIT(ipp->ipp_regs, i))
				printf("	movq -%d(%s),%s\n",
				    regoff[i], rnames[FPREG], rnames[i]);

		/* struct return needs special treatment */
		if (ftype == STRTY || ftype == UNIONTY) {
			printf("	movl 8(%%ebp),%%eax\n");
			printf("	leave\n");
			printf("	ret $%d\n", 4);
		} else {
			printf("	leave\n");
			printf("	ret\n");
		}
	} else
		printf("\tret\n");

#ifndef MACHOABI
	printf("\t.size %s,.-%s\n", ipp->ipp_name, ipp->ipp_name);
#endif
}

/*
 * add/sub/...
 *
 * Param given:
 */
void
hopcode(int f, int o)
{
	char *str;

	switch (o) {
	case PLUS:
		str = "add";
		break;
	case MINUS:
		str = "sub";
		break;
	case AND:
		str = "and";
		break;
	case OR:
		str = "or";
		break;
	case ER:
		str = "xor";
		break;
	default:
		comperr("hopcode2: %d", o);
		str = 0; /* XXX gcc */
	}
	printf("%s%c", str, f);
}

/*
 * Return type size in bytes.  Used by R2REGS, arg 2 to offset().
 */
int
tlen(NODE *p)
{
	switch(p->n_type) {
		case CHAR:
		case UCHAR:
			return(1);

		case SHORT:
		case USHORT:
			return(SZSHORT/SZCHAR);

		case DOUBLE:
			return(SZDOUBLE/SZCHAR);

		case INT:
		case UNSIGNED:
			return(SZINT/SZCHAR);

		case LONG:
		case ULONG:
		case LONGLONG:
		case ULONGLONG:
			return SZLONGLONG/SZCHAR;

		default:
			if (!ISPTR(p->n_type))
				comperr("tlen type %d not pointer");
			return SZPOINT(p->n_type)/SZCHAR;
		}
}

/*
 * Compare two floating point numbers.
 */
static void
fcomp(NODE *p)	
{

	if (p->n_left->n_op != REG)
		comperr("bad compare %p\n", p);
	if ((p->n_su & DORIGHT) == 0)
		expand(p, 0, "\tfxch\n");
	expand(p, 0, "\tfucomip %st(1),%st\n");	/* emit compare insn  */
	expand(p, 0, "\tfstp %st(0)\n");	/* pop fromstack */
	zzzcode(p, 'U');
}

int
fldexpand(NODE *p, int cookie, char **cp)
{
	comperr("fldexpand");
	return 0;
}

static void
stasg(NODE *p)
{
	struct attr *ap = attr_find(p->n_ap, ATTR_P2STRUCT);
	expand(p, INAREG, "	leaq AL,%rdi\n");
	if (ap->iarg(0) >= 8)
		printf("\tmovl $%d,%%ecx\n\trep movsq\n", ap->iarg(0) >> 3);
	if (ap->iarg(0) & 4)
		printf("\tmovsl\n");
	if (ap->iarg(0) & 2)
		printf("\tmovsw\n");
	if (ap->iarg(0) & 1)
		printf("\tmovsb\n");
}

#define	E(x)	expand(p, 0, x)
/*
 * Generate code to convert an unsigned long to xmm float/double.
 */
static void
ultofd(NODE *p)
{

	E("	movq AL,A1\n");
	E("	testq A1,A1\n");
	E("	js 2f\n");
	E("	cvtsi2sZfq A1,A3\n");
	E("	jmp 3f\n");
	E("2:\n");
	E("	movq A1,A2\n");
	E("	shrq A2\n");
	E("	andq $1,A1\n");
	E("	orq A1,A2\n");
	E("	cvtsi2sZfq A2,A3\n");
	E("	addsZf A3,A3\n");
	E("3:\n");
}

/*
 * Generate code to convert an x87 long double to an unsigned long.
 * This is ugly :-/
 */
static void
ldtoul(NODE *p)
{

	E("	subq $16,%rsp\n");
	E("	movl $0x5f000000,(%rsp)\n"); /* More than long can have */
	E("	flds (%rsp)\n");
	if (p->n_left->n_op == REG) {
		E("	fxch\n");
	} else
		E("	fldt AL\n");
	E("	fucomi %st(1), %st\n");
	E("	jae 2f\n");

	E("	fstp %st(1)\n");	 /* Pop huge val from stack */
	E("	fnstcw (%rsp)\n");	 /* store cw */
	E("	movw $0x0f3f,4(%rsp)\n");/* round towards 0 */
	E("	fldcw 4(%rsp)\n");	 /* new cw */
	E("	fistpll 8(%rsp)\n");	 /* save val */
	E("	fldcw (%rsp)\n");	 /* fetch old cw */
	E("	movq 8(%rsp),A1\n");

	E("	jmp 3f\n");

	E("2:\n");

	E("	fsubp %st, %st(1)\n");
	E("	fnstcw (%rsp)\n");	
	E("	movw $0x0f3f,4(%rsp)\n");
	E("	fldcw 4(%rsp)\n");
	E("	fistpll 8(%rsp)\n");
	E("	fldcw (%rsp)\n");
	E("	movabsq $0x8000000000000000,A1\n");
	E("	xorq 8(%rsp),A1\n");

	E("3:	addq $16,%rsp\n");
}

/*
 * Generate code to convert an SSE float/double to an unsigned long.
 */     
static void     
fdtoul(NODE *p) 
{
	if (p->n_left->n_type == FLOAT)
		E("	movabsq $0x5f000000,A1\n");
	else
		E("	movabsq $0x43e0000000000000,A1\n");
	E("	movd A1,A3\n");
	E("	ucomisZg A3,AL\n");
	E("	jae 2f\n");
	E("	cvttsZg2siq AL,A1\n");
	E("	jmp 3f\n");
	E("2:\n");
	E("	subsZg A3,AL\n");
	E("	cvttsZg2siq AL,A1\n");
	E("	movabsq $0x8000000000000000,A2\n");
	E("	xorq A2,A1\n");
	E("3:\n");
}
#undef E

void
zzzcode(NODE *p, int c)
{
	struct attr *ap, *ap2;
	NODE *l;
	int pr, lr, s;
	char **rt;

	switch (c) {
	case 'A': /* swap st0 and st1 if right is evaluated second */
		if ((p->n_su & DORIGHT) == 0) {
			if (logop(p->n_op))
				printf("	fxch\n");
			else
				printf("r");
		}
		break;

	case 'B': /* ldouble to unsigned long cast */
		ldtoul(p);
		break;

	case 'b': /* float/double to unsigned long cast */
		fdtoul(p);
		break;

	case 'C':  /* remove from stack after subroutine call */
		pr = p->n_qual;
		if (p->n_op == UCALL)
			return; /* XXX remove ZC from UCALL */
		if (pr)
			printf("	addq $%d, %s\n", pr, rnames[RSP]);
#define	STRREG 6
#define	STRSSE 8
#define	STRIF  9
#define	STRFI  10
#define	STRX87 11
		ap = attr_find(p->n_ap, ATTR_P2STRUCT);
		ap2 = attr_find(p->n_ap, ATTR_AMD64_CMPLRET);
		if ((p->n_op == STCALL || p->n_op == USTCALL) &&
		    ap->iarg(0) == 32 && ap2->iarg(0) == STRX87) {
			printf("\tfstpt -%d(%%rbp)\n", stkpos);
			printf("\tfstpt -%d(%%rbp)\n", stkpos-16);
			printf("\tleaq -%d(%%rbp),%%rax\n", stkpos);
		}
		if ((p->n_op == STCALL || p->n_op == USTCALL) &&
		    ap->iarg(0) <= 16) {
			/* store reg-passed structs on stack */
			if (ap2->iarg(0) == STRREG || ap2->iarg(0) == STRIF)
				printf("\tmovq %%rax,-%d(%%rbp)\n", stkpos);
			else
				printf("\tmovsd %%xmm0,-%d(%%rbp)\n", stkpos);
			if (ap->iarg(0) > 8) {
				if (ap2->iarg(0) == STRREG)
					printf("\tmovq %%rdx");
				else if (ap2->iarg(0) == STRFI)
					printf("\tmovq %%rax");
				else if (ap2->iarg(0) == STRIF)
					printf("\tmovsd %%xmm0");
				else
					printf("\tmovsd %%xmm1");
				printf(",-%d(%%rbp)\n", stkpos-8);
			}
			printf("\tleaq -%d(%%rbp),%%rax\n", stkpos);
		}
		break;

	case 'c': /* xor label */
		if ((ap = attr_find(p->n_ap, ATTR_AMD64_XORLBL)) == NULL)
			comperr("missing xor label");
		printf(LABFMT, ap->iarg(0));
		break;

	case 'F': /* Structure argument */
		ap = attr_find(p->n_ap, ATTR_P2STRUCT);
		printf("	subq $%d,%%rsp\n", ap->iarg(0));
		printf("	movq %%rsp,%%rsi\n");
		stasg(p);
		break;

	case 'G': /* Floating point compare */
		fcomp(p);
		break;

	case 'j': /* convert unsigned long to f/d */
		ultofd(p);
		break;

	case 'M': /* Output sconv move, if needed */
		l = getlr(p, 'L');
		/* XXX fixneed: regnum */
		pr = DECRA(p->n_reg, 0);
		lr = DECRA(l->n_reg, 0);
		if (pr == lr)
			break;
		printf("	movb %s,%s\n", rbyte[lr], rbyte[pr]);
		l->n_rval = l->n_reg = p->n_reg; /* XXX - not pretty */
		break;

	case 'N': /* output long reg name */
		printf("%s", rlong[getlr(p, '1')->n_rval]);
		break;

	case 'P': /* Put hidden argument in rdi */
		ap = attr_find(p->n_ap, ATTR_P2STRUCT);
		ap2 = attr_find(p->n_ap, ATTR_AMD64_CMPLRET);
		if (ap->iarg(0) > 16 && ap2->iarg(0) != STRX87)
			printf("\tleaq -%d(%%rbp),%%rdi\n", stkpos);
		break;

        case 'Q': /* emit struct assign */
		stasg(p);
		break;

	case 'R': /* print opname based on right type */
	case 'L': /* print opname based on left type */
		switch (getlr(p, c)->n_type) {
		case CHAR: case UCHAR: s = 'b'; break;
		case SHORT: case USHORT: s = 'w'; break;
		case INT: case UNSIGNED: s = 'l'; break;
		default: s = 'q'; break;
		printf("%c", s);
		}
		break;

	case 'U': { /* output branch insn for ucomi */
		static char *fpcb[] = { "jz", "jnz", "jbe", "jc", "jnc", "ja" };
		if (p->n_op < EQ || p->n_op > GT)
			comperr("bad fp branch");
		if (p->n_op == NE || p->n_op == GT || p->n_op == GE)
			expand(p, 0, "	jp LC\n");
		else if (p->n_op == EQ)
			printf("\tjp 1f\n");
		printf("	%s ", fpcb[p->n_op - EQ]);
		expand(p, 0, "LC\n");
		if (p->n_op == EQ)
			printf("1:\n");
		break;
		}

	case '8': /* special reg name printout (64-bit) */
	case '1': /* special reg name printout (32-bit) */
		l = getlr(p, '1');
		rt = c == '8' ? rnames : rlong;
		printf("%s", rt[l->n_rval]);
		break;

	case 'g':
		p = p->n_left;
		/* FALLTHROUGH */
	case 'f': /* float or double */
		printf("%c", p->n_type == FLOAT ? 's' : 'd');
		break;

	case 'q': /* int or long */
		printf("%c", p->n_left->n_type == LONG ? 'q' : ' ');
		break;

	default:
		comperr("zzzcode %c", c);
	}
}

int canaddr(NODE *);
int
canaddr(NODE *p)
{
	int o = p->n_op;

	if (o==NAME || o==REG || o==ICON || o==OREG ||
	    (o==UMUL && shumul(p->n_left, SOREG)))
		return(1);
	return(0);
}

/*
 * Does the bitfield shape match?
 */
int
flshape(NODE *p)
{
	comperr("flshape");
	return(0);
}

/* INTEMP shapes must not contain any temporary registers */
/* XXX should this go away now? */
int
shtemp(NODE *p)
{
	return 0;
#if 0
	int r;

	if (p->n_op == STARG )
		p = p->n_left;

	switch (p->n_op) {
	case REG:
		return (!istreg(p->n_rval));

	case OREG:
		r = p->n_rval;
		if (R2TEST(r)) {
			if (istreg(R2UPK1(r)))
				return(0);
			r = R2UPK2(r);
		}
		return (!istreg(r));

	case UMUL:
		p = p->n_left;
		return (p->n_op != UMUL && shtemp(p));
	}

	if (optype(p->n_op) != LTYPE)
		return(0);
	return(1);
#endif
}

void
adrcon(CONSZ val)
{
	printf("$" CONFMT, val);
}

void
conput(FILE *fp, NODE *p)
{
	long val = p->n_lval;

	switch (p->n_op) {
	case ICON:
		if (p->n_name[0] != '\0') {
			fprintf(fp, "%s", p->n_name);
			if (val)
				fprintf(fp, "+%ld", val);
		} else
			fprintf(fp, "%ld", val);
		return;

	default:
		comperr("illegal conput, p %p", p);
	}
}

/*ARGSUSED*/
void
insput(NODE *p)
{
	comperr("insput");
}

/*
 * Write out the upper address, like the upper register of a 2-register
 * reference, or the next memory location.
 * XXX - not needed on amd64
 */
void
upput(NODE *p, int size)
{

	size /= SZCHAR;
	switch (p->n_op) {
	case REG:
		printf("%%%s", &rnames[p->n_rval][3]);
		break;

	case NAME:
	case OREG:
		p->n_lval += size;
		adrput(stdout, p);
		p->n_lval -= size;
		break;
	case ICON:
		printf("$" CONFMT, p->n_lval >> 32);
		break;
	default:
		comperr("upput bad op %d size %d", p->n_op, size);
	}
}

void
adrput(FILE *io, NODE *p)
{
	int r;
	char **rc;
	/* output an address, with offsets, from p */

	switch (p->n_op) {

	case NAME:
		if (p->n_name[0] != '\0') {
			if (p->n_lval != 0)
				fprintf(io, CONFMT "+", p->n_lval);
			fprintf(io, "%s(%%rip)", p->n_name);
		} else
			fprintf(io, CONFMT, p->n_lval);
		return;

	case OREG:
		r = p->n_rval;
		if (p->n_name[0])
			printf("%s%s", p->n_name, p->n_lval ? "+" : "");
		if (p->n_lval)
			fprintf(io, "%lld", p->n_lval);
		if (R2TEST(r)) {
			int r1 = R2UPK1(r);
			int r2 = R2UPK2(r);
			int sh = R2UPK3(r);

			fprintf(io, "(%s,%s,%d)", 
			    r1 == MAXREGS ? "" : rnames[r1],
			    r2 == MAXREGS ? "" : rnames[r2], sh);
		} else
			fprintf(io, "(%s)", rnames[p->n_rval]);
		return;
	case ICON:
		/* addressable value of the constant */
		fputc('$', io);
		conput(io, p);
		return;

	case REG:
		switch (p->n_type) {
		case CHAR:
		case UCHAR:
			rc = rbyte;
			break;
		case SHORT:
		case USHORT:
			rc = rshort;
			break;
		case INT:
		case UNSIGNED:
			rc = rlong;
			break;
		default:
			rc = rnames;
			break;
		}
		fprintf(io, "%s", rc[p->n_rval]);
		return;

	default:
		comperr("illegal address, op %d, node %p", p->n_op, p);
		return;

	}
}

static char *
ccbranches[] = {
	"je",		/* jumpe */
	"jne",		/* jumpn */
	"jle",		/* jumple */
	"jl",		/* jumpl */
	"jge",		/* jumpge */
	"jg",		/* jumpg */
	"jbe",		/* jumple (jlequ) */
	"jb",		/* jumpl (jlssu) */
	"jae",		/* jumpge (jgequ) */
	"ja",		/* jumpg (jgtru) */
};


/*   printf conditional and unconditional branches */
void
cbgen(int o, int lab)
{
	if (o < EQ || o > UGT)
		comperr("bad conditional branch: %s", opst[o]);
	printf("	%s " LABFMT "\n", ccbranches[o-EQ], lab);
}

/*
 * gcc xasm has the ability to generate different asm types
 * via some magic.
 *
 * Only support AT&T asm for now.
 */
static char *
adjustname(char *s)
{
	int len = strlen(s);
	char *d = tmpalloc(len+1);
	int i, j, flvl, tlvl;

	flvl = tlvl = 0;
	for (i = j = 0; i < len; i++) {
		switch (s[i]) {
		case '{': tlvl++; break;
		case '}': if (tlvl)tlvl--; else flvl--; break;
		case '|': tlvl--; flvl++; break;
		default:
			if (flvl == 0)
				d[j++] = s[i];
			break;
		}
	}
	d[j] = 0;
	return d;
}

static void
fixcalls(NODE *p, void *arg)
{
	int ps;

	/* Prepare for struct return by allocating bounce space on stack */
	switch (p->n_op) {
	case STCALL:
	case USTCALL:
		ps = attr_find(p->n_ap, ATTR_P2STRUCT)->iarg(0);
		if (ps < 16)
			ps = 16;
		if (ps+p2autooff > stkpos)
			stkpos = ps+p2autooff;
		break;
	case XASM:
		p->n_name = adjustname(p->n_name);
		break;
	}
}

void
myreader(struct interpass *ipole)
{
	struct interpass *ip;

	stkpos = p2autooff;
	DLIST_FOREACH(ip, ipole, qelem) {
		if (ip->type != IP_NODE)
			continue;
		walkf(ip->ip_node, fixcalls, 0);
	}
	if (stkpos > p2autooff)
		p2autooff = stkpos;
	if (stkpos > p2maxautooff)
		p2maxautooff = stkpos;
	if (x2debug)
		printip(ipole);
}

/*
 * Remove some PCONVs after OREGs are created.
 */
static void
pconv2(NODE *p, void *arg)
{
	NODE *q;

	if (p->n_op == PLUS) {
		if (p->n_type == (PTR|SHORT) || p->n_type == (PTR|USHORT)) {
			if (p->n_right->n_op != ICON)
				return;
			if (p->n_left->n_op != PCONV)
				return;
			if (p->n_left->n_left->n_op != OREG)
				return;
			q = p->n_left->n_left;
			nfree(p->n_left);
			p->n_left = q;
			/*
			 * This will be converted to another OREG later.
			 */
		}
	}
}

void
mycanon(NODE *p)
{
	walkf(p, pconv2, 0);
}

void
myoptim(struct interpass *ip)
{
}

void
rmove(int s, int d, TWORD t)
{

	switch (t) {
	case INT:
	case UNSIGNED:
		printf("	movl %s,%s\n", rlong[s], rlong[d]);
		break;
	case CHAR:
	case UCHAR:
		printf("	movb %s,%s\n", rbyte[s], rbyte[d]);
		break;
	case SHORT:
	case USHORT:
		printf("	movw %s,%s\n", rshort[s], rshort[d]);
		break;
	case FLOAT:
		printf("	movss %s,%s\n", rnames[s], rnames[d]);
		break;
	case DOUBLE:
		printf("	movsd %s,%s\n", rnames[s], rnames[d]);
		break;
	case LDOUBLE:
#ifdef notdef
		/* a=b()*c(); will generate this */
		/* XXX can it fail anyway? */
		comperr("bad float rmove: %d %d", s, d);
#endif
		break;
	default:
		printf("	movq %s,%s\n", rnames[s], rnames[d]);
		break;
	}
}

/*
 * For class c, find worst-case displacement of the number of
 * registers in the array r[] indexed by class.
 */
int
COLORMAP(int c, int *r)
{

	switch (c) {
	case CLASSA:
		return r[CLASSA] < 14;
	case CLASSB:
		return r[CLASSB] < 16;
	case CLASSC:
		return r[CLASSC] < CREGCNT;
	}
	return 0; /* XXX gcc */
}

char *rnames[] = {
	"%rax", "%rdx", "%rcx", "%rbx", "%rsi", "%rdi", "%rbp", "%rsp",
	"%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
	"%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7",
	"%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14",
	"%xmm15",
};

/* register names for shorter sizes */
char *rbyte[] = {
	"%al", "%dl", "%cl", "%bl", "%sil", "%dil", "%bpl", "%spl",
	"%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b", 
};
char *rshort[] = {
	"%ax", "%dx", "%cx", "%bx", "%si", "%di", "%bp", "%sp",
	"%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w", 
};
char *rlong[] = {
	"%eax", "%edx", "%ecx", "%ebx", "%esi", "%edi", "%ebp", "%esp",
	"%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d", 
};


/*
 * Return a class suitable for a specific type.
 */
int
gclass(TWORD t)
{
	if (t == LDOUBLE)
		return CLASSC;
	if (t == FLOAT || t == DOUBLE)
		return CLASSB;
	return CLASSA;
}

static int
argsiz(NODE *p)
{
	TWORD t = p->n_type;

	if (p->n_left->n_op == REG)
		return 0; /* not on stack */
	if (t == LDOUBLE)
		return 16;
	if (p->n_op == STASG)
		return attr_find(p->n_ap, ATTR_P2STRUCT)->iarg(0);
	return 8;
}

/*
 * Calculate argument sizes.
 */
void
lastcall(NODE *p)
{
	NODE *op = p;
	int size = 0;

	p->n_qual = 0;
	if (p->n_op != CALL && p->n_op != FORTCALL && p->n_op != STCALL)
		return;
	for (p = p->n_right; p->n_op == CM; p = p->n_left)
		size += argsiz(p->n_right);
	size += argsiz(p);
	size = (size+15) & ~15;
	if (size)
		printf("	subq $%d,%s\n", size, rnames[RSP]);
	op->n_qual = size; /* XXX */
}

/*
 * Special shapes.
 */
int
special(NODE *p, int shape)
{
	int o = p->n_op;

	switch (shape) {
	case SFUNCALL:
		if (o == STCALL || o == USTCALL)
			return SRREG;
		break;
	case SPCON:
		if (o != ICON || p->n_name[0] ||
		    p->n_lval < 0 || p->n_lval > 0x7fffffff)
			break;
		return SRDIR;
	case SMIXOR:
		return tshape(p, SZERO);
	case SMILWXOR:
		if (o != ICON || p->n_name[0] ||
		    p->n_lval == 0 || p->n_lval & 0xffffffff)
			break;
		return SRDIR;
	case SMIHWXOR:
		if (o != ICON || p->n_name[0] ||
		     p->n_lval == 0 || (p->n_lval >> 32) != 0)
			break;
		return SRDIR;
	case SCON32:
		if (o != ICON || p->n_name[0])
			break;
		if (p->n_lval < MIN_INT || p->n_lval > MAX_INT)
			break;
		return SRDIR;
	default:
		cerror("special: %x\n", shape);
	}
	return SRNOPE;
}

/*
 * Target-dependent command-line options.
 */
void
mflags(char *str)
{
}

/*
 * Do something target-dependent for xasm arguments.
 */
int
myxasm(struct interpass *ip, NODE *p)
{
	struct interpass *ip2;
	int Cmax[] = { 31, 63, 127, 0xffff, 3, 255 };
	NODE *in = 0, *ut = 0;
	TWORD t;
	char *w;
	int reg;
	int c, cw, v;

	cw = xasmcode(p->n_name);
	if (cw & (XASMASG|XASMINOUT))
		ut = p->n_left;
	if ((cw & XASMASG) == 0)
		in = p->n_left;

	c = XASMVAL(cw);
retry:	switch (c) {
	case 'D': reg = RDI; break;
	case 'S': reg = RSI; break;
	case 'A': 
	case 'a': reg = RAX; break;
	case 'b': reg = RBX; break;
	case 'c': reg = RCX; break;
	case 'd': reg = RDX; break;

	case 'Q': reg = RDX; break; /* Always dx for now */

	case 'x':
	case 'q':
	case 't':
	case 'u':
		p->n_name = tmpstrdup(p->n_name);
		w = strchr(p->n_name, c);
		*w = 'r'; /* now reg */
		return c == 'q' || c == 'x' || c == 't' ? 0 : 1;

	case 'I':
	case 'J':
	case 'K':
	case 'L':
	case 'M':
	case 'N':
		if (p->n_left->n_op != ICON) {
			if ((c = XASMVAL1(cw))) {
				if (c == 'r') {
					p->n_name++;
					return 0;
				}
				goto retry;
			}
			uerror("xasm arg not constant");
		}
		v = p->n_left->n_lval;
		if ((c == 'K' && v < -128) ||
		    (c == 'L' && v != 0xff && v != 0xffff) ||
		    (c != 'K' && v < 0) ||
		    (v > Cmax[c-'I']))
			uerror("xasm val out of range");
		p->n_name = "i";
		return 1;

	default:
		return 0;
	}
	/* If there are requested either memory or register, delete memory */
	w = p->n_name = tmpstrdup(p->n_name);
	if (*w == '=')
		w++;
	*w++ = 'r';
	*w = 0;

	t = p->n_left->n_type;

	if (t == FLOAT || t == DOUBLE) {
		reg += 16;
	} else if (t == LDOUBLE) {
		reg += 32;
	}

	if (in && ut)
		in = tcopy(in);
	p->n_left = mklnode(REG, 0, reg, t);
	if (ut) {
		ip2 = ipnode(mkbinode(ASSIGN, ut, tcopy(p->n_left), t));
		DLIST_INSERT_AFTER(ip, ip2, qelem);
	}
	if (in) {
		ip2 = ipnode(mkbinode(ASSIGN, tcopy(p->n_left), in, t));
		DLIST_INSERT_BEFORE(ip, ip2, qelem);
	}

	return 1;
}

void
targarg(char *w, void *arg, int n)
{
	NODE **ary = arg;
	NODE *p, *q;

	if (w[1] < '0' || w[1] > (n + '0'))
		uerror("bad xasm arg number %c", w[1]);
	if (w[1] == (n + '0'))
		p = ary[(int)w[1]-'0' - 1]; /* XXX */
	else
		p = ary[(int)w[1]-'0'];
	p = p->n_left;

	if (optype(p->n_op) != LTYPE)
		comperr("bad xarg op %d", p->n_op);
	q = tcopy(p);
	if (q->n_op == REG) {
		if (*w == 'k') {
			q->n_type = INT;
		} else if (*w == 'q') {
			q->n_type = LONG;
		} else if (*w == 'h' || *w == 'b') {
			/* Can do this only because we know dx is used */
			printf("%%d%c", *w == 'h' ? 'h' : 'l');
			tfree(q);
			return;
		} else if (*w != 'w') {
			cerror("targarg"); /* XXX ??? */
			if (q->n_type > UCHAR) {
				regno(q) = regno(q)*2+8;
				if (*w == 'h')
					regno(q)++;
			}
			q->n_type = INT;
		} else
			q->n_type = SHORT;
	}
	adrput(stdout, q);
	tfree(q);
}

/*
 * target-specific conversion of numeric arguments.
 */
int
numconv(void *ip, void *p1, void *q1)
{
	NODE *p = p1, *q = q1;
	int cw = xasmcode(q->n_name);

	switch (XASMVAL(cw)) {
	case 'a':
	case 'b':
	case 'c':
	case 'd':
		p->n_name = tmpcalloc(2);
		p->n_name[0] = XASMVAL(cw);
		return 1;
	default:
		return 0;
	}
}

static struct {
	char *name; int num;
} xcr[] = {
	{ "rax", RAX },
	{ "rbx", RBX },
	{ "rcx", RCX },
	{ "rdx", RDX },
	{ "rsi", RSI },
	{ "rdi", RDI },
	{ "r8", R08 },
	{ "r9", R09 },
	{ "r10", R10 },
	{ "r11", R11 },
	{ "r12", R12 },
	{ "r13", R13 },
	{ "r14", R14 },
	{ "r15", R15 },
	{ "st", 040 },
	{ "st(0)", 040 },
	{ "st(1)", 041 },
	{ "st(2)", 042 },
	{ "st(3)", 043 },
	{ "st(4)", 044 },
	{ "st(5)", 045 },
	{ "st(6)", 046 },
	{ "st(7)", 047 },
	{ NULL, 0 },
};

/*
 * Check for other names of the xasm constraints registers.
 */
int xasmconstregs(char *s)
{
	int i;

	for (i = 0; xcr[i].name; i++)
		if (strcmp(xcr[i].name, s) == 0)
			return xcr[i].num;
	return -1;
}

