shithub: scc

ref: 89c576b05ec3efa7bfe0880be3002742fba88cd1
dir: /cc2/arch/qbe/code.c/

View raw version

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

#define ADDR_LEN (IDENTSIZ+2)

static void binary(void), load(void), store(void);

static struct opdata {
	void (*fun)(void);
	char *txt;
	char letter;
} optbl [] = {
	[ASLOAD] =  {.fun = load,   .txt = "load", .letter = 'w'},
	[ASSTB]   =  {.fun = store,  .txt = "store", .letter = 'b'},
	[ASSTH]   =  {.fun = store,  .txt = "store", .letter = 'h'},
	[ASSTW]   =  {.fun = store,  .txt = "store", .letter = 'w'},
	[ASSTL]   =  {.fun = store,  .txt = "store", .letter = 'l'},
	[ASSTS]   =  {.fun = store,  .txt = "store", .letter = 's'},
	[ASSTD]   =  {.fun = store,  .txt = "store", .letter = 'd'},

	[ASADDW]  =  {.fun = binary, .txt = "add", .letter = 'w'},
	[ASSUBW]  =  {.fun = binary, .txt = "sub", .letter = 'w'},
	[ASMULW]  =  {.fun = binary, .txt = "mul", .letter = 'w'},
	[ASMODW]  =  {.fun = binary, .txt = "rem", .letter = 'w'},
	[ASUMODW] =  {.fun = binary, .txt = "urem", .letter = 'w'},
	[ASDIVW]  =  {.fun = binary, .txt = "div", .letter = 'w'},
	[ASUDIVW] =  {.fun = binary, .txt = "udiv", .letter = 'w'},
	[ASSHLW]  =  {.fun = binary, .txt = "shl", .letter = 'w'},
	[ASSHRW]  =  {.fun = binary, .txt = "shr", .letter = 'w'},
	[ASLTW]   =  {.fun = binary, .txt = "csltw", .letter = 'w'},
	[ASULTW]  =  {.fun = binary, .txt = "cultw", .letter = 'w'},
	[ASGTW]   =  {.fun = binary, .txt = "csgtw", .letter = 'w'},
	[ASUGTW]  =  {.fun = binary, .txt = "cugtw", .letter = 'w'},
	[ASLEW]   =  {.fun = binary, .txt = "cslew", .letter = 'w'},
	[ASULEW]  =  {.fun = binary, .txt = "culew", .letter = 'w'},
	[ASGEW]   =  {.fun = binary, .txt = "csgew", .letter = 'w'},
	[ASUGEW]  =  {.fun = binary, .txt = "cugew", .letter = 'w'},
	[ASEQW]   =  {.fun = binary, .txt = "ceqw", .letter = 'w'},
	[ASNEW]   =  {.fun = binary, .txt = "cnew", .letter = 'w'},
	[ASBANDW] =  {.fun = binary, .txt = "and", .letter = 'w'},
	[ASBORW]  =  {.fun = binary, .txt = "or", .letter = 'w'},
	[ASBXORW] =  {.fun = binary, .txt = "xor", .letter = 'w'},

	[ASADDL]  =  {.fun = binary, .txt = "add", .letter = 'l'},
	[ASSUBL]  =  {.fun = binary, .txt = "sub", .letter = 'l'},
	[ASMULL]  =  {.fun = binary, .txt = "mul", .letter = 'l'},
	[ASMODL]  =  {.fun = binary, .txt = "rem", .letter = 'l'},
	[ASUMODL] =  {.fun = binary, .txt = "urem", .letter = 'l'},
	[ASDIVL]  =  {.fun = binary, .txt = "div", .letter = 'l'},
	[ASUDIVL] =  {.fun = binary, .txt = "udiv", .letter = 'l'},
	[ASSHLL]  =  {.fun = binary, .txt = "shl", .letter = 'l'},
	[ASSHRL]  =  {.fun = binary, .txt = "shr", .letter = 'l'},
	[ASLTL]   =  {.fun = binary, .txt = "csltl", .letter = 'w'},
	[ASULTL]  =  {.fun = binary, .txt = "cultl", .letter = 'w'},
	[ASGTL]   =  {.fun = binary, .txt = "csgtl", .letter = 'w'},
	[ASUGTL]  =  {.fun = binary, .txt = "cugtl", .letter = 'w'},
	[ASLEL]   =  {.fun = binary, .txt = "cslel", .letter = 'w'},
	[ASULEL]  =  {.fun = binary, .txt = "culel", .letter = 'w'},
	[ASGEL]   =  {.fun = binary, .txt = "csgel", .letter = 'w'},
	[ASUGEL]  =  {.fun = binary, .txt = "cugel", .letter = 'w'},
	[ASEQL]   =  {.fun = binary, .txt = "ceql", .letter = 'w'},
	[ASNEL]   =  {.fun = binary, .txt = "cnel", .letter = 'w'},
	[ASBANDL] =  {.fun = binary, .txt = "and", .letter = 'l'},
	[ASBORL]  =  {.fun = binary, .txt = "or", .letter = 'l'},
	[ASBXORL] =  {.fun = binary, .txt = "xor", .letter = 'l'},

	[ASADDS]  =  {.fun = binary, .txt = "add", .letter = 's'},
	[ASSUBS]  =  {.fun = binary, .txt = "sub", .letter = 's'},
	[ASMULS]  =  {.fun = binary, .txt = "mul", .letter = 's'},
	[ASDIVS]  =  {.fun = binary, .txt = "div", .letter = 's'},
	[ASLTS]   =  {.fun = binary, .txt = "clts", .letter = 'w'},
	[ASGTS]   =  {.fun = binary, .txt = "cgts", .letter = 'w'},
	[ASLES]   =  {.fun = binary, .txt = "cles", .letter = 'w'},
	[ASGES]   =  {.fun = binary, .txt = "cges", .letter = 'w'},
	[ASEQS]   =  {.fun = binary, .txt = "ceqs", .letter = 'w'},
	[ASNES]   =  {.fun = binary, .txt = "cnes", .letter = 'w'},

	[ASADDD]  =  {.fun = binary, .txt = "add", .letter = 'd'},
	[ASSUBD]  =  {.fun = binary, .txt = "sub", .letter = 'd'},
	[ASMULD]  =  {.fun = binary, .txt = "mul", .letter = 'd'},
	[ASDIVD]  =  {.fun = binary, .txt = "div", .letter = 'd'},
	[ASLTD]   =  {.fun = binary, .txt = "cltd", .letter = 'w'},
	[ASGTD]   =  {.fun = binary, .txt = "cgtd", .letter = 'w'},
	[ASLED]   =  {.fun = binary, .txt = "cled", .letter = 'w'},
	[ASGED]   =  {.fun = binary, .txt = "cged", .letter = 'w'},
	[ASEQD]   =  {.fun = binary, .txt = "ceqd", .letter = 'w'},
	[ASNED]   =  {.fun = binary, .txt = "cned", .letter = 'w'},
};

/*
 * : is for user-defined Aggregate Types
 * $ is for globals (represented by a pointer)
 * % is for function-scope temporaries
 * @ is for block labels
 */
static char
sigil(Symbol *sym)
{
	switch (sym->kind) {
	case SEXTRN:
	case SGLOB:
	case SPRIV:
	case SLOCAL:
		return '$';
	case SAUTO:
	case STMP:
		return '%';
	case SLABEL:
		return '@';
	default:
		abort();
	}
}

static char *
symname(Symbol *sym)
{
	static char name[IDENTSIZ+2];
	static unsigned short id;
	char c = sigil(sym);

	if (sym->name) {
		switch (sym->kind) {
		case SEXTRN:
		case SGLOB:
		case SPRIV:
		case SAUTO:
			sprintf(name, "%c%s", c, sym->name);
			return name;
		default:
			abort();
		}
	}

	if (sym->numid == 0 && (sym->numid = ++id) == 0)
		error(EIDOVER);
	sprintf(name, "%c.%d", c, sym->numid);

	return name;
}

static void
emitconst(Node *np)
{
	switch (np->type.size) {
	case 1:
		printf("%d", (int) np->u.i & 0xFF);
		break;
	case 2:
		printf("%d", (int) np->u.i & 0xFFFF);
		break;
	case 4:
		printf("%ld", (long) np->u.i & 0xFFFFFFFF);
		break;
        case 8:
                printf("%lld", (long long) np->u.i & 0xFFFFFFFF);
                break;
	default:
		abort();
	}
}

static void
emittree(Node *np)
{
	if (!np)
		return;

	switch (np->op) {
	case OSTRING:
		printf("\"%s\"", np->u.s);
		free(np->u.s);
		np->u.s = NULL;
		break;
	case OCONST:
		emitconst(np);
		break;
	case OADDR:
		emittree(np->left);
		break;
	case OMEM:
		fputs(symname(np->u.sym), stdout);
		break;
	default:
		emittree(np->left);
		printf(" %c ", np->op);
		emittree(np->right);
		break;
	}
}

static char *
size2asm(Type *tp)
{
	if (tp->flags & STRF) {
		return "b";
	} else {
		switch (tp->size) {
		case 0:
			return "";
		case 1:
			return "b";
		case 2:
			return "h";
		case 4:
			return "w";
		case 8:
			return "l";
		default:
			abort();
		}
	}
}

void
defglobal(Symbol *sym)
{
	if (sym->kind == SEXTRN)
		return;
	if (sym->kind == SGLOB)
		fputs("export ", stdout);
	printf("data %s = {\n", symname(sym));
	if (sym->type.flags & INITF)
		return;
	printf("\tz\t%llu\n}\n", (unsigned long long) sym->type.size);
}

void
defpar(Symbol *sym)
{
	sym->type.flags |= PARF;
}

void
defvar(Symbol *sym)
{
}

void
data(Node *np)
{
	printf("\t%s\t", size2asm(&np->type));
	emittree(np);
	putchar(',');
	putchar('\n');
}

static void
alloc(Symbol *sym)
{
	Type *tp = &sym->type;

	printf("\t%s %s=\talloc%lld\t%lld\n",
	       symname(sym), size2asm(tp),
	       (long long) tp->size, (long long) tp->align);
}

void
writeout(void)
{
	Symbol *p;
	Type *tp;
	char *sep, *name;

	if (curfun->kind == SGLOB)
		fputs("export ", stdout);
	printf("function %s %s(", size2asm(&curfun->rtype), symname(curfun));

	/* declare formal parameters */
	for (sep = "", p = locals; p; p = p->next, sep = ",") {
		if ((p->type.flags & PARF) == 0)
			break;
		printf("%s%s %s.val", sep, size2asm(&p->type), symname(p));
	}
	puts(")\n{");

	/* allocate stack space for parameters */
	for (p = locals; p && (p->type.flags & PARF) == 0; p = p->next)
		alloc(p);

	/* allocate stack space for local variables) */
	for ( ; p && p->id != TMPSYM; p = p->next)
		alloc(p);

	/* store formal parameters in parameters */
	for (p = locals; p; p = p->next) {
		tp = &p->type;
		if ((tp->flags & PARF) == 0)
			break;
		name = symname(p);
		printf("\t\tstore%s\t%s.val,%s\n", size2asm(tp), name, name);
	}

	/* emit assembler instructions */
	for (pc = prog; pc; pc = pc->next) {
		if (pc->label)
			printf("%s:\n", symname(pc->label));
		(*optbl[pc->op].fun)();
	}

	puts("}");
}

static char *
addr2txt(Addr *a)
{
	switch (a->kind) {
	case SAUTO:
	case SLABEL:
	case STMP:
		return symname(a->u.sym);
	default:
		abort();
	}
}

static void
binary(void)
{
	struct opdata *p = &optbl[pc->op];
	char to[ADDR_LEN], from1[ADDR_LEN], from2[ADDR_LEN];

	strcpy(to, addr2txt(&pc->to));
	strcpy(from1, addr2txt(&pc->from1));
	strcpy(from2, addr2txt(&pc->from2));
	printf("\t%s %c=\t%s\t%s,%s\n", to, p->letter, p->txt, from1, from2);
}

static void
store(void)
{
	struct opdata *p = &optbl[pc->op];
	char to[ADDR_LEN], from[ADDR_LEN];

	strcpy(to, addr2txt(&pc->to));
	strcpy(from, addr2txt(&pc->from1));
	printf("\t\t%s%c\t%s,%s\n", p->txt, p->letter, from, to);
}

static void
load(void)
{
	printf("\t%s %c=\t", addr2txt(&pc->to), 'w');
	printf("%s\t%s\n", optbl[pc->op].txt, addr2txt(&pc->from1));
}

void
endinit(void)
{
	puts("}");
}