shithub: scc

ref: bbfae8ece429df0093d58f0454ac681eb90aebeb
dir: /cc2/arch/qbe/cgen.c/

View raw version
/* See LICENSE file for copyright and license details. */
#include <assert.h>
#include <stdlib.h>

#include "arch.h"
#include "../../cc2.h"
#include "../../../inc/sizes.h"

enum lflags {
	FORCE = 1 << 0,
	LOADL = 1 << 1,
	LOADR = 1 << 2
};

enum sflags {
	ISTMP  = 1,
	ISCONS = 2
};

static char opasmw[] = {
	[OADD] = ASADDW,
	[OSUB] = ASSUBW,
	[OMUL] = ASMULW,
	[OMOD] = ASMODW,
	[ODIV] = ASDIVW,
	[OSHL] = ASSHLW,
	[OSHR] = ASSHRW,
	[OLT] = ASLTW,
	[OGT] = ASGTW,
	[OLE] = ASLEW,
	[OGE] = ASGEW,
	[OEQ] = ASEQW,
	[ONE] = ASNEW,
	[OBAND] = ASBANDW,
	[OBOR] = ASBORW,
	[OBXOR] = ASBXORW,
	[OCPL] = ASCPLW
};

static char opasml[] = {
	[OADD] = ASADDL,
	[OSUB] = ASSUBL,
	[OMUL] = ASMULL,
	[OMOD] = ASMODL,
	[ODIV] = ASDIVL,
	[OSHL] = ASSHLL,
	[OSHR] = ASSHRL,
	[OLT] = ASLTL,
	[OGT] = ASGTL,
	[OLE] = ASLEL,
	[OGE] = ASGEL,
	[OEQ] = ASEQL,
	[ONE] = ASNEL,
	[OBAND] = ASBANDL,
	[OBOR] = ASBORL,
	[OBXOR] = ASBXORL,
	[OCPL] = ASCPLL
};

static char opasms[] = {
	[OADD] = ASADDS,
	[OSUB] = ASSUBS,
	[OMUL] = ASMULS,
	[OMOD] = ASMODS,
	[ODIV] = ASDIVS,
	[OSHL] = ASSHLS,
	[OSHR] = ASSHRS,
	[OLT] = ASLTS,
	[OGT] = ASGTS,
	[OLE] = ASLES,
	[OGE] = ASGES,
	[OEQ] = ASEQS,
	[ONE] = ASNES,
	[OBAND] = ASBANDS,
	[OBOR] = ASBORS,
	[OBXOR] = ASBXORS,
	[OCPL] = ASCPLS
};

static char opasmd[] = {
	[OADD] = ASADDD,
	[OSUB] = ASSUBD,
	[OMUL] = ASMULD,
	[OMOD] = ASMODD,
	[ODIV] = ASDIVD,
	[OSHL] = ASSHLD,
	[OSHR] = ASSHRD,
	[OLT] = ASLTD,
	[OGT] = ASGTD,
	[OLE] = ASLED,
	[OGE] = ASGED,
	[OEQ] = ASEQD,
	[ONE] = ASNED,
	[OBAND] = ASBANDD,
	[OBOR] = ASBORD,
	[OBXOR] = ASBXORD,
	[OCPL] = ASCPLD
};

static Node *
tmpnode(Node *np)
{
	Symbol *sym;

	sym = getsym(TMPSYM);
	sym->type = np->type;
	sym->kind = STMP;
	np->u.sym = sym;
	np->op = OTMP;
	np->flags |= ISTMP;
	return np;
}

/*
 * load() load the address passed in a child of np in a temporary
 * if it is not already in a temporay. It can be forced to load
 * using the FORCE flag
 */
static Node *
load(Node *np, int flags)
{
	int op;
	Type *tp;
	Node *child;

	child = (flags & LOADL) ? np->left : np->right;
	if (!child)
		return NULL;
	tp = &child->type;

	if ((flags & FORCE) || !(child->flags & (ISTMP|ISCONS))) {
		Node *new = tmpnode(newnode(OTMP));
		new->type = *tp;
		new->left = child;

		switch (tp->size) {
		case 1:
			op = ASLDB;
			break;
		case 2:
			op = ASLDH;
			break;
		case 4:
			op = (tp->flags & INTF) ? ASLDW : ASLDS;
			break;
		case 8:
			op = (tp->flags & INTF) ? ASLDL : ASLDD;
			break;
		default:
			abort();
		}
		code(op, new, child, NULL);
		child = new;
	}

	return (flags & LOADL) ? (np->left = child) : (np->right = child);
}

static Node *
cast(Node *nd)
{
	Type *ts, *td;
	Node *tmp, *ns;
	int op, disint, sisint;
	extern Type uint32type, int32type;

	ns = load(nd, LOADL);
	td = &nd->type;
	ts = &ns->type;
	disint = (td->flags & INTF) != 0;
	sisint = (ts->flags & INTF) != 0;

	if (disint && sisint) {
		if (td->size <= ts->size)
			return nd;
		assert(td->size == 4 || td->size == 8);
		switch (ts->size) {
		case 1:
			op = (td->size == 4) ? ASEXTBW : ASEXTBL;
			break;
		case 2:
			op = (td->size == 4) ? ASEXTHW : ASEXTHL;
			break;
		case 4:
			op = ASEXTWL;
			break;
		default:
			abort();
		}
		/*
		 * unsigned version of operations are always +1 the
		 * signed version
		 */
		op += (td->flags & SIGNF) == 0;
	} else if (disint) {
		/* conversion from float to int */
		switch (ts->size) {
		case 4:
			op = (td->size == 8) ? ASSTOL : ASSTOW;
			break;
		case 8:
			op = (td->size == 8) ? ASDTOL : ASDTOW;
			break;
		default:
			abort();
		}
		/* TODO: Add signess */
	} else if (sisint) {
		/* conversion from int to float */
		switch (ts->size) {
		case 1:
		case 2:
			tmp = tmpnode(newnode(ONOP));
			tmp->type = (ts->flags&SIGNF) ? int32type : uint32type;
			tmp->left = ns;
			nd->left = ns = cast(tmp);
		case 4:
			op = (td->size == 8) ? ASSWTOD : ASSWTOS;
			break;
		case 8:
			op = (td->size == 8) ? ASSLTOD : ASSLTOS;
			break;
		default:
			abort();
		}
		/* TODO: Add signess */
	} else {
		/* conversion from float to float */
		op = (td->size == 4) ? ASEXTS : ASTRUNCD;
	}
	code(op, tmpnode(nd), ns, NULL);
	return nd;
}

static Node *
call(Node *np)
{
	int n, op;
	Type *tp = &np->type;
	Node **q, *tmp, *p, *pars[NR_FUNPARAM];

	for (n = 0, p = np->right; p; p = p->right) {
		cgen(p->left);
		pars[n++] = load(p, LOADL);
	}

	switch (tp->size) {
	case 0:
		np->left = tmpnode(newnode(OTMP));
		op = ASCALLW;
		break;
	case 1:
		op = ASCALLB;
		break;
	case 2:
		op = ASCALLH;
		break;
	case 4:
		op = (tp->flags & INTF) ? ASCALLW : ASCALLS;
		break;
	case 8:
		op = (tp->flags & INTF) ? ASCALLL : ASCALLD;
		break;
	default:
		abort();
	}
	code(op, tmpnode(np), np->left, NULL);

	for (q = pars; q < &pars[n]; ++q) {
		op = (q == &pars[n-1]) ? ASPARE : ASPAR;
		p = newnode(OTMP);
		p->type = (*q)->type;
		code(op, NULL, *q, tmpnode(p));
	}
	code(ASCALL, NULL, NULL, NULL);

	return np;
}

static Node *
abbrev(Node *np)
{
	Node *tmp;

	if (np->u.subop == 0)
		return np->right;
	tmp = newnode(np->u.subop);
	tmp->type = np->type;
	tmp->right = np->right;
	tmp->left = np->left;
	return np->right = cgen(tmp);
}

static Node *
assign(Node *to, Node *from)
{
	Type *tp = &to->type;
	int op;

	switch (tp->size) {
	case 1:
		op = ASSTB;
		break;
	case 2:
		op = ASSTH;
		break;
	case 4:
		op = (tp->flags & INTF) ? ASSTW : ASSTS;
		break;
	case 8:
		op = (tp->flags & INTF) ? ASSTL : ASSTD;
		break;
	default:
		abort();
	}
	code(op, to, from, NULL);
	return from;
}

static Node *
ternary(Node *np)
{
	Symbol *yes, *no, *phi;
	Node *ifyes, *ifno, *phinode, *colon;

	tmpnode(np);
	phi = newlabel();
	yes = newlabel();
	no = newlabel();

	ifyes = label2node(yes);
	ifno = label2node(no);
	phinode = label2node(phi);

	colon = np->right;
	cgen(np->left);
	code(ASBRANCH, load(np, LOADL), ifyes, ifno);

	setlabel(yes);
	cgen(colon->left);
	assign(np, load(colon, LOADL));
	code(ASJMP, NULL, phinode, NULL);

	setlabel(no);
	cgen(colon->right);
	assign(np, load(colon, LOADR));
	setlabel(phi);

	deltree(ifyes);
	deltree(ifno);
	deltree(phinode);

	return np;
}

static Node *
function(void)
{
	Symbol *p;

	/* allocate stack space for parameters */
	for (p = locals; p && (p->type.flags & PARF) != 0; p = p->next)
		code(ASALLOC, label2node(p), NULL, NULL);

	/* allocate stack space for local variables) */
	for ( ; p && p->id != TMPSYM; p = p->next) {
		if (p->kind != SAUTO)
			continue;
		code(ASALLOC, label2node(p), NULL, NULL);
	}
	/* store formal parameters in parameters */
	for (p = locals; p; p = p->next) {
		if ((p->type.flags & PARF) == 0)
			break;
		code(ASFORM, label2node(p), NULL, NULL);
	}
	return NULL;
}

/* TODO: Fix "memory leaks" */
Node *
cgen(Node *np)
{
	Node *ifyes, *ifno, *next;
	Symbol *sym;
	Type *tp;
	int op, off;
	char *tbl;

	if (!np)
		return NULL;

	setlabel(np->label);
	if (np->op != OCALL && np->op != OASK) {
		np->left = cgen(np->left);
		np->right = cgen(np->right);
	}
	tp = &np->type;

	switch (np->op) {
	case OSTRING:
		abort();
	case OBFUN:
		return function();
	case OEFUN:
		return NULL;
	case OCONST:
	case OLABEL:
		np->flags |= ISCONS;
	case OMEM:
	case OAUTO:
		return np;
	case OSHR:
	case OMOD:
	case ODIV:
	case OLT:
	case OGT:
	case OLE:
	case OGE:
		/*
		 * unsigned version of operations are always +1 the
		 * signed version
		 */
		off = (tp->flags & SIGNF) == 0;
		goto binary;
	case OADD:
	case OSUB:
	case OMUL:
	case OSHL:
	case OBAND:
	case OBOR:
	case OBXOR:
	case OEQ:
	case ONE:
		off = 0;
	binary:
		switch (tp->size) {
		case 4:
			tbl = (tp->flags & INTF) ? opasmw : opasms;
			break;
		case 8:
			tbl = (tp->flags & INTF) ? opasml : opasmd;
			break;
		default:
			abort();
		}
		op = tbl[np->op] + off;
		code(op, tmpnode(np), load(np, LOADL), load(np, LOADR));
		return np;
	case ONOP:
	case OBLOOP:
	case OELOOP:
		return NULL;
	case OCAST:
		return cast(np);
	case OADDR:
		np->flags |= ISTMP;
		np->op = OTMP;
		np->u.sym = np->left->u.sym;
		return np;
	case OPTR:
		load(np, LOADL);
		load(np, LOADL|FORCE);
		return np->left;
	case OCPL:
	case ONEG:
	case OINC:
	case ODEC:
		abort();
	case OASSIG:
		abbrev(np);
		return assign(np->left, load(np, LOADR));
	case OCOMMA:
		return np->right;
	case OCALL:
		return call(np);
	case OFIELD:
	case OAND:
	case OOR:
		abort();
	case OASK:
		return ternary(np);
	case OBRANCH:
		next = np->next;
		load(np, LOADL);
		if (!next->label)
			next->label = newlabel();
		ifyes = label2node(np->u.sym);
		ifno = label2node(next->label);
		op = ASBRANCH;
		np = np->left;
		goto emit_jump;
	case OJMP:
		ifyes = label2node(np->u.sym);
		op = ASJMP;
		np = ifno = NULL;
	emit_jump:
		code(op, np, ifyes, ifno);
		deltree(ifyes);
		deltree(ifno);
		return NULL;
	case ORET:
		code(ASRET, NULL, load(np, LOADL), NULL);
		return NULL;
	case OCASE:
	case ODEFAULT:
	case OESWITCH:
	case OBSWITCH:
	default:
		abort();
	}
}

/*
 * This is strongly influenced by
 * http://plan9.bell-labs.com/sys/doc/compiler.ps (/sys/doc/compiler.ps)
 * calculate addresability as follows
 *     AUTO => 11          value+fp
 *     REG => 11           reg
 *     STATIC => 11        (value)
 *     CONST => 11         $value
 * These values of addressability are not used in the code generation.
 * They are only used to calculate the Sethi-Ullman numbers. Since
 * QBE is AMD64 targered we could do a better job here, and try to
 * detect some of the complex addressing modes of these processors.
 */
Node *
sethi(Node *np)
{
	Node *lp, *rp;

	if (!np)
		return np;

	np->complex = 0;
	np->address = 0;
	lp = np->left;
	rp = np->right;

	switch (np->op) {
	case OAUTO:
	case OREG:
	case OMEM:
	case OCONST:
		np->address = 11;
		break;
	default:
		sethi(lp);
		sethi(rp);
		break;
	}

	if (np->address > 10)
		return np;
	if (lp)
		np->complex = lp->complex;
	if (rp) {
		int d = np->complex - rp->complex;

		if (d == 0)
			++np->complex;
		else if (d < 0)
			np->complex = rp->complex;
	}
	if (np->complex == 0)
		++np->complex;
	return np;
}