diff --git a/Makefile b/Makefile index 460f7bf..021c2fe 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ ntc: $(SOURCES) $(HEADERS) ifdef OW wcl386 -ml $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUG),-DDEBUG=1,) -fe="ntc.exe" -bt=dos -l=dos4g -ml $(if $(DEBUG),,-d0 -os -om -ob -oi -ol -ox) -lr -za99 -i=src $(SOURCES) else - cc $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUG),-DDEBUG=1,) -Wall -o ntc -fno-PIE -no-pie -std=gnu11 $(if $(DEBUG),-O0 -g,-Os -s) -fms-extensions -Isrc $(SOURCES) + cc $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUGA),-DDEBUG=1,) -Wall -o ntc -fsanitize=address -fno-PIE -no-pie -std=gnu11 $(if $(DEBUG),-O0 -g,-Os -s) -fms-extensions -Isrc $(SOURCES) -Wno-array-bounds endif install: ntc diff --git a/src/ast.c b/src/ast.c index 3c11884..09a0f28 100644 --- a/src/ast.c +++ b/src/ast.c @@ -3,6 +3,12 @@ #include #include #include +#include +#include + +const char *AST_KIND_STR[] = { + AST_KINDS(GEN_STRI) +}; AST *ast_expression_optimize(AST *ast) { return ast; @@ -19,6 +25,8 @@ int ast_expression_equal(AST *a, AST *b) { return a->exprUnOp.operator == b->exprUnOp.operator && ast_expression_equal(a->exprUnOp.operand, b->exprUnOp.operand); } else if(a->nodeKind == AST_EXPR_BINARY_OP) { return a->exprBinOp.operator == b->exprBinOp.operator && ast_expression_equal(a->exprBinOp.operands[0], b->exprBinOp.operands[0]) && ast_expression_equal(a->exprBinOp.operands[1], b->exprBinOp.operands[1]); + } else if(a->nodeKind == AST_EXPR_STACK_POINTER) { + return 1; } return 0; @@ -28,8 +36,8 @@ int ast_expression_equal(AST *a, AST *b) { int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) { const AST *s = chunk->chunk.statementFirst; - while(s) { - if(s->nodeKind == AST_STMT_LOOP) { + while(1) { + if(s && s->nodeKind == AST_STMT_LOOP) { int i = ast_stmt_is_after(s->stmtLoop.body, s1, s2); if(i != -1) { return i; @@ -43,6 +51,8 @@ int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) { return 1; } + if(!s) break; + if(s->nodeKind == AST_STMT_IF) { int i = ast_stmt_is_after(s->stmtIf.then, s1, s2); if(i != -1) { @@ -54,4 +64,439 @@ int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) { } return -1; -} \ No newline at end of file +} + +/* + * This pass is necessary for the purposes of optimization and regalloc. + * Because an AST may hold outdated UD-chains, this pass MUST be called + * before using the UD-chains to make sure they are valid. + * + * Each local var (VTE of kind VARTABLEENTRY_VAR) holds its own UD-chain + * that specifies the exact nodes in the AST where: + * 1. It is used + * 2. The whole statement in which it is used + * 3. The definition that *might* be in use + * + * Because multiple definitions may be in use (reachable) at the point + * of the use, a unique UseDef for each possible definition is appended + * to the chain. + * + * Reachable definitions are kept track in a ReachingDefs, also held by + * each VTE. In the case of a single, simple block of code, we know + * exactly one definition (including undefined) can reach each variable, + * which would simplify the ReachingDefs structure to a single + * definition pointer. + * + * Unfortunately, conditional blocks and loops ruin this simplicity. + * If you have code like + * x = A + * if B { + * x = C + * } + * then afterward two definitions may apply to x. + * + * A solution here is to lay ReachingDefs as a graph, with each + * ReachingDefs having an optional parent. When we enter a new block of + * code, we create an empty ReachingDefs with the previous block as its + * parent. Any definitions replace the ones in the deepest + * ReachingDefs only. + * + * How we exit a block depends on its type. If it is conditional, + * the reaching definitions should join the parent (mergedefs). + * If the block is a loop, it is even worse. Given + * x = A + * loop { + * use x + * x = B + * } + * definitions can apply to uses that come before it! + * + * Also, a different case: + * x = A + * loop { + * use x + * y = B + * } + * Because, technically, the last use of x is before y = B, y and x may + * be assigned the same physical location, corrupting data as a result. + * To fix this, fake, "useless" statements are inserted during parsing + * that make the AST look as such: + * x = A + * loop { + * use x + * y = B + * } + * x; + * Until dead code removal is implemented, this will not be a problem. + */ + +static void rawadduse(VarTableEntry *vte, UseDef *ud) { + assert(vte->kind == VARTABLEENTRY_VAR); + + assert(ud->next == NULL); + + assert(!!vte->data.var.usedefFirst == !!vte->data.var.usedefLast); + + if(!vte->data.var.usedefFirst) { + vte->data.var.usedefFirst = vte->data.var.usedefLast = ud; + } else { + vte->data.var.usedefLast->next = ud; + vte->data.var.usedefLast = ud; + } +} + +static void adduse(VarTableEntry *vte, AST *use, AST *whole) { + assert(vte->kind == VARTABLEENTRY_VAR); + + assert(vte->data.var.reachingDefs != NULL); + + for(size_t d = 0; d < vte->data.var.reachingDefs->defCount; d++) { + UseDef *ud = malloc(sizeof(*ud)); + ud->def = vte->data.var.reachingDefs->defs[d]; + ud->use = use; + ud->stmt = whole; + ud->next = NULL; + + rawadduse(vte, ud); + } +} + +static void overwritedefs(VarTableEntry *vte, AST *def) { + assert(vte->kind == VARTABLEENTRY_VAR); + + if(!vte->data.var.reachingDefs) { + vte->data.var.reachingDefs = calloc(1, sizeof(*vte->data.var.reachingDefs)); + } + + vte->data.var.reachingDefs->defCount = 1; + + if(!vte->data.var.reachingDefs->defs) { + vte->data.var.reachingDefs->defs = calloc(1, sizeof(*vte->data.var.reachingDefs->defs)); + } + + vte->data.var.reachingDefs->defs[0] = def; +} + +static void mergedefs(VarTableEntry *vte) { + assert(vte->kind == VARTABLEENTRY_VAR); + + ReachingDefs *rdefs = vte->data.var.reachingDefs; + + assert(rdefs != NULL); + assert(rdefs->parent != NULL); + + rdefs->parent->defs = realloc(rdefs->parent->defs, sizeof(*rdefs->parent->defs) * (rdefs->parent->defCount + rdefs->defCount)); + memcpy(rdefs->parent->defs + rdefs->parent->defCount, rdefs->defs, rdefs->defCount * sizeof(*rdefs->defs)); + + vte->data.var.reachingDefs = rdefs->parent; + + free(rdefs->defs); + free(rdefs); +} + +static void pushdefs(VarTableEntry *vte) { + assert(vte->kind == VARTABLEENTRY_VAR); + + ReachingDefs *rdefs = malloc(sizeof(*rdefs)); + rdefs->defCount = 0; + rdefs->defs = NULL; + rdefs->excludeParent = 0; + rdefs->parent = vte->data.var.reachingDefs; + + vte->data.var.reachingDefs = rdefs; +} + +static void pushdefsall(AST *tlc) { + for(size_t i = 0; i < tlc->chunk.varCount; i++) { + pushdefs(tlc->chunk.vars[i]); + } +} + +static void mergedefsall(AST *tlc) { + for(size_t i = 0; i < tlc->chunk.varCount; i++) { + mergedefs(tlc->chunk.vars[i]); + } +} + +static void mergedefsloop(AST *tlc, VarTableEntry *vte, AST *daLoopStmt) { + assert(vte->kind == VARTABLEENTRY_VAR); + + for(size_t d = 0; d < vte->data.var.reachingDefs->defCount; d++) { + UseDef *ud = vte->data.var.usedefFirst; + + while(ud) { + if(ast_stmt_is_after(daLoopStmt->stmtLoop.body, NULL, ud->stmt) == 1 && ud->def != vte->data.var.reachingDefs->defs[d]) { + UseDef *udnew = calloc(1, sizeof(*udnew)); + udnew->next = ud->next; + ud->next = udnew; + + udnew->def = vte->data.var.reachingDefs->defs[d]; + udnew->use = ud->use; + udnew->stmt = ud->stmt; + + if(udnew->next == NULL) { + vte->data.var.usedefLast = udnew; + } + } + + ud = ud->next; + } + } + + mergedefs(vte); +} + +static void mergedefsloopall(AST *tlc, AST *daLoopStmt) { + for(size_t i = 0; i < tlc->chunk.varCount; i++) { + mergedefsloop(tlc, tlc->chunk.vars[i], daLoopStmt); + } +} + +static void ast_usedef_pass(AST *tlc, AST *a, AST *wholestmt) { + if(a->nodeKind == AST_CHUNK) { + for(AST *s = a->chunk.statementFirst; s; s = s->statement.next) { + ast_usedef_pass(tlc, s, s); + } + } else if(a->nodeKind == AST_STMT_IF) { + pushdefsall(tlc); + + ast_usedef_pass(tlc, a->stmtIf.expression, wholestmt); + ast_usedef_pass(tlc, a->stmtIf.then, wholestmt); + + mergedefsall(tlc); + } else if(a->nodeKind == AST_STMT_LOOP) { + pushdefsall(tlc); + + ast_usedef_pass(tlc, a->stmtLoop.body, wholestmt); + + mergedefsloopall(tlc, a); + } else if(a->nodeKind == AST_STMT_ASSIGN) { + if(a->stmtAssign.what->nodeKind == AST_EXPR_VAR && a->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR) { + overwritedefs(a->stmtAssign.what->exprVar.thing, a); + } + + ast_usedef_pass(tlc, a->stmtAssign.what, wholestmt); + + if(a->stmtAssign.to) { + ast_usedef_pass(tlc, a->stmtAssign.to, wholestmt); + } + } else if(a->nodeKind == AST_STMT_EXPR) { + ast_usedef_pass(tlc, a->stmtExpr.expr, wholestmt); + } else if(a->nodeKind == AST_EXPR_VAR) { + if(a->exprVar.thing->kind == VARTABLEENTRY_VAR) { + adduse(a->exprVar.thing, a, wholestmt); + } + } else if(a->nodeKind == AST_EXPR_BINARY_OP) { + ast_usedef_pass(tlc, a->exprBinOp.operands[0], wholestmt); + ast_usedef_pass(tlc, a->exprBinOp.operands[1], wholestmt); + } else if(a->nodeKind == AST_EXPR_UNARY_OP) { + ast_usedef_pass(tlc, a->exprUnOp.operand, wholestmt); + } else if(a->nodeKind == AST_EXPR_CALL) { + ast_usedef_pass(tlc, a->exprCall.what, wholestmt); + + for(size_t p = 0; p < a->exprCall.what->expression.type->function.argCount; p++) { + ast_usedef_pass(tlc, a->exprCall.args[p], wholestmt); + } + } else if(a->nodeKind == AST_EXPR_PRIMITIVE) { + } else if(a->nodeKind == AST_EXPR_STRING_LITERAL) { + } else if(a->nodeKind == AST_EXPR_CAST) { + ast_usedef_pass(tlc, a->exprCast.what, wholestmt); + } 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(); + } +} + +void ast_usedef_reset(AST *chu) { + for(size_t i = 0; i < chu->chunk.varCount; i++) { + VarTableEntry *vte = chu->chunk.vars[i]; + + assert(vte->kind == VARTABLEENTRY_VAR); + + vte->data.var.reachingDefs = NULL; + vte->data.var.usedefFirst = NULL; + vte->data.var.usedefLast = NULL; + } + + pushdefsall(chu); + + return ast_usedef_pass(chu, chu, NULL); +} + +static char *cat(char *a, const char *b) { + if(!a) { + return strdup(b); + } + + a = realloc(a, strlen(a) + strlen(b) + 1); + strcpy(a + strlen(a), b); + return a; +} + +__attribute__((format(printf, 1, 2))) static char *malp(const char *fmt, ...) { + va_list v1, v2; + va_start(v1, fmt); + va_copy(v2, v1); + size_t len = vsnprintf(NULL, 0, fmt, v1); + va_end(v1); + va_start(v2, fmt); + char *str = malloc(len + 1); + vsnprintf(str, len + 1, fmt, v2); + str[len] = 0; + va_end(v2); + return str; +} + +char *type_to_string(Type *t) { + if(t->type == TYPE_TYPE_PRIMITIVE) { + char ret[16] = {}; + int i = 0; + + ret[i++] = t->primitive.isFloat ? 'f' : (t->primitive.isUnsigned ? 'u' : 'i'); + snprintf(ret + i, sizeof(ret) - i, "%i", t->primitive.width); + + return strdup(ret); + } else if(t->type == TYPE_TYPE_POINTER) { + char *c = type_to_string(t->pointer.of); + char *r = malp("%s*", c); + free(c); + return r; + } + + return strdup("@unimp"); +} + +static char *ast_dumpe(AST *e) { + if(e->nodeKind == AST_EXPR_PRIMITIVE) { + return malp("%i", e->exprPrim.val); + } else if(e->nodeKind == AST_EXPR_VAR) { + VarTableEntry *vte = e->exprVar.thing; + + if(vte->kind == VARTABLEENTRY_VAR) { + return strdup(vte->data.var.name); + } else if(vte->kind == VARTABLEENTRY_SYMBOL) { + return strdup(vte->data.symbol.name); + } else abort(); + } else if(e->nodeKind == AST_EXPR_UNARY_OP) { + const char *op; + switch(e->exprUnOp.operator) { + case UNOP_REF: + op = "&"; + break; + case UNOP_DEREF: + op = "*"; + break; + case UNOP_BITWISE_NOT: + op = "~"; + break; + case UNOP_NEGATE: + op = "-"; + break; + } + char *c = ast_dumpe(e->exprUnOp.operand); + char *r = malp("%s%s", op, c); + free(c); + return r; + } else if(e->nodeKind == AST_EXPR_BINARY_OP) { + char *a = ast_dumpe(e->exprBinOp.operands[0]); + char *b = ast_dumpe(e->exprBinOp.operands[1]); + const char *op; + switch(e->exprBinOp.operator) { + case BINOP_ADD: + op = "+"; + break; + case BINOP_SUB: + op = "-"; + break; + case BINOP_MUL: + op = "*"; + break; + case BINOP_DIV: + op = "/"; + break; + case BINOP_BITWISE_AND: + op = "&"; + break; + case BINOP_BITWISE_OR: + op = "|"; + break; + case BINOP_BITWISE_XOR: + op = "^"; + break; + case BINOP_EQUAL: + op = "=="; + break; + default: + abort(); + } + char *r = malp("(%s %s %s)", a, op, b); + free(a); + free(b); + return r; + } + + return malp("@unimp:%s", AST_KIND_STR[e->nodeKind]); +} + +static char *ast_dumps(AST *s) { + if(s->nodeKind == AST_STMT_DECL) { + VarTableEntry *vte = s->stmtDecl.thing; + + if(vte->kind == VARTABLEENTRY_SYMBOL) { + char *t = type_to_string(vte->type); + char *e = s->stmtDecl.expression ? ast_dumpe(s->stmtDecl.expression) : strdup(""); + char *r = malp("%s%s %s: %s;\n", vte->data.symbol.isExternal ? "external " : "", t, vte->data.symbol.name, e); + free(t); + free(e); + return r; + } + } else if(s->nodeKind == AST_STMT_ASSIGN) { + char *a = ast_dumpe(s->stmtAssign.what); + char *b = ast_dumpe(s->stmtAssign.to); + char *r = malp("%s = %s;\n", a, b); + free(a); + free(b); + return r; + } + + return malp("@unimp:%s\n", AST_KIND_STR[s->nodeKind]); +} + +char *ast_dump(AST *tlc) { + AST *stmt = tlc->chunk.statementFirst; + + char *ret = NULL; + + #define CAT(s) do { char *b = s; ret = cat(ret, (b)); free(b); } while(0) + + while(stmt) { + CAT(ast_dumps(stmt)); + + stmt = stmt->statement.next; + } + + return ret; +} + +static void *memdup(void *a, size_t len) { + void *r = malloc(len); + memcpy(r, a, len); + return r; +} + +AST *ast_deep_copy(AST *src) { + if(src->nodeKind == AST_EXPR_VAR) { + return memdup(src, sizeof(ASTExprVar)); + } else if(src->nodeKind == AST_EXPR_PRIMITIVE) { + return memdup(src, sizeof(ASTExprPrimitive)); + } + + abort(); +} diff --git a/src/ast.h b/src/ast.h index 52b413e..8749f38 100644 --- a/src/ast.h +++ b/src/ast.h @@ -18,29 +18,35 @@ #define ENUMPAK #endif -typedef enum ENUMPAK { - AST_CHUNK, - AST_STMT_DECL, - AST_TYPE_IDENTIFIER, - AST_EXPR_PRIMITIVE, - AST_STMT_IF, - AST_EXPR_BINARY_OP, - AST_EXPR_VAR, - AST_TYPE_POINTER, - AST_EXPR_UNARY_OP, - AST_STMT_LOOP, - AST_STMT_BREAK, - AST_STMT_CONTINUE, - AST_EXPR_CALL, - AST_STMT_EXPR, - AST_STMT_ASSIGN, - AST_STMT_EXT_ALIGN, - AST_EXPR_STRING_LITERAL, - AST_EXPR_CAST, - AST_EXPR_ARRAY, - AST_STMT_EXT_ORG, - AST_STMT_EXT_SECTION, -} ASTKind; +#define GEN_ENUM(x) x, +#define GEN_STRI(x) #x, + +#define AST_KINDS(K) \ + K(AST_CHUNK) \ + K(AST_STMT_DECL) \ + K(AST_TYPE_IDENTIFIER) \ + K(AST_EXPR_PRIMITIVE) \ + K(AST_STMT_IF) \ + K(AST_EXPR_BINARY_OP) \ + K(AST_EXPR_VAR) \ + K(AST_EXPR_STACK_POINTER) \ + K(AST_TYPE_POINTER) \ + K(AST_EXPR_UNARY_OP) \ + K(AST_STMT_LOOP) \ + K(AST_STMT_BREAK) \ + K(AST_STMT_CONTINUE) \ + K(AST_EXPR_CALL) \ + K(AST_STMT_EXPR) \ + K(AST_STMT_ASSIGN) \ + K(AST_STMT_EXT_ALIGN) \ + K(AST_EXPR_STRING_LITERAL) \ + K(AST_EXPR_CAST) \ + K(AST_EXPR_ARRAY) \ + K(AST_STMT_EXT_ORG) \ + K(AST_STMT_EXT_SECTION) + +typedef enum ENUMPAK { AST_KINDS(GEN_ENUM) } ASTKind; +extern const char *AST_KIND_STR[]; typedef enum ENUMPAK { BINOP_ADD = 0, @@ -126,6 +132,10 @@ typedef struct { char *data; } ASTExprStringLiteral; +typedef struct { + ASTExpr; +} ASTExprStackPointer; + typedef struct { ASTKind nodeKind; @@ -167,6 +177,8 @@ typedef struct { union AST *statementFirst; union AST *statementLast; + + size_t stackReservation; } ASTChunk; typedef struct { @@ -270,4 +282,10 @@ int ast_expression_equal(AST*, AST*); int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2); +void ast_usedef_reset(AST *chu); + +char *ast_dump(AST *tlc); + +AST *ast_deep_copy(AST*); + #endif diff --git a/src/cg.c b/src/cg.c index 83c91bc..b35a1e5 100644 --- a/src/cg.c +++ b/src/cg.c @@ -8,7 +8,7 @@ #include"x86.h" #define REGS 4 -static const char *regs[REGS][3] = {{"al", "ax", "eax"}, {"bl", "bx", "ebx"}, {"cl", "cx", "ecx"}, {"dl", "dx", "edx"}}; +static const char *regs[][3] = {{"al", "ax", "eax"}, {"bl", "bx", "ebx"}, {"cl", "cx", "ecx"}, {"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"}; @@ -94,7 +94,9 @@ static const char *xop_sz(AST *e, int sz) { char *ret = bufs[bufidx]; - if(e->nodeKind == AST_EXPR_VAR) { + 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) { @@ -204,6 +206,10 @@ void cg_chunk(AST *a) { 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", @@ -228,6 +234,10 @@ void cg_chunk(AST *a) { } 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 { @@ -316,20 +326,91 @@ void cg_chunk(AST *a) { } } +static void spill_pass(AST *tlc, AST *a, VarTableEntry *vte) { + if(a->nodeKind == AST_CHUNK) { + for(AST *s = a->chunk.statementFirst; s; s = s->statement.next) { + ast_usedef_pass(tlc, s, s); + } + } else if(a->nodeKind == AST_STMT_IF) { + pushdefsall(tlc); + + ast_usedef_pass(tlc, a->stmtIf.expression, wholestmt); + ast_usedef_pass(tlc, a->stmtIf.then, wholestmt); + + mergedefsall(tlc); + } else if(a->nodeKind == AST_STMT_LOOP) { + pushdefsall(tlc); + + ast_usedef_pass(tlc, a->stmtLoop.body, wholestmt); + + mergedefsloopall(tlc, a); + } else if(a->nodeKind == AST_STMT_ASSIGN) { + if(a->stmtAssign.what->nodeKind == AST_EXPR_VAR && a->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR) { + overwritedefs(a->stmtAssign.what->exprVar.thing, a); + } + + ast_usedef_pass(tlc, a->stmtAssign.what, wholestmt); + + if(a->stmtAssign.to) { + ast_usedef_pass(tlc, a->stmtAssign.to, wholestmt); + } + } else if(a->nodeKind == AST_STMT_EXPR) { + ast_usedef_pass(tlc, a->stmtExpr.expr, wholestmt); + } else if(a->nodeKind == AST_EXPR_VAR) { + if(a->exprVar.thing->kind == VARTABLEENTRY_VAR) { + adduse(a->exprVar.thing, a, wholestmt); + } + } else if(a->nodeKind == AST_EXPR_BINARY_OP) { + ast_usedef_pass(tlc, a->exprBinOp.operands[0], wholestmt); + ast_usedef_pass(tlc, a->exprBinOp.operands[1], wholestmt); + } else if(a->nodeKind == AST_EXPR_UNARY_OP) { + ast_usedef_pass(tlc, a->exprUnOp.operand, wholestmt); + } else if(a->nodeKind == AST_EXPR_CALL) { + ast_usedef_pass(tlc, a->exprCall.what, wholestmt); + + for(size_t p = 0; p < a->exprCall.what->expression.type->function.argCount; p++) { + ast_usedef_pass(tlc, a->exprCall.args[p], wholestmt); + } + } else if(a->nodeKind == AST_EXPR_PRIMITIVE) { + } else if(a->nodeKind == AST_EXPR_STRING_LITERAL) { + } else if(a->nodeKind == AST_EXPR_CAST) { + ast_usedef_pass(tlc, a->exprCast.what, wholestmt); + } 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(); + } +} + /* 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); } -void cg_go(AST *a) { +int cg_go(AST *a) { typedef VarTableEntry *Adjacency[2]; + ast_usedef_reset(a); + size_t adjCount = 0; Adjacency *adjs = malloc(sizeof(*adjs) * adjCount); 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; + vars[vi]->data.var.markedForSpill = 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; @@ -372,6 +453,8 @@ cont:; qsort(vars, a->chunk.varCount, sizeof(*vars), comparator); + int lastColor = 0; + /* Welsh plow my ass */ for(int v = 0; v < a->chunk.varCount; v++) { for(int c = 0;; c++) { @@ -384,6 +467,9 @@ cont:; } vars[v]->data.var.color = c; + + lastColor = lastColor < c ? c : lastColor; + break; nextColor:; @@ -392,7 +478,16 @@ nextColor:; free(adjs); + if(lastColor >= 4) { + // Spill node with highest degree + + spill_pass(a, a, vars[a->chunk.varCount - 1]); + + return 0; + } + cg_chunk(a); - free(vars); + return 1; } + diff --git a/src/cg.h b/src/cg.h index 29e551c..bc74619 100644 --- a/src/cg.h +++ b/src/cg.h @@ -3,6 +3,6 @@ #include"ast.h" -void cg_go(union AST*); +int cg_go(union AST*); #endif diff --git a/src/dumberdowner.c b/src/dumberdowner.c index 924ebc9..d3a3166 100644 --- a/src/dumberdowner.c +++ b/src/dumberdowner.c @@ -8,87 +8,11 @@ // Complex expressions are to be "broken down" into simpler ones until the AST // can be trivially translated to the target architecture. // -// This pass, along with CG is strictly dependent on x86 and will fail for -// any other architecture. +// This file along with CG is strictly for IA-32 and will fail for other +// architecture. #include"x86.h" -static void varify_change_usedefs(AST *e, AST *oldStmt, AST *newStmt) { - if(e->nodeKind == AST_EXPR_VAR) { - if(e->exprVar.thing->kind != VARTABLEENTRY_VAR) { - return; - } - - UseDef *swapWithPrev = NULL; - UseDef *swapWith = NULL; - - UseDef *udPrev = NULL; - UseDef *ud = e->exprVar.thing->data.var.usedefFirst; - while(ud && ud->use != e) { - if(ud->stmt == oldStmt && !swapWith) { - swapWithPrev = udPrev; - swapWith = ud; - } - - udPrev = ud; - ud = ud->next; - } - - if(ud) { - assert(ud->stmt == oldStmt); - ud->stmt = newStmt; - - // We must make sure all newStmt usedefs are before the oldStmt ones, so swap - if(swapWith) { - assert(!!swapWithPrev + !!udPrev != 0); - - if(swapWithPrev) { - swapWithPrev->next = ud; - } else { - e->exprVar.thing->data.var.usedefFirst = ud; - } - - if(udPrev) { - udPrev->next = swapWith; - } else { - e->exprVar.thing->data.var.usedefFirst = swapWith; - } - - UseDef *temp = ud->next; - ud->next = swapWith->next; - swapWith->next = temp; - - assert(!!ud->next + !!swapWith->next != 0); - if(!swapWith->next) { - e->exprVar.thing->data.var.usedefLast = swapWith; - } - if(!ud->next) { - e->exprVar.thing->data.var.usedefLast = ud; - } - } - } - } else if(e->nodeKind == AST_EXPR_BINARY_OP) { - varify_change_usedefs(e->exprBinOp.operands[0], oldStmt, newStmt); - varify_change_usedefs(e->exprBinOp.operands[1], oldStmt, newStmt); - } else if(e->nodeKind == AST_EXPR_UNARY_OP) { - varify_change_usedefs(e->exprUnOp.operand, oldStmt, newStmt); - } else if(e->nodeKind == AST_EXPR_CALL) { - varify_change_usedefs(e->exprCall.what, oldStmt, newStmt); - - int argCount = e->exprCall.what->expression.type->function.argCount; - for(int i = 0; i < argCount; i++) { - varify_change_usedefs(e->exprCall.args[i], oldStmt, newStmt); - } - } else if(e->nodeKind == AST_EXPR_ARRAY) { - int sz = e->exprArray.type->array.length; - for(int i = 0; i < sz; i++) { - varify_change_usedefs(e->exprArray.items[i], oldStmt, newStmt); - } - } else if(e->nodeKind == AST_EXPR_CAST) { - varify_change_usedefs(e->exprCast.what, oldStmt, newStmt); - } -} - /* Split away complex expression into a new local variable */ static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) { VarTableEntry *vte = malloc(sizeof(*vte)); @@ -125,28 +49,10 @@ static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) { } assign->next = stmt; - // Assemble ud-chain + // Reset ud-chain - UseDef *ud[2]; - ud[0] = malloc(sizeof(UseDef)); - ud[1] = malloc(sizeof(UseDef)); - - ud[0]->stmt = (AST*) assign; - ud[0]->use = assign->what; - ud[0]->def = (AST*) assign; - ud[0]->next = ud[1]; - - ud[1]->stmt = stmt; - ud[1]->use = (AST*) ev[1]; - ud[1]->def = (AST*) assign; - ud[1]->next = NULL; - - vte->data.var.usedefFirst = ud[0]; - vte->data.var.usedefLast = ud[1]; - - // Correct the ud-chain of variables used in varified expression `e` - - varify_change_usedefs(e, stmt, (AST*) assign); + vte->data.var.usedefFirst = NULL; + vte->data.var.usedefLast = NULL; return (AST*) ev[1]; } @@ -155,28 +61,6 @@ static AST *xopify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) { return varify(tlc, chunk, stmtPrev, stmt, e); } -static UseDef *get_last_usedef_before_stmt(AST *tlc, VarTableEntry *v, AST *stmt) { - assert(v->kind == VARTABLEENTRY_VAR); - - UseDef *ud = v->data.var.usedefFirst; - - if(!ud) { - return NULL; - } - - UseDef *udPrev = NULL; - - while(ud) { - if(ast_stmt_is_after(tlc, ud->stmt, stmt)) { - break; - } - udPrev = ud; - ud = ud->next; - } - - return udPrev; -} - static int dumben_chunk(AST *tlc, AST *chu) { AST *sPrev = NULL; AST *s = chu->chunk.statementFirst; @@ -230,24 +114,17 @@ static int dumben_chunk(AST *tlc, AST *chu) { effective = 1; } - if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_NEGATE && (s->stmtAssign.to->exprUnOp.operand->nodeKind != AST_EXPR_VAR || s->stmtAssign.what->exprVar.thing != s->stmtAssign.to->exprUnOp.operand->exprVar.thing)) { + if(s->stmtAssign.to && 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)) { // Turn this: // a = -b // into // a = b // a = -a - // While keeping UD-chain valid. - // Currently only for variables. While this could work for any XOP, it requires the ability to deep-copy `a`, which is not trivial when taking into account UD-chains. - // TODO: Scrap incremental UD-chain modifications. Simply rebuild them after each pass. - - ASTExprVar *ev[2]; - for(int i = 0; i < 2; i++) { - ev[i] = malloc(sizeof(ASTExprVar)); - ev[i]->nodeKind = AST_EXPR_VAR; - ev[i]->type = s->stmtAssign.what->expression.type; - ev[i]->thing = s->stmtAssign.what->exprVar.thing; - } + AST *ev[2] = { + ast_deep_copy(s->stmtAssign.what), + ast_deep_copy(s->stmtAssign.what) + }; AST *negation = s->stmtAssign.to; @@ -261,28 +138,40 @@ static int dumben_chunk(AST *tlc, AST *chu) { assign2->statement.next = s->statement.next; s->statement.next = assign2; - - UseDef *link = get_last_usedef_before_stmt(tlc, ev[0]->thing, assign2->statement.next); - - UseDef *ud[2]; - ud[0] = malloc(sizeof(UseDef)); - ud[1] = malloc(sizeof(UseDef)); - - ud[0]->stmt = assign2; - ud[0]->use = (AST*) ev[1]; - ud[0]->def = s; - ud[0]->next = ud[1]; - - ud[1]->stmt = assign2; - ud[1]->use = (AST*) ev[0]; - ud[1]->def = s; - ud[1]->next = link->next; - - link->next = ud[0]; effective = 1; } + if(is_xop(s->stmtAssign.to) == XOP_NOT_XOP) { + if(s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_DEREF) { + s->stmtAssign.to->exprUnOp.operand = varify(tlc, chu, sPrev, s, s->stmtAssign.to->exprUnOp.operand); + + effective = 1; + } else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator < BINOP_SIMPLES && !ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0])) { + // Turn this: + // a = b op c + // into + // a = b + // a = a op c + + AST *assign2 = malloc(sizeof(ASTStmtAssign)); + assign2->nodeKind = AST_STMT_ASSIGN; + assign2->stmtAssign.what = ast_deep_copy(s->stmtAssign.what); + assign2->stmtAssign.to = s->stmtAssign.to->exprBinOp.operands[0]; + + sPrev->statement.next = assign2; + assign2->statement.next = s; + + s->stmtAssign.to->exprBinOp.operands[0] = ast_deep_copy(s->stmtAssign.what); + + effective = 1; + } else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && !is_xop(s->stmtAssign.to->exprBinOp.operands[0])) { + s->stmtAssign.to->exprBinOp.operands[0] = xopify(tlc, chu, sPrev, s, s->stmtAssign.to->exprBinOp.operands[0]); + } else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && !is_xop(s->stmtAssign.to->exprBinOp.operands[1])) { + s->stmtAssign.to->exprBinOp.operands[1] = xopify(tlc, chu, sPrev, s, s->stmtAssign.to->exprBinOp.operands[1]); + } + } + } else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_CALL) { int argCount = s->stmtExpr.expr->exprCall.what->expression.type->function.argCount; diff --git a/src/ntc.c b/src/ntc.c index 105809f..60245c1 100644 --- a/src/ntc.c +++ b/src/ntc.c @@ -39,7 +39,12 @@ int main(int argc_, char **argv_) { dumben_go(chunk); - cg_go(chunk); + while(!cg_go(chunk)) { + + puts(ast_dump(chunk)); + + dumben_go(chunk); + } return 0; } diff --git a/src/parse.c b/src/parse.c index 18e97eb..a6b2e08 100644 --- a/src/parse.c +++ b/src/parse.c @@ -24,11 +24,6 @@ static inline int __builtin_clzl(unsigned long x) { } #endif -typedef struct { - UseDef ud; - VarTableEntry *to; -} UseDefToAdd; - typedef struct { Token *tokens; intmax_t i; @@ -41,10 +36,6 @@ typedef struct { // Used by pushstat to add statements ASTChunk *currentChunk; - // Used by pushstmt to assemble use-def chain - size_t udsToAddCount; - UseDefToAdd *udsToAdd; - // Used to place guard variable uses after loops to stop reg allocation from fucking up VarTable *loopScope; size_t guardedVarCount; @@ -88,28 +79,6 @@ static int maybe(Parser *P, TokenKind t) { } static void pushstat(Parser *P, void *a) { - for(size_t i = 0; i < P->udsToAddCount; i++) { - VarTableEntry *to = P->udsToAdd[i].to; - - UseDef *ud = malloc(sizeof(*ud)); - memcpy(ud, &P->udsToAdd[i].ud, sizeof(*ud)); - ud->stmt = a; - - /*if(ud->stmt->nodeKind == AST_STMT_ASSIGN && ud->stmt->stmtAssign.what == ud->use) { - // Forget. The x in `x = y` is not a use of x. - continue; - }*/ - - if(to->data.var.usedefFirst) { - to->data.var.usedefLast->next = ud; - to->data.var.usedefLast = ud; - } else { - to->data.var.usedefFirst = to->data.var.usedefLast = ud; - } - } - - P->udsToAddCount = 0; - if(P->currentChunk->statementFirst) { P->currentChunk->statementLast->statement.next = a; P->currentChunk->statementLast = a; @@ -141,7 +110,7 @@ static ASTExprPrimitive *parse_prim(Parser *P) { return ret; } -static void newusedef(Parser *P, VarTableEntry *v, AST *expr) { +/*static void newusedef(Parser *P, VarTableEntry *v, AST *expr) { ReachingDefs *defs = v->data.var.reachingDefs; while(defs) { @@ -160,7 +129,7 @@ static void newusedef(Parser *P, VarTableEntry *v, AST *expr) { defs = defs->parent; } -} +}*/ static AST *exprvar(Parser *P, VarTableEntry *v) { AST *a = malloc(sizeof(ASTExprVar)); @@ -168,10 +137,6 @@ static AST *exprvar(Parser *P, VarTableEntry *v) { a->exprVar.type = v->type; a->exprVar.thing = v; - if(v->kind == VARTABLEENTRY_VAR) { - newusedef(P, v, a); - } - if(P->loopScope) { // XXX: O(n)!!!!!!!!! @@ -254,9 +219,6 @@ AST *nct_cast_expr(AST *what, Type *to) { ret->val = what->exprPrim.val & (((int64_t) 1 << to->primitive.width) - 1); return (AST*) ret; } else { - // Silently fail :) - return what; - ASTExprCast *ret = malloc(sizeof(*ret)); ret->nodeKind = AST_EXPR_CAST; ret->type = to; @@ -273,6 +235,15 @@ AST *nct_parse_expression(Parser *P, int lOP) { if(peek(P, 0).type == TOKEN_NUMBER) { return (AST*) parse_prim(P); } else if(peek(P, 0).type == TOKEN_IDENTIFIER) { + if(!strcmp(peek(P, 0).content, "@stack")) { + get(P); + + ASTExprStackPointer *ret = malloc(sizeof(*ret)); + ret->nodeKind = AST_EXPR_STACK_POINTER; + ret->type = primitive_parse("u32"); + return ret; + } + return exprvar(P, vartable_find(P->scope, get(P).content)); } else if(peek(P, 0).type == TOKEN_STRING) { ASTExprStringLiteral *ret = malloc(sizeof(*ret)); @@ -478,10 +449,20 @@ AST *nct_parse_expression(Parser *P, int lOP) { ASTExpr *operand = &(astop->operands[1] = nct_parse_expression(P, lOP + 1))->expression; - if(operand->type->type != TYPE_TYPE_PRIMITIVE) { - stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Invalid combination of operator and operand types."); - } - + if(!type_is_number(astop->operands[0]->expression.type) + || !type_is_number(astop->operands[1]->expression.type)) { + + stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Attempt to perform arithmetic on non-number types."); + } + + if(type_size(astop->operands[0]->expression.type) < type_size(astop->operands[1]->expression.type)) { + astop->operands[0] = nct_cast_expr(astop->operands[0], astop->operands[1]->expression.type); + } + + if(type_size(astop->operands[1]->expression.type) < type_size(astop->operands[0]->expression.type)) { + astop->operands[1] = nct_cast_expr(astop->operands[1], astop->operands[0]->expression.type); + } + if(!astop->type) { astop->type = operand->type; } else { @@ -655,8 +636,8 @@ static AST *parse_declaration(Parser *P) { assign->nodeKind = AST_STMT_ASSIGN; assign->next = NULL; - entry->data.var.reachingDefs = reachingdefs_push(NULL); - reachingdefs_set(entry->data.var.reachingDefs, (AST*) assign); + //entry->data.var.reachingDefs = reachingdefs_push(NULL); + //reachingdefs_set(entry->data.var.reachingDefs, (AST*) assign); assign->what = exprvar(P, entry); assign->to = peek(P, 0).type == TOKEN_SEMICOLON ? NULL : nct_parse_expression(P, 0); @@ -710,77 +691,6 @@ backtrack: return NULL; } -/* Imagine the following pseudocode: - x = ... - while ... { - (do something with x) - x = ... - } - The second definition of x reaches the code *before* it. - Therefore we must go over the loop body a second time with known reaching definitions and add the necessary usedefs.*/ -static void loop_second_pass(AST *a) { - if(a->nodeKind == AST_CHUNK) { - for(AST *s = a->chunk.statementFirst; s; s = s->statement.next) { - loop_second_pass(s); - } - } else if(a->nodeKind == AST_STMT_IF) { - loop_second_pass(a->stmtIf.expression); - loop_second_pass(a->stmtIf.then); - } else if(a->nodeKind == AST_STMT_LOOP) { - loop_second_pass(a->stmtLoop.body); - } else if(a->nodeKind == AST_STMT_ASSIGN) { - loop_second_pass(a->stmtAssign.what); - loop_second_pass(a->stmtAssign.to); - - /* TODO: Per-variable flag.to cease additional usedefs after assignment? - It is okay to have too many usedefs, so this isn't high priority.*/ - } else if(a->nodeKind == AST_STMT_EXPR) { - loop_second_pass(a->stmtExpr.expr); - } else if(a->nodeKind == AST_EXPR_VAR) { - UseDef *udPrev = NULL; - UseDef *ud = a->exprVar.thing->data.var.usedefFirst; - while(ud && ud->use != a) { - udPrev = ud; - ud = ud->next; - } - - if(ud) { - ReachingDefs *defs = a->exprVar.thing->data.var.reachingDefs; - while(defs) { - for(size_t i = 0; i < defs->defCount; i++) { - UseDef *newud = calloc(1, sizeof(*newud)); - memcpy(newud, ud, sizeof(*ud)); - newud->def = defs->defs[i]; - - udPrev->next = newud; - newud->next = ud; - ud = newud; - } - - if(defs->excludeParent) { - break; - } - - defs = defs->parent; - } - } - } else if(a->nodeKind == AST_EXPR_BINARY_OP) { - loop_second_pass(a->exprBinOp.operands[0]); - loop_second_pass(a->exprBinOp.operands[1]); - } else if(a->nodeKind == AST_EXPR_UNARY_OP) { - loop_second_pass(a->exprUnOp.operand); - } else if(a->nodeKind == AST_EXPR_CAST) { - loop_second_pass(a->exprCast.what); - } else if(a->nodeKind == AST_EXPR_CALL) { - loop_second_pass(a->exprCall.what); - - size_t argCount = a->exprCall.what->expression.type->function.argCount; - for(size_t i = 0; i < argCount; i++) { - loop_second_pass(a->exprCall.args[i]); - } - } -} - ASTChunk *nct_parse_chunk(Parser*, int, int); void nct_parse_statement(Parser *P) { if(maybe(P, TOKEN_IF)) { @@ -815,22 +725,26 @@ void nct_parse_statement(Parser *P) { ret->body = (AST*) nct_parse_chunk(P, 0, 1); expect(P, TOKEN_SQUIGGLY_R); + pushstat(P, ret); + if(isFirstLoop) { P->loopScope = NULL; for(size_t i = 0; i < P->guardedVarCount; i++) { ASTExprVar *ev = P->guardedVars[i]; - newusedef(P, ev->thing, ev); + AST *es = calloc(1, sizeof(ASTStmtExpr)); + es->nodeKind = AST_STMT_EXPR; + es->stmtExpr.expr = ev; + + pushstat(P, es); } + P->guardedVarCount = 0; free(P->guardedVars); P->guardedVars = NULL; } - loop_second_pass(ret->body); - - pushstat(P, ret); return; } else if(maybe(P, TOKEN_BREAK)) { ASTStmtBreak *ret = malloc(sizeof(*ret)); @@ -840,6 +754,7 @@ void nct_parse_statement(Parser *P) { expect(P, TOKEN_SEMICOLON); pushstat(P, ret); + return; } else if(maybe(P, TOKEN_CONTINUE)) { ASTStmtContinue *ret = malloc(sizeof(*ret)); @@ -849,6 +764,7 @@ void nct_parse_statement(Parser *P) { expect(P, TOKEN_SEMICOLON); pushstat(P, ret); + return; } else if(peek(P, 0).type == TOKEN_IDENTIFIER) { if(!strcmp(peek(P, 0).content, "@align")) { @@ -920,9 +836,9 @@ void nct_parse_statement(Parser *P) { ret->what = e; ret->to = nct_cast_expr(nct_parse_expression(P, 0), ret->what->expression.type); - if(ret->what->nodeKind == AST_EXPR_VAR) { - reachingdefs_set(ret->what->exprVar.thing->data.var.reachingDefs, (AST*) ret); - } + //if(ret->what->nodeKind == AST_EXPR_VAR) { + // reachingdefs_set(ret->what->exprVar.thing->data.var.reachingDefs, (AST*) ret); + //} expect(P, TOKEN_SEMICOLON); @@ -942,7 +858,7 @@ void nct_parse_statement(Parser *P) { } ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) { - AST *ret = malloc(sizeof(ASTChunk)); + AST *ret = calloc(1, sizeof(ASTChunk)); ret->nodeKind = AST_CHUNK; ret->chunk.statementFirst = ret->chunk.statementLast = NULL; ret->chunk.varCount = 0; @@ -957,7 +873,7 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) { P->topLevel = &ret->chunk; } - vartable_new_reachingdefs_for_all_vars(P->scope); + //vartable_new_reachingdefs_for_all_vars(P->scope); /* Find all symbol names and struct types ahead of time. Searches for colons as those can only mean symbol declarations */ @@ -1001,7 +917,7 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) { nct_parse_statement(P); } - vartable_coalesce_reachingdefs_for_all_vars(P->scope); + //vartable_coalesce_reachingdefs_for_all_vars(P->scope); size_t nonSymbols = 0; for(size_t i = 0; i < P->scope->count; i++) { diff --git a/src/reporting.h b/src/reporting.h index 5aed95c..ead912c 100644 --- a/src/reporting.h +++ b/src/reporting.h @@ -1,7 +1,7 @@ #ifndef NCTREF_REPORTING_H #define NCTREF_REPORTING_H -#ifndef _GNUC +#ifndef __GNUC__ #define __attribute__(x) #endif diff --git a/src/types.c b/src/types.c index d390f40..e4894f4 100644 --- a/src/types.c +++ b/src/types.c @@ -132,6 +132,10 @@ Type *type_pointer_wrap(Type *t) { return (Type*) ret; } +int type_is_number(Type *t) { + return t->type == TYPE_TYPE_POINTER || t->type == TYPE_TYPE_PRIMITIVE; +} + int type_is_castable(Type *from, Type *to) { if(type_equal(from, to)) return 2; @@ -152,4 +156,4 @@ int type_is_castable(Type *from, Type *to) { } return 0; -} \ No newline at end of file +} diff --git a/src/types.h b/src/types.h index 8136fdc..ed2b0c4 100644 --- a/src/types.h +++ b/src/types.h @@ -71,4 +71,8 @@ Type *type_pointer_wrap(Type*); /* 0 = not castable, 1 = explicitly castable, 2 = implicitly castable */ int type_is_castable(Type *from, Type *to); +int type_is_number(Type *t); + +char *type_to_string(Type*); + #endif diff --git a/src/vartable.h b/src/vartable.h index a97f7fb..95385d9 100644 --- a/src/vartable.h +++ b/src/vartable.h @@ -2,9 +2,10 @@ #define NCTREF_VARTABLE_H #include"types.h" +#include typedef enum { - VARTABLEENTRY_SYMBOL, VARTABLEENTRY_TYPE, VARTABLEENTRY_VAR + VARTABLEENTRY_SYMBOL, VARTABLEENTRY_STACK, VARTABLEENTRY_VAR } VarTableEntryKind; union AST; @@ -49,7 +50,7 @@ typedef struct VarTableEntry { // Register allocation // vars in loops have higher priority - // a more advanced approach would be to use weights for different colors (e.g. vars for mul "should" be in eax) + // a more advanced approach would be to use weights for different colors (e.g. multipliers "should" be in eax) uint8_t priority; uint16_t color, degree; diff --git a/src/x86.h b/src/x86.h index 8550f0c..3666d06 100644 --- a/src/x86.h +++ b/src/x86.h @@ -29,6 +29,8 @@ static inline int is_xop(AST *e) { } } } + } else if(e->nodeKind == AST_EXPR_STACK_POINTER) { + return XOP_NOT_MEM; } return XOP_NOT_XOP; diff --git a/tests/bit-rounding.nct b/tests/bit-rounding.nct index dae6e2d..3228620 100644 --- a/tests/bit-rounding.nct +++ b/tests/bit-rounding.nct @@ -1,6 +1,11 @@ u32 x: 123; u33 y: 5; -u3 *o = 5000; -u3 z = *o; +u3* o = 5000; +u32 z = *o; u3 p = *(o + z); +u3 *g = o - z; +p = *g; + +u32 c = 566; +u16 j = c; diff --git a/tests/spill.nct b/tests/spill.nct new file mode 100644 index 0000000..d84a62f --- /dev/null +++ b/tests/spill.nct @@ -0,0 +1,6 @@ +u8 a = 4; +u8 b = 1; +u8 c = 9; +u8 d = 5; +u8 e = 22; +u8 f = a + b + c + d + e; diff --git a/tests/stack.nct b/tests/stack.nct new file mode 100644 index 0000000..422e742 --- /dev/null +++ b/tests/stack.nct @@ -0,0 +1 @@ +@stack = @stack - 123;