#include #include #include #include #include"ntc.h" #include"reporting.h" #include"ast.h" #include"arch.h" #include"utils.h" 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(ScopeItem *v, int sz) { assert(v->kind == SCOPEITEM_VAR); if(sz == 0) sz = arch_ptr_size(); #define XVBUFS 8 #define XVBUFSZ 16 static char bufs[XVBUFS][XVBUFSZ]; static int bufidx = 0; char *ret = bufs[bufidx]; if(ntc_get_int("rcd")) { snprintf(ret, XVBUFSZ, "%i@%i", v->data.var.registerClass, v->data.var.color); } else { int cls = v->data.var.registerClass, reg = v->data.var.color; if(type_size(v->type) != sz) { if(sz == 4) { reg_cast_up(&cls, ®); } else abort(); } snprintf(ret, XVBUFSZ, "%s", REG_CLASSES[cls].rsN[reg]); } bufidx = (bufidx + 1) % XVBUFS; return ret; } static const char *xv(ScopeItem *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 == SCOPEITEM_SYMBOL) { return e; } if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_ADD && e->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && e->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && e->exprBinOp.operands[0]->exprVar.thing->kind == SCOPEITEM_VAR) { 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_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 == SCOPEITEM_VAR && p->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_VAR) { snprintf(ret, XOPBUFSZ, "%s [%s + %s]", spec(sz), xv_sz(p->exprBinOp.operands[0]->exprVar.thing, 0), xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 0)); } 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 == SCOPEITEM_SYMBOL && p->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_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, 0)); } else if(is_field_access(e)) { e = is_field_access(e); if(e->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP) { assert(e->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF); 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 { assert(e->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR); ScopeItem *vte = e->exprBinOp.operands[0]->exprVar.thing; snprintf(ret, XOPBUFSZ, "%s [%s + %i]", spec(sz), REG_CLASSES[vte->data.var.registerClass].rsN[vte->data.var.color], 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 == SCOPEITEM_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 == SCOPEITEM_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, 0)); } else if(p->nodeKind == AST_EXPR_VAR && p->exprVar.thing->kind == SCOPEITEM_VAR) { snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(p->exprVar.thing, 0)); } 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"); } else if(e->nodeKind == AST_EXPR_VAR) { ScopeItem *v = e->exprVar.thing; if(v->kind == SCOPEITEM_VAR) { return xv_sz(v, sz); } else if(v->kind == SCOPEITEM_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 == SCOPEITEM_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 lr_empty(ScopeItem *a) { assert(a->kind == SCOPEITEM_VAR); //assert(!a->data.var.liveRangeStart == !a->data.var.liveRangeEnd); return !a->data.var.liveRangeStart; } 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) { ScopeItem *v = s->stmtDecl.thing; if(v->kind == SCOPEITEM_SYMBOL) { if(v->data.symbol.isExternal) { // Do nothing. // All external symbols are handled at once in the top-level chunk. //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); // Generic functions have non-NULL code blocks if(!type_is_generic(s->stmtDecl.expression->expression.type)) { ast_segmented_dereference(s->stmtDecl.expression->exprFunc.chunk); ast_sroa(s->stmtDecl.expression->exprFunc.chunk); dumben_pre(s->stmtDecl.expression->exprFunc.chunk); 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(v->kind == SCOPEITEM_VAR) { assert(s->stmtDecl.expression == NULL); } } else if(s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.to && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_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 == SCOPEITEM_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 && is_str_equal_check_null(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 == SCOPEITEM_VAR); if(!strcmp(xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]))) { printf("imul %s, %s, %s\n", xop(a, s->stmtAssign.what), 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 == SCOPEITEM_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 == SCOPEITEM_VAR && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_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 == SCOPEITEM_SYMBOL && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_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 == SCOPEITEM_VAR) { printf("lea %s, [%s + %i]\n", xv_sz(s->stmtAssign.what->exprVar.thing, 0), xv_sz(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, 0), s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val); } else if(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) { if(type_size(s->stmtAssign.what->expression.type) > type_size(s->stmtAssign.to->expression.type)) { printf("movzx %s, %s\n", xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to->exprCast.what)); } else { const char *dest = xop_sz(cg->tlc, s->stmtAssign.what, 4); const char *src = xop_sz(cg->tlc, s->stmtAssign.to->exprCast.what, 4); if(strcmp(dest, src)) { printf("mov %s, %s\n", dest, src); } } } else { printf("mov %s, %s\n", xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to)); } } } 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 == SCOPEITEM_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 if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_UNARY_OP && s->stmtExpr.expr->exprUnOp.operator == UNOP_DEREF && s->stmtExpr.expr->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && s->stmtExpr.expr->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && s->stmtExpr.expr->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_STACK_POINTER && s->stmtExpr.expr->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) { /* Loop guard for a spilled variable, probably. */ } else { stahp_node(s, "Unknown statement caught by code generator."); } s = s->statement.next; } } typedef ScopeItem *Adjacency[2]; static bool var_collision(AST *tlc, ScopeItem *v1, ScopeItem *v2) { /* 1D intersection test */ bool liveRangeIntersection = !lr_empty(v1) && !lr_empty(v2) && (v1->data.var.liveRangeStart == v2->data.var.liveRangeStart || ( (ast_stmt_is_after(tlc, v1->data.var.liveRangeStart, v2->data.var.liveRangeStart) == 1 && ast_stmt_is_after(tlc, v2->data.var.liveRangeEnd, v1->data.var.liveRangeStart) == 1) || (ast_stmt_is_after(tlc, v1->data.var.liveRangeEnd, v2->data.var.liveRangeStart) == 1 && ast_stmt_is_after(tlc, v2->data.var.liveRangeEnd, v1->data.var.liveRangeEnd) == 1) )); bool resourceIntersection = (REG_CLASSES[v1->data.var.registerClass].rMask & REG_CLASSES[v2->data.var.registerClass].rMask) != 0; return liveRangeIntersection && resourceIntersection; } struct CalleeSavedState { AST *targetTLC; ScopeItem *calleeUsers[MAX_REGS_PER_CLASS]; size_t calleeOffsets[MAX_REGS_PER_CLASS]; // 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 for(int i = 0; i < MAX_REGS_PER_CLASS; i++) { ScopeItem *vte = this->calleeUsers[i]; if(!vte) { continue; } 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 = this->calleeOffsets[i]; 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 = vte->type; ev->thing = vte; 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); } } else if(n->nodeKind == AST_STMT_RETURN && n != this->lastProcessedReturn) { // Function exit this->lastProcessedReturn = n; for(int i = 0; i < MAX_REGS_PER_CLASS; i++) { ScopeItem *vte = this->calleeUsers[i]; if(!vte) { continue; } 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 = this->calleeOffsets[i]; 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 = vte->type; ev->thing = vte; 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; } stmtPrev = (AST*) assign; } } } static void callee_saved(AST *tlc) { ScopeItem *ebxuser = NULL, *ediuser = NULL, *esiuser = NULL; for(size_t v = 0; v < tlc->chunk.varCount; v++) { if(is_reg_b(tlc->chunk.vars[v]->data.var.registerClass, tlc->chunk.vars[v]->data.var.color)) { ebxuser = tlc->chunk.vars[v]; } if(is_reg_di(tlc->chunk.vars[v]->data.var.registerClass, tlc->chunk.vars[v]->data.var.color)) { ediuser = tlc->chunk.vars[v]; } if(is_reg_si(tlc->chunk.vars[v]->data.var.registerClass, tlc->chunk.vars[v]->data.var.color)) { esiuser = tlc->chunk.vars[v]; } } struct CalleeSavedState state = {}; state.targetTLC = tlc; size_t nextUser = 0; if(ebxuser) { state.calleeOffsets[nextUser] = nextUser * 4; state.calleeUsers[nextUser] = ebxuser; nextUser++; } if(esiuser) { state.calleeOffsets[nextUser] = nextUser * 4; state.calleeUsers[nextUser] = esiuser; nextUser++; } if(ediuser) { state.calleeOffsets[nextUser] = nextUser * 4; state.calleeUsers[nextUser] = ediuser; nextUser++; } ast_grow_stack_frame(tlc, nextUser * 4); if(nextUser) { generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, callee_saved_visitor, NULL); } } static void determine_register_classes(AST *tlc) { for(size_t v = 0; v < tlc->chunk.varCount; v++) { if(tlc->chunk.vars[v]->data.var.preclassed) { continue; } if(type_size(tlc->chunk.vars[v]->type) == 1) { tlc->chunk.vars[v]->data.var.registerClass = REG_CLASS_8; } else { tlc->chunk.vars[v]->data.var.registerClass = REG_CLASS_NOT_8; } } } /* Welsh-Powell graph coloring */ static int comparator(const void *A, const void *B) { ScopeItem *const *a = A; ScopeItem *const *b = B; return ((*a)->data.var.degree * (*a)->data.var.priority) - ((*b)->data.var.degree * (*b)->data.var.priority); } static int assign_coloring(AST *a, ScopeItem **vars, size_t adjCount, Adjacency *adjs, bool preclassed) { int mustSpillRegisterClass = -1; /* Welsh plow my ass */ for(int v = 0; v < a->chunk.varCount; v++) { if(vars[v]->data.var.preclassed != preclassed) { continue; } if(vars[v]->data.var.color != -1) { // Already assigned. continue; } for(int c = 0;; c++) { if(c >= MAX_REGS_PER_CLASS || REG_CLASSES[vars[v]->data.var.registerClass].rs[c] == 0) { // Ran out of resources mustSpillRegisterClass = vars[v]->data.var.registerClass; break; } int resource = REG_CLASSES[vars[v]->data.var.registerClass].rs[c]; if(REG_CLASSES[vars[v]->data.var.registerClass].rsS[c] != type_size(vars[v]->type)) { continue; } for(int a = 0; a < adjCount; a++) { ScopeItem *me = NULL, *they = NULL; if(adjs[a][0] == vars[v]) { me = adjs[a][0]; they = adjs[a][1]; } else if(adjs[a][1] == vars[v]) { me = adjs[a][1]; they = adjs[a][0]; } else continue; if(they->data.var.color == -1) { continue; } int theyRes = REG_CLASSES[they->data.var.registerClass].rs[they->data.var.color]; if((resource & theyRes) != 0) { goto nextColor; } } vars[v]->data.var.color = c; break; nextColor:; } } return mustSpillRegisterClass; } int cg_go(AST *a) { assert(a->nodeKind == AST_CHUNK); for(size_t e = 0; e < a->chunk.externCount; e++) { assert(a->chunk.externs[e]->kind == SCOPEITEM_SYMBOL); assert(a->chunk.externs[e]->data.symbol.isExternal); printf("extern %s\n", a->chunk.externs[e]->data.symbol.name); } ast_usedef_reset(a); size_t adjCount = 0; Adjacency *adjs = calloc(adjCount, sizeof(*adjs)); ScopeItem **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.preclassed) { vars[vi]->data.var.registerClass = -1; } if(!vars[vi]->data.var.precolored) { vars[vi]->data.var.color = -1; } } determine_register_classes(a); for(size_t v1i = 0; v1i < a->chunk.varCount; v1i++) { for(size_t v2i = 0; v2i < a->chunk.varCount; v2i++) { if(v1i == v2i) continue; ScopeItem *v1 = vars[v1i]; ScopeItem *v2 = vars[v2i]; if(var_collision(a, v1, v2)) { ScopeItem *min = v1 < v2 ? v1 : v2; ScopeItem *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 mustSpillRegisterClass; // Important to prioritize preclassed variables mustSpillRegisterClass = assign_coloring(a, vars, adjCount, adjs, true); if(mustSpillRegisterClass == -1) { mustSpillRegisterClass = assign_coloring(a, vars, adjCount, adjs, false); } free(adjs); if(mustSpillRegisterClass != -1) { // Spill node with highest degree ScopeItem *chosen = NULL; for(ssize_t i = a->chunk.varCount - 1; i >= 0; i--) { if(vars[i]->data.var.registerClass == mustSpillRegisterClass && !vars[i]->data.var.precolored) { chosen = vars[i]; break; } } assert(chosen != NULL); if(ntc_get_int("pdbg")) { char *astdump = ast_dump(a); fprintf(stderr, "### CG FAILURE ###\n%s\n", astdump); free(astdump); } 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; }