More AST functions (incl new second-class record pass)

This commit is contained in:
Mid 2025-10-09 11:35:35 +03:00
parent b21ce51435
commit dfbadfecb9
7 changed files with 143 additions and 37 deletions

View File

@ -285,3 +285,55 @@ AST *ast_cast_expr(AST *what, Type *to) {
fail:
stahp_node(what, "Cannot cast type %s into %s", type_to_string(what->expression.type), type_to_string(to));
}
struct ReferencesStackState {
bool yes;
};
static void references_stack_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *n = *nptr;
if(n->nodeKind == AST_EXPR_STACK_POINTER) {
((struct ReferencesStackState*) ud)->yes = true;
}
}
bool ast_references_stack(AST *a) {
struct ReferencesStackState state = {};
generic_visitor(&a, NULL, NULL, NULL, NULL, &state, references_stack_visitor, NULL);
return state.yes;
}
struct IsScopeItemReferenced {
bool yes;
ScopeItem *si;
};
static void is_scopeitem_referenced_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct IsScopeItemReferenced *state = ud;
AST *n = *aptr;
if(n->nodeKind == AST_EXPR_UNARY_OP) {
if(n->exprUnOp.operand->nodeKind == AST_EXPR_VAR && n->exprUnOp.operator == UNOP_REF && n->exprUnOp.operand->exprVar.thing == state->si) {
state->yes = true;
}
}
}
bool ast_is_scopeitem_referenced(AST *tlc, ScopeItem *si) {
struct IsScopeItemReferenced state = {.si = si};
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, NULL, is_scopeitem_referenced_visitor);
return state.yes;
}
ScopeItem *ast_tlc_new_var(AST *tlc, char *name, Type *itstype) {
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 = name;
// 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;
}

View File

@ -381,6 +381,13 @@ void ast_typecheck(AST *tlc);
AST *ast_get_label_by_name(AST *tlc, const char *name);
bool ast_references_stack(AST *a);
bool ast_is_scopeitem_referenced(AST *tlc, ScopeItem *si);
// name becomes OWNED
ScopeItem *ast_tlc_new_var(AST *tlc, char *name, Type *itstype);
#include"dump.h"
#include"stack.h"
#include"desegment.h"
@ -388,5 +395,6 @@ AST *ast_get_label_by_name(AST *tlc, const char *name);
#include"linearize.h"
#include"usedef.h"
#include"commutativity.h"
#include"scr.h"
#endif

61
src/ast/scr.c Normal file
View File

@ -0,0 +1,61 @@
#include"sroa.h"
#include"ast.h"
#include"ntc.h"
#include"utils.h"
#include<stdlib.h>
#include<assert.h>
static bool is_sroa_candidate(ScopeItem *si) {
return si->type->type == TYPE_TYPE_RECORD && type_size(si->type) <= ntc_get_int_default("sroa-threshold", 32);
}
struct SCRState {
AST *tlc;
};
static void ast_scr_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *n = *aptr;
struct SCRState *state = ud;
if(state->tlc != tlc) return;
if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.what->expression.type->type == TYPE_TYPE_RECORD) {
Type *rectype = n->stmtAssign.what->expression.type;
for(size_t f = 0; f < rectype->record.fieldCount; f++) {
ASTExprDot *dot1 = calloc(1, sizeof(*dot1));
dot1->nodeKind = AST_EXPR_DOT;
dot1->type = rectype->record.fieldTypes[f];
dot1->a = ast_deep_copy(n->stmtAssign.what);
dot1->b = strdup(rectype->record.fieldNames[f]);
ASTExprDot *dot2 = calloc(1, sizeof(*dot2));
dot2->nodeKind = AST_EXPR_DOT;
dot2->type = rectype->record.fieldTypes[f];
dot2->a = ast_deep_copy(n->stmtAssign.to);
dot2->b = strdup(rectype->record.fieldNames[f]);
ASTStmtAssign *assign = calloc(1, sizeof(*assign));
assign->nodeKind = AST_STMT_ASSIGN;
assign->what = (AST*) dot1;
assign->to = (AST*) dot2;
stmtPrev->statement.next = (AST*) assign;
stmtPrev = assign;
}
stmtPrev->statement.next = stmt->statement.next;
}
}
void ast_secondclass_record(AST *tlc) {
struct SCRState state = {.tlc = tlc};
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, ast_scr_visitor, NULL);
if(ntc_get_int("pdbg")) {
char *astdump = ast_dump(tlc);
fprintf(stderr, "### SCR ###\n%s\n", astdump);
free(astdump);
}
}

12
src/ast/scr.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
// In machine code structs/records are second-class concepts.
// Either the record gets split into its separate fields (SRoA),
// or it must be spilled into memory.
//
// In any case, many QoL features programmers love (assigning structs to
// structs or passing structs as arguments to functions) don't exist in
// machine code and must be converted to a valid but equivalent form.
union AST;
void ast_secondclass_record(union AST *tlc);

View File

@ -10,26 +10,6 @@ static bool is_sroa_candidate(ScopeItem *si) {
return si->type->type == TYPE_TYPE_RECORD && type_size(si->type) <= ntc_get_int_default("sroa-threshold", 32);
}
struct IsScopeItemReferenced {
bool yes;
ScopeItem *si;
};
static void is_scopeitem_referenced_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct IsScopeItemReferenced *state = ud;
AST *n = *aptr;
if(n->nodeKind == AST_EXPR_UNARY_OP) {
if(n->exprUnOp.operand->nodeKind == AST_EXPR_VAR && n->exprUnOp.operator == UNOP_REF && n->exprUnOp.operand->exprVar.thing == state->si) {
state->yes = true;
}
}
}
static bool is_scopeitem_referenced(AST *tlc, ScopeItem *si) {
struct IsScopeItemReferenced state = {.si = si};
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, NULL, is_scopeitem_referenced_visitor);
return state.yes;
}
struct DecomposeAutomaticRecordState {
AST *tlc;
ScopeItem *target;
@ -89,7 +69,7 @@ void ast_sroa(AST *tlc) {
continue;
}
if(is_scopeitem_referenced(tlc, si)) {
if(ast_is_scopeitem_referenced(tlc, si)) {
continue;
}
@ -101,4 +81,10 @@ void ast_sroa(AST *tlc) {
/* Restart */
i = -1;
}
if(ntc_get_int("pdbg")) {
char *astdump = ast_dump(tlc);
fprintf(stderr, "### SROA ###\n%s\n", astdump);
free(astdump);
}
}

View File

@ -97,6 +97,7 @@ int main(int argc_, char **argv_) {
}
ast_segmented_dereference(chunk);
ast_secondclass_record(chunk);
ast_sroa(chunk);
ast_linearize(chunk);

View File

@ -16,21 +16,7 @@
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;
return ast_tlc_new_var(tlc, malp("$dumb%lu", vidx++), itstype);
}
/* Split away complex expression into a new local variable */
@ -781,7 +767,7 @@ void dumben_pre(AST *tlc) {
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) {
if(ast_is_scopeitem_referenced(tlc, tlc->chunk.vars[t]) || tlc->chunk.vars[t]->type->type == TYPE_TYPE_RECORD) {
ast_spill_to_stack(tlc, tlc->chunk.vars[t]);
} else {
t++;