#include"cg.h" #include #include #include #include #include"x86.h" #define REGS 4 static const char *regs[][3] = { [COLOR_EAX] = {"al", "ax", "eax"}, [COLOR_ECX] = {"cl", "cx", "ecx"}, [COLOR_EDX] = {"dl", "dx", "edx"}, [COLOR_EBX] = {"bl", "bx", "ebx"}, {"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; int isFunction; AST *tlc; } 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"; case BINOP_LESS: return "b"; case BINOP_GREATER: return "a"; case BINOP_LEQUAL: return "be"; case BINOP_GEQUAL: return "ae"; default: abort(); return NULL; } } static AST *is_field_access(AST *e) { if(e->nodeKind != AST_EXPR_UNARY_OP || e->exprUnOp.operator != UNOP_DEREF) { return NULL; } e = e->exprUnOp.operand; if(e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER) { e = e->exprCast.what; } if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_ADD && e->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && e->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) { return e; } return NULL; } static const char *xop_sz(AST *tlc, 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_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER) { e = e->exprCast.what; } if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF) { AST *p = e->exprUnOp.operand; if(p->nodeKind == AST_EXPR_CAST && p->exprCast.to->type == TYPE_TYPE_POINTER) { p = p->exprCast.what; } if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR && p->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) { snprintf(ret, XOPBUFSZ, "%s [%s + %s]", spec(sz), xv_sz(p->exprBinOp.operands[0]->exprVar.thing, 4), xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 4)); } else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && p->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && p->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) { snprintf(ret, XOPBUFSZ, "%s [%s + %s]", spec(sz), p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name, xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 4)); } else if(is_field_access(e)) { e = is_field_access(e); snprintf(ret, XOPBUFSZ, "%s [%s + %i]", spec(sz), e->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name, e->exprBinOp.operands[1]->exprPrim.val); } else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && p->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && p->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && p->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && p->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) { snprintf(ret, XOPBUFSZ, "%s [%s + %i * %s]", spec(sz), p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name, p->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val, xv_sz(p->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing, 4)); } else if(p->nodeKind == AST_EXPR_VAR && p->exprVar.thing->kind == VARTABLEENTRY_VAR) { snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(p->exprVar.thing, 4)); } else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_STACK_POINTER && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) { snprintf(ret, XOPBUFSZ, "[esp + %i]", p->exprBinOp.operands[1]->exprPrim.val); } else { return NULL; } } else if(e->nodeKind == AST_EXPR_STACK_POINTER) { snprintf(ret, XOPBUFSZ, "esp", tlc->chunk.stackReservation); } 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, "%s %i", spec(type_size(e->exprPrim.type)), e->exprPrim.val); } 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 { return NULL; } bufidx = (bufidx + 1) % XOPBUFS; return ret; } static const char *xop(AST *tlc, AST *e) { return xop_sz(tlc, e, type_size(e->expression.type)); } 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; } 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); dumben_go(s->stmtDecl.expression->exprFunc.chunk); while(!cg_go(s->stmtDecl.expression->exprFunc.chunk)) { dumben_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.to && 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(cg->tlc, 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(s->stmtAssign.to && is_xop(s->stmtAssign.what) == XOP_NOT_MEM && is_xop(s->stmtAssign.to) == XOP_NOT_MEM && !strcmp(xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to))) { // It's a noop } else if(s->stmtAssign.to) { if(x86_imul_supported() && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_MUL) { assert(s->stmtAssign.what->nodeKind == AST_EXPR_VAR); assert(s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR); if(!strcmp(xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]))) { printf("imul %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[1])); } else { printf("imul %s, %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]), xop(a, s->stmtAssign.to->exprBinOp.operands[1])); } } else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_MULHI) { assert(s->stmtAssign.what->nodeKind == AST_EXPR_VAR); assert(s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR); assert(s->stmtAssign.what->exprVar.thing->data.var.color == COLOR_EDX); assert(s->statement.next->nodeKind == AST_STMT_ASSIGN); assert(s->statement.next->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP); assert(s->statement.next->stmtAssign.to->exprBinOp.operator == BINOP_MUL); AST *otherop = NULL; if(ast_expression_equal(s->statement.next->stmtAssign.to->exprBinOp.operands[0], s->statement.next->stmtAssign.what)) { otherop = s->statement.next->stmtAssign.to->exprBinOp.operands[1]; } else if(ast_expression_equal(s->statement.next->stmtAssign.to->exprBinOp.operands[1], s->statement.next->stmtAssign.what)) { otherop = s->statement.next->stmtAssign.to->exprBinOp.operands[0]; } else abort(); printf("mul %s\n", xop(a, otherop)); // Skip next statement, because they come in a pair s = s->statement.next; } 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_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\n", instrs[s->stmtAssign.to->exprBinOp.operator == BINOP_SUB], xop(cg->tlc, 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(cg->tlc, s->stmtAssign.what), xop(cg->tlc, 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(cg->tlc, s->stmtAssign.what)); } else if(is_xop(s->stmtAssign.what) && s->stmtAssign.to->nodeKind == AST_EXPR_CAST) { printf("movzx %s, %s\n", xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, 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(cg->tlc, s->stmtAssign.what, 4), xop_sz(cg->tlc, s->stmtAssign.to, type_size(s->stmtAssign.what->expression.type))); } else { printf("mov %s, %s\n", xop(cg->tlc, s->stmtAssign.what), xop_sz(cg->tlc, 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(cg->tlc, s->stmtIf.expression->exprBinOp.operands[0]), xop(cg->tlc, 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); } else if(s->nodeKind == AST_STMT_RETURN) { if(s->stmtReturn.val) { assert(s->stmtReturn.val->nodeKind == AST_EXPR_VAR); assert(s->stmtReturn.val->exprVar.thing->kind == VARTABLEENTRY_VAR); assert(s->stmtReturn.val->exprVar.thing->data.var.color == COLOR_EAX); } if(a->chunk.stackReservation) { printf("add esp, %lu\n", a->chunk.stackReservation); } printf("ret\n"); } else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_VAR) { /* Loop guard, probably. */ } else abort(); s = s->statement.next; } } struct Spill2VarState { VarTableEntry *target; }; static void spill2var_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { static size_t vidx = 0; struct Spill2VarState *this = ud; AST *a = *aptr; if(a->nodeKind == AST_STMT_ASSIGN && a->stmtAssign.to->nodeKind == AST_EXPR_CALL) { assert(a->stmtAssign.what->nodeKind == AST_EXPR_VAR && a->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR); if(a->stmtAssign.what->exprVar.thing == this->target) { VarTableEntry *new = calloc(1, sizeof(*new)); new->kind = VARTABLEENTRY_VAR; new->type = a->stmtAssign.what->exprVar.thing->type; new->data.var.name = malp("$s2v_%lu", vidx++); tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount)); tlc->chunk.vars[tlc->chunk.varCount - 1] = new; ASTExprVar *ev0 = calloc(1, sizeof(*ev0)); ev0->nodeKind = AST_EXPR_VAR; ev0->type = new->type; ev0->thing = a->stmtAssign.what->exprVar.thing; ASTExprVar *ev1 = calloc(1, sizeof(*ev1)); ev1->nodeKind = AST_EXPR_VAR; ev1->type = new->type; ev1->thing = new; ASTStmtAssign *ass = calloc(1, sizeof(*ass)); ass->nodeKind = AST_STMT_ASSIGN; ass->what = (AST*) ev0; ass->to = (AST*) ev1; a->stmtAssign.what->exprVar.thing = new; ass->next = a->statement.next; a->statement.next = (AST*) ass; if(!ass->next) { chunk->chunk.statementLast = (AST*) ass; } } } if(a->nodeKind == AST_STMT_RETURN && a->stmtReturn.val && a->stmtReturn.val->nodeKind == AST_EXPR_VAR && a->stmtReturn.val->exprVar.thing == this->target) { VarTableEntry *new = calloc(1, sizeof(*new)); new->kind = VARTABLEENTRY_VAR; new->type = a->stmtReturn.val->exprVar.thing->type; new->data.var.name = malp("$s2v_%lu", vidx++); tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount)); tlc->chunk.vars[tlc->chunk.varCount - 1] = new; ASTExprVar *ev0 = calloc(1, sizeof(*ev0)); ev0->nodeKind = AST_EXPR_VAR; ev0->type = new->type; ev0->thing = a->stmtReturn.val->exprVar.thing; ASTExprVar *ev1 = calloc(1, sizeof(*ev1)); ev1->nodeKind = AST_EXPR_VAR; ev1->type = new->type; ev1->thing = new; ASTStmtAssign *ass = calloc(1, sizeof(*ass)); ass->nodeKind = AST_STMT_ASSIGN; ass->what = (AST*) ev1; ass->to = (AST*) ev0; stmt->stmtReturn.val->exprVar.thing = new; ass->next = stmt; if(stmtPrev) { stmtPrev->statement.next = (AST*) ass; } else { chunk->chunk.statementFirst = (AST*) ass; } } } struct PrecolorState { VarTableEntry *mustBeSpilled; }; static void precolor_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { struct PrecolorState *this = ud; if(this->mustBeSpilled) { /* Since something must be spilled first, we can't do anything else. Quit ASAP. */ return; } AST *n = *nptr; if(n == tlc) { for(size_t i = 0; i < n->chunk.varCount; i++) { n->chunk.vars[i]->data.var.color = -1; n->chunk.vars[i]->data.var.precolored = false; } } if(n->nodeKind == AST_STMT_RETURN && n->stmtReturn.val) { assert(n->stmtReturn.val->nodeKind == AST_EXPR_VAR && n->stmtReturn.val->exprVar.thing->kind == VARTABLEENTRY_VAR); VarTableEntry *vte = n->stmtReturn.val->exprVar.thing; const int requiredColor = COLOR_EAX; if(vte->data.var.precolored && vte->data.var.color != requiredColor) { // Precoloring collision! this->mustBeSpilled = vte; return; } vte->data.var.color = requiredColor; vte->data.var.precolored = true; } else if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.to->nodeKind == AST_EXPR_CALL) { assert(n->stmtAssign.what->nodeKind == AST_EXPR_VAR && n->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR); VarTableEntry *vte = n->stmtAssign.what->exprVar.thing; const int requiredColor = COLOR_EAX; if(vte->data.var.precolored && vte->data.var.color != requiredColor) { // Precoloring collision! this->mustBeSpilled = vte; return; } vte->data.var.color = requiredColor; vte->data.var.precolored = true; } else if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.what->nodeKind == AST_EXPR_VAR && n->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && n->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && n->stmtAssign.to->exprBinOp.operator == BINOP_MUL) { VarTableEntry *vte = n->stmtAssign.what->exprVar.thing; const int requiredColor = COLOR_EAX; if(vte->data.var.precolored && vte->data.var.color != requiredColor) { // Precoloring collision! this->mustBeSpilled = vte; return; } vte->data.var.color = requiredColor; vte->data.var.precolored = true; } else if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.what->nodeKind == AST_EXPR_VAR && n->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && n->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && n->stmtAssign.to->exprBinOp.operator == BINOP_MULHI) { VarTableEntry *vte = n->stmtAssign.what->exprVar.thing; const int requiredColor = COLOR_EDX; if(vte->data.var.precolored && vte->data.var.color != requiredColor) { // Precoloring collision! this->mustBeSpilled = vte; return; } vte->data.var.color = requiredColor; vte->data.var.precolored = true; } } typedef VarTableEntry *Adjacency[2]; static bool var_collision(AST *tlc, VarTableEntry *v1, VarTableEntry *v2) { /* 1D intersection test */ return !ud_empty(v1) && !ud_empty(v2) && ( (ast_stmt_is_after(tlc, v1->data.var.usedefFirst->stmt, v2->data.var.usedefFirst->stmt) == 1 && ast_stmt_is_after(tlc, v2->data.var.usedefLast->stmt, v1->data.var.usedefFirst->stmt) == 1) || (ast_stmt_is_after(tlc, v1->data.var.usedefLast->stmt, v2->data.var.usedefFirst->stmt) == 1 && ast_stmt_is_after(tlc, v2->data.var.usedefLast->stmt, v1->data.var.usedefLast->stmt) == 1) ); } static VarTableEntry *get_precolor_spill_candidate(AST *tlc) { Adjacency *adjs = NULL; size_t adjCount = 0; for(size_t i = 0; i < tlc->chunk.varCount; i++) { tlc->chunk.vars[i]->data.var.degree = 0; for(size_t j = 0; j < tlc->chunk.varCount; j++) { if( tlc->chunk.vars[i]->data.var.precolored && tlc->chunk.vars[j]->data.var.precolored && tlc->chunk.vars[i]->data.var.color == tlc->chunk.vars[j]->data.var.color && var_collision(tlc, tlc->chunk.vars[i], tlc->chunk.vars[j])) { adjs = realloc(adjs, sizeof(*adjs) * ++adjCount); adjs[adjCount - 1][0] = tlc->chunk.vars[i]; adjs[adjCount - 1][1] = tlc->chunk.vars[j]; } } } if(adjCount == 0) { return NULL; } for(size_t a = 0; a < adjCount; a++) { adjs[a][0]->data.var.degree++; adjs[a][1]->data.var.degree++; } VarTableEntry *maxdeg = tlc->chunk.vars[0]; for(size_t v = 1; v < tlc->chunk.varCount; v++) { if(maxdeg->data.var.degree < tlc->chunk.vars[v]->data.var.degree) { maxdeg = tlc->chunk.vars[v]; } } free(adjs); return maxdeg; } struct CalleeSavedState { AST *targetTLC; VarTableEntry *ebxuser; // To make sure we don't process the same return statement to infinity AST *lastProcessedReturn; }; static void callee_saved_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { struct CalleeSavedState *this = ud; AST *n = *nptr; if(tlc != this->targetTLC) { // Don't do anything. return; } if(n == tlc) { // Function entry ASTExprStackPointer *stk = calloc(1, sizeof(*stk)); stk->nodeKind = AST_EXPR_STACK_POINTER; stk->type = primitive_parse("u32"); ASTExprPrimitive *offset = calloc(1, sizeof(*offset)); offset->nodeKind = AST_EXPR_PRIMITIVE; offset->type = primitive_parse("u32"); offset->val = tlc->chunk.stackReservation; ASTExprBinaryOp *sum = calloc(1, sizeof(*sum)); sum->nodeKind = AST_EXPR_BINARY_OP; sum->type = offset->type; sum->operator = BINOP_ADD; sum->operands[0] = (AST*) stk; sum->operands[1] = (AST*) offset; ASTExprUnaryOp *deref = calloc(1, sizeof(*deref)); deref->nodeKind = AST_EXPR_UNARY_OP; deref->type = offset->type; deref->operator = UNOP_DEREF; deref->operand = (AST*) sum; ASTExprVar *ev = calloc(1, sizeof(*ev)); ev->nodeKind = AST_EXPR_VAR; ev->type = this->ebxuser->type; ev->thing = this->ebxuser; ASTStmtAssign *assign = calloc(1, sizeof(*assign)); assign->nodeKind = AST_STMT_ASSIGN; assign->what = (AST*) deref; assign->to = (AST*) ev; assign->next = tlc->chunk.statementFirst; tlc->chunk.statementFirst = (AST*) assign; assert(tlc->chunk.statementLast != NULL); tlc->chunk.stackReservation += 4; } else if(n->nodeKind == AST_STMT_RETURN && n != this->lastProcessedReturn) { // Function exit this->lastProcessedReturn = n; ASTExprStackPointer *stk = calloc(1, sizeof(*stk)); stk->nodeKind = AST_EXPR_STACK_POINTER; stk->type = primitive_parse("u32"); ASTExprPrimitive *offset = calloc(1, sizeof(*offset)); offset->nodeKind = AST_EXPR_PRIMITIVE; offset->type = primitive_parse("u32"); offset->val = tlc->chunk.stackReservation; ASTExprBinaryOp *sum = calloc(1, sizeof(*sum)); sum->nodeKind = AST_EXPR_BINARY_OP; sum->type = offset->type; sum->operator = BINOP_ADD; sum->operands[0] = (AST*) stk; sum->operands[1] = (AST*) offset; ASTExprUnaryOp *deref = calloc(1, sizeof(*deref)); deref->nodeKind = AST_EXPR_UNARY_OP; deref->type = offset->type; deref->operator = UNOP_DEREF; deref->operand = (AST*) sum; ASTExprVar *ev = calloc(1, sizeof(*ev)); ev->nodeKind = AST_EXPR_VAR; ev->type = this->ebxuser->type; ev->thing = this->ebxuser; ASTStmtAssign *assign = calloc(1, sizeof(*assign)); assign->nodeKind = AST_STMT_ASSIGN; assign->what = (AST*) ev; assign->to = (AST*) deref; assign->next = stmt; if(stmtPrev) { stmtPrev->statement.next = (AST*) assign; } else { tlc->chunk.statementFirst = (AST*) assign; } } } static void callee_saved(AST *tlc) { VarTableEntry *ebxuser = NULL; for(size_t v = 0; v < tlc->chunk.varCount; v++) { if(tlc->chunk.vars[v]->data.var.color == COLOR_EBX) { ebxuser = tlc->chunk.vars[v]; break; } } if(ebxuser) { struct CalleeSavedState state; state.targetTLC = tlc; state.ebxuser = ebxuser; generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, callee_saved_visitor); } } /* 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) { 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.degree = 0; if(!vars[vi]->data.var.precolored) { vars[vi]->data.var.color = -1; } } 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]; if(var_collision(a, v1, v2)) { 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 VarTableEntry *chosen = NULL; for(ssize_t i = a->chunk.varCount - 1; i >= 0; i--) { if(!vars[i]->data.var.precolored) { chosen = vars[i]; break; } } assert(chosen != NULL); ast_spill_to_stack(a, chosen); return 0; } if(a->chunk.functionType) { callee_saved(a); } CGState cg; memset(&cg, 0, sizeof(cg)); cg.tlc = a; cg.isFunction = !!a->chunk.functionType; cg_chunk(&cg, a); return 1; }