#include"dumberdowner.h" #include #include // This is the dumbing down pass. // // Complex expressions are to be "broken down" into simpler ones until the AST // can be trivially translated to the target architecture. // // This pass, along with CG is strictly dependent on x86 and will fail for // any other architecture. // Can expression be expressed as a single x86 operand? #define XOP_NOT_XOP 0 #define XOP_NOT_MEM 1 #define XOP_MEM 2 static int is_xop(AST *e) { if(e->nodeKind == AST_EXPR_PRIMITIVE) { return XOP_NOT_MEM; } else if(e->nodeKind == AST_EXPR_VAR) { return e->exprVar.thing->kind == VARTABLEENTRY_VAR ? XOP_NOT_MEM : XOP_MEM; } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) { return XOP_NOT_MEM; } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF) { AST *c = e->exprUnOp.operand; if(c->nodeKind == AST_EXPR_VAR && c->exprVar.thing->kind == VARTABLEENTRY_VAR) { return XOP_MEM; } else if(c->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operator == BINOP_ADD && c->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && c->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && c->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR) { if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) { return XOP_MEM; } else if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && c->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && c->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) { int scale = c->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val; if(scale == 1 || scale == 2 || scale == 4 || scale == 8) { return XOP_MEM; } } } } return XOP_NOT_XOP; } static void varify_change_usedefs(AST *e, AST *oldStmt, AST *newStmt) { if(e->nodeKind == AST_EXPR_VAR) { if(e->exprVar.thing->kind != VARTABLEENTRY_VAR) { return; } UseDef *swapWithPrev = NULL; UseDef *swapWith = NULL; UseDef *udPrev = NULL; UseDef *ud = e->exprVar.thing->data.var.usedefFirst; while(ud && ud->use != e) { if(ud->stmt == oldStmt && !swapWith) { swapWithPrev = udPrev; swapWith = ud; } udPrev = ud; ud = ud->next; } if(ud) { assert(ud->stmt == oldStmt); ud->stmt = newStmt; // We must make sure all newStmt usedefs are before the oldStmt ones, so swap if(swapWith) { assert(!!swapWithPrev + !!udPrev != 0); if(swapWithPrev) { swapWithPrev->next = ud; } else { e->exprVar.thing->data.var.usedefFirst = ud; } if(udPrev) { udPrev->next = swapWith; } else { e->exprVar.thing->data.var.usedefFirst = swapWith; } UseDef *temp = ud->next; ud->next = swapWith->next; swapWith->next = temp; assert(!!ud->next + !!swapWith->next != 0); if(!swapWith->next) { e->exprVar.thing->data.var.usedefLast = swapWith; } if(!ud->next) { e->exprVar.thing->data.var.usedefLast = ud; } } } } else if(e->nodeKind == AST_EXPR_BINARY_OP) { varify_change_usedefs(e->exprBinOp.operands[0], oldStmt, newStmt); varify_change_usedefs(e->exprBinOp.operands[1], oldStmt, newStmt); } else if(e->nodeKind == AST_EXPR_UNARY_OP) { varify_change_usedefs(e->exprUnOp.operand, oldStmt, newStmt); } else if(e->nodeKind == AST_EXPR_CALL) { varify_change_usedefs(e->exprCall.what, oldStmt, newStmt); int argCount = e->exprCall.what->expression.type->function.argCount; for(int i = 0; i < argCount; i++) { varify_change_usedefs(e->exprCall.args[i], oldStmt, newStmt); } } else if(e->nodeKind == AST_EXPR_ARRAY) { int sz = e->exprArray.type->array.length; for(int i = 0; i < sz; i++) { varify_change_usedefs(e->exprArray.items[i], oldStmt, newStmt); } } else if(e->nodeKind == AST_EXPR_CAST) { varify_change_usedefs(e->exprCast.what, oldStmt, newStmt); } } /* Split away complex expression into a new local variable */ static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) { VarTableEntry *vte = malloc(sizeof(*vte)); vte->kind = VARTABLEENTRY_VAR; vte->type = e->expression.type; vte->data.var.color = 0; vte->data.var.degree = 0; vte->data.var.priority = 0; vte->data.var.reachingDefs = NULL; // Add to var array tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount)); tlc->chunk.vars[tlc->chunk.varCount - 1] = vte; // Alter AST ASTExprVar *ev[2]; for(int i = 0; i < 2; i++) { ev[i] = malloc(sizeof(ASTExprVar)); ev[i]->nodeKind = AST_EXPR_VAR; ev[i]->type = e->expression.type; ev[i]->thing = vte; } ASTStmtAssign *assign = malloc(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; // Assemble ud-chain UseDef *ud[2]; ud[0] = malloc(sizeof(UseDef)); ud[1] = malloc(sizeof(UseDef)); ud[0]->stmt = (AST*) assign; ud[0]->use = assign->what; ud[0]->def = (AST*) assign; ud[0]->next = ud[1]; ud[1]->stmt = stmt; ud[1]->use = (AST*) ev[1]; ud[1]->def = (AST*) assign; ud[1]->next = NULL; vte->data.var.usedefFirst = ud[0]; vte->data.var.usedefLast = ud[1]; // Correct the ud-chain of variables used in varified expression `e` varify_change_usedefs(e, stmt, (AST*) assign); return (AST*) ev[1]; } static AST *xopify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) { return varify(tlc, chunk, stmtPrev, stmt, e); } static int dumben_chunk(AST *tlc, AST *chu) { AST *sPrev = NULL; AST *s = chu->chunk.statementFirst; int effective = 0; while(s) { if(s->nodeKind == AST_STMT_IF) { AST *e = s->stmtIf.expression; if(e->nodeKind == AST_EXPR_BINARY_OP && binop_is_comparison(e->exprBinOp.operator)) { if(is_xop(e->exprBinOp.operands[0]) == XOP_NOT_XOP) { e->exprBinOp.operands[0] = xopify(tlc, chu, sPrev, s, e->exprBinOp.operands[0]); effective = 1; } if(is_xop(e->exprBinOp.operands[1]) == XOP_NOT_XOP) { e->exprBinOp.operands[1] = xopify(tlc, chu, sPrev, s, e->exprBinOp.operands[1]); effective = 1; } if(is_xop(e->exprBinOp.operands[0]) == XOP_MEM && is_xop(e->exprBinOp.operands[1]) == XOP_MEM) { // Can't have two mems; put one in var e->exprBinOp.operands[1] = varify(tlc, chu, sPrev, s, e->exprBinOp.operands[1]); effective = 1; } } else { s->stmtIf.expression = varify(tlc, chu, sPrev, s, e); effective = 1; } effective |= dumben_chunk(tlc, s->stmtIf.then); } else if(s->nodeKind == AST_STMT_LOOP) { effective |= dumben_chunk(tlc, s->stmtLoop.body); } else if(s->nodeKind == AST_STMT_ASSIGN) { if(s->stmtAssign.what->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.what->exprUnOp.operator == UNOP_DEREF && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_DEREF) { s->stmtAssign.to = varify(tlc, chu, sPrev, s, s->stmtAssign.to); effective = 1; } } else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_CALL) { int argCount = s->stmtExpr.expr->exprCall.what->expression.type->function.argCount; for(int i = 0; i < argCount; i++) { if(is_xop(s->stmtExpr.expr->exprCall.args[i]) == XOP_NOT_XOP) { s->stmtExpr.expr->exprCall.args[i] = xopify(tlc, chu, sPrev, s, s->stmtExpr.expr->exprCall.args[i]); effective = 1; } } } sPrev = s; s = s->statement.next; } return effective; } void dumben_go(AST* tlc) { while(dumben_chunk(tlc, tlc)); }