From 7f855f3931a56e4a932478f49ec9fde72c602651 Mon Sep 17 00:00:00 2001 From: Mid <> Date: Fri, 15 Aug 2025 17:31:05 +0300 Subject: [PATCH] Despaghettify source --- src/ast.c | 1271 --------------------------------------- src/ast/ast.c | 269 +++++++++ src/{ => ast}/ast.h | 24 +- src/ast/commutativity.c | 21 + src/ast/commutativity.h | 8 + src/ast/desegment.c | 117 ++++ src/ast/desegment.h | 7 + src/ast/dump.c | 327 ++++++++++ src/ast/dump.h | 5 + src/ast/linearize.c | 161 +++++ src/ast/linearize.h | 4 + src/ast/sroa.c | 104 ++++ src/ast/sroa.h | 4 + src/ast/stack.c | 101 ++++ src/ast/stack.h | 9 + src/ast/usedef.c | 215 +++++++ src/ast/usedef.h | 4 + src/parse.h | 2 +- src/reporting.c | 2 +- src/scope.c | 25 - src/types.c | 2 +- src/x86/arch.h | 2 +- src/x86/cg.c | 2 +- 23 files changed, 1369 insertions(+), 1317 deletions(-) delete mode 100644 src/ast.c create mode 100644 src/ast/ast.c rename src/{ => ast}/ast.h (94%) create mode 100644 src/ast/commutativity.c create mode 100644 src/ast/commutativity.h create mode 100644 src/ast/desegment.c create mode 100644 src/ast/desegment.h create mode 100644 src/ast/dump.c create mode 100644 src/ast/dump.h create mode 100644 src/ast/linearize.c create mode 100644 src/ast/linearize.h create mode 100644 src/ast/sroa.c create mode 100644 src/ast/sroa.h create mode 100644 src/ast/stack.c create mode 100644 src/ast/stack.h create mode 100644 src/ast/usedef.c create mode 100644 src/ast/usedef.h diff --git a/src/ast.c b/src/ast.c deleted file mode 100644 index 7045f25..0000000 --- a/src/ast.c +++ /dev/null @@ -1,1271 +0,0 @@ -#include"ast.h" - -#include -#include -#include -#include -#include -#include"ntc.h" -#include"reporting.h" -#include"utils.h" -#include"x86/arch.h" - -const char *AST_KIND_STR[] = { AST_KINDS(GEN_STRI) }; - -void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud, GenericVisitorHandler preHandler, GenericVisitorHandler postHandler) { - if(preHandler) preHandler(nptr, stmt, stmtPrev, chu, tlc, ud); - - AST *n = *nptr; - - if(n->nodeKind == AST_CHUNK) { - AST *sPrev = NULL; - AST **s = &n->chunk.statementFirst; - while(*s) { - generic_visitor(s, *s, sPrev, n, tlc, ud, preHandler, postHandler); - - sPrev = *s; - s = &sPrev->statement.next; - } - } else if(n->nodeKind == AST_STMT_ASSIGN) { - generic_visitor(&n->stmtAssign.what, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - if(n->stmtAssign.to) { - generic_visitor(&n->stmtAssign.to, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } - } else if(n->nodeKind == AST_STMT_IF) { - generic_visitor(&n->stmtIf.expression, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - generic_visitor(&n->stmtIf.then, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - if(n->stmtIf.elss) { - generic_visitor(&n->stmtIf.elss, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } - } else if(n->nodeKind == AST_STMT_LOOP) { - generic_visitor(&n->stmtLoop.body, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } else if(n->nodeKind == AST_STMT_BREAK) { - } else if(n->nodeKind == AST_STMT_CONTINUE) { - } else if(n->nodeKind == AST_STMT_EXT_ALIGN) { - } else if(n->nodeKind == AST_STMT_DECL) { - if(n->stmtDecl.expression) { - generic_visitor(&n->stmtDecl.expression, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } - } else if(n->nodeKind == AST_STMT_EXPR) { - generic_visitor(&n->stmtExpr.expr, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } else if(n->nodeKind == AST_STMT_EXT_ORG) { - } else if(n->nodeKind == AST_STMT_EXT_SECTION) { - } else if(n->nodeKind == AST_STMT_RETURN) { - if(n->stmtReturn.val) { - generic_visitor(&n->stmtReturn.val, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } - } else if(n->nodeKind == AST_EXPR_BINARY_OP) { - generic_visitor(&n->exprBinOp.operands[0], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - generic_visitor(&n->exprBinOp.operands[1], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } else if(n->nodeKind == AST_EXPR_CALL) { - generic_visitor(&n->exprCall.what, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - - for(size_t i = 0; i < n->exprCall.what->expression.type->pointer.of->function.argCount; i++) { - generic_visitor(&n->exprCall.args[i], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } - } else if(n->nodeKind == AST_EXPR_CAST) { - generic_visitor(&n->exprCast.what, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } else if(n->nodeKind == AST_EXPR_FUNC) { - generic_visitor(&n->exprFunc.chunk, NULL, NULL, n->exprFunc.chunk, n->exprFunc.chunk, ud, preHandler, postHandler); - } else if(n->nodeKind == AST_EXPR_UNARY_OP) { - generic_visitor(&n->exprUnOp.operand, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } else if(n->nodeKind == AST_EXPR_VAR) { - } else if(n->nodeKind == AST_EXPR_STACK_POINTER) { - } else if(n->nodeKind == AST_EXPR_PRIMITIVE) { - } else if(n->nodeKind == AST_EXPR_STRING_LITERAL) { - } else if(n->nodeKind == AST_EXPR_ARRAY) { - assert(n->expression.type->type == TYPE_TYPE_ARRAY); - assert(n->expression.type->array.length != 0); - - for(size_t i = 0; i < n->expression.type->array.length; i++) { - generic_visitor(&n->exprArray.items[i], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } - } else if(n->nodeKind == AST_EXPR_EXT_SALLOC) { - } else if(n->nodeKind == AST_EXPR_DOT) { - generic_visitor(&n->exprDot.a, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } else if(n->nodeKind == AST_EXPR_EXT_SIZEOF) { - if(n->exprExtSizeOf.ofExpr) { - generic_visitor(&n->exprExtSizeOf.ofExpr, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } - } else if(n->nodeKind == AST_STMT_JUMP) { - if(n->stmtJump.condition) { - generic_visitor(&n->stmtJump.condition, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); - } - } else if(n->nodeKind == AST_STMT_LABEL) { - } else { - stahp_node(n, "generic_visitor: unhandled %s", AST_KIND_STR[n->nodeKind]); - } - - if(postHandler) postHandler(nptr, stmt, stmtPrev, chu, tlc, ud); -} - -int ast_expression_equal(AST *a, AST *b) { - if(a->nodeKind != b->nodeKind) return 0; - - if(a->nodeKind == AST_EXPR_PRIMITIVE) { - return a->exprPrim.val == b->exprPrim.val; - } else if(a->nodeKind == AST_EXPR_VAR) { - return a->exprVar.thing == b->exprVar.thing; - } else if(a->nodeKind == AST_EXPR_UNARY_OP) { - 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; - } else if(a->nodeKind == AST_EXPR_CAST) { - return ast_expression_equal(a->exprCast.what, b->exprCast.what) && type_equal(a->exprCast.to, b->exprCast.to) && a->exprCast.reinterpretation == b->exprCast.reinterpretation; - } else { - stahp_node(a, "ast_expression_equal: unhandled %s", AST_KIND_STR[a->nodeKind]); - } -} - -// This function may return three values: YES (1), NO (0) or UNKNOWN (-1). -// ... Ew -int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) { - const AST *s = chunk->chunk.statementFirst; - - while(1) { - if(s && s->nodeKind == AST_STMT_LOOP) { - int i = ast_stmt_is_after(s->stmtLoop.body, s1, s2); - if(i == 1 || (i == 0 && s1 != NULL)) { - return i; - } - } - - if(s == s1) { - return 0; - } - if(s == 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 || (i == 0 && s1 != NULL)) { - return i; - } - - if(s->stmtIf.elss) { - i = ast_stmt_is_after(s->stmtIf.elss, s1, s2); - if(i == 1 || (i == 0 && s1 != NULL)) { - return i; - } - } - } - - s = s->statement.next; - } - - return -1; -} - -AST *ast_get_label_by_name(AST *tlc, const char *name) { - for(AST *s = tlc->chunk.statementFirst; s; s = s->statement.next) { - if(s->nodeKind == AST_STMT_LABEL && !strcmp(s->stmtLabel.name, name)) { - return s; - } - } - - stahp(0, 0, "Label %s not found", name); -} - -static void rd_kill(ReachingDefs *a, ScopeItem *var) { - for(size_t i = a->defCount; i --> 0;) { - AST *def = a->defs[i]; - - assert(def->nodeKind == AST_STMT_ASSIGN); - assert(def->stmtAssign.what->nodeKind == AST_EXPR_VAR); - assert(def->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR); - - if(def->stmtAssign.what->exprVar.thing == var) { - memmove(&a->defs[i], &a->defs[i + 1], sizeof(*a->defs) * (a->defCount - i - 1)); - a->defCount--; - } - } -} - -static bool rd_equal(ReachingDefs *a, ReachingDefs *b) { - if(a->defCount != b->defCount) { - return false; - } - - for(size_t i = 0; i < a->defCount; i++) { - if(a->defs[i] != b->defs[i]) { - return false; - } - } - - return true; -} - -static int compar_ptr(const void *a, const void *b) { - return *(uintptr_t*) a - *(uintptr_t*) b; -} - -static bool rd_find(ReachingDefs *dest, union AST *ast) { - return !!bsearch(&ast, dest->defs, dest->defCount, sizeof(*dest->defs), compar_ptr); -} - -static void rd_add(ReachingDefs *dest, union AST *ast) { - if(rd_find(dest, ast)) { - return; - } - - dest->defs = realloc(dest->defs, sizeof(*dest->defs) * (++dest->defCount)); - dest->defs[dest->defCount - 1] = ast; - qsort(dest->defs, dest->defCount, sizeof(*dest->defs), compar_ptr); -} - -static void rd_union(ReachingDefs *dest, ReachingDefs *src) { - for(size_t i = 0; i < src->defCount; i++) { - rd_add(dest, src->defs[i]); - } -} - -static ReachingDefs rd_compute_in(AST *tlc, AST *stmt, AST *stmtPrev) { - ReachingDefs rd = {}; - - // The previous statement is a predecessor unless it's an unconditional jump statement - if(stmtPrev && (stmtPrev->nodeKind != AST_STMT_JUMP || stmtPrev->stmtJump.condition)) { - rd_union(&rd, &stmtPrev->statement.rd); - } - - // If this is a label statement, then all jumps to this statement are predecessors - if(stmt->nodeKind == AST_STMT_LABEL) { - for(AST *s = tlc->chunk.statementFirst; s; s = s->statement.next) { - if(s->nodeKind == AST_STMT_JUMP && !strcmp(s->stmtJump.label, stmt->stmtLabel.name)) { - rd_union(&rd, &s->statement.rd); - } - } - } - - return rd; -} - -static void rd_step(AST *tlc, AST *stmt, AST *stmtPrev) { - stmt->statement.dirty = false; - - ReachingDefs rd = rd_compute_in(tlc, stmt, stmtPrev); - - if(stmt->nodeKind == AST_STMT_ASSIGN && stmt->stmtAssign.what->nodeKind == AST_EXPR_VAR && stmt->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR) { - rd_kill(&rd, stmt->stmtAssign.what->exprVar.thing); - rd_add(&rd, stmt); - } - - if(!rd_equal(&rd, &stmt->statement.rd)) { - // Set dirty flag on all successors - - // The next statement is a successor unless it's an unconditional jump statement - if(stmt->statement.next && (stmt->nodeKind != AST_STMT_JUMP || stmt->stmtJump.condition)) { - stmt->statement.next->statement.dirty = true; - } - - // If this is a jump statement, the target label is a successor - if(stmt->nodeKind == AST_STMT_JUMP) { - AST *label = ast_get_label_by_name(tlc, stmt->stmtJump.label); - label->statement.dirty = true; - } - - stmt->statement.rd = rd; - } -} - -char *ast_dump(AST *tlc); -char *ast_dumpc(AST *tlc, AST *chu); - -static void usedef_generation_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { - AST *n = *nptr; - - if(n->nodeKind == AST_EXPR_VAR) { - ReachingDefs *rd = &stmt->statement.rd; - - ScopeItem *si = n->exprVar.thing; - - for(size_t rdi = 0; rdi < rd->defCount; rdi++) { - AST *def = rd->defs[rdi]; - - if(def->stmtAssign.what->exprVar.thing == si) { - UseDef *ud = calloc(1, sizeof(*ud)); - ud->def = def; - ud->use = n; - ud->stmt = stmt; - - if(!si->data.var.usedefFirst) { - si->data.var.usedefFirst = si->data.var.usedefLast = ud; - } else { - si->data.var.usedefLast->next = ud; - si->data.var.usedefLast = ud; - } - } - } - } -} -void ast_usedef_reset(AST *chu) { - for(size_t i = 0; i < chu->chunk.varCount; i++) { - ScopeItem *vte = chu->chunk.vars[i]; - - assert(vte->kind == SCOPEITEM_VAR); - - vte->data.var.usedefFirst = NULL; - vte->data.var.usedefLast = NULL; - vte->data.var.liveRangeStart = NULL; - vte->data.var.liveRangeEnd = NULL; - } - - for(AST *s = chu->chunk.statementFirst; s; s = s->statement.next) { - if(s->nodeKind == AST_STMT_IF || s->nodeKind == AST_STMT_LOOP) { - stahp(0, 0, "UD-chain generation requires a completely linear IR"); - } - - s->statement.dirty = true; - } - - for(size_t rdsteps = 0;; rdsteps++) { - //fprintf(stderr, "RD step %lu\n", rdsteps); - - AST *prev = NULL; - AST *dirty = NULL; - - // Find at least one dirty statement - for(AST *s = chu->chunk.statementFirst; s; prev = s, s = s->statement.next) { - if(s->statement.dirty) { - dirty = s; - break; - } - } - - if(!dirty) { - // Completed reaching definition computation - break; - } - - rd_step(chu, dirty, prev); - } - - generic_visitor(&chu, NULL, NULL, chu, chu, NULL, usedef_generation_visitor, NULL); - - for(size_t i = 0; i < chu->chunk.varCount; i++) { - ScopeItem *vte = chu->chunk.vars[i]; - - assert(vte->kind == SCOPEITEM_VAR); - - assert(!!vte->data.var.usedefFirst == !!vte->data.var.usedefLast); - - vte->data.var.liveRangeStart = vte->data.var.usedefFirst->stmt; - vte->data.var.liveRangeEnd = vte->data.var.usedefLast->stmt; - } - - // fix liveRangeStart and/or liveRangeEnd depending on goto targets - for(AST *s = chu->chunk.statementFirst; s; s = s->statement.next) { - if(s->nodeKind == AST_STMT_JUMP) { - AST *target = ast_get_label_by_name(chu, s->stmtJump.label); - - for(size_t sii = 0; sii < chu->chunk.varCount; sii++) { - ScopeItem *si = chu->chunk.vars[sii]; - - if(ast_stmt_is_after(chu, si->data.var.liveRangeEnd, s) == 0 && ast_stmt_is_after(chu, target, si->data.var.liveRangeEnd) == 0 && ast_stmt_is_after(chu, si->data.var.declaration, target) == 0) { - - si->data.var.liveRangeEnd = s; - - } - } - } - } - - if(ntc_get_int("pdbg")) { - char *astdump = ast_dump(chu); - fprintf(stderr, "### USEDEF GENERATED ###\n%s\n", astdump); - free(astdump); - } -} - -static void ast_tlc_add_var(AST *tlc, ScopeItem *si) { - tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * ++tlc->chunk.varCount); - tlc->chunk.vars[tlc->chunk.varCount - 1] = si; -} - -/* Split away complex expression into a new local variable */ -static AST *varify(AST *tlc, AST *chunk, AST **stmtPrev, AST *stmt, AST *e) { - static size_t idx = 0; - - ScopeItem *vte = calloc(1, sizeof(*vte)); - vte->kind = SCOPEITEM_VAR; - vte->type = e->expression.type; - vte->data.var.name = malp("$varify_%lu", idx++); - - ast_tlc_add_var(tlc, vte); - - // Alter AST - - ASTExprVar *ev[2]; - for(int i = 0; i < 2; i++) { - ev[i] = calloc(1, sizeof(ASTExprVar)); - ev[i]->nodeKind = AST_EXPR_VAR; - ev[i]->type = e->expression.type; - ev[i]->thing = vte; - } - - ASTStmtAssign *assign = calloc(1, sizeof(*assign)); - assign->nodeKind = AST_STMT_ASSIGN; - assign->what = (AST*) ev[0]; - assign->to = e; - - if(*stmtPrev) { - (*stmtPrev)->statement.next = (AST*) assign; - } else { - chunk->chunk.statementFirst = (AST*) assign; - } - assign->next = stmt; - *stmtPrev = (AST*) assign; - - // Reset ud-chain - - vte->data.var.usedefFirst = NULL; - vte->data.var.usedefLast = NULL; - - return (AST*) ev[1]; -} - -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; - } else if(t->type == TYPE_TYPE_RECORD) { - return malp("%s", t->record.name); - } else if(t->type == TYPE_TYPE_GENERIC) { - return malp("%s", t->generic.paramName); - } else if(t->type == TYPE_TYPE_ARRAY) { - char *of = type_to_string(t->array.of); - char *len = NULL; - if(t->array.lengthIsGeneric) { - len = malp(""); - } else { - len = malp("%li", t->array.length); - } - char *r = malp("%s[%s]", of, len); - free(of); - free(len); - return r; - } - - return strdup("@unimp"); -} - -static char *ast_dumpe(AST *tlc, AST *e) { - if(!e) { - return malp("(null)"); - } - - if(e->nodeKind == AST_EXPR_PRIMITIVE) { - return malp("%i", e->exprPrim.val); - } else if(e->nodeKind == AST_EXPR_VAR) { - ScopeItem *vte = e->exprVar.thing; - - if(vte->kind == SCOPEITEM_VAR) { - return strdup(vte->data.var.name); - } else if(vte->kind == SCOPEITEM_SYMBOL) { - return strdup(vte->data.symbol.name); - } else abort(); - } else if(e->nodeKind == AST_EXPR_UNARY_OP) { - const char *op = NULL; - 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; - case UNOP_NOT: - op = "!"; - break; - default: - abort(); - } - char *c = ast_dumpe(tlc, 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(tlc, e->exprBinOp.operands[0]); - char *b = ast_dumpe(tlc, 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; - case BINOP_NEQUAL: - op = "!="; - break; - case BINOP_LESS: - op = "<"; - break; - case BINOP_GREATER: - op = ">"; - break; - case BINOP_LEQUAL: - op = "<="; - break; - case BINOP_GEQUAL: - op = ">="; - break; - case BINOP_MULHI: - op = "*^"; - break; - default: - abort(); - } - char *r = malp("(%s %s %s)", a, op, b); - free(a); - free(b); - return r; - } else if(e->nodeKind == AST_EXPR_STACK_POINTER) { - return malp("@stack"); - } else if(e->nodeKind == AST_EXPR_FUNC) { - char *out = NULL; - - if(type_is_generic(e->expression.type)) { - out = malp("(generic)"); - return out; - } - - { - char *rettype = type_to_string(e->expression.type->function.ret); - out = malp("%s(", rettype); - free(rettype); - } - - for(int i = 0; i < e->expression.type->function.argCount; i++) { - char *argtype = type_to_string(e->expression.type->function.args[i]); - char *out2 = malp(i == e->expression.type->function.argCount - 1 ? "%s%s" : "%s%s, ", out, argtype); - free(out); - free(argtype); - out = out2; - } - - { - char *choonk = ast_dumpc(tlc, e->exprFunc.chunk); - char *out2 = malp("%s) {\n%s}", out, choonk); - free(out); - free(choonk); - out = out2; - } - - return out; - } else if(e->nodeKind == AST_EXPR_CALL) { - char *w = ast_dumpe(tlc, e->exprCall.what); - char *out = malp("%s(", w); - free(w); - size_t argCount = e->exprCall.what->expression.type->pointer.of->function.argCount; - for(size_t i = 0; i < argCount; i++) { - char *a = ast_dumpe(tlc, e->exprCall.args[i]); - char *out2 = malp(i == argCount - 1 ? "%s%s)" : "%s%s, ", out, a); - free(a); - free(out); - out = out2; - } - return out; - } else if(e->nodeKind == AST_EXPR_EXT_SALLOC) { - char *w = type_to_string(e->exprExtSalloc.size); - char *out = malp("@salloc(%s)", w); - free(w); - return out; - } else if(e->nodeKind == AST_EXPR_CAST) { - char *a = ast_dumpe(tlc, e->exprCast.what); - char *b = type_to_string(e->exprCast.to); - char *out = malp("(%s as %s)", a, b); - free(a); - free(b); - return out; - } else if(e->nodeKind == AST_EXPR_DOT) { - char *a = ast_dumpe(tlc, e->exprDot.a); - char *out = malp(e->nodeKind == AST_EXPR_BINARY_OP ? "(%s).%s" : "%s.%s", a, e->exprDot.b); - free(a); - return out; - } - - return malp("@unimp:%s", AST_KIND_STR[e->nodeKind]); -} - -static char *ast_dumps(AST *tlc, AST *s) { - if(s->nodeKind == AST_STMT_DECL) { - ScopeItem *vte = s->stmtDecl.thing; - - if(vte->kind == SCOPEITEM_SYMBOL) { - char *t = type_to_string(vte->type); - char *e = s->stmtDecl.expression ? ast_dumpe(tlc, s->stmtDecl.expression) : strdup(""); - char *r = malp("%s%s %s: %s;", vte->data.symbol.isExternal ? "external " : "", t, vte->data.symbol.name, e); - free(t); - free(e); - return r; - } - } else if(s->nodeKind == AST_STMT_ASSIGN) { - if(s->stmtAssign.to) { - char *a = ast_dumpe(tlc, s->stmtAssign.what); - char *b = ast_dumpe(tlc, s->stmtAssign.to); - char *r = malp("%s = %s;", a, b); - free(a); - free(b); - return r; - } else { - char *a = ast_dumpe(tlc, s->stmtAssign.what); - char *r = malp("%s = ; /* fake def */", a); - free(a); - return r; - } - } else if(s->nodeKind == AST_STMT_JUMP) { - char *a = ast_dumpe(tlc, s->stmtJump.condition); - char *r = malp("jump %s if %s;", s->stmtJump.label, a); - free(a); - return r; - } else if(s->nodeKind == AST_STMT_LABEL) { - return malp("@label %s;", s->stmtLabel.name); - } else if(s->nodeKind == AST_STMT_LOOP) { - char *inner = ast_dumpc(tlc, s->stmtLoop.body); - char *c = malp("loop {\n%s}", inner); - free(inner); - return c; - } else if(s->nodeKind == AST_STMT_IF) { - char *cond = ast_dumpe(tlc, s->stmtIf.expression); - char *inner = ast_dumpc(tlc, s->stmtIf.then); - char *elss = s->stmtIf.elss ? ast_dumpc(tlc, s->stmtIf.elss) : NULL; - char *c; - if(elss) { - c = malp("if(%s) {\n%s} else {\n%s}", cond, inner, elss); - free(elss); - } else { - c = malp("if(%s) {\n%s}", cond, inner); - } - free(cond); - free(inner); - return c; - } else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_VAR) { - const char *name; - - if(s->stmtExpr.expr->exprVar.thing->kind == SCOPEITEM_VAR) { - name = s->stmtExpr.expr->exprVar.thing->data.var.name; - } else { - name = s->stmtExpr.expr->exprVar.thing->data.symbol.name; - } - - return malp("%s; /* loop guard */", name); - } else if(s->nodeKind == AST_STMT_EXPR) { - return ast_dumpe(tlc, s->stmtExpr.expr); - } else if(s->nodeKind == AST_STMT_RETURN) { - if(s->stmtReturn.val) { - char *e = ast_dumpe(tlc, s->stmtReturn.val); - char *c = malp("return %s;", e); - free(e); - return c; - } else { - return malp("return;"); - } - } - - return malp("@unimp:%s;", AST_KIND_STR[s->nodeKind]); -} - -static char *stmt_live_range_dbgs(AST *tlc, AST *stmt) { - char *ret = malp(""); - for(size_t v = 0; v < tlc->chunk.varCount; v++) { - ScopeItem *si = tlc->chunk.vars[v]; - - if(si->data.var.liveRangeStart == stmt) { - char *ret2 = malp("%s (%s start)", ret, si->data.var.name); - free(ret); - ret = ret2; - } - - if(si->data.var.liveRangeEnd == stmt) { - char *ret2 = malp("%s (%s end)", ret, si->data.var.name); - free(ret); - ret = ret2; - } - } - return ret; -} - -char *ast_dumpc(AST *tlc, AST *chu) { - AST *stmt = chu->chunk.statementFirst; - - char *ret = malp(""); - - while(stmt) { - char *new = ast_dumps(tlc, stmt); - char *users = stmt_live_range_dbgs(tlc, stmt); - char *ret2 = malp("%s%s%s\n", ret, new, users); - free(ret); - free(new); - free(users); - ret = ret2; - - stmt = stmt->statement.next; - } - - return ret; -} - -char *ast_dump(AST *tlc) { - return ast_dumpc(tlc, tlc); -} - -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(); -} - -AST *ast_cast_expr(AST *what, Type *to) { - if(what == NULL) { - stahp(0, 0, "NULL what passed to ast_cast_expr"); - goto fail; - } - - /* Only exists at parse-time, hence not part of type system and is handled separately */ - if(what->nodeKind == AST_EXPR_STRING_LITERAL) { - if(to->type == TYPE_TYPE_ARRAY && type_equal(primitive_parse("u8"), to->array.of) && (to->array.length == what->exprStrLit.length || to->array.length == -1)) { - ASTExprArray *ret = calloc(1, sizeof(*ret)); - ret->nodeKind = AST_EXPR_ARRAY; - ret->items = calloc(what->exprStrLit.length, sizeof(*ret->items)); - ret->type = to; - - // If this declaration is of an unknown-length array type (eg u8[?]) then update the length - if(to->array.length == -1) { - to->array.length = what->exprStrLit.length; - } - - for(int i = 0; i < what->exprStrLit.length; i++) { - uint8_t bajt = what->exprStrLit.data[i]; - - ASTExprPrimitive *item = calloc(1, sizeof(*item)); - item->nodeKind = AST_EXPR_PRIMITIVE; - item->type = to->array.of; - item->val = bajt; - - ret->items[i] = (AST*) item; - } - - return (AST*) ret; - } else if(to->type == TYPE_TYPE_PRIMITIVE) { - if(to->primitive.width != what->exprStrLit.length * 8) { - stahp_node(what, "Size mismatch between string literal and target type"); - } - - ASTExprPrimitive *ret = calloc(1, sizeof(*ret)); - ret->nodeKind = AST_EXPR_PRIMITIVE; - ret->type = to; - memcpy(&ret->val, what->exprStrLit.data, sizeof(ret->val)); - return (AST*) ret; - } else abort(); - } - - // Make sure an unparametrized generic int parameter hasn't sneaked its way in - while(what->nodeKind == AST_EXPR_VAR && what->exprVar.thing->kind == SCOPEITEM_CEXPR && what->exprVar.thing->data.cexpr.concrete) { - what = what->exprVar.thing->data.cexpr.concrete; - } - assert(!(what->nodeKind == AST_EXPR_VAR && what->exprVar.thing->kind == SCOPEITEM_CEXPR)); - - if(type_equal(what->expression.type, to)) return what; - - if(!type_is_castable(what->expression.type, to)) { - goto fail; - } - - if(what->nodeKind == AST_EXPR_PRIMITIVE && (to->type == TYPE_TYPE_PRIMITIVE || to->type == TYPE_TYPE_POINTER)) { - ASTExprPrimitive *ret = calloc(1, sizeof(*ret)); - ret->nodeKind = AST_EXPR_PRIMITIVE; - ret->type = to; - - if(to->type == TYPE_TYPE_PRIMITIVE) { - ret->val = what->exprPrim.val & ((1UL << to->primitive.width) - 1); - } else { - ret->val = what->exprPrim.val & ((1UL << (8 * type_size(to))) - 1); - } - - return (AST*) ret; - } else { - ASTExprCast *ret = calloc(1, sizeof(*ret)); - ret->nodeKind = AST_EXPR_CAST; - ret->type = to; - ret->what = what; - ret->to = to; - return (AST*) ret; - } - -fail: - stahp_node(what, "Cannot cast type %s into %s", type_to_string(what->expression.type), type_to_string(to)); -} - -struct Spill2StackState { - AST *targetTLC; - ScopeItem *target; // can be NULL - size_t stackGrowth; -}; -static void spill2stack_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { - struct Spill2StackState *this = ud; - - if(tlc != this->targetTLC) { - // Don't do anything. - return; - } - - AST *a = *aptr; - - if(a == tlc) { - a->chunk.stackReservation += this->stackGrowth; - } else if(a->nodeKind == AST_EXPR_VAR) { - - if(a->exprVar.thing == this->target) { - // DO THE SPILL - - ASTExprStackPointer *rsp = calloc(1, sizeof(*rsp)); - rsp->nodeKind = AST_EXPR_STACK_POINTER; - rsp->type = primitive_parse("u32"); - - ASTExprPrimitive *offset = calloc(1, sizeof(*offset)); - offset->nodeKind = AST_EXPR_PRIMITIVE; - offset->type = rsp->type; - offset->val = -this->stackGrowth; // This will be affected by the other part of this pass, so we must reverse - offset->stackGrowth = true; - - ASTExprBinaryOp *bop = calloc(1, sizeof(*bop)); - bop->nodeKind = AST_EXPR_BINARY_OP; - bop->type = rsp->type; - bop->operator = BINOP_ADD; - bop->operands[0] = (AST*) rsp; - bop->operands[1] = (AST*) offset; - - ASTExprUnaryOp *deref = calloc(1, sizeof(*deref)); - deref->nodeKind = AST_EXPR_UNARY_OP; - deref->type = a->expression.type; - deref->operator = UNOP_DEREF; - deref->operand = (AST*) bop; - - *aptr = (AST*) deref; - } - - } else if(a->nodeKind == AST_EXPR_PRIMITIVE && a->exprPrim.stackGrowth) { - - // Guaranteed to not require more dumbification - a->exprPrim.val += this->stackGrowth; - - } -} - -void ast_spill_to_stack(AST *tlc, ScopeItem *vte) { - assert(tlc->nodeKind == AST_CHUNK); - assert(vte != NULL); - assert(vte->kind == SCOPEITEM_VAR); - - if(ntc_get_int("pdbg")) { - fprintf(stderr, "### SPILLING %s ###\n", vte->data.var.name); - } - - struct Spill2StackState state; - memset(&state, 0, sizeof(state)); - state.target = vte; - state.targetTLC = tlc; - state.stackGrowth = (type_size(vte->type) + 7) & ~7; - - generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, spill2stack_visitor, NULL); - - size_t vteIndex = 0; - while(tlc->chunk.vars[vteIndex] != vte) vteIndex++; - - memmove(&tlc->chunk.vars[vteIndex], &tlc->chunk.vars[vteIndex + 1], sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount - vteIndex - 1)); - tlc->chunk.varCount--; -} - -void ast_grow_stack_frame(AST *tlc, size_t bytes) { - if(bytes == 0) { - return; - } - - struct Spill2StackState state; - memset(&state, 0, sizeof(state)); - state.target = NULL; // spill nothing - state.targetTLC = tlc; - state.stackGrowth = bytes; - - generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, spill2stack_visitor, NULL); -} - -// This pass makes sure that integer literals are always operand 1 in a binary operation, -// simplifying passes further down -static void commutativity_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { - AST *n = *nptr; - - if(n->nodeKind == AST_EXPR_BINARY_OP) { - if(binop_is_commutative(n->exprBinOp.operator)) { - if(n->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[1]->nodeKind != AST_EXPR_PRIMITIVE) { - AST *tmp = n->exprBinOp.operands[0]; - n->exprBinOp.operands[0] = n->exprBinOp.operands[1]; - n->exprBinOp.operands[1] = tmp; - } - } - } -} -void ast_commutativity_pass(AST *tlc) { - generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, NULL, commutativity_visitor); -} - -static bool is_sroa_candidate(ScopeItem *si) { - return si->type->type == TYPE_TYPE_RECORD && type_size(si->type) <= ntc_get_int_default("sroa-threshold", 32); -} - -struct IsScopeItemReferenced { - bool yes; - ScopeItem *si; -}; -static void is_scopeitem_referenced_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { - struct IsScopeItemReferenced *state = ud; - - AST *n = *aptr; - if(n->nodeKind == AST_EXPR_UNARY_OP) { - if(n->exprUnOp.operand->nodeKind == AST_EXPR_VAR && n->exprUnOp.operator == UNOP_REF && n->exprUnOp.operand->exprVar.thing == state->si) { - state->yes = true; - } - } -} -static bool is_scopeitem_referenced(AST *tlc, ScopeItem *si) { - struct IsScopeItemReferenced state = {.si = si}; - generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, NULL, is_scopeitem_referenced_visitor); - return state.yes; -} - -struct DecomposeAutomaticRecordState { - AST *tlc; - ScopeItem *target; - ScopeItem **replacements; -}; -static void ast_decompose_automatic_record_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { - AST *n = *aptr; - - struct DecomposeAutomaticRecordState *state = ud; - - if(state->tlc != tlc) return; - - if(n->nodeKind == AST_EXPR_DOT && n->exprDot.a->nodeKind == AST_EXPR_VAR && n->exprDot.a->exprVar.thing == state->target) { - size_t idx; - for(idx = 0; strcmp(state->target->type->record.fieldNames[idx], n->exprDot.b); idx++); - - AST *ev = calloc(1, sizeof(ASTExprVar)); - ev->nodeKind = AST_EXPR_VAR; - ev->expression.type = state->target->type->record.fieldTypes[idx]; - ev->exprVar.thing = state->replacements[idx]; - - *aptr = (AST*) ev; - } -} -static void ast_decompose_automatic_record(AST *tlc, ScopeItem *target) { - assert(target->kind == SCOPEITEM_VAR); - assert(target->type->type == TYPE_TYPE_RECORD); - - struct DecomposeAutomaticRecordState state = { - .tlc = tlc, - .target = target, - .replacements = calloc(target->type->record.fieldCount, sizeof(ScopeItem)), - }; - - tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount + target->type->record.fieldCount)); - - for(size_t f = 0; f < target->type->record.fieldCount; f++) { - ScopeItem *si = calloc(1, sizeof(*si)); - si->kind = SCOPEITEM_VAR; - si->type = target->type->record.fieldTypes[f]; - si->data.var.name = malp("%s_sroa_%s", target->data.var.name, target->type->record.fieldNames[f]); - state.replacements[f] = si; - - tlc->chunk.vars[tlc->chunk.varCount++] = si; - } - - generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, ast_decompose_automatic_record_visitor, NULL); - - free(state.replacements); -} - -void ast_sroa(AST *tlc) { - for(int i = 0; i < tlc->chunk.varCount; i++) { - ScopeItem *si = tlc->chunk.vars[i]; - - if(!is_sroa_candidate(si)) { - continue; - } - - if(is_scopeitem_referenced(tlc, si)) { - continue; - } - - ast_decompose_automatic_record(tlc, si); - - memmove(tlc->chunk.vars + i, tlc->chunk.vars + i + 1, sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount - i - 1)); - tlc->chunk.varCount--; - - /* Restart */ - i = -1; - } -} - -static void ast_segmented_dereference_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { - if(tlc != ud) { - return; - } - - AST *n = *aptr; - if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_DEREF && type_is_segmented_pointer(n->exprUnOp.operand->expression.type)) { - static size_t idx = 0; - - AST *v; - if(n->exprUnOp.operand->nodeKind == AST_EXPR_VAR) - v = n->exprUnOp.operand; - else - v = varify(tlc, chunk, &stmtPrev, stmt, n->exprUnOp.operand); - - ScopeItem *si = calloc(1, sizeof(*si)); - si->kind = SCOPEITEM_VAR; - si->type = primitive_parse("u16"); - si->data.var.preclassed = true; - si->data.var.registerClass = REG_CLASS_DATASEGS; - si->data.var.precolored = true; - si->data.var.color = 0; - si->data.var.name = malp("$segtemp_%lu", idx++); - ast_tlc_add_var(tlc, si); - - ASTExprVar *ev = calloc(1, sizeof(*ev)); - ev->nodeKind = AST_EXPR_VAR; - ev->type = si->type; - ev->thing = si; - - ASTExprDot *edseg = calloc(1, sizeof(*edseg)); - edseg->type = n->exprUnOp.operand->expression.type->record.fieldTypes[0]; - edseg->nodeKind = AST_EXPR_DOT; - edseg->a = v; - edseg->b = strdup("segment"); - - ASTStmtAssign *ass = calloc(1, sizeof(*ass)); - ass->nodeKind = AST_STMT_ASSIGN; - ass->what = (AST*) ev; - ass->to = (AST*) edseg; - ass->next = (AST*) stmt; - if(stmtPrev) - stmtPrev->statement.next = (AST*) ass; - else - chunk->chunk.statementFirst = (AST*) ass; - stmtPrev = (AST*) ass; - - ASTExprDot *ed = calloc(1, sizeof(*ed)); - ed->type = n->exprUnOp.operand->expression.type->record.fieldTypes[1]; - ed->nodeKind = AST_EXPR_DOT; - ed->a = ast_deep_copy(v); - ed->b = strdup("offset"); - - n->exprUnOp.operand = (AST*) ed; - } -} - -void ast_segmented_dereference(AST *tlc) { - generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, ast_segmented_dereference_visitor, NULL); -} - -static void ast_patch_in_chunk(AST *chunkOuter, AST *stmtBefore, AST *chunkInner, AST *stmtAfter) { - if(chunkInner->chunk.statementFirst) { - stmtBefore->statement.next = chunkInner->chunk.statementFirst; - - for(AST *z = chunkInner->chunk.statementFirst; z; z = z->statement.next) { - if(!z->statement.next) { - z->statement.next = stmtAfter; - break; - } - } - } else { - stmtBefore->statement.next = stmtAfter; - } -} - -#define LOOPSTACKSIZE 64 -struct LinearizeState { - size_t currentDepth; - size_t loopStackStart[LOOPSTACKSIZE]; - size_t loopStackEnd[LOOPSTACKSIZE]; - AST *loopAfters[LOOPSTACKSIZE]; -}; -static void ast_linearize_visitor_pre(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { - static size_t nextLabelIdx = 0; - - struct LinearizeState *state = ud; - - AST *a = *aptr; - - if(a->nodeKind == AST_STMT_IF) { - if(a->stmtIf.elss == NULL) { - ASTExprUnaryOp *notcond = calloc(1, sizeof(*notcond)); - notcond->nodeKind = AST_EXPR_UNARY_OP; - notcond->operator = UNOP_NOT; - notcond->operand = a->stmtIf.expression; - notcond->type = a->stmtIf.expression->expression.type; - - ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump)); - jump->nodeKind = AST_STMT_JUMP; - jump->condition = (AST*) notcond; - jump->label = malp("$Lin%lu", nextLabelIdx++); - - ASTStmtLabel *label = calloc(1, sizeof(ASTStmtLabel)); - label->nodeKind = AST_STMT_LABEL; - label->name = strdup(jump->label); - - if(stmtPrev) { - stmtPrev->statement.next = (AST*) jump; - } else { - chunk->chunk.statementFirst = (AST*) jump; - } - - ast_patch_in_chunk(chunk, (AST*) jump, a->stmtIf.then, (AST*) label); - - label->next = a->statement.next; - } else { - ASTExprUnaryOp *notcond = calloc(1, sizeof(*notcond)); - notcond->nodeKind = AST_EXPR_UNARY_OP; - notcond->operator = UNOP_NOT; - notcond->operand = a->stmtIf.expression; - notcond->type = a->stmtIf.expression->expression.type; - - ASTStmtJump *jump2Else = calloc(1, sizeof(ASTStmtJump)); - jump2Else->nodeKind = AST_STMT_JUMP; - jump2Else->condition = (AST*) notcond; - jump2Else->label = malp("$Lin%lu", nextLabelIdx++); - - ASTStmtJump *jump2End = calloc(1, sizeof(ASTStmtJump)); - jump2End->nodeKind = AST_STMT_JUMP; - jump2End->label = malp("$Lin%lu", nextLabelIdx++); - - ASTStmtLabel *labelElse = calloc(1, sizeof(ASTStmtLabel)); - labelElse->nodeKind = AST_STMT_LABEL; - labelElse->name = strdup(jump2Else->label); - - ASTStmtLabel *labelEnd = calloc(1, sizeof(ASTStmtLabel)); - labelEnd->nodeKind = AST_STMT_LABEL; - labelEnd->name = strdup(jump2End->label); - - if(stmtPrev) { - stmtPrev->statement.next = (AST*) jump2Else; - } else { - chunk->chunk.statementFirst = (AST*) jump2Else; - } - - ast_patch_in_chunk(chunk, (AST*) jump2Else, a->stmtIf.then, (AST*) jump2End); - - jump2End->next = (AST*) labelElse; - - ast_patch_in_chunk(chunk, (AST*) labelElse, a->stmtIf.elss, (AST*) labelEnd); - - labelEnd->next = a->statement.next; - } - } else if(a->nodeKind == AST_STMT_LOOP) { - size_t startIdx = nextLabelIdx++; - size_t endIdx = nextLabelIdx++; - - ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump)); - jump->nodeKind = AST_STMT_JUMP; - jump->condition = NULL; - jump->label = malp("$Lin%lu", startIdx); - - ASTStmtLabel *startLabel = calloc(1, sizeof(ASTStmtLabel)); - startLabel->nodeKind = AST_STMT_LABEL; - startLabel->name = strdup(jump->label); - - ASTStmtLabel *endLabel = calloc(1, sizeof(ASTStmtLabel)); - endLabel->nodeKind = AST_STMT_LABEL; - endLabel->name = malp("$Lin%lu", endIdx); - - if(stmtPrev) { - stmtPrev->statement.next = (AST*) startLabel; - } else { - chunk->chunk.statementFirst = (AST*) startLabel; - } - - ast_patch_in_chunk(chunk, (AST*) startLabel, a->stmtLoop.body, (AST*) jump); - - jump->next = (AST*) endLabel; - endLabel->next = a->statement.next; - - state->currentDepth++; - state->loopStackStart[state->currentDepth - 1] = startIdx; - state->loopStackEnd[state->currentDepth - 1] = endIdx; - state->loopAfters[state->currentDepth - 1] = (AST*) endLabel; - } else if(a->nodeKind == AST_STMT_BREAK) { - ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump)); - jump->nodeKind = AST_STMT_JUMP; - jump->condition = NULL; - jump->label = malp("$Lin%lu", state->loopStackEnd[state->currentDepth - 1]); - jump->next = a->statement.next; - *aptr = (AST*) jump; - } else if(a->nodeKind == AST_STMT_CONTINUE) { - ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump)); - jump->nodeKind = AST_STMT_JUMP; - jump->condition = NULL; - jump->label = malp("$Lin%lu", state->loopStackStart[state->currentDepth - 1]); - jump->next = a->statement.next; - *aptr = (AST*) jump; - } -} -static void ast_linearize_visitor_post(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { - struct LinearizeState *state = ud; - - AST *a = *aptr; - - if(state->currentDepth && state->loopAfters[state->currentDepth - 1] == a) { - state->currentDepth--; - } -} -void ast_linearize(AST *tlc) { - struct LinearizeState state = {}; - generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, ast_linearize_visitor_pre, ast_linearize_visitor_post); -} diff --git a/src/ast/ast.c b/src/ast/ast.c new file mode 100644 index 0000000..bcdc3ba --- /dev/null +++ b/src/ast/ast.c @@ -0,0 +1,269 @@ +#include"ast.h" + +#include +#include +#include +#include +#include +#include"ntc.h" +#include"reporting.h" +#include"utils.h" +#include"x86/arch.h" + +const char *AST_KIND_STR[] = { AST_KINDS(GEN_STRI) }; + +void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud, GenericVisitorHandler preHandler, GenericVisitorHandler postHandler) { + if(preHandler) preHandler(nptr, stmt, stmtPrev, chu, tlc, ud); + + AST *n = *nptr; + + if(n->nodeKind == AST_CHUNK) { + AST *sPrev = NULL; + AST **s = &n->chunk.statementFirst; + while(*s) { + generic_visitor(s, *s, sPrev, n, tlc, ud, preHandler, postHandler); + + sPrev = *s; + s = &sPrev->statement.next; + } + } else if(n->nodeKind == AST_STMT_ASSIGN) { + generic_visitor(&n->stmtAssign.what, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + if(n->stmtAssign.to) { + generic_visitor(&n->stmtAssign.to, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } + } else if(n->nodeKind == AST_STMT_IF) { + generic_visitor(&n->stmtIf.expression, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + generic_visitor(&n->stmtIf.then, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + if(n->stmtIf.elss) { + generic_visitor(&n->stmtIf.elss, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } + } else if(n->nodeKind == AST_STMT_LOOP) { + generic_visitor(&n->stmtLoop.body, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } else if(n->nodeKind == AST_STMT_BREAK) { + } else if(n->nodeKind == AST_STMT_CONTINUE) { + } else if(n->nodeKind == AST_STMT_EXT_ALIGN) { + } else if(n->nodeKind == AST_STMT_DECL) { + if(n->stmtDecl.expression) { + generic_visitor(&n->stmtDecl.expression, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } + } else if(n->nodeKind == AST_STMT_EXPR) { + generic_visitor(&n->stmtExpr.expr, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } else if(n->nodeKind == AST_STMT_EXT_ORG) { + } else if(n->nodeKind == AST_STMT_EXT_SECTION) { + } else if(n->nodeKind == AST_STMT_RETURN) { + if(n->stmtReturn.val) { + generic_visitor(&n->stmtReturn.val, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } + } else if(n->nodeKind == AST_EXPR_BINARY_OP) { + generic_visitor(&n->exprBinOp.operands[0], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + generic_visitor(&n->exprBinOp.operands[1], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } else if(n->nodeKind == AST_EXPR_CALL) { + generic_visitor(&n->exprCall.what, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + + for(size_t i = 0; i < n->exprCall.what->expression.type->pointer.of->function.argCount; i++) { + generic_visitor(&n->exprCall.args[i], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } + } else if(n->nodeKind == AST_EXPR_CAST) { + generic_visitor(&n->exprCast.what, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } else if(n->nodeKind == AST_EXPR_FUNC) { + generic_visitor(&n->exprFunc.chunk, NULL, NULL, n->exprFunc.chunk, n->exprFunc.chunk, ud, preHandler, postHandler); + } else if(n->nodeKind == AST_EXPR_UNARY_OP) { + generic_visitor(&n->exprUnOp.operand, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } else if(n->nodeKind == AST_EXPR_VAR) { + } else if(n->nodeKind == AST_EXPR_STACK_POINTER) { + } else if(n->nodeKind == AST_EXPR_PRIMITIVE) { + } else if(n->nodeKind == AST_EXPR_STRING_LITERAL) { + } else if(n->nodeKind == AST_EXPR_ARRAY) { + assert(n->expression.type->type == TYPE_TYPE_ARRAY); + assert(n->expression.type->array.length != 0); + + for(size_t i = 0; i < n->expression.type->array.length; i++) { + generic_visitor(&n->exprArray.items[i], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } + } else if(n->nodeKind == AST_EXPR_EXT_SALLOC) { + } else if(n->nodeKind == AST_EXPR_DOT) { + generic_visitor(&n->exprDot.a, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } else if(n->nodeKind == AST_EXPR_EXT_SIZEOF) { + if(n->exprExtSizeOf.ofExpr) { + generic_visitor(&n->exprExtSizeOf.ofExpr, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } + } else if(n->nodeKind == AST_STMT_JUMP) { + if(n->stmtJump.condition) { + generic_visitor(&n->stmtJump.condition, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler); + } + } else if(n->nodeKind == AST_STMT_LABEL) { + } else { + stahp_node(n, "generic_visitor: unhandled %s", AST_KIND_STR[n->nodeKind]); + } + + if(postHandler) postHandler(nptr, stmt, stmtPrev, chu, tlc, ud); +} + +int ast_expression_equal(AST *a, AST *b) { + if(a->nodeKind != b->nodeKind) return 0; + + if(a->nodeKind == AST_EXPR_PRIMITIVE) { + return a->exprPrim.val == b->exprPrim.val; + } else if(a->nodeKind == AST_EXPR_VAR) { + return a->exprVar.thing == b->exprVar.thing; + } else if(a->nodeKind == AST_EXPR_UNARY_OP) { + 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; + } else if(a->nodeKind == AST_EXPR_CAST) { + return ast_expression_equal(a->exprCast.what, b->exprCast.what) && type_equal(a->exprCast.to, b->exprCast.to) && a->exprCast.reinterpretation == b->exprCast.reinterpretation; + } else { + stahp_node(a, "ast_expression_equal: unhandled %s", AST_KIND_STR[a->nodeKind]); + } +} + +// This function may return three values: YES (1), NO (0) or UNKNOWN (-1). +// ... Ew +int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) { + const AST *s = chunk->chunk.statementFirst; + + while(1) { + if(s && s->nodeKind == AST_STMT_LOOP) { + int i = ast_stmt_is_after(s->stmtLoop.body, s1, s2); + if(i == 1 || (i == 0 && s1 != NULL)) { + return i; + } + } + + if(s == s1) { + return 0; + } + if(s == 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 || (i == 0 && s1 != NULL)) { + return i; + } + + if(s->stmtIf.elss) { + i = ast_stmt_is_after(s->stmtIf.elss, s1, s2); + if(i == 1 || (i == 0 && s1 != NULL)) { + return i; + } + } + } + + s = s->statement.next; + } + + return -1; +} + +AST *ast_get_label_by_name(AST *tlc, const char *name) { + for(AST *s = tlc->chunk.statementFirst; s; s = s->statement.next) { + if(s->nodeKind == AST_STMT_LABEL && !strcmp(s->stmtLabel.name, name)) { + return s; + } + } + + stahp(0, 0, "Label %s not found", name); +} + +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(); +} + +AST *ast_cast_expr(AST *what, Type *to) { + if(what == NULL) { + stahp(0, 0, "NULL what passed to ast_cast_expr"); + goto fail; + } + + /* Only exists at parse-time, hence not part of type system and is handled separately */ + if(what->nodeKind == AST_EXPR_STRING_LITERAL) { + if(to->type == TYPE_TYPE_ARRAY && type_equal(primitive_parse("u8"), to->array.of) && (to->array.length == what->exprStrLit.length || to->array.length == -1)) { + ASTExprArray *ret = calloc(1, sizeof(*ret)); + ret->nodeKind = AST_EXPR_ARRAY; + ret->items = calloc(what->exprStrLit.length, sizeof(*ret->items)); + ret->type = to; + + // If this declaration is of an unknown-length array type (eg u8[?]) then update the length + if(to->array.length == -1) { + to->array.length = what->exprStrLit.length; + } + + for(int i = 0; i < what->exprStrLit.length; i++) { + uint8_t bajt = what->exprStrLit.data[i]; + + ASTExprPrimitive *item = calloc(1, sizeof(*item)); + item->nodeKind = AST_EXPR_PRIMITIVE; + item->type = to->array.of; + item->val = bajt; + + ret->items[i] = (AST*) item; + } + + return (AST*) ret; + } else if(to->type == TYPE_TYPE_PRIMITIVE) { + if(to->primitive.width != what->exprStrLit.length * 8) { + stahp_node(what, "Size mismatch between string literal and target type"); + } + + ASTExprPrimitive *ret = calloc(1, sizeof(*ret)); + ret->nodeKind = AST_EXPR_PRIMITIVE; + ret->type = to; + memcpy(&ret->val, what->exprStrLit.data, sizeof(ret->val)); + return (AST*) ret; + } else abort(); + } + + // Make sure an unparametrized generic int parameter hasn't sneaked its way in + while(what->nodeKind == AST_EXPR_VAR && what->exprVar.thing->kind == SCOPEITEM_CEXPR && what->exprVar.thing->data.cexpr.concrete) { + what = what->exprVar.thing->data.cexpr.concrete; + } + assert(!(what->nodeKind == AST_EXPR_VAR && what->exprVar.thing->kind == SCOPEITEM_CEXPR)); + + if(type_equal(what->expression.type, to)) return what; + + if(!type_is_castable(what->expression.type, to)) { + goto fail; + } + + if(what->nodeKind == AST_EXPR_PRIMITIVE && (to->type == TYPE_TYPE_PRIMITIVE || to->type == TYPE_TYPE_POINTER)) { + ASTExprPrimitive *ret = calloc(1, sizeof(*ret)); + ret->nodeKind = AST_EXPR_PRIMITIVE; + ret->type = to; + + if(to->type == TYPE_TYPE_PRIMITIVE) { + ret->val = what->exprPrim.val & ((1UL << to->primitive.width) - 1); + } else { + ret->val = what->exprPrim.val & ((1UL << (8 * type_size(to))) - 1); + } + + return (AST*) ret; + } else { + ASTExprCast *ret = calloc(1, sizeof(*ret)); + ret->nodeKind = AST_EXPR_CAST; + ret->type = to; + ret->what = what; + ret->to = to; + return (AST*) ret; + } + +fail: + stahp_node(what, "Cannot cast type %s into %s", type_to_string(what->expression.type), type_to_string(to)); +} diff --git a/src/ast.h b/src/ast/ast.h similarity index 94% rename from src/ast.h rename to src/ast/ast.h index d13984b..7c1cbec 100644 --- a/src/ast.h +++ b/src/ast/ast.h @@ -370,28 +370,20 @@ 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*); AST *ast_cast_expr(AST *what, Type *to); -void ast_spill_to_stack(AST *tlc, ScopeItem *vte); - void ast_typecheck(AST *tlc); -void ast_grow_stack_frame(AST *tlc, size_t bytes); +AST *ast_get_label_by_name(AST *tlc, const char *name); -void ast_commutativity_pass(AST *tlc); - -void ast_sroa(AST *tlc); - -// Convert segmented derefences like *x into segment move and *x.offset. -// Must be done before ast_sroa. -void ast_segmented_dereference(AST *tlc); - -void ast_linearize(AST *tlc); +#include"dump.h" +#include"stack.h" +#include"desegment.h" +#include"sroa.h" +#include"linearize.h" +#include"usedef.h" +#include"commutativity.h" #endif diff --git a/src/ast/commutativity.c b/src/ast/commutativity.c new file mode 100644 index 0000000..ff1d316 --- /dev/null +++ b/src/ast/commutativity.c @@ -0,0 +1,21 @@ +#include"commutativity.h" + +#include"ast.h" + +static void commutativity_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { + AST *n = *nptr; + + if(n->nodeKind == AST_EXPR_BINARY_OP) { + if(binop_is_commutative(n->exprBinOp.operator)) { + if(n->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[1]->nodeKind != AST_EXPR_PRIMITIVE) { + AST *tmp = n->exprBinOp.operands[0]; + n->exprBinOp.operands[0] = n->exprBinOp.operands[1]; + n->exprBinOp.operands[1] = tmp; + } + } + } +} +void ast_commutativity_pass(AST *tlc) { + generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, NULL, commutativity_visitor); +} + diff --git a/src/ast/commutativity.h b/src/ast/commutativity.h new file mode 100644 index 0000000..1fd72b6 --- /dev/null +++ b/src/ast/commutativity.h @@ -0,0 +1,8 @@ +#pragma once + +union AST; + +// This pass makes sure that integer literals are always operand 1 +// (the right operand) in a commutative binary operation, +// simplifying passes further down. +void ast_commutativity_pass(union AST *chu); diff --git a/src/ast/desegment.c b/src/ast/desegment.c new file mode 100644 index 0000000..f124ebf --- /dev/null +++ b/src/ast/desegment.c @@ -0,0 +1,117 @@ +#include"desegment.h" + +#include"ast.h" +#include"ntc.h" + +#include"x86/arch.h" + +#include"utils.h" +#include + +static void ast_tlc_add_var(AST *tlc, ScopeItem *si) { + tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * ++tlc->chunk.varCount); + tlc->chunk.vars[tlc->chunk.varCount - 1] = si; +} + +/* Split away complex expression into a new local variable */ +static AST *varify(AST *tlc, AST *chunk, AST **stmtPrev, AST *stmt, AST *e) { + static size_t idx = 0; + + ScopeItem *vte = calloc(1, sizeof(*vte)); + vte->kind = SCOPEITEM_VAR; + vte->type = e->expression.type; + vte->data.var.name = malp("$varify_%lu", idx++); + + ast_tlc_add_var(tlc, vte); + + // Alter AST + + ASTExprVar *ev[2]; + for(int i = 0; i < 2; i++) { + ev[i] = calloc(1, sizeof(ASTExprVar)); + ev[i]->nodeKind = AST_EXPR_VAR; + ev[i]->type = e->expression.type; + ev[i]->thing = vte; + } + + ASTStmtAssign *assign = calloc(1, sizeof(*assign)); + assign->nodeKind = AST_STMT_ASSIGN; + assign->what = (AST*) ev[0]; + assign->to = e; + + if(*stmtPrev) { + (*stmtPrev)->statement.next = (AST*) assign; + } else { + chunk->chunk.statementFirst = (AST*) assign; + } + assign->next = stmt; + *stmtPrev = (AST*) assign; + + // Reset ud-chain + + vte->data.var.usedefFirst = NULL; + vte->data.var.usedefLast = NULL; + + return (AST*) ev[1]; +} + +static void ast_segmented_dereference_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { + if(tlc != ud) { + return; + } + + AST *n = *aptr; + if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_DEREF && type_is_segmented_pointer(n->exprUnOp.operand->expression.type)) { + static size_t idx = 0; + + AST *v; + if(n->exprUnOp.operand->nodeKind == AST_EXPR_VAR) + v = n->exprUnOp.operand; + else + v = varify(tlc, chunk, &stmtPrev, stmt, n->exprUnOp.operand); + + ScopeItem *si = calloc(1, sizeof(*si)); + si->kind = SCOPEITEM_VAR; + si->type = primitive_parse("u16"); + si->data.var.preclassed = true; + si->data.var.registerClass = REG_CLASS_DATASEGS; + si->data.var.precolored = true; + si->data.var.color = 0; + si->data.var.name = malp("$segtemp_%lu", idx++); + ast_tlc_add_var(tlc, si); + + ASTExprVar *ev = calloc(1, sizeof(*ev)); + ev->nodeKind = AST_EXPR_VAR; + ev->type = si->type; + ev->thing = si; + + ASTExprDot *edseg = calloc(1, sizeof(*edseg)); + edseg->type = n->exprUnOp.operand->expression.type->record.fieldTypes[0]; + edseg->nodeKind = AST_EXPR_DOT; + edseg->a = v; + edseg->b = strdup("segment"); + + ASTStmtAssign *ass = calloc(1, sizeof(*ass)); + ass->nodeKind = AST_STMT_ASSIGN; + ass->what = (AST*) ev; + ass->to = (AST*) edseg; + ass->next = (AST*) stmt; + if(stmtPrev) + stmtPrev->statement.next = (AST*) ass; + else + chunk->chunk.statementFirst = (AST*) ass; + stmtPrev = (AST*) ass; + + ASTExprDot *ed = calloc(1, sizeof(*ed)); + ed->type = n->exprUnOp.operand->expression.type->record.fieldTypes[1]; + ed->nodeKind = AST_EXPR_DOT; + ed->a = ast_deep_copy(v); + ed->b = strdup("offset"); + + n->exprUnOp.operand = (AST*) ed; + } +} + +void ast_segmented_dereference(AST *tlc) { + generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, ast_segmented_dereference_visitor, NULL); +} diff --git a/src/ast/desegment.h b/src/ast/desegment.h new file mode 100644 index 0000000..644ab3b --- /dev/null +++ b/src/ast/desegment.h @@ -0,0 +1,7 @@ +#pragma once + +union AST; + +// Convert segmented derefences like *x into segment move and *x.offset. +// Must be done before ast_sroa. +void ast_segmented_dereference(union AST *tlc); diff --git a/src/ast/dump.c b/src/ast/dump.c new file mode 100644 index 0000000..bae8a8b --- /dev/null +++ b/src/ast/dump.c @@ -0,0 +1,327 @@ +#include"dump.h" + +#include"ast.h" +#include"types.h" +#include +#include +#include +#include"utils.h" + +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; + } else if(t->type == TYPE_TYPE_RECORD) { + return malp("%s", t->record.name); + } else if(t->type == TYPE_TYPE_GENERIC) { + return malp("%s", t->generic.paramName); + } else if(t->type == TYPE_TYPE_ARRAY) { + char *of = type_to_string(t->array.of); + char *len = NULL; + if(t->array.lengthIsGeneric) { + len = malp(""); + } else { + len = malp("%li", t->array.length); + } + char *r = malp("%s[%s]", of, len); + free(of); + free(len); + return r; + } + + return strdup("@unimp"); +} + +static char *ast_dumpe(AST *tlc, AST *e) { + if(!e) { + return malp("(null)"); + } + + if(e->nodeKind == AST_EXPR_PRIMITIVE) { + return malp("%i", e->exprPrim.val); + } else if(e->nodeKind == AST_EXPR_VAR) { + ScopeItem *vte = e->exprVar.thing; + + if(vte->kind == SCOPEITEM_VAR) { + return strdup(vte->data.var.name); + } else if(vte->kind == SCOPEITEM_SYMBOL) { + return strdup(vte->data.symbol.name); + } else abort(); + } else if(e->nodeKind == AST_EXPR_UNARY_OP) { + const char *op = NULL; + 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; + case UNOP_NOT: + op = "!"; + break; + default: + abort(); + } + char *c = ast_dumpe(tlc, 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(tlc, e->exprBinOp.operands[0]); + char *b = ast_dumpe(tlc, 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; + case BINOP_NEQUAL: + op = "!="; + break; + case BINOP_LESS: + op = "<"; + break; + case BINOP_GREATER: + op = ">"; + break; + case BINOP_LEQUAL: + op = "<="; + break; + case BINOP_GEQUAL: + op = ">="; + break; + case BINOP_MULHI: + op = "*^"; + break; + default: + abort(); + } + char *r = malp("(%s %s %s)", a, op, b); + free(a); + free(b); + return r; + } else if(e->nodeKind == AST_EXPR_STACK_POINTER) { + return malp("@stack"); + } else if(e->nodeKind == AST_EXPR_FUNC) { + char *out = NULL; + + if(type_is_generic(e->expression.type)) { + out = malp("(generic)"); + return out; + } + + { + char *rettype = type_to_string(e->expression.type->function.ret); + out = malp("%s(", rettype); + free(rettype); + } + + for(int i = 0; i < e->expression.type->function.argCount; i++) { + char *argtype = type_to_string(e->expression.type->function.args[i]); + char *out2 = malp(i == e->expression.type->function.argCount - 1 ? "%s%s" : "%s%s, ", out, argtype); + free(out); + free(argtype); + out = out2; + } + + { + char *choonk = ast_dumpc(tlc, e->exprFunc.chunk); + char *out2 = malp("%s) {\n%s}", out, choonk); + free(out); + free(choonk); + out = out2; + } + + return out; + } else if(e->nodeKind == AST_EXPR_CALL) { + char *w = ast_dumpe(tlc, e->exprCall.what); + char *out = malp("%s(", w); + free(w); + size_t argCount = e->exprCall.what->expression.type->pointer.of->function.argCount; + for(size_t i = 0; i < argCount; i++) { + char *a = ast_dumpe(tlc, e->exprCall.args[i]); + char *out2 = malp(i == argCount - 1 ? "%s%s)" : "%s%s, ", out, a); + free(a); + free(out); + out = out2; + } + return out; + } else if(e->nodeKind == AST_EXPR_EXT_SALLOC) { + char *w = type_to_string(e->exprExtSalloc.size); + char *out = malp("@salloc(%s)", w); + free(w); + return out; + } else if(e->nodeKind == AST_EXPR_CAST) { + char *a = ast_dumpe(tlc, e->exprCast.what); + char *b = type_to_string(e->exprCast.to); + char *out = malp("(%s as %s)", a, b); + free(a); + free(b); + return out; + } else if(e->nodeKind == AST_EXPR_DOT) { + char *a = ast_dumpe(tlc, e->exprDot.a); + char *out = malp(e->nodeKind == AST_EXPR_BINARY_OP ? "(%s).%s" : "%s.%s", a, e->exprDot.b); + free(a); + return out; + } + + return malp("@unimp:%s", AST_KIND_STR[e->nodeKind]); +} + +static char *ast_dumps(AST *tlc, AST *s) { + if(s->nodeKind == AST_STMT_DECL) { + ScopeItem *vte = s->stmtDecl.thing; + + if(vte->kind == SCOPEITEM_SYMBOL) { + char *t = type_to_string(vte->type); + char *e = s->stmtDecl.expression ? ast_dumpe(tlc, s->stmtDecl.expression) : strdup(""); + char *r = malp("%s%s %s: %s;", vte->data.symbol.isExternal ? "external " : "", t, vte->data.symbol.name, e); + free(t); + free(e); + return r; + } + } else if(s->nodeKind == AST_STMT_ASSIGN) { + if(s->stmtAssign.to) { + char *a = ast_dumpe(tlc, s->stmtAssign.what); + char *b = ast_dumpe(tlc, s->stmtAssign.to); + char *r = malp("%s = %s;", a, b); + free(a); + free(b); + return r; + } else { + char *a = ast_dumpe(tlc, s->stmtAssign.what); + char *r = malp("%s = ; /* fake def */", a); + free(a); + return r; + } + } else if(s->nodeKind == AST_STMT_JUMP) { + char *a = ast_dumpe(tlc, s->stmtJump.condition); + char *r = malp("jump %s if %s;", s->stmtJump.label, a); + free(a); + return r; + } else if(s->nodeKind == AST_STMT_LABEL) { + return malp("@label %s;", s->stmtLabel.name); + } else if(s->nodeKind == AST_STMT_LOOP) { + char *inner = ast_dumpc(tlc, s->stmtLoop.body); + char *c = malp("loop {\n%s}", inner); + free(inner); + return c; + } else if(s->nodeKind == AST_STMT_IF) { + char *cond = ast_dumpe(tlc, s->stmtIf.expression); + char *inner = ast_dumpc(tlc, s->stmtIf.then); + char *elss = s->stmtIf.elss ? ast_dumpc(tlc, s->stmtIf.elss) : NULL; + char *c; + if(elss) { + c = malp("if(%s) {\n%s} else {\n%s}", cond, inner, elss); + free(elss); + } else { + c = malp("if(%s) {\n%s}", cond, inner); + } + free(cond); + free(inner); + return c; + } else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_VAR) { + const char *name; + + if(s->stmtExpr.expr->exprVar.thing->kind == SCOPEITEM_VAR) { + name = s->stmtExpr.expr->exprVar.thing->data.var.name; + } else { + name = s->stmtExpr.expr->exprVar.thing->data.symbol.name; + } + + return malp("%s; /* loop guard */", name); + } else if(s->nodeKind == AST_STMT_EXPR) { + return ast_dumpe(tlc, s->stmtExpr.expr); + } else if(s->nodeKind == AST_STMT_RETURN) { + if(s->stmtReturn.val) { + char *e = ast_dumpe(tlc, s->stmtReturn.val); + char *c = malp("return %s;", e); + free(e); + return c; + } else { + return malp("return;"); + } + } + + return malp("@unimp:%s;", AST_KIND_STR[s->nodeKind]); +} + +static char *stmt_live_range_dbgs(AST *tlc, AST *stmt) { + char *ret = malp(""); + for(size_t v = 0; v < tlc->chunk.varCount; v++) { + ScopeItem *si = tlc->chunk.vars[v]; + + if(si->data.var.liveRangeStart == stmt) { + char *ret2 = malp("%s (%s start)", ret, si->data.var.name); + free(ret); + ret = ret2; + } + + if(si->data.var.liveRangeEnd == stmt) { + char *ret2 = malp("%s (%s end)", ret, si->data.var.name); + free(ret); + ret = ret2; + } + } + return ret; +} + +char *ast_dumpc(AST *tlc, AST *chu) { + AST *stmt = chu->chunk.statementFirst; + + char *ret = malp(""); + + while(stmt) { + char *new = ast_dumps(tlc, stmt); + char *users = stmt_live_range_dbgs(tlc, stmt); + char *ret2 = malp("%s%s%s\n", ret, new, users); + free(ret); + free(new); + free(users); + ret = ret2; + + stmt = stmt->statement.next; + } + + return ret; +} + +char *ast_dump(AST *tlc) { + return ast_dumpc(tlc, tlc); +} diff --git a/src/ast/dump.h b/src/ast/dump.h new file mode 100644 index 0000000..a7ddd73 --- /dev/null +++ b/src/ast/dump.h @@ -0,0 +1,5 @@ +#pragma once + +union AST; +char *ast_dump(union AST *chu); +char *ast_dumpc(union AST *tlc, union AST *chu); diff --git a/src/ast/linearize.c b/src/ast/linearize.c new file mode 100644 index 0000000..2e1b4db --- /dev/null +++ b/src/ast/linearize.c @@ -0,0 +1,161 @@ +#include"linearize.h" + +#include"ast.h" +#include"ntc.h" +#include"utils.h" +#include + +static void ast_patch_in_chunk(AST *chunkOuter, AST *stmtBefore, AST *chunkInner, AST *stmtAfter) { + if(chunkInner->chunk.statementFirst) { + stmtBefore->statement.next = chunkInner->chunk.statementFirst; + + for(AST *z = chunkInner->chunk.statementFirst; z; z = z->statement.next) { + if(!z->statement.next) { + z->statement.next = stmtAfter; + break; + } + } + } else { + stmtBefore->statement.next = stmtAfter; + } +} + +#define LOOPSTACKSIZE 64 +struct LinearizeState { + size_t currentDepth; + size_t loopStackStart[LOOPSTACKSIZE]; + size_t loopStackEnd[LOOPSTACKSIZE]; + AST *loopAfters[LOOPSTACKSIZE]; +}; +static void ast_linearize_visitor_pre(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { + static size_t nextLabelIdx = 0; + + struct LinearizeState *state = ud; + + AST *a = *aptr; + + if(a->nodeKind == AST_STMT_IF) { + if(a->stmtIf.elss == NULL) { + ASTExprUnaryOp *notcond = calloc(1, sizeof(*notcond)); + notcond->nodeKind = AST_EXPR_UNARY_OP; + notcond->operator = UNOP_NOT; + notcond->operand = a->stmtIf.expression; + notcond->type = a->stmtIf.expression->expression.type; + + ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump)); + jump->nodeKind = AST_STMT_JUMP; + jump->condition = (AST*) notcond; + jump->label = malp("$Lin%lu", nextLabelIdx++); + + ASTStmtLabel *label = calloc(1, sizeof(ASTStmtLabel)); + label->nodeKind = AST_STMT_LABEL; + label->name = strdup(jump->label); + + if(stmtPrev) { + stmtPrev->statement.next = (AST*) jump; + } else { + chunk->chunk.statementFirst = (AST*) jump; + } + + ast_patch_in_chunk(chunk, (AST*) jump, a->stmtIf.then, (AST*) label); + + label->next = a->statement.next; + } else { + ASTExprUnaryOp *notcond = calloc(1, sizeof(*notcond)); + notcond->nodeKind = AST_EXPR_UNARY_OP; + notcond->operator = UNOP_NOT; + notcond->operand = a->stmtIf.expression; + notcond->type = a->stmtIf.expression->expression.type; + + ASTStmtJump *jump2Else = calloc(1, sizeof(ASTStmtJump)); + jump2Else->nodeKind = AST_STMT_JUMP; + jump2Else->condition = (AST*) notcond; + jump2Else->label = malp("$Lin%lu", nextLabelIdx++); + + ASTStmtJump *jump2End = calloc(1, sizeof(ASTStmtJump)); + jump2End->nodeKind = AST_STMT_JUMP; + jump2End->label = malp("$Lin%lu", nextLabelIdx++); + + ASTStmtLabel *labelElse = calloc(1, sizeof(ASTStmtLabel)); + labelElse->nodeKind = AST_STMT_LABEL; + labelElse->name = strdup(jump2Else->label); + + ASTStmtLabel *labelEnd = calloc(1, sizeof(ASTStmtLabel)); + labelEnd->nodeKind = AST_STMT_LABEL; + labelEnd->name = strdup(jump2End->label); + + if(stmtPrev) { + stmtPrev->statement.next = (AST*) jump2Else; + } else { + chunk->chunk.statementFirst = (AST*) jump2Else; + } + + ast_patch_in_chunk(chunk, (AST*) jump2Else, a->stmtIf.then, (AST*) jump2End); + + jump2End->next = (AST*) labelElse; + + ast_patch_in_chunk(chunk, (AST*) labelElse, a->stmtIf.elss, (AST*) labelEnd); + + labelEnd->next = a->statement.next; + } + } else if(a->nodeKind == AST_STMT_LOOP) { + size_t startIdx = nextLabelIdx++; + size_t endIdx = nextLabelIdx++; + + ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump)); + jump->nodeKind = AST_STMT_JUMP; + jump->condition = NULL; + jump->label = malp("$Lin%lu", startIdx); + + ASTStmtLabel *startLabel = calloc(1, sizeof(ASTStmtLabel)); + startLabel->nodeKind = AST_STMT_LABEL; + startLabel->name = strdup(jump->label); + + ASTStmtLabel *endLabel = calloc(1, sizeof(ASTStmtLabel)); + endLabel->nodeKind = AST_STMT_LABEL; + endLabel->name = malp("$Lin%lu", endIdx); + + if(stmtPrev) { + stmtPrev->statement.next = (AST*) startLabel; + } else { + chunk->chunk.statementFirst = (AST*) startLabel; + } + + ast_patch_in_chunk(chunk, (AST*) startLabel, a->stmtLoop.body, (AST*) jump); + + jump->next = (AST*) endLabel; + endLabel->next = a->statement.next; + + state->currentDepth++; + state->loopStackStart[state->currentDepth - 1] = startIdx; + state->loopStackEnd[state->currentDepth - 1] = endIdx; + state->loopAfters[state->currentDepth - 1] = (AST*) endLabel; + } else if(a->nodeKind == AST_STMT_BREAK) { + ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump)); + jump->nodeKind = AST_STMT_JUMP; + jump->condition = NULL; + jump->label = malp("$Lin%lu", state->loopStackEnd[state->currentDepth - 1]); + jump->next = a->statement.next; + *aptr = (AST*) jump; + } else if(a->nodeKind == AST_STMT_CONTINUE) { + ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump)); + jump->nodeKind = AST_STMT_JUMP; + jump->condition = NULL; + jump->label = malp("$Lin%lu", state->loopStackStart[state->currentDepth - 1]); + jump->next = a->statement.next; + *aptr = (AST*) jump; + } +} +static void ast_linearize_visitor_post(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { + struct LinearizeState *state = ud; + + AST *a = *aptr; + + if(state->currentDepth && state->loopAfters[state->currentDepth - 1] == a) { + state->currentDepth--; + } +} +void ast_linearize(AST *tlc) { + struct LinearizeState state = {}; + generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, ast_linearize_visitor_pre, ast_linearize_visitor_post); +} diff --git a/src/ast/linearize.h b/src/ast/linearize.h new file mode 100644 index 0000000..e509f40 --- /dev/null +++ b/src/ast/linearize.h @@ -0,0 +1,4 @@ +#pragma once + +union AST; +void ast_linearize(union AST *tlc); diff --git a/src/ast/sroa.c b/src/ast/sroa.c new file mode 100644 index 0000000..a599644 --- /dev/null +++ b/src/ast/sroa.c @@ -0,0 +1,104 @@ +#include"sroa.h" + +#include"ast.h" +#include"ntc.h" +#include"utils.h" +#include +#include + +static bool is_sroa_candidate(ScopeItem *si) { + return si->type->type == TYPE_TYPE_RECORD && type_size(si->type) <= ntc_get_int_default("sroa-threshold", 32); +} + +struct IsScopeItemReferenced { + bool yes; + ScopeItem *si; +}; +static void is_scopeitem_referenced_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { + struct IsScopeItemReferenced *state = ud; + + AST *n = *aptr; + if(n->nodeKind == AST_EXPR_UNARY_OP) { + if(n->exprUnOp.operand->nodeKind == AST_EXPR_VAR && n->exprUnOp.operator == UNOP_REF && n->exprUnOp.operand->exprVar.thing == state->si) { + state->yes = true; + } + } +} +static bool is_scopeitem_referenced(AST *tlc, ScopeItem *si) { + struct IsScopeItemReferenced state = {.si = si}; + generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, NULL, is_scopeitem_referenced_visitor); + return state.yes; +} + +struct DecomposeAutomaticRecordState { + AST *tlc; + ScopeItem *target; + ScopeItem **replacements; +}; +static void ast_decompose_automatic_record_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { + AST *n = *aptr; + + struct DecomposeAutomaticRecordState *state = ud; + + if(state->tlc != tlc) return; + + if(n->nodeKind == AST_EXPR_DOT && n->exprDot.a->nodeKind == AST_EXPR_VAR && n->exprDot.a->exprVar.thing == state->target) { + size_t idx; + for(idx = 0; strcmp(state->target->type->record.fieldNames[idx], n->exprDot.b); idx++); + + AST *ev = calloc(1, sizeof(ASTExprVar)); + ev->nodeKind = AST_EXPR_VAR; + ev->expression.type = state->target->type->record.fieldTypes[idx]; + ev->exprVar.thing = state->replacements[idx]; + + *aptr = (AST*) ev; + } +} +static void ast_decompose_automatic_record(AST *tlc, ScopeItem *target) { + assert(target->kind == SCOPEITEM_VAR); + assert(target->type->type == TYPE_TYPE_RECORD); + + struct DecomposeAutomaticRecordState state = { + .tlc = tlc, + .target = target, + .replacements = calloc(target->type->record.fieldCount, sizeof(ScopeItem)), + }; + + tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount + target->type->record.fieldCount)); + + for(size_t f = 0; f < target->type->record.fieldCount; f++) { + ScopeItem *si = calloc(1, sizeof(*si)); + si->kind = SCOPEITEM_VAR; + si->type = target->type->record.fieldTypes[f]; + si->data.var.name = malp("%s_sroa_%s", target->data.var.name, target->type->record.fieldNames[f]); + state.replacements[f] = si; + + tlc->chunk.vars[tlc->chunk.varCount++] = si; + } + + generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, ast_decompose_automatic_record_visitor, NULL); + + free(state.replacements); +} + +void ast_sroa(AST *tlc) { + for(int i = 0; i < tlc->chunk.varCount; i++) { + ScopeItem *si = tlc->chunk.vars[i]; + + if(!is_sroa_candidate(si)) { + continue; + } + + if(is_scopeitem_referenced(tlc, si)) { + continue; + } + + ast_decompose_automatic_record(tlc, si); + + memmove(tlc->chunk.vars + i, tlc->chunk.vars + i + 1, sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount - i - 1)); + tlc->chunk.varCount--; + + /* Restart */ + i = -1; + } +} diff --git a/src/ast/sroa.h b/src/ast/sroa.h new file mode 100644 index 0000000..ec2b880 --- /dev/null +++ b/src/ast/sroa.h @@ -0,0 +1,4 @@ +#pragma once + +union AST; +void ast_sroa(union AST *tlc); diff --git a/src/ast/stack.c b/src/ast/stack.c new file mode 100644 index 0000000..d0a8eea --- /dev/null +++ b/src/ast/stack.c @@ -0,0 +1,101 @@ +#include"stack.h" + +#include"ast.h" +#include"ntc.h" +#include"scope.h" +#include +#include + +struct Spill2StackState { + AST *targetTLC; + ScopeItem *target; // can be NULL + size_t stackGrowth; +}; +static void spill2stack_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { + struct Spill2StackState *this = ud; + + if(tlc != this->targetTLC) { + // Don't do anything. + return; + } + + AST *a = *aptr; + + if(a == tlc) { + a->chunk.stackReservation += this->stackGrowth; + } else if(a->nodeKind == AST_EXPR_VAR) { + + if(a->exprVar.thing == this->target) { + // DO THE SPILL + + ASTExprStackPointer *rsp = calloc(1, sizeof(*rsp)); + rsp->nodeKind = AST_EXPR_STACK_POINTER; + rsp->type = primitive_parse("u32"); + + ASTExprPrimitive *offset = calloc(1, sizeof(*offset)); + offset->nodeKind = AST_EXPR_PRIMITIVE; + offset->type = rsp->type; + offset->val = -this->stackGrowth; // This will be affected by the other part of this pass, so we must reverse + offset->stackGrowth = true; + + ASTExprBinaryOp *bop = calloc(1, sizeof(*bop)); + bop->nodeKind = AST_EXPR_BINARY_OP; + bop->type = rsp->type; + bop->operator = BINOP_ADD; + bop->operands[0] = (AST*) rsp; + bop->operands[1] = (AST*) offset; + + ASTExprUnaryOp *deref = calloc(1, sizeof(*deref)); + deref->nodeKind = AST_EXPR_UNARY_OP; + deref->type = a->expression.type; + deref->operator = UNOP_DEREF; + deref->operand = (AST*) bop; + + *aptr = (AST*) deref; + } + + } else if(a->nodeKind == AST_EXPR_PRIMITIVE && a->exprPrim.stackGrowth) { + + // Guaranteed to not require more dumbification + a->exprPrim.val += this->stackGrowth; + + } +} + +void ast_spill_to_stack(AST *tlc, ScopeItem *vte) { + assert(tlc->nodeKind == AST_CHUNK); + assert(vte != NULL); + assert(vte->kind == SCOPEITEM_VAR); + + if(ntc_get_int("pdbg")) { + fprintf(stderr, "### SPILLING %s ###\n", vte->data.var.name); + } + + struct Spill2StackState state; + memset(&state, 0, sizeof(state)); + state.target = vte; + state.targetTLC = tlc; + state.stackGrowth = (type_size(vte->type) + 7) & ~7; + + generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, spill2stack_visitor, NULL); + + size_t vteIndex = 0; + while(tlc->chunk.vars[vteIndex] != vte) vteIndex++; + + memmove(&tlc->chunk.vars[vteIndex], &tlc->chunk.vars[vteIndex + 1], sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount - vteIndex - 1)); + tlc->chunk.varCount--; +} + +void ast_grow_stack_frame(AST *tlc, size_t bytes) { + if(bytes == 0) { + return; + } + + struct Spill2StackState state; + memset(&state, 0, sizeof(state)); + state.target = NULL; // spill nothing + state.targetTLC = tlc; + state.stackGrowth = bytes; + + generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, spill2stack_visitor, NULL); +} diff --git a/src/ast/stack.h b/src/ast/stack.h new file mode 100644 index 0000000..b9ddc56 --- /dev/null +++ b/src/ast/stack.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +union AST; +struct ScopeItem; + +void ast_spill_to_stack(union AST *tlc, struct ScopeItem *vte); +void ast_grow_stack_frame(union AST *tlc, size_t bytes); diff --git a/src/ast/usedef.c b/src/ast/usedef.c new file mode 100644 index 0000000..a69ae17 --- /dev/null +++ b/src/ast/usedef.c @@ -0,0 +1,215 @@ +#include"usedef.h" + +#include"ast.h" +#include"ntc.h" +#include +#include +#include"reporting.h" +#include"scope.h" + +static void rd_kill(ReachingDefs *a, ScopeItem *var) { + for(size_t i = a->defCount; i --> 0;) { + AST *def = a->defs[i]; + + assert(def->nodeKind == AST_STMT_ASSIGN); + assert(def->stmtAssign.what->nodeKind == AST_EXPR_VAR); + assert(def->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR); + + if(def->stmtAssign.what->exprVar.thing == var) { + memmove(&a->defs[i], &a->defs[i + 1], sizeof(*a->defs) * (a->defCount - i - 1)); + a->defCount--; + } + } +} + +static bool rd_equal(ReachingDefs *a, ReachingDefs *b) { + if(a->defCount != b->defCount) { + return false; + } + + for(size_t i = 0; i < a->defCount; i++) { + if(a->defs[i] != b->defs[i]) { + return false; + } + } + + return true; +} + +static int compar_ptr(const void *a, const void *b) { + return *(uintptr_t*) a - *(uintptr_t*) b; +} + +static bool rd_find(ReachingDefs *dest, union AST *ast) { + return !!bsearch(&ast, dest->defs, dest->defCount, sizeof(*dest->defs), compar_ptr); +} + +static void rd_add(ReachingDefs *dest, union AST *ast) { + if(rd_find(dest, ast)) { + return; + } + + dest->defs = realloc(dest->defs, sizeof(*dest->defs) * (++dest->defCount)); + dest->defs[dest->defCount - 1] = ast; + qsort(dest->defs, dest->defCount, sizeof(*dest->defs), compar_ptr); +} + +static void rd_union(ReachingDefs *dest, ReachingDefs *src) { + for(size_t i = 0; i < src->defCount; i++) { + rd_add(dest, src->defs[i]); + } +} + +static ReachingDefs rd_compute_in(AST *tlc, AST *stmt, AST *stmtPrev) { + ReachingDefs rd = {}; + + // The previous statement is a predecessor unless it's an unconditional jump statement + if(stmtPrev && (stmtPrev->nodeKind != AST_STMT_JUMP || stmtPrev->stmtJump.condition)) { + rd_union(&rd, &stmtPrev->statement.rd); + } + + // If this is a label statement, then all jumps to this statement are predecessors + if(stmt->nodeKind == AST_STMT_LABEL) { + for(AST *s = tlc->chunk.statementFirst; s; s = s->statement.next) { + if(s->nodeKind == AST_STMT_JUMP && !strcmp(s->stmtJump.label, stmt->stmtLabel.name)) { + rd_union(&rd, &s->statement.rd); + } + } + } + + return rd; +} + +static void rd_step(AST *tlc, AST *stmt, AST *stmtPrev) { + stmt->statement.dirty = false; + + ReachingDefs rd = rd_compute_in(tlc, stmt, stmtPrev); + + if(stmt->nodeKind == AST_STMT_ASSIGN && stmt->stmtAssign.what->nodeKind == AST_EXPR_VAR && stmt->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR) { + rd_kill(&rd, stmt->stmtAssign.what->exprVar.thing); + rd_add(&rd, stmt); + } + + if(!rd_equal(&rd, &stmt->statement.rd)) { + // Set dirty flag on all successors + + // The next statement is a successor unless it's an unconditional jump statement + if(stmt->statement.next && (stmt->nodeKind != AST_STMT_JUMP || stmt->stmtJump.condition)) { + stmt->statement.next->statement.dirty = true; + } + + // If this is a jump statement, the target label is a successor + if(stmt->nodeKind == AST_STMT_JUMP) { + AST *label = ast_get_label_by_name(tlc, stmt->stmtJump.label); + label->statement.dirty = true; + } + + stmt->statement.rd = rd; + } +} + +static void usedef_generation_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { + AST *n = *nptr; + + if(n->nodeKind == AST_EXPR_VAR) { + ReachingDefs *rd = &stmt->statement.rd; + + ScopeItem *si = n->exprVar.thing; + + for(size_t rdi = 0; rdi < rd->defCount; rdi++) { + AST *def = rd->defs[rdi]; + + if(def->stmtAssign.what->exprVar.thing == si) { + UseDef *ud = calloc(1, sizeof(*ud)); + ud->def = def; + ud->use = n; + ud->stmt = stmt; + + if(!si->data.var.usedefFirst) { + si->data.var.usedefFirst = si->data.var.usedefLast = ud; + } else { + si->data.var.usedefLast->next = ud; + si->data.var.usedefLast = ud; + } + } + } + } +} +void ast_usedef_reset(AST *chu) { + for(size_t i = 0; i < chu->chunk.varCount; i++) { + ScopeItem *vte = chu->chunk.vars[i]; + + assert(vte->kind == SCOPEITEM_VAR); + + vte->data.var.usedefFirst = NULL; + vte->data.var.usedefLast = NULL; + vte->data.var.liveRangeStart = NULL; + vte->data.var.liveRangeEnd = NULL; + } + + for(AST *s = chu->chunk.statementFirst; s; s = s->statement.next) { + if(s->nodeKind == AST_STMT_IF || s->nodeKind == AST_STMT_LOOP) { + stahp(0, 0, "UD-chain generation requires a completely linear IR"); + } + + s->statement.dirty = true; + } + + for(size_t rdsteps = 0;; rdsteps++) { + //fprintf(stderr, "RD step %lu\n", rdsteps); + + AST *prev = NULL; + AST *dirty = NULL; + + // Find at least one dirty statement + for(AST *s = chu->chunk.statementFirst; s; prev = s, s = s->statement.next) { + if(s->statement.dirty) { + dirty = s; + break; + } + } + + if(!dirty) { + // Completed reaching definition computation + break; + } + + rd_step(chu, dirty, prev); + } + + generic_visitor(&chu, NULL, NULL, chu, chu, NULL, usedef_generation_visitor, NULL); + + for(size_t i = 0; i < chu->chunk.varCount; i++) { + ScopeItem *vte = chu->chunk.vars[i]; + + assert(vte->kind == SCOPEITEM_VAR); + + assert(!!vte->data.var.usedefFirst == !!vte->data.var.usedefLast); + + vte->data.var.liveRangeStart = vte->data.var.usedefFirst->stmt; + vte->data.var.liveRangeEnd = vte->data.var.usedefLast->stmt; + } + + // fix liveRangeStart and/or liveRangeEnd depending on goto targets + for(AST *s = chu->chunk.statementFirst; s; s = s->statement.next) { + if(s->nodeKind == AST_STMT_JUMP) { + AST *target = ast_get_label_by_name(chu, s->stmtJump.label); + + for(size_t sii = 0; sii < chu->chunk.varCount; sii++) { + ScopeItem *si = chu->chunk.vars[sii]; + + if(ast_stmt_is_after(chu, si->data.var.liveRangeEnd, s) == 0 && ast_stmt_is_after(chu, target, si->data.var.liveRangeEnd) == 0 && ast_stmt_is_after(chu, si->data.var.declaration, target) == 0) { + + si->data.var.liveRangeEnd = s; + + } + } + } + } + + if(ntc_get_int("pdbg")) { + char *astdump = ast_dump(chu); + fprintf(stderr, "### USEDEF GENERATED ###\n%s\n", astdump); + free(astdump); + } +} diff --git a/src/ast/usedef.h b/src/ast/usedef.h new file mode 100644 index 0000000..457ef09 --- /dev/null +++ b/src/ast/usedef.h @@ -0,0 +1,4 @@ +#pragma once + +union AST; +void ast_usedef_reset(union AST *chu); diff --git a/src/parse.h b/src/parse.h index c673b49..4533de4 100644 --- a/src/parse.h +++ b/src/parse.h @@ -1,7 +1,7 @@ #ifndef NCTREF_PARSE_H #define NCTREF_PARSE_H -#include"ast.h" +#include"ast/ast.h" AST *nct_parse(Token*); diff --git a/src/reporting.c b/src/reporting.c index be7b088..23ff516 100644 --- a/src/reporting.c +++ b/src/reporting.c @@ -3,7 +3,7 @@ #include #include #include -#include"ast.h" +#include"ast/ast.h" #include"lexer.h" static void stahp_va(int row, int column, const char *error, va_list l) { diff --git a/src/scope.c b/src/scope.c index 30d46fb..706fcdb 100644 --- a/src/scope.c +++ b/src/scope.c @@ -6,31 +6,6 @@ #include #include -/*struct ReachingDefs *reachingdefs_push(struct ReachingDefs *this) { - struct ReachingDefs *ret = calloc(1, sizeof(*ret)); - ret->parent = this; - return ret; -} - -struct ReachingDefs *reachingdefs_coalesce(struct ReachingDefs *this) { - struct ReachingDefs *parent = this->parent; - if(parent) { - parent->defs = realloc(parent->defs, sizeof(*parent->defs) * (parent->defCount + this->defCount)); - memcpy(&parent->defs[parent->defCount], this->defs, sizeof(*this->defs) * this->defCount); - parent->defCount += this->defCount; - } - free(this->defs); - free(this); - return parent; -} - -void reachingdefs_set(struct ReachingDefs *this, union AST *def) { - this->defCount = 1; - this->defs = realloc(this->defs, sizeof(*this->defs) * this->defCount); - this->defs[0] = def; - this->excludeParent = 1; -}*/ - Scope *scope_new(Scope *parent) { Scope *ret = calloc(1, sizeof(*ret)); ret->parent = parent; diff --git a/src/types.c b/src/types.c index 0734837..362d825 100644 --- a/src/types.c +++ b/src/types.c @@ -4,7 +4,7 @@ #include #include #include -#include"ast.h" +#include"ast/ast.h" #include"reporting.h" #include diff --git a/src/x86/arch.h b/src/x86/arch.h index 134950b..6ffddea 100644 --- a/src/x86/arch.h +++ b/src/x86/arch.h @@ -5,7 +5,7 @@ #include #include #include -#include"ast.h" +#include"ast/ast.h" #define HWR_AL 1 #define HWR_AH 2 diff --git a/src/x86/cg.c b/src/x86/cg.c index bc46517..609177c 100644 --- a/src/x86/cg.c +++ b/src/x86/cg.c @@ -4,7 +4,7 @@ #include #include"ntc.h" #include"reporting.h" -#include"ast.h" +#include"ast/ast.h" #include"arch.h" #include"utils.h"