nctref/src/cg.c
2024-11-26 18:42:20 +02:00

552 lines
21 KiB
C

#include"cg.h"
#include<stdlib.h>
#include<signal.h>
#include<string.h>
#include<assert.h>
#include"x86.h"
#define REGS 4
static const char *regs[][3] = {
[COLOR_EAX] = {"al", "ax", "eax"},
[COLOR_EBX] = {"bl", "bx", "ebx"},
[COLOR_ECX] = {"cl", "cx", "ecx"},
[COLOR_EDX] = {"dl", "dx", "edx"},
{"fu", "fk", "fck"}
};
static const char *BINOP_SIMPLE_INSTRS[] = {[BINOP_ADD] = "add", [BINOP_SUB] = "sub", [BINOP_BITWISE_AND] = "and", [BINOP_BITWISE_OR] = "or", [BINOP_BITWISE_XOR] = "xor"};
static size_t nextLocalLabel = 0;
typedef struct {
#define LOOPSTACKSIZE 96
size_t loopStackStart[LOOPSTACKSIZE];
size_t loopStackEnd[LOOPSTACKSIZE];
size_t loopStackIdx;
} CGState;
static const char *direct(int size) {
switch(size) {
case 0:
case 1: return "db";
case 2: return "dw";
case 4: return "dd";
case 8: return "dq";
}
abort();
}
static const char *spec(int size) {
switch(size) {
case 0:
case 1: return "byte";
case 2: return "word";
case 4: return "dword";
case 8: return "qword";
}
abort();
}
static int log_size(int size) {
switch(size) {
case 0:
case 1: return 0;
case 2: return 1;
case 4: return 2;
case 8: return 3;
}
abort();
}
static const char *specexpr(AST *e) {
return spec(type_size(e->expression.type));
}
static const char *xv_sz(VarTableEntry *v, int sz) {
assert(v->kind == VARTABLEENTRY_VAR);
#define XVBUFS 8
#define XVBUFSZ 8
static char bufs[XVBUFS][XVBUFSZ];
static int bufidx = 0;
char *ret = bufs[bufidx];
#ifdef DEBUG
snprintf(ret, XVBUFSZ, "@%i", v->data.var.color);
#else
snprintf(ret, XVBUFSZ, "%s", regs[v->data.var.color][log_size(sz)]);
#endif
bufidx = (bufidx + 1) % XVBUFS;
return ret;
}
static const char *xv(VarTableEntry *v) {
return xv_sz(v, type_size(v->type));
}
static const char *xj(BinaryOp op) {
switch(op) {
case BINOP_EQUAL: return "e";
case BINOP_NEQUAL: return "ne";
default: return "wtf";
}
}
static const char *xop_sz(AST *e, int sz) {
#define XOPBUFS 16
#define XOPBUFSZ 24
static char bufs[XOPBUFS][XOPBUFSZ];
static int bufidx = 0;
char *ret = bufs[bufidx];
if(e->nodeKind == AST_EXPR_STACK_POINTER) {
snprintf(ret, XOPBUFSZ, "esp");
} else if(e->nodeKind == AST_EXPR_VAR) {
VarTableEntry *v = e->exprVar.thing;
if(v->kind == VARTABLEENTRY_VAR) {
return xv_sz(v, sz);
} else if(v->kind == VARTABLEENTRY_SYMBOL) {
snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), v->data.symbol.name);
} else abort();
} else if(e->nodeKind == AST_EXPR_PRIMITIVE) {
snprintf(ret, XOPBUFSZ, "%i", e->exprPrim.val);
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
xv_sz(e->exprUnOp.operand->exprBinOp.operands[0]->exprVar.thing, 4),
xv_sz(e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing, 4));
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
xv_sz(e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing, 4));
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s + %i * %s]",
spec(sz),
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val,
xv_sz(e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing, 4));
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) {
snprintf(ret, XOPBUFSZ, "%s", e->exprUnOp.operand->exprVar.thing->data.symbol.name);
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(e->exprUnOp.operand->exprVar.thing, 4));
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == UNOP_DEREF && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_STACK_POINTER && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
snprintf(ret, XOPBUFSZ, "[esp + %i]", e->exprUnOp.operand->exprBinOp.operands[1]->exprPrim.val);
} else {
return NULL;
}
bufidx = (bufidx + 1) % XOPBUFS;
return ret;
}
static const char *xop(AST *e) {
return xop_sz(e, type_size(e->expression.type));
}
void cg_chunk(CGState *cg, AST *a) {
AST *s = a->chunk.statementFirst;
if(a->chunk.stackReservation) {
printf("sub esp, %lu\n", a->chunk.stackReservation);
}
// Potentially complex pattern matching
while(s) {
if(s->nodeKind == AST_STMT_EXT_SECTION) {
Token t = s->stmtExtSection.name;
printf("section %.*s\n", (int) t.length, t.content);
} else if(s->nodeKind == AST_STMT_EXT_ORG) {
printf("org %lu\n", s->stmtExtOrg.val);
} else if(s->nodeKind == AST_STMT_EXT_ALIGN) {
uint32_t val = s->stmtExtAlign.val;
if((val & (val - 1))) {
// nasm does not support non-PoT alignments, so pad manually
printf("times ($ - $$ + %u) / %u * %u - ($ - $$) db 0\n", val - 1, val, val);
} else {
printf("align %u\n", val);
}
} else if(s->nodeKind == AST_STMT_DECL && s->stmtDecl.thing->kind == VARTABLEENTRY_SYMBOL) {
VarTableEntry *v = s->stmtDecl.thing;
if(v->data.symbol.isExternal) {
printf("extern %s\n", v->data.symbol.name);
} else {
if(!v->data.symbol.isLocal) {
printf("global %s\n", v->data.symbol.name);
}
if(s->stmtDecl.expression) {
printf("%s:", v->data.symbol.name);
if(v->type->type == TYPE_TYPE_PRIMITIVE) {
assert(s->stmtDecl.expression->nodeKind == AST_EXPR_PRIMITIVE);
printf("%s %i", direct(type_size(v->type)), s->stmtDecl.expression->exprPrim.val);
} else if(v->type->type == TYPE_TYPE_ARRAY && v->type->array.of->type == TYPE_TYPE_PRIMITIVE) {
printf("%s ", direct(type_size(v->type->array.of)));
for(size_t i = 0; i < v->type->array.length; i++) {
printf("%i,", s->stmtDecl.expression->exprArray.items[i]->exprPrim.val);
}
} else if(v->type->type == TYPE_TYPE_FUNCTION) {
putchar('\n');
assert(s->stmtDecl.expression->nodeKind == AST_EXPR_FUNC);
cg_go(s->stmtDecl.expression->exprFunc.chunk);
} else abort();
putchar('\n');
} else {
printf("%s resb %lu\n", v->data.symbol.name, type_size(s->stmtDecl.thing->type));
}
}
} else if(s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_CALL) {
AST *e = s->stmtAssign.to;
puts("push ecx");
puts("push edx");
int argCount = e->exprCall.what->expression.type->function.argCount;
size_t argSize = 0;
for(int i = argCount - 1; i >= 0; i--) {
printf("push %s\n", xop_sz(e->exprCall.args[i], 4));
argSize += (type_size(e->exprCall.args[i]->expression.type) + 3) & ~3;
}
assert(e->exprCall.what->nodeKind == AST_EXPR_VAR && e->exprCall.what->exprVar.thing->kind == VARTABLEENTRY_SYMBOL);
printf("call %s\n", e->exprCall.what->exprVar.thing->data.symbol.name);
if(argSize) printf("add esp, %lu\n", argSize);
puts("pop edx");
puts("pop ecx");
} else if(s->nodeKind == AST_STMT_ASSIGN) {
if(is_xop(s->stmtAssign.what) == XOP_NOT_MEM && is_xop(s->stmtAssign.to) == XOP_NOT_MEM && !strcmp(xop(s->stmtAssign.what), xop(s->stmtAssign.to))) {
// It's a noop
} else if(s->stmtAssign.to) {
if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0]) && (s->stmtAssign.to->exprBinOp.operator == BINOP_ADD || s->stmtAssign.to->exprBinOp.operator == BINOP_SUB) && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val == 1) {
// inc or dec
static const char *instrs[] = {"inc", "dec"};
printf("%s %s %s\n", instrs[s->stmtAssign.to->exprBinOp.operator == BINOP_SUB], specexpr(s->stmtAssign.what), xop(s->stmtAssign.what));
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0]) && s->stmtAssign.to->exprBinOp.operator < BINOP_SIMPLES) {
printf("%s %s, %s\n", BINOP_SIMPLE_INSTRS[s->stmtAssign.to->exprBinOp.operator], xop(s->stmtAssign.what), xop(s->stmtAssign.to->exprBinOp.operands[1]));
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
printf("lea %s, [%s + %s]\n",
xv(s->stmtAssign.what->exprVar.thing),
xv(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing),
xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing));
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
printf("lea %s, [%s + %s]\n",
xv(s->stmtAssign.what->exprVar.thing),
s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing));
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
printf("lea %s, [%s + %i]\n",
xv_sz(s->stmtAssign.what->exprVar.thing, 4),
xv_sz(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, 4),
s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val);
} else if(is_xop(s->stmtAssign.what) != NULL && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_NEGATE && ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprUnOp.operand)) {
printf("neg %s\n", xop(s->stmtAssign.what));
} else if(is_xop(s->stmtAssign.what) && s->stmtAssign.to->nodeKind == AST_EXPR_CAST) {
printf("movzx %s, %s\n", xop(s->stmtAssign.what), xop(s->stmtAssign.to->exprCast.what));
} else {
if(is_xop(s->stmtAssign.to) == XOP_MEM && type_size(s->stmtAssign.what->expression.type) != type_size(s->stmtAssign.to->expression.type)) {
printf("movzx %s, %s\n", xop_sz(s->stmtAssign.what, 4), xop_sz(s->stmtAssign.to, type_size(s->stmtAssign.what->expression.type)));
} else {
printf("mov %s, %s\n", xop(s->stmtAssign.what), xop_sz(s->stmtAssign.to, type_size(s->stmtAssign.what->expression.type)));
}
}
}
} else if(s->nodeKind == AST_STMT_LOOP) {
size_t lbl0 = nextLocalLabel++;
size_t lbl1 = nextLocalLabel++;
cg->loopStackStart[cg->loopStackIdx] = lbl0;
cg->loopStackEnd[cg->loopStackIdx] = lbl1;
cg->loopStackIdx++;
printf(".L%lu:\n", lbl0);
cg_chunk(cg, s->stmtLoop.body);
printf("jmp .L%lu\n", lbl0);
printf(".L%lu:\n", lbl1);
cg->loopStackIdx--;
} else if(s->nodeKind == AST_STMT_BREAK) {
printf("jmp .L%lu\n", cg->loopStackEnd[cg->loopStackIdx - 1]);
} else if(s->nodeKind == AST_STMT_CONTINUE) {
printf("jmp .L%lu\n", cg->loopStackStart[cg->loopStackIdx - 1]);
} else if(s->nodeKind == AST_STMT_IF) {
assert(s->stmtIf.expression->nodeKind == AST_EXPR_BINARY_OP && binop_is_comparison(s->stmtIf.expression->exprBinOp.operator));
size_t lbl = nextLocalLabel++;
printf("cmp %s, %s\n", xop(s->stmtIf.expression->exprBinOp.operands[0]), xop(s->stmtIf.expression->exprBinOp.operands[1]));
printf("j%s .L%lu\n", xj(binop_comp_opposite(s->stmtIf.expression->exprBinOp.operator)), lbl);
cg_chunk(cg, s->stmtIf.then);
printf(".L%lu:\n", lbl);
}
s = s->statement.next;
}
if(a->chunk.stackReservation) {
printf("add esp, %lu\n", a->chunk.stackReservation);
}
}
// MUST FIRST CALL with *aptr == tlc SO stackReservation IS UPDATED!!
static void spill_pass(AST *tlc, AST **aptr, VarTableEntry *vte) {
AST *a = *aptr;
if(a->nodeKind == AST_CHUNK) {
for(AST **s = &a->chunk.statementFirst; *s; s = &((*s)->statement.next)) {
spill_pass(tlc, s, vte);
}
tlc->chunk.stackReservation += 4;
} else if(a->nodeKind == AST_STMT_IF) {
spill_pass(tlc, &a->stmtIf.expression, vte);
spill_pass(tlc, &a->stmtIf.then, vte);
} else if(a->nodeKind == AST_STMT_LOOP) {
spill_pass(tlc, &a->stmtLoop.body, vte);
} else if(a->nodeKind == AST_STMT_ASSIGN) {
spill_pass(tlc, &a->stmtAssign.what, vte);
if(a->stmtAssign.to) {
spill_pass(tlc, &a->stmtAssign.to, vte);
}
} else if(a->nodeKind == AST_STMT_EXPR) {
spill_pass(tlc, &a->stmtExpr.expr, vte);
} else if(a->nodeKind == AST_EXPR_VAR) {
if(a->exprVar.thing == vte) {
// FINALLY SPILL
ASTExprStackPointer *rsp = malloc(sizeof(*rsp));
rsp->nodeKind = AST_EXPR_STACK_POINTER;
rsp->type = primitive_parse("u32");
ASTExprPrimitive *offset = malloc(sizeof(*offset));
offset->nodeKind = AST_EXPR_PRIMITIVE;
offset->type = rsp->type;
offset->val = -tlc->chunk.stackReservation;
ASTExprBinaryOp *bop = malloc(sizeof(*bop));
bop->nodeKind = AST_EXPR_BINARY_OP;
bop->type = rsp->type;
bop->operator = BINOP_ADD;
bop->operands[0] = (AST*) rsp;
bop->operands[1] = (AST*) offset;
ASTExprUnaryOp *deref = malloc(sizeof(*deref));
deref->nodeKind = AST_EXPR_UNARY_OP;
deref->type = a->expression.type;
deref->operator = UNOP_DEREF;
deref->operand = (AST*) bop;
*aptr = (AST*) deref;
}
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
spill_pass(tlc, &a->exprBinOp.operands[0], vte);
spill_pass(tlc, &a->exprBinOp.operands[1], vte);
} else if(a->nodeKind == AST_EXPR_UNARY_OP) {
spill_pass(tlc, &a->exprUnOp.operand, vte);
} else if(a->nodeKind == AST_EXPR_CALL) {
spill_pass(tlc, &a->exprCall.what, vte);
for(size_t p = 0; p < a->exprCall.what->expression.type->function.argCount; p++) {
spill_pass(tlc, &a->exprCall.args[p], vte);
}
} else if(a->nodeKind == AST_EXPR_PRIMITIVE) {
} else if(a->nodeKind == AST_EXPR_STRING_LITERAL) {
} else if(a->nodeKind == AST_EXPR_CAST) {
spill_pass(tlc, &a->exprCast.what, vte);
} else if(a->nodeKind == AST_EXPR_STACK_POINTER) {
} else if(a->nodeKind == AST_STMT_BREAK) {
} else if(a->nodeKind == AST_STMT_CONTINUE) {
} else if(a->nodeKind == AST_STMT_EXT_ALIGN) {
} else if(a->nodeKind == AST_STMT_EXT_ORG) {
} else if(a->nodeKind == AST_STMT_EXT_SECTION) {
} else if(a->nodeKind == AST_STMT_DECL) {
assert(a->stmtDecl.thing->kind != VARTABLEENTRY_VAR || a->stmtDecl.expression);
} else {
abort();
}
}
static int ud_empty(VarTableEntry *a) {
assert(a->kind == VARTABLEENTRY_VAR);
assert(!a->data.var.usedefFirst == !a->data.var.usedefLast);
return !a->data.var.usedefFirst;
}
/* Welsh-Powell graph coloring */
static int comparator(const void *A, const void *B) {
VarTableEntry *const *a = A;
VarTableEntry *const *b = B;
return ((*a)->data.var.degree * (*a)->data.var.priority) - ((*b)->data.var.degree * (*b)->data.var.priority);
}
int cg_go(AST *a) {
typedef VarTableEntry *Adjacency[2];
ast_usedef_reset(a);
size_t adjCount = 0;
Adjacency *adjs = calloc(adjCount, sizeof(*adjs));
VarTableEntry **vars = a->chunk.vars;
for(size_t vi = 0; vi < a->chunk.varCount; vi++) {
vars[vi]->data.var.priority = 1;
//vars[vi]->data.var.color = 0;
vars[vi]->data.var.degree = 0;
}
for(size_t v1i = 0; v1i < a->chunk.varCount; v1i++) {
for(size_t v2i = 0; v2i < a->chunk.varCount; v2i++) {
if(v1i == v2i) continue;
VarTableEntry *v1 = vars[v1i];
VarTableEntry *v2 = vars[v2i];
/* 1D intersection test */
if(!ud_empty(v1) && !ud_empty(v2) && (
(ast_stmt_is_after(a, v1->data.var.usedefFirst->stmt, v2->data.var.usedefFirst->stmt) == 1
&& ast_stmt_is_after(a, v2->data.var.usedefLast->stmt, v1->data.var.usedefFirst->stmt) == 1)
||
(ast_stmt_is_after(a, v1->data.var.usedefLast->stmt, v2->data.var.usedefFirst->stmt) == 1
&& ast_stmt_is_after(a, v2->data.var.usedefLast->stmt, v1->data.var.usedefLast->stmt) == 1)
)) {
VarTableEntry *min = v1 < v2 ? v1 : v2;
VarTableEntry *max = v1 < v2 ? v2 : v1;
for(size_t a = 0; a < adjCount; a++) {
if(adjs[a][0] == min && adjs[a][1] == max) {
goto cont;
}
}
adjs = realloc(adjs, sizeof(*adjs) * ++adjCount);
adjs[adjCount - 1][0] = min;
adjs[adjCount - 1][1] = max;
cont:;
}
}
}
for(size_t a = 0; a < adjCount; a++) {
adjs[a][0]->data.var.degree++;
adjs[a][1]->data.var.degree++;
}
qsort(vars, a->chunk.varCount, sizeof(*vars), comparator);
int lastColor = 0;
/* Welsh plow my ass */
for(int v = 0; v < a->chunk.varCount; v++) {
if(vars[v]->data.var.color != -1) {
// Already assigned.
continue;
}
for(int c = 0;; c++) {
for(int a = 0; a < adjCount; a++) {
if(adjs[a][0] == vars[v] && adjs[a][1]->data.var.color != -1 && adjs[a][1]->data.var.color == c) {
goto nextColor;
} else if(adjs[a][1] == vars[v] && adjs[a][0]->data.var.color != -1 && adjs[a][0]->data.var.color == c) {
goto nextColor;
}
}
vars[v]->data.var.color = c;
lastColor = lastColor < c ? c : lastColor;
break;
nextColor:;
}
}
free(adjs);
if(lastColor >= 4) {
// Spill node with highest degree
spill_pass(a, &a, vars[a->chunk.varCount - 1]);
return 0;
}
CGState cg;
memset(&cg, 0, sizeof(cg));
cg_chunk(&cg, a);
return 1;
}