#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.reachingDefs = NULL; 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; } } struct DumbenState { int effective; }; static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud) { struct DumbenState *this = ud; AST *s = *nptr; 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, 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 { s->stmtIf.expression = varify(tlc, chu, stmtPrev, s, e); this->effective = 1; } } else if(s->nodeKind == AST_STMT_LOOP) { } else if(s->nodeKind == AST_STMT_RETURN) { // ret specifically 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 && !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; vte_precolor(retval->exprVar.thing, REG_CLASS_16_32, 1); } } 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); vte_precolor(s->stmtExpr.expr->exprVar.thing, REG_CLASS_16_32, 1); // 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); vte_precolor(tmp, REG_CLASS_16_32, 1); 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) { ASTExprCall *call = &s->stmtAssign.to->exprCall; int argCount = call->what->expression.type->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; } } } 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); vte_precolor(hihalf->exprVar.thing, REG_CLASS_16_32, 7); } 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; vte_precolor(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, REG_CLASS_16_32, 1); 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 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; } } } } } struct DenoopState { AST *targetTLC; bool success; }; 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 = primitive_parse("u32"); offset->val = 4 + i * 4; 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*) deref; 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; } 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` *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_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 *nptr = n->exprCast.what; *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; } } void dumben_pre(AST *tlc) { 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++; } } if(ntc_get_int("pdbg")) { char *astdump = ast_dump(tlc); fprintf(stderr, "### BEFORE DENOOP ###\n%s\n", astdump); free(astdump); } bool success; do { success = false; generic_visitor(&tlc, NULL, NULL, tlc, tlc, &success, denoop_visitor, NULL); } while(success); 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; memset(&state, 0, sizeof(state)); 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); } } }