/*
 * Copyright (c) 2008 David Crawshaw <david@zentus.com>
 * 
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "pass1.h"
#include "pass2.h"


char *
rnames[] = {
	/* "\%g0", always zero, removed due to 31-element class limit */
	        "\%g1", "\%g2", "\%g3", "\%g4", "\%g5", "\%g6", "\%g7",
	"\%o0", "\%o1", "\%o2", "\%o3", "\%o4", "\%o5", "\%o6", "\%o7",
	"\%l0", "\%l1", "\%l2", "\%l3", "\%l4", "\%l5", "\%l6", "\%l7",
	"\%i0", "\%i1", "\%i2", "\%i3", "\%i4", "\%i5", "\%i6", "\%i7",

	"\%f0",  "\%f1",  "\%f2",  "\%f3",  "\%f4",  "\%f5",  "\%f6",  "\%f7",
	"\%f8",  "\%f9",  "\%f10", "\%f11", "\%f12", "\%f13", "\%f14", "\%f15",
	"\%f16", "\%f17", "\%f18", "\%f19", "\%f20", "\%f21", "\%f22", "\%f23",
	"\%f24", "\%f25", "\%f26", "\%f27", "\%f28", "\%f29", "\%f30",
	/*, "\%f31" XXX removed due to 31-element class limit */

	"\%f0",  "\%f2",  "\%f4",  "\%f6",  "\%f8",  "\%f10", "\%f12", "\%f14",
	"\%f16", "\%f18", "\%f20", "\%f22", "\%f24", "\%f26", "\%f28", "\%f30",

	"\%sp", "\%fp",
};

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

void
prologue(struct interpass_prolog *ipp)
{
	int i, stack;

	stack = V9RESERVE + V9STEP(p2maxautooff);

	for (i = ipp->ipp_regs[0]; i; i >>= 1)
		if (i & 1)
			stack += 16;

	/* TODO printf("\t.proc %d\n"); */
	if (ipp->ipp_vis)
		printf("\t.global %s\n", ipp->ipp_name);
	printf("\t.align 4\n");
	printf("%s:\n", ipp->ipp_name);
	if (SIMM13(stack))
		printf("\tsave %%sp,-%d,%%sp\n", stack);
	else {
		printf("\tsetx -%d,%%g4,%%g1\n", stack);
		printf("\tsave %%sp,%%g1,%%sp\n");
	}
}

void
eoftn(struct interpass_prolog *ipp)
{
	printf("\tret\n");
	printf("\trestore\n");
	printf("\t.type %s,#function\n", ipp->ipp_name);
	printf("\t.size %s,(.-%s)\n", ipp->ipp_name, ipp->ipp_name);
}

void
hopcode(int f, int o)
{
	char *str;

	switch (o) {
		case EQ:        str = "brz"; break;
		case NE:        str = "brnz"; break;
		case ULE:
		case LE:        str = "brlez"; break;
		case ULT:
		case LT:        str = "brlz";  break;
		case UGE:
		case GE:        str = "brgez"; break;
		case UGT:
		case GT:        str = "brgz";  break;
		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("unknown hopcode: %d (with %c)", o, f);
			return;
	}

	printf("%s%c", str, f);
}

int
tlen(NODE *p)
{
	switch (p->n_type) {
		case CHAR:
		case UCHAR:
			return 1;
		case SHORT:
		case USHORT:
			return (SZSHORT / SZCHAR);
		case FLOAT:
			return (SZFLOAT / 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 unknown: %d");
			return SZPOINT(p->n_type) / SZCHAR;
	}
}

void
zzzcode(NODE * p, int c)
{
	char *str;
	NODE *l, *r;
	l = p->n_left;
	r = p->n_right;

	switch (c) {

	case 'A':	/* Add const. */
		if (ISPTR(l->n_type) && l->n_rval == FP)
			r->n_lval += V9BIAS;

		if (SIMM13(r->n_lval))
			expand(p, 0, "\tadd AL,AR,A1\t\t! add const\n");
		else
			expand(p, 0, "\tsetx AR,A3,A2\t\t! add const\n"
			             "\tadd AL,A2,A1\n");
		break;
	case 'B':	/* Subtract const. */
		if (ISPTR(l->n_type) && l->n_rval == FP)
			r->n_lval -= V9BIAS;

		if (SIMM13(r->n_lval))
			expand(p, 0, "\tsub AL,AR,A1\t\t! subtract const\n");
		else
			expand(p, 0, "\tsetx AR,A3,A2\t\t! subtract const\n"
			             "\tsub AL,A2,A1\n");
		break;
	case 'C':	/* Load constant to register. */
		if (ISPTR(p->n_type))
			expand(p, 0,
				"\tsethi %h44(AL),A1\t\t! load label\n"
				"\tor A1,%m44(AL),A1\n"
				"\tsllx A1,12,A1\n"
				"\tor A1,%l44(AL),A1\n");
		else if (SIMM13(p->n_lval))
			expand(p, 0, "\tor %g0,AL,A1\t\t\t! load const\n");
		else
			expand(p, 0, "\tsetx AL,A2,A1\t\t! load const\n");
		break;
	case 'F':	/* Floating-point comparison, cf. hopcode(). */
		switch (p->n_op) {
			case EQ:        str = "fbe"; break;
			case NE:        str = "fbne"; break;
			case ULE:
			case LE:        str = "fbule"; break;
			case ULT:
			case LT:        str = "fbul";  break;
			case UGE:
			case GE:        str = "fbuge"; break;
			case UGT:
			case GT:        str = "fbug";  break;
			/* XXX
			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("unknown float code: %d", p->n_op);
				return;
		}
		printf(str);
		break;

	case 'Q':	/* Structure assignment. */
		/* TODO Check if p->n_stsize is small and use a few ldx's
		        to move the struct instead of memcpy. The equiv.
			could be done on all the architectures. */
		if (l->n_rval != O0)
			printf("\tmov %s,%s\n", rnames[l->n_rval], rnames[O0]);
		if (SIMM13(p->n_stsize))
			printf("\tor %%g0,%d,%%o2\n", p->n_stsize);
		else
			printf("\tsetx %d,%%g1,%%o2\n", p->n_stsize);
		printf("\tcall memcpy\t\t\t! struct assign (dest, src, len)\n");
		printf("\tnop\n");
		break;
	default:
		cerror("unknown zzzcode call: %c", c);
	}
}

int
rewfld(NODE * p)
{
	return (1);
}

int
fldexpand(NODE *p, int cookie, char **cp)
{
	printf("XXX fldexpand called\n"); /* XXX */
	return 1;
}

int
flshape(NODE * p)
{
	return SRREG;
}

int
shtemp(NODE * p)
{
	return 0;
}


void
adrcon(CONSZ val)
{
}

void
conput(FILE * fp, NODE * p)
{
	if (p->n_op != ICON) {
		comperr("conput got bad op: %s", copst(p->n_op));
		return;
	}

	if (p->n_name[0] != '\0') {
		fprintf(fp, "%s", p->n_name);
		if (p->n_lval > 0)
			fprintf(fp, "+");
		if (p->n_lval)
			fprintf(fp, CONFMT, p->n_lval);
	} else
		fprintf(fp, CONFMT, p->n_lval);
}

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

void
upput(NODE *p, int size)
{
	comperr("upput");
}

void
adrput(FILE * io, NODE * p)
{
	int64_t off;

	if (p->n_op == FLD) {
		printf("adrput a FLD\n");
		p = p->n_left;
	}

	if (p->n_op == UMUL && p->n_right == 0)
		p = p->n_left;

	off = p->n_lval;

	switch (p->n_op) {
	case NAME:
		if (p->n_name[0] != '\0')
			fputs(p->n_name, io);
		if (off > 0)
			fprintf(io, "+");
		if (off != 0)
			fprintf(io, CONFMT, (long long int)off);
		return;
	case OREG:
		fprintf(io, "%s", rnames[p->n_rval]);
		if (p->n_rval == FP)
			off += V9BIAS;
		if (p->n_rval == SP)
			off += V9BIAS + V9RESERVE;
		if (off > 0)
			fprintf(io, "+");
		if (off)
			fprintf(io, CONFMT, (CONSZ)off);
		return;
	case ICON:
		/* addressable value of the constant */
		conput(io, p);
		return;
	case REG:
		fputs(rnames[p->n_rval], io);
		return;
	case FUNARG:
		/* We do something odd and store the stack offset in n_rval. */
		fprintf(io, "%d", V9BIAS + V9RESERVE + p->n_rval);
		return;
	default:
		comperr("bad address, %s, node %p", copst(p->n_op), p);
		return;
	}
}

void
cbgen(int o, int lab)
{
}

void
myreader(struct interpass * ipole)
{
}

void
mycanon(NODE * p)
{
}

void
myoptim(struct interpass * ipole)
{
}

void
rmove(int s, int d, TWORD t)
{
	printf("\t");

	if (t == FLOAT)	      printf("fmovs");
	else if (t == DOUBLE) printf("fmovd");
	else                  printf("mov");

	printf(" %s,%s\t\t\t! rmove()\n", rnames[s], rnames[d]);
}

int
gclass(TWORD t)
{
	if (t == FLOAT)
		return CLASSB;
	if (t == DOUBLE)
		return CLASSC;
	return CLASSA;
}

void
lastcall(NODE *p)
{
}

int
special(NODE *p, int shape)
{
	return SRNOPE;
}

void mflags(char *str)
{
}

int
COLORMAP(int c, int *r)
{
	int num=0;

	switch (c) {
		case CLASSA:
			num += r[CLASSA];
			return num < 32;
		case CLASSB:
			num += r[CLASSB];
			num += 2*r[CLASSC];
			return num < 32;;
		case CLASSC:
			num += r[CLASSC];
			num += 2*r[CLASSB];
			return num < 17;
		case CLASSD:
			return 0;
		default:
			comperr("COLORMAP: unknown class: %d", c);
			return 0;
	}
}
/*
 * Do something target-dependent for xasm arguments.
 * Supposed to find target-specific constraints and rewrite them.
 */
int
myxasm(struct interpass *ip, NODE *p)
{
	return 0;
}
