shithub: scc

ref: 3ae3ab16c27973a7fdee2bd6daad1aad9cc9910c
dir: /cc1/code.c/

View raw version

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include "../inc/cc.h"
#include "cc1.h"

static void emitbin(unsigned, void *),
            emitcast(unsigned, void *), emitswitch(unsigned, void *),
            emitsym(unsigned, void *), emitfield(unsigned, void *),
            emitexp(unsigned, void *),
            emitsymid(unsigned, void *), emittext(unsigned, void *),
            emitprint(unsigned, void *), emitfun(unsigned, void *),
            emitret(unsigned, void *), emitdcl(unsigned, void *);

char *optxt[] = {
	[OADD] = "+",
	[OSUB] = "-",
	[OMUL] = "*",
	[OINC] = ";+",
	[ODEC] =  ";-",
	[OPTR] = "@",
	[OMOD] = "%",
	[ODIV] = "/",
	[OSHL] = "l",
	[OSHR]  = "r",
	[OLT] = "<",
	[OGT] = ">",
	[OGE] = "]",
	[OLE] =  "[",
	[OEQ] = "=",
	[ONE] = "!",
	[OBAND] = "&",
	[OBXOR]  = "^",
	[OBOR] = "|",
	[OASSIGN] = ":",
	[OA_MUL] = ":*",
	[OA_DIV] = ":/",
	[OA_MOD] = ":%",
	[OA_ADD] = ":+",
	[OA_SUB] = ":-",
	[OA_SHL] = ":l",
	[OA_SHR] = ":r",
	[OA_AND] = ":&",
	[OA_XOR] = ":^",
	[OA_OR] = ":|",
	[OADDR] = "a",
	[ONEG] = "_",
	[OCPL] = "~",
	[OAND] = "y",
	[OOR] = "o",
	[OASK] = "?",
	[OCOMMA] = ",",
	[OLABEL] = "L%d\n",
	[ODEFAULT] = "\tf\tL%d\n",
	[OCASE] = "\tw\tL%d",
	[OSTRUCT] = "S%d\t(\n",
	[OJUMP] = "\tj\tL%d\n",
	[OBRANCH] = "\tj\tL%d",
	[OEFUN] = "}",
	[OESTRUCT] = ")",
	[OELOOP] = "\tb",
	[OBLOOP] = "\td"
};

void (*opcode[])(unsigned, void *) = {
	[OADD] = emitbin,
	[OSUB] = emitbin,
	[OMUL] = emitbin,
	[OINC] = emitbin,
	[ODEC] =  emitbin,
	[OPTR] = emitbin,
	[OMOD] = emitbin,
	[ODIV] = emitbin,
	[OSHL] = emitbin,
	[OSHR]  = emitbin,
	[OLT] = emitbin,
	[OGT] = emitbin,
	[OGE] = emitbin,
	[OLE] =  emitbin,
	[OEQ] = emitbin,
	[ONE] = emitbin,
	[OBAND] = emitbin,
	[OBXOR]  = emitbin,
	[OBOR] = emitbin,
	[OASSIGN] = emitbin,
	[OA_MUL] = emitbin,
	[OA_DIV] = emitbin,
	[OA_MOD] = emitbin,
	[OA_ADD] = emitbin,
	[OA_SUB] = emitbin,
	[OA_SHL] = emitbin,
	[OA_SHR] = emitbin,
	[OA_AND] = emitbin,
	[OA_XOR] = emitbin,
	[OA_OR] = emitbin,
	[OADDR] = emitbin,
	[ONEG] = emitbin,
	[OCPL] = emitbin,
	[OAND] = emitbin,
	[OOR] = emitbin,
	[OCOMMA] = emitbin,
	[OCAST] = emitcast,
	[OSYM] = emitsym,
	[OASK] = emitbin,
	[OCOLON] = emitbin,
	[OFIELD]= emitfield,
	[OEXPR] = emitexp,
	[OLABEL] = emitsymid,
	[ODEFAULT] = emitsymid,
	[OCASE] = emitsymid,
	[OSTRUCT] = emitsymid,
	[OJUMP] = emitsymid,
	[OBRANCH] = emitsymid,
	[OEFUN] = emittext,
	[OESTRUCT] = emittext,
	[OELOOP] = emittext,
	[OBLOOP] = emittext,
	[OFUN] = emitfun,
	[ORET] = emitret,
	[ODECL] = emitdcl,
	[OSWITCH] = emitswitch
};

static Node *simple_add(Type *, Node *, Node *),
            *simple_sub(Type *, Node *, Node *),
            *simple_mul(Type *, Node *, Node *),
            *simple_div(Type *, Node *, Node *),
            *simple_mod(Type *, Node *, Node *);

void
freetree(Node *np)
{
	if (!np)
		return;
	freetree(np->left);
	freetree(np->right);
	free(np);
}

static void
emitnode(Node *np)
{
	if (np)
		(*opcode[np->op])(np->op, np);
}

void
emit(unsigned op, void *arg)
{
	extern int failure;

	if (failure)
		return;
	(*opcode[op])(op, arg);
}

static void
emitvar(Symbol *sym)
{
	char c;

	if (sym->flags & ISSTATIC)
		c = (sym->flags & ISGLOBAL) ? 'Y' : 'T';
	else if (sym->flags & ISGLOBAL)
		c = 'G';
	else if (sym->flags & ISREGISTER)
		c = 'K';
	else if (sym->flags & ISFIELD)
		c = 'M';
	else if (sym->flags & ISPARAM)
		c = 'P';
	else
		c = 'A';
	printf("%c%d", c, sym->id);
}

static void
emitconst(Node *np)
{
	char *bp, c;
	Symbol *sym = np->sym;

	switch (BTYPE(np)) {
	case INT:
		printf("#%c%x", np->type->letter, sym->u.i);
		break;
	case ARY:
		/*
		 * FIX: At this point we are going to assume
		 * that all the arrays are strings
		 */
		putchar('"');
		for (bp = sym->u.s; c = *bp; ++bp)
			printf("%02X", c & 0xFF);
		break;
	default:
		/* TODO: Handle other kind of constants */
		abort();
	}
}

static void
emitsym(unsigned op, void *arg)
{
	Node *np = arg;
	putchar('\t');
	(np->constant) ? emitconst(np) : emitvar(np->sym);
}

static void
emitletter(Type *tp)
{
	putchar(tp->letter);
	if (tp->op == ARY)
		printf("%d", tp->id);
}

static void
emittype(Type *tp)
{
	if (tp->printed)
		return;

	switch (tp->op) {
	case ARY:
		emittype(tp->type);
		printf("V%d\t", tp->id);
		emitletter(tp->type);
		printf("\t#%d\n", tp->n.elem);
		return;
	case PTR:
		emittype(tp->type);
		return;
	default:
		abort();
	}
}

static void
emitdcl(unsigned op, void *arg)
{
	Symbol *sym = arg;

	emittype(sym->type);
	emitvar(sym);
	putchar('\t');
	emitletter(sym->type);
	putchar('\n');
}

static void
emitcast(unsigned op, void *arg)
{
	Node *np = arg, *lp = np->left;

	emitnode(lp);
	printf("\t%c%c", lp->type->letter, np->type->letter);
}

static void
emitbin(unsigned op, void *arg)
{
	Node *np = arg;
	char *s;

	emitnode(np->left);
	emitnode(np->right);
	if ((s = optxt[op]) != NULL)
		printf("\t%s%c", s, np->type->letter);
}

static void
emitexp(unsigned op, void *arg)
{
	Node *np = arg;

	emitnode(np);
	putchar('\n');
	freetree(np);
}

static void
emitfun(unsigned op, void *arg)
{
	Symbol *sym = arg;

	printf("%c%d\tF\t%s\t{\n",
	       sym->flags & ISGLOBAL ? 'G' : 'Y', sym->id, sym->name);
}

static void
emitret(unsigned op, void *arg)
{
	Type *tp = arg;

	fputs("\ty", stdout);
	emitletter(tp);
}

static void
emittext(unsigned op, void *arg)
{
	puts(optxt[op]);
}

static void
emitsymid(unsigned op, void *arg)
{
	Symbol *sym = arg;
	printf(optxt[op], sym->id);
}

static void
emitswitch(unsigned op, void *arg)
{
	Caselist *lcase = arg;

	printf("\teI\t#%0x", lcase->nr);
}

void
emitfield(unsigned op, void *arg)
{
	Node *np = arg;

	emitnode(np->left);
	putchar('\t');
	emitvar(np->sym);
}

Node *
node(unsigned op, Type *tp, Node *lp, Node *rp)
{
	Node *np;

	np = xmalloc(sizeof(*np));
	np->op = op;
	np->type = tp;
	np->sym = NULL;
	np->constant = np->symbol = np->lvalue = 0;
	np->left = lp;
	np->right = rp;

	return np;
}

Node *
varnode(Symbol *sym)
{
	Node *np;

	np = node(OSYM, sym->type, NULL, NULL);
	np->lvalue = 1;
	np->constant = 0;
	np->symbol = 1;
	np->sym = sym;
	return np;
}

Node *
constnode(Symbol *sym)
{
	Node *np;

	np = node(OSYM, sym->type, NULL, NULL);
	np->symbol = 1;
	np->constant = 1;
	np->sym = sym;
	return np;
}

Node *
sizeofnode(Type *tp)
{
	Node *np;
	Symbol *sym;

	sym = newsym(NS_IDEN);
	sym->type = sizettype;
	sym->u.i = tp->size;
	return constnode(sym);
}

#define SYMICMP(sym, val) (((sym)->type->sign) ?         \
	(sym)->u.i == (val) : (sym)->u.u == (val))

#define FOLDINT(sym, ls, rs, op) (((sym)->type->sign) ? \
	((sym)->u.i = ((ls)->u.i op (rs)->u.i)) :       \
	((sym)->u.u = ((ls)->u.u op (rs)->u.u)))

#define CMPISYM(sym, ls, rs, op) (((sym)->type->sign) ? \
	((ls)->u.i op (rs)->u.i) : ((ls)->u.u op (rs)->u.u))

Node *
simplify(unsigned char op, Type *tp, Node *lp, Node *rp)
{
	Symbol *sym, *ls, *rs, aux;

	if (!lp->constant || !rp->constant)
		goto no_simplify;
	ls = lp->sym, rs = rp->sym;
	aux.type = tp;

	/* TODO: Add overflow checkings */

	if (isnodecmp(op)) {
		/*
		 * Comparision nodes have integer type
		 * but the operands can have different
		 * type.
		 */
		switch (BTYPE(lp)) {
		case INT:   goto cmp_integers;
		case FLOAT: goto cmp_floats;
		default:    goto no_simplify;
		}
	}

	switch (tp->op) {
	case INT:
	cmp_integers:
		switch (op) {
		case OADD:
			FOLDINT(&aux, ls, rs, +);
			break;
		case OSUB:
			FOLDINT(&aux, ls, rs, -);
			break;
		case OMUL:
			FOLDINT(&aux, ls, rs, *);
			break;
		case ODIV:
			if (SYMICMP(&aux, 0))
				goto division_by_0;
			FOLDINT(&aux, ls, rs, /);
			break;
		case OMOD:
			if (SYMICMP(&aux, 0))
				goto division_by_0;
			FOLDINT(&aux, ls, rs, %);
			break;
		case OSHL:
			FOLDINT(&aux, ls, rs, <<);
			break;
		case OSHR:
			FOLDINT(&aux, ls, rs, >>);
			break;
		case OBAND:
			FOLDINT(&aux, ls, rs, &);
			break;
		case OBXOR:
			FOLDINT(&aux, ls, rs, ^);
			break;
		case OBOR:
			FOLDINT(&aux, ls, rs, |);
			break;
		case OAND:
			FOLDINT(&aux, ls, rs, &&);
			break;
		case OOR:
			FOLDINT(&aux, ls, rs, ||);
			break;
		case OLT:
			aux.u.i = CMPISYM(&aux, ls, rs, <);
			break;
		case OGT:
			aux.u.i = CMPISYM(&aux, ls, rs, >);
			break;
		case OGE:
			aux.u.i = CMPISYM(&aux, ls, rs, >=);
			break;
		case OLE:
			aux.u.i = CMPISYM(&aux, ls, rs, <=);
			break;
		case OEQ:
			aux.u.i = CMPISYM(&aux, ls, rs, ==);
			break;
		case ONE:
			aux.u.i = CMPISYM(&aux, ls, rs, !=);
			break;
		}
		break;
	case FLOAT:
	cmp_floats:
		switch (op) {
		case OADD:
			aux.u.f = ls->u.f + rs->u.f;
			break;
		case OSUB:
			aux.u.f = ls->u.f - rs->u.f;
			break;
		case OMUL:
			aux.u.f = ls->u.f * rs->u.f;
			break;
		case ODIV:
			if (rs->u.f == 0.0)
				goto division_by_0;
			aux.u.f = ls->u.f / rs->u.f;
			break;
		case OLT:
			aux.u.i = ls->u.f < rs->u.f;
			break;
		case OGT:
			aux.u.i = ls->u.f > rs->u.f;
			break;
		case OGE:
			aux.u.i = ls->u.f >= rs->u.f;
			break;
		case OLE:
			aux.u.i = ls->u.f <= rs->u.f;
			break;
		case OEQ:
			aux.u.i = ls->u.f == rs->u.f;
			break;
		case ONE:
			aux.u.i = ls->u.f != rs->u.f;
			break;
		}
		break;
	default:
		goto no_simplify;
	}

	sym = newsym(NS_IDEN);
	sym->type = tp;
	sym->u = aux.u;
	return constnode(sym);

division_by_0:
	warn("division by 0");

no_simplify:
	return node(op, tp, lp, rp);
}

#define UFOLDINT(sym, ls, op) (((sym)->type->sign) ?     \
	((sym)->u.i = (op (ls)->u.i)) :                  \
	((sym)->u.u = (op (ls)->u.u)))

Node *
usimplify(unsigned char op, Type *tp, Node *np)
{
	Symbol *sym, *ns, aux;

	if (!np->constant)
		goto no_simplify;
	ns = np->sym;
	aux.type = tp;

	switch (tp->op) {
	case INT:
		switch (op) {
		case ONEG:
			UFOLDINT(&aux, ns, -);
			break;
		case OCPL:
			UFOLDINT(&aux, ns, ~);
			break;
		default:
			goto no_simplify;
		}
		break;
	case FLOAT:
		if (op != ONEG)
			goto no_simplify;
		aux.u.f = -ns->u.f;
		break;
	default:
		goto no_simplify;
	}

	sym = newsym(NS_IDEN);
	sym->type = tp;
	sym->u = aux.u;
	return constnode(sym);

no_simplify:
	return node(op, tp, np, NULL);
}