|
|
|
|
@@ -84,6 +84,11 @@ void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, v
|
|
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
@@ -146,334 +151,190 @@ int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 SCOPEITEM_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(ScopeItem *vte, UseDef *ud) {
|
|
|
|
|
assert(vte->kind == SCOPEITEM_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(ScopeItem *vte, AST *use, AST *whole) {
|
|
|
|
|
assert(vte->kind == SCOPEITEM_VAR);
|
|
|
|
|
|
|
|
|
|
assert(vte->data.var.reachingDefs != NULL);
|
|
|
|
|
|
|
|
|
|
ReachingDefs *rd = vte->data.var.reachingDefs;
|
|
|
|
|
|
|
|
|
|
while(rd && rd->defCount == 0) rd = rd->parent;
|
|
|
|
|
|
|
|
|
|
if(rd) {
|
|
|
|
|
for(size_t d = 0; d < rd->defCount; d++) {
|
|
|
|
|
UseDef *ud = calloc(1, sizeof(*ud));
|
|
|
|
|
ud->def = rd->defs[d];
|
|
|
|
|
ud->use = use;
|
|
|
|
|
ud->stmt = whole;
|
|
|
|
|
ud->next = NULL;
|
|
|
|
|
|
|
|
|
|
rawadduse(vte, ud);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
UseDef *ud = calloc(1, sizeof(*ud));
|
|
|
|
|
ud->def = NULL;
|
|
|
|
|
ud->use = use;
|
|
|
|
|
ud->stmt = whole;
|
|
|
|
|
ud->next = NULL;
|
|
|
|
|
|
|
|
|
|
rawadduse(vte, ud);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void overwritedefs(ScopeItem *vte, AST *def) {
|
|
|
|
|
assert(vte->kind == SCOPEITEM_VAR);
|
|
|
|
|
|
|
|
|
|
if(!vte->data.var.reachingDefs) {
|
|
|
|
|
vte->data.var.reachingDefs = calloc(1, sizeof(*vte->data.var.reachingDefs));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vte->data.var.reachingDefs->defCount = 1;
|
|
|
|
|
|
|
|
|
|
vte->data.var.reachingDefs->defs = realloc(vte->data.var.reachingDefs->defs, sizeof(*vte->data.var.reachingDefs->defs));
|
|
|
|
|
vte->data.var.reachingDefs->defs[0] = def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void mergedefs(ScopeItem *vte) {
|
|
|
|
|
assert(vte->kind == SCOPEITEM_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(ScopeItem *vte) {
|
|
|
|
|
assert(vte->kind == SCOPEITEM_VAR);
|
|
|
|
|
|
|
|
|
|
ReachingDefs *rdefs = calloc(1, 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, ScopeItem *vte, AST *daLoopStmt) {
|
|
|
|
|
assert(vte->kind == SCOPEITEM_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;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mergedefs(vte);
|
|
|
|
|
stahp(0, 0, "Label %s not found", name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 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--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct UsedefPassState {
|
|
|
|
|
size_t loopDepth;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void ast_usedef_pass(struct UsedefPassState *state, 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(state, tlc, s, s);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
} else if(a->nodeKind == AST_STMT_IF) {
|
|
|
|
|
pushdefsall(tlc);
|
|
|
|
|
|
|
|
|
|
ast_usedef_pass(state, tlc, a->stmtIf.expression, wholestmt);
|
|
|
|
|
ast_usedef_pass(state, tlc, a->stmtIf.then, wholestmt);
|
|
|
|
|
|
|
|
|
|
mergedefsall(tlc);
|
|
|
|
|
} else if(a->nodeKind == AST_STMT_LOOP) {
|
|
|
|
|
pushdefsall(tlc);
|
|
|
|
|
|
|
|
|
|
state->loopDepth++;
|
|
|
|
|
|
|
|
|
|
ast_usedef_pass(state, tlc, a->stmtLoop.body, wholestmt);
|
|
|
|
|
|
|
|
|
|
state->loopDepth--;
|
|
|
|
|
|
|
|
|
|
if(state->loopDepth == 0) {
|
|
|
|
|
for(size_t vi = 0; vi < tlc->chunk.varCount; vi++) {
|
|
|
|
|
ScopeItem *si = tlc->chunk.vars[vi];
|
|
|
|
|
|
|
|
|
|
if(si->data.var.usedInLoop) {
|
|
|
|
|
if(ast_stmt_is_after(a->stmtLoop.body, si->data.var.declaration, si->data.var.declaration) == -1) {
|
|
|
|
|
if(ast_stmt_is_after(tlc, a, si->data.var.liveRangeStart) == 0) {
|
|
|
|
|
si->data.var.liveRangeStart = a;
|
|
|
|
|
}
|
|
|
|
|
si->data.var.liveRangeEnd = a;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
si->data.var.usedInLoop = 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
|
|
|
|
|
|
|
|
|
|
mergedefsloopall(tlc, a);
|
|
|
|
|
} else if(a->nodeKind == AST_STMT_ASSIGN) {
|
|
|
|
|
if(a->stmtAssign.what->nodeKind == AST_EXPR_VAR && a->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR) {
|
|
|
|
|
overwritedefs(a->stmtAssign.what->exprVar.thing, a);
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_usedef_pass(state, tlc, a->stmtAssign.what, wholestmt);
|
|
|
|
|
|
|
|
|
|
if(a->stmtAssign.to) {
|
|
|
|
|
ast_usedef_pass(state, tlc, a->stmtAssign.to, wholestmt);
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
} else if(a->nodeKind == AST_STMT_EXPR) {
|
|
|
|
|
ast_usedef_pass(state, tlc, a->stmtExpr.expr, wholestmt);
|
|
|
|
|
} else if(a->nodeKind == AST_EXPR_VAR) {
|
|
|
|
|
ScopeItem *si = a->exprVar.thing;
|
|
|
|
|
|
|
|
|
|
if(si->kind == SCOPEITEM_VAR) {
|
|
|
|
|
if(state->loopDepth > 0) {
|
|
|
|
|
si->data.var.usedInLoop = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
adduse(si, a, wholestmt);
|
|
|
|
|
|
|
|
|
|
if(si->data.var.liveRangeStart == NULL) {
|
|
|
|
|
si->data.var.liveRangeStart = wholestmt;
|
|
|
|
|
}
|
|
|
|
|
si->data.var.liveRangeEnd = wholestmt;
|
|
|
|
|
}
|
|
|
|
|
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
|
|
|
|
|
ast_usedef_pass(state, tlc, a->exprBinOp.operands[0], wholestmt);
|
|
|
|
|
ast_usedef_pass(state, tlc, a->exprBinOp.operands[1], wholestmt);
|
|
|
|
|
} else if(a->nodeKind == AST_EXPR_UNARY_OP) {
|
|
|
|
|
ast_usedef_pass(state, tlc, a->exprUnOp.operand, wholestmt);
|
|
|
|
|
} else if(a->nodeKind == AST_EXPR_CALL) {
|
|
|
|
|
ast_usedef_pass(state, tlc, a->exprCall.what, wholestmt);
|
|
|
|
|
|
|
|
|
|
for(size_t p = 0; p < a->exprCall.what->expression.type->pointer.of->function.argCount; p++) {
|
|
|
|
|
ast_usedef_pass(state, 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(state, tlc, a->exprCast.what, wholestmt);
|
|
|
|
|
} else if(a->nodeKind == AST_EXPR_STACK_POINTER) {
|
|
|
|
|
} else if(a->nodeKind == AST_EXPR_EXT_SALLOC) {
|
|
|
|
|
} 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) {
|
|
|
|
|
ScopeItem *si = a->stmtDecl.thing;
|
|
|
|
|
|
|
|
|
|
//assert(si->kind != SCOPEITEM_VAR || a->stmtDecl.expression);
|
|
|
|
|
|
|
|
|
|
if(si->kind == SCOPEITEM_VAR) {
|
|
|
|
|
assert(!si->data.var.declaration || si->data.var.declaration == a);
|
|
|
|
|
si->data.var.declaration = a;
|
|
|
|
|
}
|
|
|
|
|
} else if(a->nodeKind == AST_STMT_RETURN) {
|
|
|
|
|
if(a->stmtReturn.val) {
|
|
|
|
|
ast_usedef_pass(state, tlc, a->stmtReturn.val, wholestmt);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
stahp_node(a, "ast_usedef_pass: unhandled %s", AST_KIND_STR[a->nodeKind]);
|
|
|
|
|
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.reachingDefs = NULL;
|
|
|
|
|
vte->data.var.usedefFirst = NULL;
|
|
|
|
|
vte->data.var.usedefLast = NULL;
|
|
|
|
|
vte->data.var.liveRangeStart = NULL;
|
|
|
|
|
vte->data.var.liveRangeEnd = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pushdefsall(chu);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct UsedefPassState state = {};
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_usedef_pass(&state, chu, chu, NULL);
|
|
|
|
|
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];
|
|
|
|
|
@@ -481,8 +342,26 @@ void ast_usedef_reset(AST *chu) {
|
|
|
|
|
assert(vte->kind == SCOPEITEM_VAR);
|
|
|
|
|
|
|
|
|
|
assert(!!vte->data.var.usedefFirst == !!vte->data.var.usedefLast);
|
|
|
|
|
assert(!!vte->data.var.usedefFirst == !!vte->data.var.liveRangeStart);
|
|
|
|
|
//assert(!!vte->data.var.liveRangeStart == !!vte->data.var.liveRangeEnd);
|
|
|
|
|
|
|
|
|
|
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")) {
|
|
|
|
|
@@ -575,6 +454,10 @@ char *type_to_string(Type *t) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
@@ -600,6 +483,9 @@ static char *ast_dumpe(AST *tlc, AST *e) {
|
|
|
|
|
case UNOP_NEGATE:
|
|
|
|
|
op = "-";
|
|
|
|
|
break;
|
|
|
|
|
case UNOP_NOT:
|
|
|
|
|
op = "!";
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
@@ -755,6 +641,13 @@ static char *ast_dumps(AST *tlc, AST *s) {
|
|
|
|
|
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);
|
|
|
|
|
@@ -1204,3 +1097,120 @@ static void ast_segmented_dereference_visitor(AST **aptr, AST *stmt, AST *stmtPr
|
|
|
|
|
void ast_segmented_dereference(AST *tlc) {
|
|
|
|
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, ast_segmented_dereference_visitor, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#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) {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
if(a->stmtIf.then->chunk.statementFirst) {
|
|
|
|
|
jump->next = a->stmtIf.then->chunk.statementFirst;
|
|
|
|
|
for(AST *z = a->stmtIf.then->chunk.statementFirst; z; z = z->statement.next) {
|
|
|
|
|
if(!z->statement.next) {
|
|
|
|
|
z->statement.next = (AST*) label;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
jump->next = (AST*) label;
|
|
|
|
|
}
|
|
|
|
|
label->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;
|
|
|
|
|
}
|
|
|
|
|
if(a->stmtLoop.body->chunk.statementFirst) {
|
|
|
|
|
startLabel->next = a->stmtLoop.body->chunk.statementFirst;
|
|
|
|
|
for(AST *z = a->stmtLoop.body->chunk.statementFirst; z; z = z->statement.next) {
|
|
|
|
|
if(!z->statement.next) {
|
|
|
|
|
z->statement.next = (AST*) jump;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
startLabel->next = (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);
|
|
|
|
|
}
|
|
|
|
|
|