#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; s->statement.rd.defCount = 0; free(s->statement.rd.defs); s->statement.rd.defs = NULL; } 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); } }