nctref/src/dumberdowner.c
2025-05-03 10:00:20 +03:00

518 lines
18 KiB
C

#include"dumberdowner.h"
#include<stdlib.h>
#include<assert.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.
#include"x86.h"
#include<string.h>
static VarTableEntry *create_dumbtemp(AST *tlc, Type *itstype) {
static size_t vidx = 0;
VarTableEntry *vte = calloc(1, sizeof(*vte));
vte->kind = VARTABLEENTRY_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) {
VarTableEntry *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);
}
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 == VARTABLEENTRY_VAR && retval->exprVar.thing->data.var.color != COLOR_EAX))) {
retval = s->stmtReturn.val = varify(tlc, chu, stmtPrev, s, retval);
this->effective = 1;
vte_precolor(retval->exprVar.thing, COLOR_EAX);
}
} 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, COLOR_EAX);
// 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 != VARTABLEENTRY_VAR)) {
VarTableEntry *tmp = create_dumbtemp(tlc, s->stmtAssign.what->expression.type);
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);
this->effective = 1;
} else if(s->stmtAssign.what && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_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);
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, COLOR_EDX);
}
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, COLOR_EAX);
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;
}
}
}
}
}
static void pre_dumb_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *n = *nptr;
if(n == tlc) {
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++) {
VarTableEntry *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) {
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) {
AST *n = *nptr;
bool *success = ud;
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;
}
}
void dumben_pre(AST *tlc) {
generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, pre_dumb_visitor, NULL);
generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, decompose_symbol_record_field_access, NULL);
for(size_t t = 0; t < tlc->chunk.varCount; t++) {
if(tlc->chunk.vars[t]->type->type == TYPE_TYPE_RECORD) {
ast_spill_to_stack(tlc, tlc->chunk.vars[t]);
}
}
bool success;
do {
success = false;
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &success, denoop_visitor, NULL);
} while(success);
}
void dumben_go(AST* tlc) {
size_t i = 0;
while(1) {
if(i == 20000) {
puts("TOO MANY DUMBS");
abort();
}
fprintf(stderr, "; Dumbing down %lu...\n", i++);
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;
}
fputs(ast_dump(tlc), stderr);
}
}