Restructure source tree, SRoA and variable pointer size

This commit is contained in:
Mid 2025-06-30 20:37:51 +03:00
parent 438c3b3467
commit 923ec25d79
16 changed files with 413 additions and 748 deletions

155
src/ast.c
View File

@ -7,10 +7,9 @@
#include<stdarg.h>
#include"ntc.h"
#include"reporting.h"
#include"utils.h"
const char *AST_KIND_STR[] = {
AST_KINDS(GEN_STRI)
};
const char *AST_KIND_STR[] = { AST_KINDS(GEN_STRI) };
void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud, GenericVisitorHandler preHandler, GenericVisitorHandler postHandler) {
if(preHandler) preHandler(nptr, stmt, stmtPrev, chu, tlc, ud);
@ -85,7 +84,7 @@ void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, v
generic_visitor(&n->exprExtSizeOf.ofExpr, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
}
} else {
abort();
stahp_node(n, "generic_visitor: unhandled %s", AST_KIND_STR[n->nodeKind]);
}
if(postHandler) postHandler(nptr, stmt, stmtPrev, chu, tlc, ud);
@ -398,7 +397,7 @@ static void ast_usedef_pass(AST *tlc, AST *a, AST *wholestmt) {
ast_usedef_pass(tlc, a->stmtReturn.val, wholestmt);
}
} else {
abort();
stahp_node(a, "ast_usedef_pass: unhandled %s", AST_KIND_STR[a->nodeKind]);
}
}
@ -418,20 +417,6 @@ void ast_usedef_reset(AST *chu) {
return ast_usedef_pass(chu, chu, NULL);
}
__attribute__((format(printf, 1, 2))) char *malp(const char *fmt, ...) {
va_list v1, v2;
va_start(v1, fmt);
va_copy(v2, v1);
size_t len = vsnprintf(NULL, 0, fmt, v1);
va_end(v1);
va_start(v2, fmt);
char *str = malloc(len + 1);
vsnprintf(str, len + 1, fmt, v2);
str[len] = 0;
va_end(v2);
return str;
}
char *type_to_string(Type *t) {
if(t->type == TYPE_TYPE_PRIMITIVE) {
char ret[16] = {};
@ -893,39 +878,8 @@ void ast_grow_stack_frame(AST *tlc, size_t bytes) {
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, spill2stack_visitor, NULL);
}
static void typecheck_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *a = *aptr;
if(a->nodeKind == AST_EXPR_CALL) {
if(a->exprCall.what->expression.type->type != TYPE_TYPE_FUNCTION) {
stahp_node(a, "Only function types may be called.");
}
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
if(!type_is_number(a->exprBinOp.operands[0]->expression.type) || !type_is_number(a->exprBinOp.operands[1]->expression.type)) {
stahp_node(a, "Operands must be numbers.");
}
if(type_size(a->exprBinOp.operands[0]->expression.type) < type_size(a->exprBinOp.operands[1]->expression.type)) {
a->exprBinOp.operands[0] = ast_cast_expr(a->exprBinOp.operands[0], a->exprBinOp.operands[1]->expression.type);
}
if(type_size(a->exprBinOp.operands[1]->expression.type) < type_size(a->exprBinOp.operands[0]->expression.type)) {
a->exprBinOp.operands[1] = ast_cast_expr(a->exprBinOp.operands[1], a->exprBinOp.operands[0]->expression.type);
}
if(!a->exprBinOp.type) {
a->exprBinOp.type = a->exprBinOp.operands[0]->expression.type;
}
} else if(a->nodeKind == AST_EXPR_UNARY_OP) {
}
}
void ast_type_check(AST *tlc, ScopeItem *vte) {
generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, NULL, typecheck_visitor);
}
// The commutativity pass makes sure that integer literals are always operand 1 in a binary operation
// This simplifies passes further down
// This pass makes sure that integer literals are always operand 1 in a binary operation,
// simplifying passes further down
static void commutativity_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *n = *nptr;
@ -942,3 +896,100 @@ static void commutativity_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu
void ast_commutativity_pass(AST *tlc) {
generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, NULL, commutativity_visitor);
}
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;
ScopeItem **replacements;
};
static void ast_decompose_automatic_record_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *n = *aptr;
struct DecomposeAutomaticRecordState *state = ud;
if(state->tlc != tlc) return;
if(n->nodeKind == AST_EXPR_DOT && n->exprDot.a->nodeKind == AST_EXPR_VAR && n->exprDot.a->exprVar.thing == state->target) {
size_t idx;
for(idx = 0; strcmp(state->target->type->record.fieldNames[idx], n->exprDot.b); idx++);
AST *ev = calloc(1, sizeof(ASTExprVar));
ev->nodeKind = AST_EXPR_VAR;
ev->expression.type = state->target->type->record.fieldTypes[idx];
ev->exprVar.thing = state->replacements[idx];
*aptr = (AST*) ev;
}
}
static void ast_decompose_automatic_record(AST *tlc, ScopeItem *target) {
assert(target->kind == SCOPEITEM_VAR);
assert(target->type->type == TYPE_TYPE_RECORD);
struct DecomposeAutomaticRecordState state = {
.tlc = tlc,
.target = target,
.replacements = calloc(target->type->record.fieldCount, sizeof(ScopeItem)),
};
tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount + target->type->record.fieldCount));
for(size_t f = 0; f < target->type->record.fieldCount; f++) {
ScopeItem *si = calloc(1, sizeof(*si));
si->kind = SCOPEITEM_VAR;
si->type = target->type->record.fieldTypes[f];
si->data.var.name = malp("%s_sroa_%s", target->data.var.name, target->type->record.fieldNames[f]);
state.replacements[f] = si;
tlc->chunk.vars[tlc->chunk.varCount++] = si;
}
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, ast_decompose_automatic_record_visitor, NULL);
free(state.replacements);
}
void ast_sroa(AST *tlc) {
for(size_t i = 0; i < tlc->chunk.varCount; i++) {
ScopeItem *si = tlc->chunk.vars[i];
if(!is_sroa_candidate(si)) {
continue;
}
if(is_scopeitem_referenced(tlc, si)) {
continue;
}
ast_decompose_automatic_record(tlc, si);
memmove(tlc->chunk.vars + i, tlc->chunk.vars + i + 1, sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount - i - 1));
tlc->chunk.varCount--;
/* Restart */
i = 0;
}
}

View File

@ -3,7 +3,7 @@
#include"types.h"
#include"lexer.h"
#include"vartable.h"
#include"scope.h"
#pragma pack(push, 1)
@ -19,13 +19,11 @@
#define AST_KINDS(K) \
K(AST_CHUNK) \
K(AST_STMT_DECL) \
K(AST_TYPE_IDENTIFIER) \
K(AST_EXPR_PRIMITIVE) \
K(AST_STMT_IF) \
K(AST_EXPR_BINARY_OP) \
K(AST_EXPR_VAR) \
K(AST_EXPR_STACK_POINTER) \
K(AST_TYPE_POINTER) \
K(AST_EXPR_UNARY_OP) \
K(AST_STMT_LOOP) \
K(AST_STMT_BREAK) \
@ -177,25 +175,6 @@ typedef struct {
size_t endTokI;
} ASTExprFunc;
typedef struct {
ASTBase;
size_t size;
} ASTType;
typedef struct {
ASTType;
Token identifier;
} ASTTypeIdentifier;
typedef struct {
ASTType;
union AST *child;
int levels;
} ASTTypePointer;
typedef struct {
ASTBase;
union AST *next;
@ -378,6 +357,8 @@ void ast_typecheck(AST *tlc);
void ast_grow_stack_frame(AST *tlc, size_t bytes);
__attribute__((format(printf, 1, 2))) char *malp(const char *fmt, ...);
void ast_commutativity_pass(AST *tlc);
void ast_sroa(AST *tlc);
#endif

View File

@ -1,8 +0,0 @@
#ifndef H_CG
#define H_CG
#include"ast.h"
int cg_go(union AST*);
#endif

View File

@ -1,6 +0,0 @@
#pragma once
#include"ast.h"
void dumben_pre(AST *tlc);
void dumben_go(AST* tlc);

View File

@ -7,15 +7,12 @@
#include"parse.h"
#include"ntc.h"
#include"reporting.h"
#include"cg.h"
#include"dumberdowner.h"
#include"x86.h"
#include"utils.h"
static int argc;
static char **argv;
static char **includePaths;
static const char **includePaths;
const char **ntc_get_import_paths() {
return (const char**) includePaths;
}
@ -35,15 +32,19 @@ const char* ntc_get_arg(const char *name) {
}
intmax_t ntc_get_int(const char *name) {
return ntc_get_int_default(name, 0);
}
intmax_t ntc_get_int_default(const char *name, intmax_t def) {
const char *val = ntc_get_arg(name);
if(!val) {
return 0;
return def;
}
long result = 0;
if(!unstupid_strtol(val, (const char**) &val, 0, &result)) {
return 0;
if(!unstupid_strtol(val, (char**) &val, 0, &result)) {
return def;
}
return result;
@ -53,7 +54,7 @@ int main(int argc_, char **argv_) {
argc = argc_;
argv = argv_;
if(x86_target() == IUNKNOWN86) {
if(!arch_verify_target()) {
stahp(0, 0, "Unknown architecture %s", ntc_get_arg("target"));
}
@ -93,17 +94,12 @@ int main(int argc_, char **argv_) {
free(astdump);
}
ast_sroa(chunk);
dumben_pre(chunk);
dumben_go(chunk);
while(!cg_go(chunk)) {
dumben_go(chunk);
if(ntc_get_int("pdbg")) {
char *astdump = ast_dump(chunk);
fprintf(stderr, "### CG FAILURE ###\n%s\n", astdump);
free(astdump);
}
}
return 0;

View File

@ -3,11 +3,20 @@
#include<stddef.h>
#include<stdint.h>
#include<stdbool.h>
const char **ntc_get_import_paths();
const char* ntc_get_arg(const char *name);
intmax_t ntc_get_int(const char *name);
intmax_t ntc_get_int_default(const char *name, intmax_t def);
union AST;
bool arch_verify_target();
int arch_ptr_size();
void dumben_pre(union AST *tlc);
void dumben_go(union AST *tlc);
int cg_go(union AST *tlc);
#endif

View File

@ -4,11 +4,11 @@
#include<stdlib.h>
#include<string.h>
#include"utils.h"
#include"vartable.h"
#include"scope.h"
#include"reporting.h"
#include<stdint.h>
#include<signal.h>
#include"x86.h"
#include"ntc.h"
#ifndef __GNUC__
static inline int __builtin_clzl(unsigned long x) {
@ -177,9 +177,10 @@ static AST *exprvar(Parser *P, ScopeItem *v) {
return a;
}
ASTChunk *nct_parse_chunk(Parser*, int, int, Scope*, Type *ft);
Type *nct_parse_typename(Parser *P);
static ASTChunk *nct_parse_chunk(Parser*, int, int, Scope*, Type *ft);
static Type *nct_parse_typename(Parser *P);
static bool parse_parametrization(Parser *P);
static void parse_genericization(Parser *P);
static char *parametrize_function_name(Type *t, const char *original, Scope *scope);
static void binop_implicit_cast(/*Parser *P, */ASTExprBinaryOp *binop) {
@ -571,8 +572,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
astop->type = NULL;
astop->operator = op;
astop->operands[0] = ret;
ASTExpr *operand = &(astop->operands[1] = nct_parse_expression(P, lOP + 1))->expression;
astop->operands[1] = nct_parse_expression(P, lOP + 1);
if(!type_is_number(astop->operands[0]->expression.type)
|| !type_is_number(astop->operands[1]->expression.type)) {
@ -713,7 +713,7 @@ backtrack:
}
/* Since this function backtracks, don't use aborting functions like expect. */
Type *nct_parse_typename(Parser *P) {
static Type *nct_parse_typename(Parser *P) {
int oldIdx = P->i;
bool generics = peek(P, 0).type == TOKEN_SQUAREN_L;
@ -1028,47 +1028,7 @@ static char *parametrize_function_name(Type *t, const char *original, Scope *sco
return s;
}
/*static void add_parametrizations_to_scope(Parser *P, Parametrizations *parametrizations, Parametrizations *renames) {
Parametrization *c = parametrizations->typeParams;
Parametrization *g = renames->typeParams;
while(c && g) {
Type *ct = (Type*) c->param;
Type *gt = (Type*) g->param;
assert(!!ct == !!gt);
ScopeItem *vte = calloc(1, sizeof(*vte));
vte->kind = SCOPEITEM_TYPE;
vte->data.type.ptr = ct;
scope_set(P->scope, gt->generic.paramName, vte);
c = c->next;
g = g->next;
}
size_t idx = 0;
c = parametrizations->intParams;
g = renames->intParams;
while(c && g) {
AST *node = c->param;
char *name = g->param;
assert(!!node == !!name);
ScopeItem *vte = calloc(1, sizeof(*vte));
vte->kind = SCOPEITEM_CEXPR;
vte->data.cexpr.paramIdx = idx;
vte->data.cexpr.paramName = name;
vte->data.cexpr.concrete = node;
scope_set(P->scope, name, vte);
c = c->next;
g = g->next;
idx++;
}
}*/
void nct_parse_statement(Parser *P) {
static void nct_parse_statement(Parser *P) {
if(maybe(P, TOKEN_IF)) {
expect(P, TOKEN_PAREN_L);
AST *e = nct_parse_expression(P, 0);
@ -1369,7 +1329,7 @@ void nct_parse_statement(Parser *P) {
* This function inserts VTEs into the *current* scope.
* Make sure to create a child scope before using.
* */
void parse_genericization(Parser *P) {
static void parse_genericization(Parser *P) {
expect(P, TOKEN_SQUAREN_L);
bool integerMode = false;
@ -1602,7 +1562,7 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize, Scope *t
ret->chunk.vars = NULL;
ret->chunk.stackReservation = 0;
AST *oldChunk = (AST*) P->currentChunk;
ASTChunk *oldChunk = P->currentChunk;
P->currentChunk = &ret->chunk;
Scope *oldScope = P->scope;

View File

@ -12,7 +12,6 @@ static void stahp_va(int row, int column, const char *error, va_list l) {
fputc('\n', stderr);
}
/* Abort immediately on first error (for now) */
void stahp(int row, int column, const char *error, ...) {
va_list l;
va_start(l, error);

View File

@ -99,7 +99,7 @@ size_t type_size(Type *t) {
return w;
} else if(t->type == TYPE_TYPE_POINTER || t->type == TYPE_TYPE_FUNCTION) {
return 4;
return arch_ptr_size();
} else if(t->type == TYPE_TYPE_ARRAY) {
return type_size(t->array.of) * t->array.length;
} else if(t->type == TYPE_TYPE_RECORD) {

View File

@ -5,6 +5,8 @@
#include<stdbool.h>
#include<errno.h>
#include<stdlib.h>
#include<stdarg.h>
#include<stdio.h>
inline static size_t djb2(const char *str) {
size_t hash = 5381;
@ -38,4 +40,18 @@ inline static bool unstupid_strtol(const char *str, char **endptr, int base, lon
return true;
}
__attribute__((format(printf, 1, 2))) static inline char *malp(const char *fmt, ...) {
va_list v1, v2;
va_start(v1, fmt);
va_copy(v2, v1);
size_t len = vsnprintf(NULL, 0, fmt, v1);
va_end(v1);
va_start(v2, fmt);
char *str = malloc(len + 1);
vsnprintf(str, len + 1, fmt, v2);
str[len] = 0;
va_end(v2);
return str;
}
#endif

View File

@ -1,112 +0,0 @@
#include"vartable.h"
#include"utils.h"
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<stdio.h>
struct ReachingDefs *reachingdefs_push(struct ReachingDefs *this) {
struct ReachingDefs *ret = calloc(1, sizeof(*ret));
ret->parent = this;
return ret;
}
struct ReachingDefs *reachingdefs_coalesce(struct ReachingDefs *this) {
struct ReachingDefs *parent = this->parent;
if(parent) {
parent->defs = realloc(parent->defs, sizeof(*parent->defs) * (parent->defCount + this->defCount));
memcpy(&parent->defs[parent->defCount], this->defs, sizeof(*this->defs) * this->defCount);
parent->defCount += this->defCount;
}
free(this->defs);
free(this);
return parent;
}
void reachingdefs_set(struct ReachingDefs *this, union AST *def) {
this->defCount = 1;
this->defs = realloc(this->defs, sizeof(*this->defs) * this->defCount);
this->defs[0] = def;
this->excludeParent = 1;
}
Scope *scope_new(Scope *parent) {
Scope *ret = calloc(1, sizeof(*ret));
ret->parent = parent;
ret->count = 0;
ret->names = NULL;
ret->data = NULL;
return ret;
}
ScopeItem *scope_get(Scope *this, const char *name) {
for(size_t v = 0; v < this->count; v++) {
if(!strcmp(name, this->names[v])) return this->data[v];
}
return NULL;
}
ScopeItem *scope_find(Scope *this, const char *name) {
Scope *tbl = this;
while(tbl) {
ScopeItem *entry = scope_get(tbl, name);
if(entry) {
return entry;
}
tbl = tbl->parent;
}
return NULL;
}
ScopeItem *scope_set(Scope *this, const char *name, ScopeItem *e) {
name = strdup(name);
this->names = realloc(this->names, sizeof(*this->names) * (this->count + 1));
this->data = realloc(this->data, sizeof(*this->data) * (this->count + 1));
this->names[this->count] = name;
this->data[this->count] = e;
this->count++;
if(e->kind == SCOPEITEM_VAR) e->data.var.name = name;
e->owner = this;
return e;
}
ScopeItem *scope_find_int(Scope *scope, intmax_t val, const char *suffix) {
char buf[64];
snprintf(buf, sizeof(buf), "%li%s", val, suffix ? suffix : "");
return scope_find(scope, buf);
}
Scope *scope_merge(Scope *child) {
Scope *parent = child->parent;
parent->names = realloc(parent->names, sizeof(*parent->names) * (parent->count + child->count));
parent->data = realloc(parent->data, sizeof(*parent->data) * (parent->count + child->count));
for(size_t i = 0; i < child->count; i++) {
child->data[i]->owner = parent;
parent->names[parent->count] = child->names[i];
parent->data[parent->count] = child->data[i];
parent->count++;
}
//free(child->names);
//free(child->data);
//free(child);
return parent;
}
void vte_precolor(ScopeItem *vte, int class, int color) {
assert(vte->kind == SCOPEITEM_VAR && "vte must be var");
assert(!vte->data.var.precolored && "already precolored");
vte->data.var.precolored = true;
vte->data.var.registerClass = class;
vte->data.var.color = color;
}

View File

@ -1,111 +0,0 @@
#ifndef NCTREF_VARTABLE_H
#define NCTREF_VARTABLE_H
#include"types.h"
#include<stdbool.h>
struct Token;
union AST;
typedef enum {
SCOPEITEM_SYMBOL, SCOPEITEM_VAR, SCOPEITEM_TYPE, SCOPEITEM_CEXPR
} ScopeItemKind;
union AST;
typedef struct UseDef {
union AST *def; // assign stmt
union AST *use; // corresponding AST_EXPR_VAR
union AST *stmt; // whole using stmt
struct UseDef *next;
} UseDef;
// Stack, necessary for "possible reaching defs" such as from if statements
typedef struct ReachingDefs {
size_t defCount;
union AST **defs;
int excludeParent;
struct ReachingDefs *parent;
} ReachingDefs;
struct ReachingDefs *reachingdefs_push(struct ReachingDefs*);
struct ReachingDefs *reachingdefs_coalesce(struct ReachingDefs*);
void reachingdefs_set(struct ReachingDefs*, union AST*);
struct Scope;
typedef struct ScopeItem {
Type *type;
struct Scope *owner;
ScopeItemKind kind;
union {
struct {
char isLocal;
char isExternal;
const char *name;
struct {
struct Scope *scope;
struct Token *rangeTokens;
size_t startTokI;
size_t endTokI;
} genfunc;
} symbol;
struct {
// For debugging
const char *name;
// Register allocation
// vars in loops have higher priority
// a more advanced approach would be to use weights for different colors (e.g. multipliers "should" be in eax)
uint8_t priority;
int16_t color, degree;
bool precolored;
int registerClass;
// Used during parsing
ReachingDefs *reachingDefs;
// Optimizations
UseDef *usedefFirst;
UseDef *usedefLast;
} var;
struct {
Type *ptr;
} type;
struct {
// cexpr is used for expression parametization as opposed to type parametrization
// I don't like the idea of having a special ScopeItem kind for these, but all other places were worse
const char *paramName;
size_t paramIdx;
// If the cexpr has been parametrized (as opposed to just being a symbol), this field will be non-NULL
union AST *concrete;
} cexpr;
} data;
} ScopeItem;
typedef struct Scope {
struct Scope *parent;
size_t count;
const char **names;
ScopeItem **data;
} Scope;
Scope *scope_new(Scope*);
ScopeItem *scope_get(Scope*, const char*);
ScopeItem *scope_find(Scope*, const char*);
ScopeItem *scope_set(Scope*, const char*, ScopeItem*);
ScopeItem *scope_find_int(Scope*, intmax_t, const char*);
Scope *scope_merge(Scope *child);
void vte_precolor(ScopeItem *vte, int resclass, int color);
#endif

52
src/x86/arch.c Normal file
View File

@ -0,0 +1,52 @@
#include"arch.h"
RegisterClass REG_CLASSES[] = {
[REG_CLASS_8] = {
.rMask = HWR_GPR, .w = 1, .p = 8,
.rs = {HWR_AL, HWR_AH, HWR_BL, HWR_BH, HWR_CL, HWR_CH, HWR_DL, HWR_DH},
.rsN = {"al", "ah", "bl", "bh", "cl", "ch", "dl", "dh"},
.rsS = {1, 1, 1, 1, 1, 1, 1, 1},
},
[REG_CLASS_NOT_8] = {
.rMask = HWR_GPR, .w = 2, .p = 4,
.rs = {HWR_AX, HWR_EAX, HWR_BX, HWR_EBX, HWR_CX, HWR_ECX, HWR_DX, HWR_EDX, HWR_DI, HWR_EDI, HWR_SI, HWR_ESI},
.rsN = {"ax", "eax", "bx", "ebx", "cx", "ecx", "dx", "edx", "di", "edi", "si", "esi"},
.rsS = {2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4},
},
[REG_CLASS_16_32] = {
.rMask = HWR_GPR, .w = 2, .p = 4,
.rs = {HWR_AX, HWR_EAX, HWR_BX, HWR_EBX, HWR_CX, HWR_ECX, HWR_DX, HWR_EDX},
.rsN = {"ax", "eax", "bx", "ebx", "cx", "ecx", "dx", "edx"},
.rsS = {2, 4, 2, 4, 2, 4, 2, 4},
},
[REG_CLASS_IND] = {
.rMask = HWR_IND, .w = 2, .p = 2,
.rs = {HWR_DI, HWR_EDI, HWR_SI, HWR_ESI},
.rsN = {"di", "edi", "si", "esi"},
.rsS = {2, 4, 2, 4},
},
[REG_CLASS_IA16_PTRS] = {
.rMask = HWR_IND | HWR_BX,
.rs = {HWR_DI, HWR_SI, HWR_BX},
.rsN = {"di", "si", "bx"},
.rsS = {2, 2, 2},
},
[REG_CLASS_DATASEGS] = {
.rMask = HWR_IND | HWR_BX,
.rs = {HWR_DS, HWR_ES, HWR_FS, HWR_GS},
.rsN = {"ds", "es", "fs", "gs"},
.rsS = {2, 2, 2},
},
};
bool arch_verify_target() {
if(x86_target() == IUNKNOWN86) {
return false;
}
return true;
}
int arch_ptr_size() {
return x86_ia16() ? 2 : 4;
}

View File

@ -18,6 +18,11 @@
#define HWR_DI 256
#define HWR_SI 512
#define HWR_DS 1024
#define HWR_ES 2048
#define HWR_FS 4096
#define HWR_GS 8192
#define HWR_AX (HWR_AL | HWR_AH)
#define HWR_BX (HWR_BL | HWR_BH)
#define HWR_CX (HWR_CL | HWR_CH)
@ -50,7 +55,9 @@ typedef struct RegisterClass {
#define REG_CLASS_NOT_8 1
#define REG_CLASS_16_32 2
#define REG_CLASS_IND 3
extern RegisterClass REG_CLASSES[4];
#define REG_CLASS_IA16_PTRS 4
#define REG_CLASS_DATASEGS 5
extern RegisterClass REG_CLASSES[];
// lol
static inline bool is_reg_b(int cls, int res) {
@ -88,6 +95,58 @@ static inline void reg_cast_up(int *cls, int *res) {
}
}
typedef enum {
I086 = 0,
I186 = 1,
I286 = 2,
I386 = 3,
IUNKNOWN86 = 255,
} X86Target;
static inline X86Target x86_target() {
const char *str = ntc_get_arg("target");
if(!str) return I386;
if(!strcmp(str, "086")) {
return I086;
}
if(!strcmp(str, "186")) {
return I186;
}
if(!strcmp(str, "286")) {
return I286;
}
if(!strcmp(str, "386")) {
return I386;
}
return IUNKNOWN86;
}
static inline bool x86_ia16() {
return x86_target() < I386;
}
static inline size_t x86_max_gpr_size() {
switch(x86_target()) {
case I086:
case I186:
case I286:
return 2;
case I386:
return 4;
default: abort();
}
}
static inline bool x86_imul_supported() {
return x86_target() >= I186;
}
// Can expression be expressed as a single x86 operand?
#define XOP_NOT_XOP 0
#define XOP_NOT_MEM 1
@ -113,6 +172,12 @@ static inline int is_xop(AST *e) {
}
if(c->nodeKind == AST_EXPR_VAR && c->exprVar.thing->kind == SCOPEITEM_VAR) {
if(x86_ia16()) { // In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
if(!c->exprVar.thing->data.var.preclassed || c->exprVar.thing->data.var.registerClass != REG_CLASS_IA16_PTRS) {
return XOP_NOT_XOP;
}
}
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) ||
@ -150,54 +215,6 @@ typedef enum {
GUCCI,
} WhysItBad_Huh;
typedef enum {
I086 = 0,
I186 = 1,
I286 = 2,
I386 = 3,
IUNKNOWN86 = 255,
} X86Target;
static inline X86Target x86_target() {
const char *str = ntc_get_arg("target");
if(!str) return I386;
if(!strcmp(str, "086")) {
return I086;
}
if(!strcmp(str, "186")) {
return I186;
}
if(!strcmp(str, "286")) {
return I286;
}
if(!strcmp(str, "386")) {
return I386;
}
return IUNKNOWN86;
}
static inline size_t x86_max_gpr_size() {
switch(x86_target()) {
case I086:
case I186:
case I286:
return 2;
case I386:
return 4;
default: abort();
}
}
static inline bool x86_imul_supported() {
return x86_target() >= I186;
}
static inline WhysItBad_Huh x86_test_mul_matching_mulhi(AST *mulhi, AST *mul) {
assert(mulhi->statement.next == mul);

View File

@ -1,14 +1,12 @@
#include"cg.h"
#include<stdlib.h>
#include<signal.h>
#include<string.h>
#include<assert.h>
#include"dumberdowner.h"
#include"ntc.h"
#include"reporting.h"
#include"ast.h"
#include"x86.h"
#include"arch.h"
#include"utils.h"
static const char *BINOP_SIMPLE_INSTRS[] = {[BINOP_ADD] = "add", [BINOP_SUB] = "sub", [BINOP_BITWISE_AND] = "and", [BINOP_BITWISE_OR] = "or", [BINOP_BITWISE_XOR] = "xor"};
@ -65,6 +63,8 @@ static const char *specexpr(AST *e) {
static const char *xv_sz(ScopeItem *v, int sz) {
assert(v->kind == SCOPEITEM_VAR);
if(sz == 0) sz = arch_ptr_size();
#define XVBUFS 8
#define XVBUFSZ 16
static char bufs[XVBUFS][XVBUFSZ];
@ -137,11 +137,6 @@ static const char *xop_sz(AST *tlc, AST *e, int sz) {
char *ret = bufs[bufidx];
//if(e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER) {
/*if(e->nodeKind == AST_EXPR_CAST) {
e = e->exprCast.what;
}*/
if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF) {
AST *p = e->exprUnOp.operand;
@ -152,13 +147,13 @@ static const char *xop_sz(AST *tlc, AST *e, int sz) {
if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprVar.thing->kind == SCOPEITEM_VAR && p->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
xv_sz(p->exprBinOp.operands[0]->exprVar.thing, 4),
xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 4));
xv_sz(p->exprBinOp.operands[0]->exprVar.thing, 0),
xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 0));
} else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && p->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == SCOPEITEM_SYMBOL && p->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 4));
xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 0));
} else if(is_field_access(e)) {
e = is_field_access(e);
@ -184,9 +179,9 @@ static const char *xop_sz(AST *tlc, AST *e, int sz) {
spec(sz),
p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
p->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val,
xv_sz(p->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing, 4));
xv_sz(p->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing, 0));
} else if(p->nodeKind == AST_EXPR_VAR && p->exprVar.thing->kind == SCOPEITEM_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(p->exprVar.thing, 4));
snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(p->exprVar.thing, 0));
} else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_STACK_POINTER && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
snprintf(ret, XOPBUFSZ, "[esp + %i]", p->exprBinOp.operands[1]->exprPrim.val);
} else {
@ -291,6 +286,8 @@ void cg_chunk(CGState *cg, AST *a) {
// Generic functions have non-NULL code blocks
if(!type_is_generic(s->stmtDecl.expression->expression.type)) {
ast_sroa(s->stmtDecl.expression->exprFunc.chunk);
dumben_pre(s->stmtDecl.expression->exprFunc.chunk);
dumben_go(s->stmtDecl.expression->exprFunc.chunk);
@ -397,8 +394,8 @@ void cg_chunk(CGState *cg, AST *a) {
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing->kind == SCOPEITEM_VAR) {
printf("lea %s, [%s + %i]\n",
xv_sz(s->stmtAssign.what->exprVar.thing, 4),
xv_sz(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, 4),
xv_sz(s->stmtAssign.what->exprVar.thing, 0),
xv_sz(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, 0),
s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val);
} else if(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)) {
@ -501,174 +498,6 @@ void cg_chunk(CGState *cg, AST *a) {
}
}
struct Spill2VarState {
ScopeItem *target;
};
static void spill2var_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
static size_t vidx = 0;
struct Spill2VarState *this = ud;
AST *a = *aptr;
if(a->nodeKind == AST_STMT_ASSIGN && a->stmtAssign.to->nodeKind == AST_EXPR_CALL) {
assert(a->stmtAssign.what->nodeKind == AST_EXPR_VAR && a->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR);
if(a->stmtAssign.what->exprVar.thing == this->target) {
ScopeItem *new = calloc(1, sizeof(*new));
new->kind = SCOPEITEM_VAR;
new->type = a->stmtAssign.what->exprVar.thing->type;
new->data.var.name = malp("$s2v_%lu", vidx++);
tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount));
tlc->chunk.vars[tlc->chunk.varCount - 1] = new;
ASTExprVar *ev0 = calloc(1, sizeof(*ev0));
ev0->nodeKind = AST_EXPR_VAR;
ev0->type = new->type;
ev0->thing = a->stmtAssign.what->exprVar.thing;
ASTExprVar *ev1 = calloc(1, sizeof(*ev1));
ev1->nodeKind = AST_EXPR_VAR;
ev1->type = new->type;
ev1->thing = new;
ASTStmtAssign *ass = calloc(1, sizeof(*ass));
ass->nodeKind = AST_STMT_ASSIGN;
ass->what = (AST*) ev0;
ass->to = (AST*) ev1;
a->stmtAssign.what->exprVar.thing = new;
ass->next = a->statement.next;
a->statement.next = (AST*) ass;
if(!ass->next) {
chunk->chunk.statementLast = (AST*) ass;
}
}
}
if(a->nodeKind == AST_STMT_RETURN && a->stmtReturn.val && a->stmtReturn.val->nodeKind == AST_EXPR_VAR && a->stmtReturn.val->exprVar.thing == this->target) {
ScopeItem *new = calloc(1, sizeof(*new));
new->kind = SCOPEITEM_VAR;
new->type = a->stmtReturn.val->exprVar.thing->type;
new->data.var.name = malp("$s2v_%lu", vidx++);
tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount));
tlc->chunk.vars[tlc->chunk.varCount - 1] = new;
ASTExprVar *ev0 = calloc(1, sizeof(*ev0));
ev0->nodeKind = AST_EXPR_VAR;
ev0->type = new->type;
ev0->thing = a->stmtReturn.val->exprVar.thing;
ASTExprVar *ev1 = calloc(1, sizeof(*ev1));
ev1->nodeKind = AST_EXPR_VAR;
ev1->type = new->type;
ev1->thing = new;
ASTStmtAssign *ass = calloc(1, sizeof(*ass));
ass->nodeKind = AST_STMT_ASSIGN;
ass->what = (AST*) ev1;
ass->to = (AST*) ev0;
stmt->stmtReturn.val->exprVar.thing = new;
ass->next = stmt;
if(stmtPrev) {
stmtPrev->statement.next = (AST*) ass;
} else {
chunk->chunk.statementFirst = (AST*) ass;
}
}
}
/*struct PrecolorState {
ScopeItem *mustBeSpilled;
};
static void precolor_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct PrecolorState *this = ud;
if(this->mustBeSpilled) {
// Since something must be spilled first, we can't do anything else. Quit ASAP.
return;
}
AST *n = *nptr;
if(n == tlc) {
for(size_t i = 0; i < n->chunk.varCount; i++) {
n->chunk.vars[i]->data.var.color = -1;
n->chunk.vars[i]->data.var.precolored = false;
}
}
if(n->nodeKind == AST_STMT_RETURN && n->stmtReturn.val) {
assert(n->stmtReturn.val->nodeKind == AST_EXPR_VAR && n->stmtReturn.val->exprVar.thing->kind == SCOPEITEM_VAR);
ScopeItem *vte = n->stmtReturn.val->exprVar.thing;
const int requiredColor = COLOR_EAX;
if(vte->data.var.precolored && vte->data.var.color != requiredColor) {
// Precoloring collision!
this->mustBeSpilled = vte;
return;
}
vte->data.var.color = requiredColor;
vte->data.var.precolored = true;
} else if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.to->nodeKind == AST_EXPR_CALL) {
assert(n->stmtAssign.what->nodeKind == AST_EXPR_VAR && n->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR);
ScopeItem *vte = n->stmtAssign.what->exprVar.thing;
const int requiredColor = COLOR_EAX;
if(vte->data.var.precolored && vte->data.var.color != requiredColor) {
// Precoloring collision!
this->mustBeSpilled = vte;
return;
}
vte->data.var.color = requiredColor;
vte->data.var.precolored = true;
} else if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.what->nodeKind == AST_EXPR_VAR && n->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR && n->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && n->stmtAssign.to->exprBinOp.operator == BINOP_MUL) {
ScopeItem *vte = n->stmtAssign.what->exprVar.thing;
const int requiredColor = COLOR_EAX;
if(vte->data.var.precolored && vte->data.var.color != requiredColor) {
// Precoloring collision!
this->mustBeSpilled = vte;
return;
}
vte->data.var.color = requiredColor;
vte->data.var.precolored = true;
} else if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.what->nodeKind == AST_EXPR_VAR && n->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR && n->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && n->stmtAssign.to->exprBinOp.operator == BINOP_MULHI) {
ScopeItem *vte = n->stmtAssign.what->exprVar.thing;
const int requiredColor = COLOR_EDX;
if(vte->data.var.precolored && vte->data.var.color != requiredColor) {
// Precoloring collision!
this->mustBeSpilled = vte;
return;
}
vte->data.var.color = requiredColor;
vte->data.var.precolored = true;
}
}*/
typedef ScopeItem *Adjacency[2];
static bool var_collision(AST *tlc, ScopeItem *v1, ScopeItem *v2) {
@ -686,51 +515,6 @@ static bool var_collision(AST *tlc, ScopeItem *v1, ScopeItem *v2) {
return liveRangeIntersection && resourceIntersection;
}
/*static ScopeItem *get_precolor_spill_candidate(AST *tlc) {
Adjacency *adjs = NULL;
size_t adjCount = 0;
for(size_t i = 0; i < tlc->chunk.varCount; i++) {
tlc->chunk.vars[i]->data.var.degree = 0;
for(size_t j = 0; j < tlc->chunk.varCount; j++) {
if( tlc->chunk.vars[i]->data.var.precolored
&& tlc->chunk.vars[j]->data.var.precolored
&& tlc->chunk.vars[i]->data.var.color == tlc->chunk.vars[j]->data.var.color
&& var_collision(tlc, tlc->chunk.vars[i], tlc->chunk.vars[j])) {
adjs = realloc(adjs, sizeof(*adjs) * ++adjCount);
adjs[adjCount - 1][0] = tlc->chunk.vars[i];
adjs[adjCount - 1][1] = tlc->chunk.vars[j];
}
}
}
if(adjCount == 0) {
return NULL;
}
for(size_t a = 0; a < adjCount; a++) {
adjs[a][0]->data.var.degree++;
adjs[a][1]->data.var.degree++;
}
ScopeItem *maxdeg = tlc->chunk.vars[0];
for(size_t v = 1; v < tlc->chunk.varCount; v++) {
if(maxdeg->data.var.degree < tlc->chunk.vars[v]->data.var.degree) {
maxdeg = tlc->chunk.vars[v];
}
}
free(adjs);
return maxdeg;
}*/
struct CalleeSavedState {
AST *targetTLC;
@ -895,36 +679,18 @@ static void callee_saved(AST *tlc) {
}
}
/*struct DetermineRegisterClassesState {
int isInDereference;
};
static void determine_register_classes_visitor_pre(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct DetermineRegisterClassesState *this = ud;
AST *n = *nptr;
if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_DEREF) {
this->isInDereference++;
}
}
static void determine_register_classes_visitor_post(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct DetermineRegisterClassesState *this = ud;
AST *n = *nptr;
if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_DEREF) {
this->isInDereference--;
}
}*/
static void determine_register_classes(AST *tlc) {
for(size_t v = 0; v < tlc->chunk.varCount; v++) {
if(tlc->chunk.vars[v]->data.var.preclassed) {
continue;
}
if(type_size(tlc->chunk.vars[v]->type) == 1) {
tlc->chunk.vars[v]->data.var.registerClass = REG_CLASS_8;
} else {
tlc->chunk.vars[v]->data.var.registerClass = REG_CLASS_NOT_8;
}
}
//struct DetermineRegisterClassesState state = {};
//generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, determine_register_classes_visitor_pre, determine_register_classes_visitor_post);
}
/* Welsh-Powell graph coloring */
@ -933,72 +699,16 @@ static int comparator(const void *A, const void *B) {
ScopeItem *const *b = B;
return ((*a)->data.var.degree * (*a)->data.var.priority) - ((*b)->data.var.degree * (*b)->data.var.priority);
}
int cg_go(AST *a) {
assert(a->nodeKind == AST_CHUNK);
for(size_t e = 0; e < a->chunk.externCount; e++) {
assert(a->chunk.externs[e]->kind == SCOPEITEM_SYMBOL);
assert(a->chunk.externs[e]->data.symbol.isExternal);
printf("extern %s\n", a->chunk.externs[e]->data.symbol.name);
}
ast_usedef_reset(a);
size_t adjCount = 0;
Adjacency *adjs = calloc(adjCount, sizeof(*adjs));
ScopeItem **vars = a->chunk.vars;
for(size_t vi = 0; vi < a->chunk.varCount; vi++) {
vars[vi]->data.var.priority = 1;
vars[vi]->data.var.degree = 0;
vars[vi]->data.var.registerClass = -1;
if(!vars[vi]->data.var.precolored) {
vars[vi]->data.var.color = -1;
}
}
determine_register_classes(a);
for(size_t v1i = 0; v1i < a->chunk.varCount; v1i++) {
for(size_t v2i = 0; v2i < a->chunk.varCount; v2i++) {
if(v1i == v2i) continue;
ScopeItem *v1 = vars[v1i];
ScopeItem *v2 = vars[v2i];
if(var_collision(a, v1, v2)) {
ScopeItem *min = v1 < v2 ? v1 : v2;
ScopeItem *max = v1 < v2 ? v2 : v1;
for(size_t a = 0; a < adjCount; a++) {
if(adjs[a][0] == min && adjs[a][1] == max) {
goto cont;
}
}
adjs = realloc(adjs, sizeof(*adjs) * ++adjCount);
adjs[adjCount - 1][0] = min;
adjs[adjCount - 1][1] = max;
cont:;
}
}
}
for(size_t a = 0; a < adjCount; a++) {
adjs[a][0]->data.var.degree++;
adjs[a][1]->data.var.degree++;
}
qsort(vars, a->chunk.varCount, sizeof(*vars), comparator);
static int assign_coloring(AST *a, ScopeItem **vars, size_t adjCount, Adjacency *adjs, bool preclassed) {
int mustSpillRegisterClass = -1;
/* Welsh plow my ass */
for(int v = 0; v < a->chunk.varCount; v++) {
if(vars[v]->data.var.preclassed != preclassed) {
continue;
}
if(vars[v]->data.var.color != -1) {
// Already assigned.
continue;
@ -1047,6 +757,82 @@ nextColor:;
}
}
return mustSpillRegisterClass;
}
int cg_go(AST *a) {
assert(a->nodeKind == AST_CHUNK);
for(size_t e = 0; e < a->chunk.externCount; e++) {
assert(a->chunk.externs[e]->kind == SCOPEITEM_SYMBOL);
assert(a->chunk.externs[e]->data.symbol.isExternal);
printf("extern %s\n", a->chunk.externs[e]->data.symbol.name);
}
ast_usedef_reset(a);
size_t adjCount = 0;
Adjacency *adjs = calloc(adjCount, sizeof(*adjs));
ScopeItem **vars = a->chunk.vars;
for(size_t vi = 0; vi < a->chunk.varCount; vi++) {
vars[vi]->data.var.priority = 1;
vars[vi]->data.var.degree = 0;
if(!vars[vi]->data.var.preclassed) {
vars[vi]->data.var.registerClass = -1;
}
if(!vars[vi]->data.var.precolored) {
vars[vi]->data.var.color = -1;
}
}
determine_register_classes(a);
for(size_t v1i = 0; v1i < a->chunk.varCount; v1i++) {
for(size_t v2i = 0; v2i < a->chunk.varCount; v2i++) {
if(v1i == v2i) continue;
ScopeItem *v1 = vars[v1i];
ScopeItem *v2 = vars[v2i];
if(var_collision(a, v1, v2)) {
ScopeItem *min = v1 < v2 ? v1 : v2;
ScopeItem *max = v1 < v2 ? v2 : v1;
for(size_t a = 0; a < adjCount; a++) {
if(adjs[a][0] == min && adjs[a][1] == max) {
goto cont;
}
}
adjs = realloc(adjs, sizeof(*adjs) * ++adjCount);
adjs[adjCount - 1][0] = min;
adjs[adjCount - 1][1] = max;
cont:;
}
}
}
for(size_t a = 0; a < adjCount; a++) {
adjs[a][0]->data.var.degree++;
adjs[a][1]->data.var.degree++;
}
qsort(vars, a->chunk.varCount, sizeof(*vars), comparator);
int mustSpillRegisterClass;
// Important to prioritize preclassed variables
mustSpillRegisterClass = assign_coloring(a, vars, adjCount, adjs, true);
if(mustSpillRegisterClass == -1) {
mustSpillRegisterClass = assign_coloring(a, vars, adjCount, adjs, false);
}
free(adjs);
if(mustSpillRegisterClass != -1) {
@ -1062,6 +848,12 @@ nextColor:;
assert(chosen != NULL);
if(ntc_get_int("pdbg")) {
char *astdump = ast_dump(a);
fprintf(stderr, "### CG FAILURE ###\n%s\n", astdump);
free(astdump);
}
ast_spill_to_stack(a, chosen);
return 0;

View File

@ -1,11 +1,10 @@
#include"dumberdowner.h"
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"x86.h"
#include"arch.h"
#include"reporting.h"
#include"utils.h"
// This is the dumbing down pass.
//
@ -73,10 +72,21 @@ 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;
@ -188,11 +198,15 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
&& 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) {
@ -236,6 +250,8 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
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) {
@ -330,6 +346,11 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
}
}
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;
@ -387,6 +408,8 @@ static void pre_dumb_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, A
}
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) {
@ -438,9 +461,13 @@ static bool is_double_field_access(AST *e) {
}
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 = ud;
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`
@ -497,11 +524,13 @@ static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST
void dumben_pre(AST *tlc) {
generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, pre_dumb_visitor, NULL);
generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, decompose_symbol_record_field_access, NULL);
generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, decompose_symbol_record_field_access, NULL);
for(size_t t = 0; t < tlc->chunk.varCount; t++) {
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++;
}
}