#include #include #include #include"arch.h" #include"reporting.h" #include"utils.h" // 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 file along with CG is strictly for IA-32 and will fail for other // architectures. static ScopeItem *create_dumbtemp(AST *tlc, Type *itstype) { static size_t vidx = 0; ScopeItem *vte = calloc(1, sizeof(*vte)); vte->kind = SCOPEITEM_VAR; vte->type = itstype; vte->data.var.color = -1; vte->data.var.precolored = false; vte->data.var.degree = 0; vte->data.var.priority = 0; vte->data.var.name = malp("$dumb%lu", vidx++); // 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; return vte; } /* Split away complex expression into a new local variable */ static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) { ScopeItem *vte = create_dumbtemp(tlc, e->expression.type); // 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; // Reset ud-chain vte->data.var.usedefFirst = NULL; vte->data.var.usedefLast = NULL; 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 void mark_ptr(AST *a) { assert(a->nodeKind == AST_EXPR_VAR); assert(a->exprVar.thing->kind == SCOPEITEM_VAR); if(x86_ia16()) { assert(!a->exprVar.thing->data.var.preclassed || a->exprVar.thing->data.var.registerClass == REG_CLASS_IA16_PTRS); a->exprVar.thing->data.var.preclassed = true; a->exprVar.thing->data.var.registerClass = REG_CLASS_IA16_PTRS; } } static void mark_a(ScopeItem *si) { size_t sz = type_size(si->type); if(sz <= 1) { vte_precolor(si, REG_CLASS_8, 0); } else if(sz == 2) { vte_precolor(si, REG_CLASS_16_32, 0); } else if(sz == 4 || sz == 0) { vte_precolor(si, REG_CLASS_16_32, 1); } else { abort(); } } static void mark_d(ScopeItem *si) { size_t sz = type_size(si->type); if(sz <= 1) { vte_precolor(si, REG_CLASS_8, 6); } else if(sz == 2) { vte_precolor(si, REG_CLASS_16_32, 6); } else if(sz == 4 || sz == 0) { vte_precolor(si, REG_CLASS_16_32, 7); } else { abort(); } } struct DumbenState { AST *targetTLC; int effective; }; static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud) { struct DumbenState *this = ud; if(this->targetTLC != tlc) return; AST *s = *nptr; if(s->nodeKind == AST_STMT_JUMP && s->stmtJump.condition) { AST *e = s->stmtJump.condition; 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, stmtPrev, s, e->exprBinOp.operands[0]); this->effective = 1; } if(is_xop(e->exprBinOp.operands[1]) == XOP_NOT_XOP) { e->exprBinOp.operands[1] = xopify(tlc, chu, stmtPrev, s, e->exprBinOp.operands[1]); this->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, stmtPrev, s, e->exprBinOp.operands[1]); this->effective = 1; } } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_NOT && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && (e->exprUnOp.operand->exprBinOp.operator == BINOP_LOGICAL_AND || e->exprUnOp.operand->exprBinOp.operator == BINOP_LOGICAL_OR)) { AST *binop = e->exprUnOp.operand; e->exprUnOp.operand = binop->exprBinOp.operands[0]; binop->exprBinOp.operands[0] = e; AST *unop2 = calloc(1, sizeof(ASTExprUnaryOp)); unop2->nodeKind = AST_EXPR_UNARY_OP; unop2->expression.type = binop->exprBinOp.operands[1]->expression.type; unop2->exprUnOp.operator = UNOP_NOT; unop2->exprUnOp.operand = binop->exprBinOp.operands[1]; binop->exprBinOp.operands[1] = unop2; binop->exprBinOp.operator = binop->exprBinOp.operator == BINOP_LOGICAL_AND ? BINOP_LOGICAL_OR : BINOP_LOGICAL_AND; s->stmtJump.condition = binop; ast_denoop(tlc, &s->stmtJump.condition); this->effective = 1; } else if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_LOGICAL_OR) { AST *cond0 = e->exprBinOp.operands[0]; AST *cond1 = e->exprBinOp.operands[1]; s->stmtJump.condition = cond0; AST *jump2 = calloc(1, sizeof(ASTStmtJump)); jump2->nodeKind = AST_STMT_JUMP; jump2->stmtJump.condition = cond1; jump2->stmtJump.label = strdup(s->stmtJump.label); jump2->statement.next = s->statement.next; s->statement.next = jump2; this->effective = 1; } else if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_LOGICAL_AND) { static size_t idx = 0; AST *cond0 = e->exprBinOp.operands[0]; AST *cond1 = e->exprBinOp.operands[1]; AST *lbl = calloc(1, sizeof(ASTStmtLabel)); lbl->nodeKind = AST_STMT_LABEL; lbl->stmtLabel.name = malp("$dla%lu", idx++); lbl->statement.next = s->statement.next; AST *unop = calloc(1, sizeof(ASTExprUnaryOp)); unop->nodeKind = AST_EXPR_UNARY_OP; unop->expression.type = cond0->expression.type; unop->exprUnOp.operator = UNOP_NOT; unop->exprUnOp.operand = cond0; s->stmtJump.condition = unop; AST *jump2 = calloc(1, sizeof(ASTStmtJump)); jump2->nodeKind = AST_STMT_JUMP; jump2->stmtJump.condition = cond1; jump2->stmtJump.label = strdup(s->stmtJump.label); jump2->statement.next = lbl; s->stmtJump.label = strdup(lbl->stmtLabel.name); s->statement.next = jump2; ast_denoop(tlc, &s->stmtJump.condition); this->effective = 1; } else { AST *v = e; bool not = v->nodeKind == AST_EXPR_UNARY_OP && v->exprUnOp.operator == UNOP_NOT; if(not) { v = v->exprUnOp.operand; } if(v->nodeKind != AST_EXPR_VAR) { v = varify(tlc, chu, stmtPrev, s, e); } AST *zero = calloc(1, sizeof(ASTExprPrimitive)); zero->nodeKind = AST_EXPR_PRIMITIVE; zero->expression.type = v->expression.type; zero->exprPrim.val = 0; AST *binop = calloc(1, sizeof(ASTExprBinaryOp)); binop->nodeKind = AST_EXPR_BINARY_OP; binop->expression.type = v->expression.type; binop->exprBinOp.operator = not ? BINOP_EQUAL : BINOP_NEQUAL; binop->exprBinOp.operands[0] = v; binop->exprBinOp.operands[1] = zero; s->stmtJump.condition = binop; ast_denoop(tlc, &s->stmtJump.condition); this->effective = 1; } } else if(s->nodeKind == AST_STMT_LOOP) { } else if(s->nodeKind == AST_STMT_RETURN) { // ret returns in eax always, so it needs to be in a precolored var AST *retval = s->stmtReturn.val; if(retval && (!is_xop(retval) || retval->nodeKind == AST_EXPR_PRIMITIVE || (retval->nodeKind == AST_EXPR_VAR && retval->exprVar.thing->kind == SCOPEITEM_VAR && (!retval->exprVar.thing->data.var.precolored || !strchr(REG_CLASSES[retval->exprVar.thing->data.var.registerClass].rsN[retval->exprVar.thing->data.var.color], 'a'))))) { retval = s->stmtReturn.val = varify(tlc, chu, stmtPrev, s, retval); this->effective = 1; mark_a(retval->exprVar.thing); } } else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_CALL) { // Turn this: // f(x); // into: // a = f(x); s->stmtExpr.expr = varify(tlc, chu, stmtPrev, s, s->stmtExpr.expr); mark_a(s->stmtExpr.expr->exprVar.thing); // Purge this statement entirely, otherwise we'd have // a = f(x); // a; if(stmtPrev) { assert(stmtPrev->statement.next->statement.next == s); stmtPrev->statement.next->statement.next = s->statement.next; } else { assert(chu->chunk.statementFirst->statement.next == s); chu->chunk.statementFirst->statement.next = s->statement.next; } this->effective = 1; } else if(s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.to) { if(ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to) && stmtPrev) { // This is a NOP, remove it from the AST stmtPrev->statement.next = s->statement.next; this->effective = 1; } else { if(s->stmtAssign.to->nodeKind == AST_EXPR_CALL && (s->stmtAssign.what->nodeKind != AST_EXPR_VAR || s->stmtAssign.what->exprVar.thing->kind != SCOPEITEM_VAR || !s->stmtAssign.what->exprVar.thing->data.var.precolored)) { ScopeItem *tmp = create_dumbtemp(tlc, s->stmtAssign.what->expression.type); mark_a(tmp); ASTExprVar *ev[2] = {calloc(1, sizeof(**ev)), calloc(1, sizeof(**ev))}; ev[0]->nodeKind = AST_EXPR_VAR; ev[0]->type = tmp->type; ev[0]->thing = tmp; memcpy(ev[1], ev[0], sizeof(**ev)); ASTStmtAssign *assign2 = calloc(1, sizeof(*assign2)); assign2->nodeKind = AST_STMT_ASSIGN; assign2->what = (AST*) s->stmtAssign.what; assign2->to = (AST*) ev[0]; s->stmtAssign.what = (AST*) ev[1]; assign2->next = s->stmtAssign.next; s->stmtAssign.next = (AST*) assign2; this->effective = 1; } else 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, stmtPrev, s, s->stmtAssign.to); this->effective = 1; } else if(s->stmtAssign.what->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.what->exprUnOp.operator == UNOP_DEREF && !is_xop(s->stmtAssign.what)) { s->stmtAssign.what->exprUnOp.operand = varify(tlc, chu, stmtPrev, s, s->stmtAssign.what->exprUnOp.operand); mark_ptr(s->stmtAssign.what->exprUnOp.operand); this->effective = 1; } else if(s->stmtAssign.what && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_CALL && is_xop(s->stmtAssign.to->exprCall.what) == XOP_NOT_XOP) { s->stmtAssign.to->exprCall.what = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprCall.what); this->effective = 1; } else if(s->stmtAssign.what && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_CALL) { ASTExprCall *call = &s->stmtAssign.to->exprCall; int argCount = call->what->expression.type->pointer.of->function.argCount; for(int i = 0; i < argCount; i++) { if(is_xop(call->args[i]) == XOP_NOT_XOP) { call->args[i] = xopify(tlc, chu, stmtPrev, s, call->args[i]); this->effective = 1; // Xopify one argument at a time otherwise stmtPrev becomes incorrect break; } } } else if(s->stmtAssign.to && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_NEGATE && !ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprUnOp.operand)) { // Turn this: // a = -b // into // a = b // a = -a AST *ev[2] = { ast_deep_copy(s->stmtAssign.what), ast_deep_copy(s->stmtAssign.what) }; AST *negation = s->stmtAssign.to; s->stmtAssign.to = negation->exprUnOp.operand; negation->exprUnOp.operand = ev[0]; AST *assign2 = calloc(1, sizeof(ASTStmtAssign)); assign2->nodeKind = AST_STMT_ASSIGN; assign2->stmtAssign.what = ev[1]; assign2->stmtAssign.to = negation; assign2->statement.next = s->statement.next; s->statement.next = assign2; this->effective = 1; } else if(is_xop(s->stmtAssign.to) == XOP_NOT_XOP) { if(s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_DEREF) { s->stmtAssign.to->exprUnOp.operand = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprUnOp.operand); mark_ptr(s->stmtAssign.to->exprUnOp.operand); this->effective = 1; } else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_MUL) { WhysItBad_Huh because = x86_test_mul(stmtPrev, s); if(because == SRC0_IS_BAD_XOP) { s->stmtAssign.to->exprBinOp.operands[0] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[0]); this->effective = 1; } else if(because == SRC1_IS_BAD_XOP) { s->stmtAssign.to->exprBinOp.operands[1] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[1]); this->effective = 1; } else if(because == MISSING_MULHI_FOR_MUL || because == DEST_NOT_SRC0) {// !stmtPrev || stmtPrev->nodeKind != AST_STMT_ASSIGN || stmtPrev->stmtAssign.to->nodeKind != AST_EXPR_BINARY_OP || stmtPrev->stmtAssign.to->exprBinOp.operator != BINOP_MULHI) { // Turn this: // a = b * c // Into this: // d = b // e = d *^ c // d = d * c // a = d ASTExprVar *originalDestination = s->stmtAssign.what; s->stmtAssign.to->exprBinOp.operands[0] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[0]); assert(stmtPrev->statement.next->nodeKind == AST_STMT_ASSIGN && stmtPrev->statement.next->stmtAssign.what->nodeKind == AST_EXPR_VAR && ast_expression_equal(stmtPrev->statement.next->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0])); if(!x86_imul_supported()) { ASTExprBinaryOp *mulhi = calloc(1, sizeof(*mulhi)); mulhi->nodeKind = AST_EXPR_BINARY_OP; mulhi->type = s->stmtAssign.to->expression.type; mulhi->operator = BINOP_MULHI; mulhi->operands[0] = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]); mulhi->operands[1] = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[1]); AST *hihalf = varify(tlc, chu, stmtPrev->statement.next, s, mulhi); mark_d(hihalf->exprVar.thing); } s->stmtAssign.what = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]); ASTStmtAssign *redest = calloc(1, sizeof(*redest)); redest->nodeKind = AST_STMT_ASSIGN; redest->what = originalDestination; redest->to = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]); redest->next = s->stmtAssign.next; s->stmtAssign.next = (AST*) redest; mark_a(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing); this->effective = 1; } else assert(because == NOT_AT_ALL_IT || because == GUCCI); } else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator < BINOP_SIMPLES && !ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0])) { // Turn this: // a = b op c // into // a = b // a = a op c if(x86_is_lea(s)) { // We do the lea trick in cg.c } else { AST *assign2 = calloc(1, sizeof(ASTStmtAssign)); assign2->nodeKind = AST_STMT_ASSIGN; assign2->stmtAssign.what = ast_deep_copy(s->stmtAssign.what); assign2->stmtAssign.to = s->stmtAssign.to->exprBinOp.operands[0]; if(stmtPrev) { stmtPrev->statement.next = assign2; } else { chu->chunk.statementFirst = assign2; } assign2->statement.next = s; s->stmtAssign.to->exprBinOp.operands[0] = ast_deep_copy(s->stmtAssign.what); this->effective = 1; } } else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && !is_xop(s->stmtAssign.to->exprBinOp.operands[0])) { s->stmtAssign.to->exprBinOp.operands[0] = xopify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[0]); this->effective = 1; } else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && !is_xop(s->stmtAssign.to->exprBinOp.operands[1])) { s->stmtAssign.to->exprBinOp.operands[1] = xopify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[1]); this->effective = 1; } else if(s->stmtAssign.to->nodeKind == AST_EXPR_CAST && s->stmtAssign.to->exprCast.what->nodeKind != AST_EXPR_VAR) { s->stmtAssign.to->exprCast.what = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprCast.what); this->effective = 1; } } } } } static void pre_dumb_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { AST *n = *nptr; if(n == ud) { if(tlc->chunk.functionType) { size_t argCount = n->chunk.functionType->function.argCount; // First argCount vtes in list are the arguments for(size_t i = 0; i < argCount; i++) { ScopeItem *vte = n->chunk.vars[i]; ASTExprStackPointer *stck = calloc(1, sizeof(*stck)); stck->nodeKind = AST_EXPR_STACK_POINTER; stck->type = type_pointer_wrap(vte->type); ASTExprPrimitive *offset = calloc(1, sizeof(*offset)); offset->nodeKind = AST_EXPR_PRIMITIVE; offset->type = type_u(8 * x86_max_gpr_size()); offset->val = 4 + i * x86_max_gpr_size(); offset->stackGrowth = true; ASTExprBinaryOp *sum = calloc(1, sizeof(*sum)); sum->nodeKind = AST_EXPR_BINARY_OP; sum->type = stck->type; sum->operands[0] = (AST*) stck; sum->operands[1] = (AST*) offset; sum->operator = BINOP_ADD; ASTExprUnaryOp *deref = calloc(1, sizeof(*deref)); deref->nodeKind = AST_EXPR_UNARY_OP; deref->type = vte->type; deref->operand = (AST*) sum; deref->operator = UNOP_DEREF; ASTExprVar *evar = calloc(1, sizeof(*evar)); evar->nodeKind = AST_EXPR_VAR; evar->type = vte->type; evar->thing = vte; ASTStmtAssign *ass = calloc(1, sizeof(*ass)); ass->nodeKind = AST_STMT_ASSIGN; ass->next = NULL; ass->what = (AST*) evar; ass->to = ast_cast_expr((AST*) deref, vte->type); // Must cast because of "convention correctness" ass->next = n->chunk.statementFirst; if(n->chunk.statementFirst) { n->chunk.statementFirst = ass; } else { assert(!n->chunk.statementLast); n->chunk.statementFirst = n->chunk.statementLast = ass; } } } } } static void decompose_symbol_record_field_access(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { if(tlc != (AST*) ud) return; AST *n = *nptr; if(n->nodeKind == AST_EXPR_DOT) { ASTExprUnaryOp *ref = calloc(1, sizeof(*ref)); ref->nodeKind = AST_EXPR_UNARY_OP; ref->type = type_pointer_wrap(n->exprDot.a->expression.type); ref->operator = UNOP_REF; ref->operand = n->exprDot.a; ASTExprPrimitive *offset = calloc(1, sizeof(*offset)); offset->nodeKind = AST_EXPR_PRIMITIVE; offset->type = primitive_parse("u32"); size_t f; for(f = 0; f < n->exprDot.a->expression.type->record.fieldCount; f++) { if(!strcmp(n->exprDot.a->expression.type->record.fieldNames[f], n->exprDot.b)) { break; } } assert(f < n->exprDot.a->expression.type->record.fieldCount); offset->val = n->exprDot.a->expression.type->record.fieldOffsets[f]; ASTExprBinaryOp *sum = calloc(1, sizeof(*sum)); sum->nodeKind = AST_EXPR_BINARY_OP; sum->type = ref->type; sum->operator = BINOP_ADD; sum->operands[0] = (AST*) ref; sum->operands[1] = (AST*) offset; AST *cast = ast_cast_expr((AST*) sum, type_pointer_wrap(n->exprDot.a->expression.type->record.fieldTypes[f])); ASTExprUnaryOp *deref = calloc(1, sizeof(*deref)); deref->nodeKind = AST_EXPR_UNARY_OP; deref->type = cast->expression.type->pointer.of; deref->operator = UNOP_DEREF; deref->operand = cast; *nptr = (AST*) deref; } } static bool is_pointer2pointer_cast(AST *e) { return e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER; } static bool is_double_field_access(AST *e) { return e->nodeKind == AST_EXPR_BINARY_OP && is_pointer2pointer_cast(e->exprBinOp.operands[0]) && e->exprBinOp.operands[0]->exprCast.what->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operands[0]->exprCast.what->exprBinOp.operator == e->exprBinOp.operator && e->exprBinOp.operator == BINOP_ADD && e->exprBinOp.operands[0]->exprCast.what->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && e->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE; } struct DenoopState { AST *targetTLC; bool success; }; static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { struct DenoopState *state = ud; // if(state->targetTLC != tlc) return; AST *n = *nptr; bool *success = &state->success; if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_REF && n->exprUnOp.operand->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operand->exprUnOp.operator == UNOP_DEREF) { // Turn `&*a` into `a` // Artificially change type of casted expression to keep types valid for subsequent passes n->exprUnOp.operand->exprUnOp.operand->expression.type = n->expression.type; *nptr = n->exprUnOp.operand->exprUnOp.operand; *success = true; } else if(is_double_field_access(n)) { // Turn `(p + x) as C + y` into `(p + (x + y)) as C` n->exprBinOp.operands[0]->exprCast.what->exprBinOp.operands[1]->exprPrim.val += n->exprBinOp.operands[1]->exprPrim.val; *nptr = n->exprBinOp.operands[0]; *success = true; } else if(n->nodeKind == AST_EXPR_CAST && n->exprCast.what->nodeKind == AST_EXPR_CAST) { // Turn `(p as A) as B` to `p as B` n->exprCast.what = n->exprCast.what->exprCast.what; *success = true; } else if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operands[0]->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[0]->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[0]->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) { // Turn `(x + a) + b` into `x + (a + b)` n->exprBinOp.operands[0]->exprBinOp.operands[1]->exprPrim.val += n->exprBinOp.operands[1]->exprPrim.val; *nptr = n->exprBinOp.operands[0]; *success = true; } else if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[1]->exprPrim.val == 0) { // Turn `x + 0` into `x` // Artificially change type of casted expression to keep types valid for subsequent passes n->exprBinOp.operands[0]->expression.type = n->expression.type; *nptr = n->exprBinOp.operands[0]; *success = true; } else if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_NOT && n->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && binop_comp_opposite(n->exprUnOp.operand->exprBinOp.operator) != BINOP_WTF) { // Turn `!(a op b)` to `(a !op b)` n->exprUnOp.operand->exprBinOp.operator = binop_comp_opposite(n->exprUnOp.operand->exprBinOp.operator); *nptr = n->exprUnOp.operand; *success = true; } else if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_NOT && n->exprUnOp.operand->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operand->exprUnOp.operator == UNOP_NOT) { // Turn `!!x` to `x` *nptr = n->exprUnOp.operand->exprUnOp.operand; *success = true; } else if(n->nodeKind == AST_EXPR_CAST && n->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && n->exprCast.to->type == TYPE_TYPE_POINTER) { // Turn (x as A*) into x, since all pointer types are identical in Nectar's AST // Artificially change type of casted expression to keep types valid for subsequent passes n->exprCast.what->expression.type = n->exprCast.to; *nptr = n->exprCast.what; *success = true; } else if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) { // Constant propagation of + operator AST *prim = n->exprBinOp.operands[0]; prim->expression.type = n->exprBinOp.type; prim->exprPrim.val = n->exprBinOp.operands[0]->exprPrim.val + n->exprBinOp.operands[1]->exprPrim.val; *nptr = prim; *success = true; } else if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_SUB && n->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) { // Constant propagation of - operator AST *prim = n->exprBinOp.operands[0]; prim->expression.type = n->exprBinOp.type; prim->exprPrim.val = n->exprBinOp.operands[0]->exprPrim.val - n->exprBinOp.operands[1]->exprPrim.val; *nptr = prim; *success = true; } else if(n->nodeKind == AST_EXPR_EXT_SIZEOF) { ASTExprPrimitive *prim = calloc(1, sizeof(*prim)); prim->nodeKind = AST_EXPR_PRIMITIVE; prim->type = n->expression.type; assert(!!n->exprExtSizeOf.ofExpr != !!n->exprExtSizeOf.ofType); if(n->exprExtSizeOf.ofType) { prim->val = type_size(n->exprExtSizeOf.ofType); } else { prim->val = type_size(n->exprExtSizeOf.ofExpr->expression.type); } *nptr = (AST*) prim; *success = true; } else if(n->nodeKind == AST_EXPR_CAST) { // TODO: Move all compile-time constant casting from ast_cast_expr to here. AST *what = n->exprCast.what; Type *to = n->exprCast.to; 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); } *nptr = ret; *success = true; } } } void ast_denoop(AST *tlc, AST **node) { if(!node) { node = &tlc; } struct DenoopState state = {.targetTLC = tlc}; do { state.success = false; generic_visitor(node, NULL, NULL, tlc, tlc, &state, denoop_visitor, NULL); } while(state.success); } /* * The convention correctness pass converts all function calls & function sources to the form * hat matches the architecture most closely. For example, arguments (and return values) in * cdecl are always passed as 32-bit integers, even if they are defined as 8-bit or 16-bit in * the source. * * TODO: convert records to proper form also. */ static void convention_correctness_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) { if(tlc != ud) { return; } AST *n = *nptr; if(n->nodeKind == AST_EXPR_CALL) { Type *type = n->exprCall.what->expression.type; assert(type->type == TYPE_TYPE_POINTER); type = type->pointer.of; assert(type->type == TYPE_TYPE_FUNCTION); for(size_t i = 0; i < type->function.argCount; i++) { if(type->function.args[i]->type == TYPE_TYPE_PRIMITIVE) { n->exprCall.args[i] = ast_cast_expr(n->exprCall.args[i], type_prim_cast(type->function.args[i], 8 * x86_max_gpr_size())); } } } } void dumben_pre(AST *tlc) { generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, convention_correctness_visitor, NULL); generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, pre_dumb_visitor, NULL); generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, decompose_symbol_record_field_access, NULL); for(size_t t = 0; t < tlc->chunk.varCount;) { if(tlc->chunk.vars[t]->type->type == TYPE_TYPE_RECORD) { ast_spill_to_stack(tlc, tlc->chunk.vars[t]); } else { t++; } } ast_denoop(tlc, NULL); ast_commutativity_pass(tlc); } void dumben_go(AST* tlc) { size_t i = 0; while(1) { if(i == 20000) { stahp(0, 0, "TOO MANY DUMBS. TOO MANY DUMBS."); } struct DumbenState state = {.targetTLC = tlc}; generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, dumben_visitor, NULL); int successful = state.effective; if(!successful) { break; } if(ntc_get_int("pdbg")) { fprintf(stderr, "### DUMBED DOWN %lu ###\n", i++); char *astdump = ast_dump(tlc); fputs(astdump, stderr); free(astdump); } } }