Compare commits

...

9 Commits

Author SHA1 Message Date
Mid
7569420fe0 MapSOaLDhS 2025-09-19 19:08:22 +03:00
Mid
fb6fd76d0b More denoops 2025-09-19 19:05:18 +03:00
Mid
e80f6643dc Dumbify callee expressions 2025-09-19 19:04:53 +03:00
Mid
8d8d1cf067 Match resource masks instead of resource names 2025-09-19 19:04:26 +03:00
Mid
1708faf14d Cast fixes 2025-09-19 19:03:50 +03:00
Mid
14ab1f432d Correct type sizes under different ABIs 2025-09-15 17:10:38 +03:00
Mid
f03e8517df document xop_sz 2025-09-14 10:44:28 +03:00
Mid
273e6d1058 Add ugpr and umem types 2025-09-14 10:44:08 +03:00
Mid
251d24fb30 in bits, not bytes 2025-09-14 10:43:37 +03:00
14 changed files with 319 additions and 202 deletions

View File

@ -2,7 +2,7 @@
* ListDLC: Dynamic, Linear growth, C-managed * ListDLC: Dynamic, Linear growth, C-managed
*/ */
extern u8*(u8*, u32) realloc; extern u8*(u8*, ugpr) realloc;
record ListDLC[T, S; growth] { record ListDLC[T, S; growth] {
S capacity; S capacity;
@ -31,7 +31,7 @@ ListDLC_remove: [T, S; growth]u0(ListDLC[T, S; growth]* this, S index) -> {
ListDLC_add: [T, S; growth]u0(ListDLC[T, S; growth]* this, T value) -> { ListDLC_add: [T, S; growth]u0(ListDLC[T, S; growth]* this, T value) -> {
if((*this).size == (*this).capacity) { if((*this).size == (*this).capacity) {
u32 newcap = (*this).capacity + growth; S newcap = (*this).capacity + growth;
(*this).capacity = newcap; (*this).capacity = newcap;
(*this).data = realloc((*this).data, newcap * @sizeof T); (*this).data = realloc((*this).data, newcap * @sizeof T);
} }

31
examples/MapSOaLDhS.nct Normal file
View File

@ -0,0 +1,31 @@
/*
* SOaLDhS: Statically-allocated, Open-addressing, Linear probing, Dynamic hash, sentinels
*/
record KVPair[K, V] {
K key;
V value;
}
record MapSOaLDhS[K, V, S; capacity, sentinel] {
S(K*) hash;
KVPair[capacity] data;
}
MapSOaLDhS_add: [K, V, S; capacity, sentinel]u1(MapSOaLDhS[K, V, S; capacity, sentinel]* this, K* key, V* value) -> {
S index = ((*this).hash)(value);
index = index & (capacity - 1);
loop {
KVPair[K, V]* pair = &((*this).data[index]);
if(((*pair).key == *key) || ((*pair).value == sentinel)) {
(*pair).key = *key;
(*pair).value = *value;
}
index = index + 1;
}
return 1;
};
@instantiate MapSOaLDhS_add[ugpr, ugpr, ugpr; 128, 0];
MapSOaLDhS[ugpr, ugpr, ugpr; 128, 0] map;

View File

@ -2,16 +2,16 @@ use ListDLC;
@section(".text"); @section(".text");
@instantiate ListDLC_remove[u32, u32; 9]; @instantiate ListDLC_remove[u16, u16; 9];
@instantiate ListDLC_add[u32, u32; 9]; @instantiate ListDLC_add[u16, u16; 9];
main: u0() -> { main: u0() -> {
ListDLC[u32, u32; 9] list; ListDLC[u16, u16; 9] list;
ListDLC_add[u32, u32; 9](&list, 1234); ListDLC_add[u16, u16; 9](&list, 1234);
ListDLC_add[u32, u32; 9](&list, 4321); ListDLC_add[u16, u16; 9](&list, 4321);
ListDLC_add[u32, u32; 9](&list, 7777); ListDLC_add[u16, u16; 9](&list, 7777);
ListDLC_add[u32, u32; 9](&list, 6969); ListDLC_add[u16, u16; 9](&list, 6969);
ListDLC_remove[u32, u32; 9](&list, 1); ListDLC_remove[u16, u16; 9](&list, 1);
return; return;
}; };

View File

@ -1,4 +1,4 @@
fibonacci: u32(u32 n) -> { fibonacci: u16(u16 n) -> {
if(n <= 1) { if(n <= 1) {
return n; return n;
} }

View File

@ -177,6 +177,11 @@ static void *memdup(void *a, size_t len) {
return r; return r;
} }
/*
* WARNING: Just because you deep copy an AST node, does not mean
* ast_expression_equal will return true! This matters for example with
* function calls (a function call is not equal to itself).
*/
AST *ast_deep_copy(AST *src) { AST *ast_deep_copy(AST *src) {
if(src->nodeKind == AST_EXPR_VAR) { if(src->nodeKind == AST_EXPR_VAR) {
return memdup(src, sizeof(ASTExprVar)); return memdup(src, sizeof(ASTExprVar));

View File

@ -56,6 +56,8 @@ static char *ast_dumpe(AST *tlc, AST *e) {
return strdup(vte->data.var.name); return strdup(vte->data.var.name);
} else if(vte->kind == SCOPEITEM_SYMBOL) { } else if(vte->kind == SCOPEITEM_SYMBOL) {
return strdup(vte->data.symbol.name); return strdup(vte->data.symbol.name);
} else if(vte->kind == SCOPEITEM_CEXPR) {
return ast_dumpe(tlc, vte->data.cexpr.concrete);
} else abort(); } else abort();
} else if(e->nodeKind == AST_EXPR_UNARY_OP) { } else if(e->nodeKind == AST_EXPR_UNARY_OP) {
const char *op = NULL; const char *op = NULL;
@ -274,6 +276,18 @@ static char *ast_dumps(AST *tlc, AST *s) {
return malp("%s; /* loop guard */", name); return malp("%s; /* loop guard */", name);
} else if(s->nodeKind == AST_STMT_EXPR) { } else if(s->nodeKind == AST_STMT_EXPR) {
return ast_dumpe(tlc, s->stmtExpr.expr); return ast_dumpe(tlc, s->stmtExpr.expr);
} else if(s->nodeKind == AST_STMT_DECL) {
char *a = type_to_string(s->stmtDecl.thing->type);
char *c;
if(s->stmtDecl.expression) {
char *b = ast_dumpe(tlc, s->stmtDecl.expression);
c = malp("%s %s = %s;", a, s->stmtDecl.thing->data.var.name, b);
free(b);
} else {
c = malp("%s %s;", a, s->stmtDecl.thing->data.var.name);
}
free(a);
return c;
} else if(s->nodeKind == AST_STMT_RETURN) { } else if(s->nodeKind == AST_STMT_RETURN) {
if(s->stmtReturn.val) { if(s->stmtReturn.val) {
char *e = ast_dumpe(tlc, s->stmtReturn.val); char *e = ast_dumpe(tlc, s->stmtReturn.val);

View File

@ -31,7 +31,7 @@ static void spill2stack_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk
ASTExprStackPointer *rsp = calloc(1, sizeof(*rsp)); ASTExprStackPointer *rsp = calloc(1, sizeof(*rsp));
rsp->nodeKind = AST_EXPR_STACK_POINTER; rsp->nodeKind = AST_EXPR_STACK_POINTER;
rsp->type = type_u(arch_gpr_size()); rsp->type = type_u(8 * arch_gpr_size());
ASTExprPrimitive *offset = calloc(1, sizeof(*offset)); ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
offset->nodeKind = AST_EXPR_PRIMITIVE; offset->nodeKind = AST_EXPR_PRIMITIVE;

View File

@ -152,9 +152,10 @@ static char *parametrize_function_name(Type *t, const char *original, Scope *sco
static void binop_implicit_cast(/*Parser *P, */ASTExprBinaryOp *binop) { static void binop_implicit_cast(/*Parser *P, */ASTExprBinaryOp *binop) {
if(type_size(binop->operands[0]->expression.type) < type_size(binop->operands[1]->expression.type)) { if(type_size(binop->operands[0]->expression.type) < type_size(binop->operands[1]->expression.type)) {
binop->operands[0] = ast_cast_expr(binop->operands[0], binop->operands[1]->expression.type); binop->operands[0] = ast_cast_expr(binop->operands[0], binop->operands[1]->expression.type);
} } else if(type_size(binop->operands[1]->expression.type) < type_size(binop->operands[0]->expression.type)) {
binop->operands[1] = ast_cast_expr(binop->operands[1], binop->operands[0]->expression.type);
if(type_size(binop->operands[1]->expression.type) < type_size(binop->operands[0]->expression.type)) { } else {
binop->operands[0] = ast_cast_expr(binop->operands[0], binop->operands[0]->expression.type);
binop->operands[1] = ast_cast_expr(binop->operands[1], binop->operands[0]->expression.type); binop->operands[1] = ast_cast_expr(binop->operands[1], binop->operands[0]->expression.type);
} }
@ -228,7 +229,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
ASTExprStackPointer *ret = alloc_node(P, sizeof(*ret)); ASTExprStackPointer *ret = alloc_node(P, sizeof(*ret));
ret->nodeKind = AST_EXPR_STACK_POINTER; ret->nodeKind = AST_EXPR_STACK_POINTER;
ret->type = type_u(arch_sp_size()); ret->type = type_u(8 * arch_sp_size());
e = (AST*) ret; e = (AST*) ret;
} else if(!strcmp(peek(P, 0).content, "@salloc")) { } else if(!strcmp(peek(P, 0).content, "@salloc")) {
@ -255,7 +256,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
ret->ofExpr = nct_parse_expression(P, lOP - 1); ret->ofExpr = nct_parse_expression(P, lOP - 1);
} }
ret->type = type_u(arch_gpr_size()); ret->type = type_u(8 * arch_gpr_size());
e = (AST*) ret; e = (AST*) ret;
} else { } else {
@ -461,7 +462,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
if(typesize != 1) { if(typesize != 1) {
ASTExprPrimitive *scale = alloc_node(P, sizeof(*scale)); ASTExprPrimitive *scale = alloc_node(P, sizeof(*scale));
scale->nodeKind = AST_EXPR_PRIMITIVE; scale->nodeKind = AST_EXPR_PRIMITIVE;
scale->type = type_u(arch_gpr_size()); scale->type = type_u(8 * arch_gpr_size());
scale->val = typesize; scale->val = typesize;
ASTExprBinaryOp *mul = alloc_node(P, sizeof(*mul)); ASTExprBinaryOp *mul = alloc_node(P, sizeof(*mul));

View File

@ -5,6 +5,7 @@
#include<string.h> #include<string.h>
#include<assert.h> #include<assert.h>
#include<stdio.h> #include<stdio.h>
#include"x86/arch.h"
Scope *scope_new(Scope *parent) { Scope *scope_new(Scope *parent) {
Scope *ret = calloc(1, sizeof(*ret)); Scope *ret = calloc(1, sizeof(*ret));
@ -81,7 +82,12 @@ void vte_precolor(ScopeItem *vte, int class, int color) {
assert(vte->kind == SCOPEITEM_VAR && "vte must be var"); assert(vte->kind == SCOPEITEM_VAR && "vte must be var");
assert(!vte->data.var.precolored && "already precolored"); assert(!vte->data.var.precolored && "already precolored");
if(type_size(vte->type) > 0) {
assert(type_size(vte->type) == REG_CLASSES[class].rsS[color] && "Sizes must match in precoloring");
}
vte->data.var.precolored = true; vte->data.var.precolored = true;
vte->data.var.preclassed = true;
vte->data.var.registerClass = class; vte->data.var.registerClass = class;
vte->data.var.color = color; vte->data.var.color = color;
} }

View File

@ -7,6 +7,7 @@
#include"ast/ast.h" #include"ast/ast.h"
#include"reporting.h" #include"reporting.h"
#include<assert.h> #include<assert.h>
#include"x86/arch.h"
#include"ntc.h" #include"ntc.h"
@ -23,6 +24,12 @@ Type *primitive_parse(const char *src) {
} }
} }
if(!strcmp(src, "ugpr")) {
return type_u(8 * arch_gpr_size());
} else if(!strcmp(src, "umem")) {
return type_u(arch_memory_width());
}
TypePrimitive *ret = calloc(1, sizeof(*ret)); TypePrimitive *ret = calloc(1, sizeof(*ret));
ret->type = TYPE_TYPE_PRIMITIVE; ret->type = TYPE_TYPE_PRIMITIVE;
ret->src = strdup(src); ret->src = strdup(src);
@ -286,7 +293,10 @@ Type *type_parametrize(Type *t, Scope *scope) {
AST *n = vte->data.cexpr.concrete; AST *n = vte->data.cexpr.concrete;
if(n) { while(n->nodeKind == AST_EXPR_VAR && n->exprVar.thing->kind == SCOPEITEM_CEXPR && n->exprVar.thing->data.cexpr.concrete != NULL) {
n = n->exprVar.thing->data.cexpr.concrete;
}
if(n->nodeKind == AST_EXPR_PRIMITIVE) { if(n->nodeKind == AST_EXPR_PRIMITIVE) {
t->array.length = n->exprPrim.val; t->array.length = n->exprPrim.val;
t->array.lengthIsGeneric = false; t->array.lengthIsGeneric = false;
@ -300,7 +310,6 @@ Type *type_parametrize(Type *t, Scope *scope) {
} }
} }
} }
}
return t; return t;
} }
@ -357,3 +366,11 @@ Type *type_shallow_copy(Type *t) {
} }
abort(); abort();
} }
Type *type_prim_cast(Type *t, size_t bits) {
assert(t->type == TYPE_TYPE_PRIMITIVE);
Type *t2 = type_shallow_copy(t);
t2->primitive.width = bits;
return t2;
}

View File

@ -111,13 +111,21 @@ Type *type_shallow_copy(Type *t);
bool type_is_generic(Type *t); bool type_is_generic(Type *t);
Type *type_prim_cast(Type *t, size_t bits);
static inline bool type_is_segmented_pointer(Type *type) { static inline bool type_is_segmented_pointer(Type *type) {
return type->type == TYPE_TYPE_RECORD && !!strstr(type->record.name, " @far*"); return type->type == TYPE_TYPE_RECORD && !!strstr(type->record.name, " @far*");
} }
static inline Type *type_u(size_t size) { static inline Type *type_u(size_t bits) {
char buf[32]; char buf[32];
snprintf(buf, sizeof(buf), "u%lu", size); snprintf(buf, sizeof(buf), "u%lu", bits);
return primitive_parse(buf);
}
static inline Type *type_s(size_t bits) {
char buf[32];
snprintf(buf, sizeof(buf), "s%lu", bits);
return primitive_parse(buf); return primitive_parse(buf);
} }

View File

@ -241,10 +241,24 @@ static inline WhysItBad_Huh x86_test_mul(AST *stmtPrev, AST *stmt) {
} }
if(x86_imul_supported()) { if(x86_imul_supported()) {
return GUCCI; if(ast_expression_equal(stmt->stmtAssign.what, stmt->stmtAssign.to->exprBinOp.operands[0])) {
} else if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) { if(is_xop(stmt->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_XOP) {
return SRC1_IS_BAD_XOP; return SRC1_IS_BAD_XOP;
} }
return GUCCI;
} else {
if(is_xop(stmt->stmtAssign.to->exprBinOp.operands[0]) == XOP_NOT_XOP) {
return SRC0_IS_BAD_XOP;
}
if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
return GUCCI;
}
}
} else {
if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
return SRC1_IS_BAD_XOP;
}
}
if(is_xop(stmt->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_XOP) { if(is_xop(stmt->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_XOP) {
return SRC1_IS_BAD_XOP; return SRC1_IS_BAD_XOP;
@ -294,6 +308,18 @@ static inline int arch_gpr_size() {
return x86_max_gpr_size(); return x86_max_gpr_size();
} }
static inline int arch_memory_width() {
switch(x86_target()) {
case I086:
case I186:
return 20;
case I286:
return 24;
default:
return 32;
}
}
// lol // lol
static inline bool is_reg_b(int cls, int res) { static inline bool is_reg_b(int cls, int res) {
const char *name = REG_CLASSES[cls].rsN[res]; const char *name = REG_CLASSES[cls].rsN[res];

View File

@ -12,6 +12,11 @@ static const char *BINOP_SIMPLE_INSTRS[] = {[BINOP_ADD] = "add", [BINOP_SUB] = "
/*static size_t nextLocalLabel = 0;*/ /*static size_t nextLocalLabel = 0;*/
struct CalleeSavedState {
const char *reg[MAX_REGS_PER_CLASS];
size_t stackOffset[MAX_REGS_PER_CLASS];
};
typedef struct { typedef struct {
/*#define LOOPSTACKSIZE 96 /*#define LOOPSTACKSIZE 96
size_t loopStackStart[LOOPSTACKSIZE]; size_t loopStackStart[LOOPSTACKSIZE];
@ -21,6 +26,8 @@ typedef struct {
int isFunction; int isFunction;
AST *tlc; AST *tlc;
struct CalleeSavedState calleeSaved;
} CGState; } CGState;
static const char *direct(int size) { static const char *direct(int size) {
@ -129,6 +136,10 @@ static AST *is_field_access(AST *e) {
return NULL; return NULL;
} }
/*
* Convert a XOP-able expression into an x86 operand.
* Result MUST be determinstic and always the same, for the same given expression.
* */
static const char *xop_sz(AST *tlc, AST *e, int sz) { static const char *xop_sz(AST *tlc, AST *e, int sz) {
#define XOPBUFS 16 #define XOPBUFS 16
#define XOPBUFSZ 32 #define XOPBUFSZ 32
@ -254,6 +265,17 @@ void cg_chunk(CGState *cg, AST *a) {
} else { } else {
printf("sub esp, %lu\n", a->chunk.stackReservation); printf("sub esp, %lu\n", a->chunk.stackReservation);
} }
for(int i = 0; i < MAX_REGS_PER_CLASS && cg->calleeSaved.reg[i]; i++) {
if(x86_ia16()) {
printf("mov [bp + %li], %s\n", cg->calleeSaved.stackOffset[i] - a->chunk.stackReservation, cg->calleeSaved.reg[i]);
} else {
printf("mov [esp + %li], %s\n", cg->calleeSaved.stackOffset[i], cg->calleeSaved.reg[i]);
}
}
} else {
// If there's no stack reservation, there can't be callee-saved regs.
assert(cg->calleeSaved.reg[0] == NULL);
} }
// Potentially complex pattern matching // Potentially complex pattern matching
@ -412,7 +434,7 @@ void cg_chunk(CGState *cg, AST *a) {
assert(s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR); assert(s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR);
if(!strcmp(xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]))) { if(!strcmp(xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]))) {
printf("imul %s, %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[1])); printf("imul %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[1]));
} else { } else {
printf("imul %s, %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]), xop(a, s->stmtAssign.to->exprBinOp.operands[1])); printf("imul %s, %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]), xop(a, s->stmtAssign.to->exprBinOp.operands[1]));
} }
@ -475,10 +497,18 @@ void cg_chunk(CGState *cg, AST *a) {
} else if(is_xop(s->stmtAssign.what) && s->stmtAssign.to->nodeKind == AST_EXPR_CAST) { } else if(is_xop(s->stmtAssign.what) && s->stmtAssign.to->nodeKind == AST_EXPR_CAST) {
if(type_size(s->stmtAssign.what->expression.type) > type_size(s->stmtAssign.to->expression.type)) { if(type_size(s->stmtAssign.what->expression.type) > type_size(s->stmtAssign.to->exprCast.what->expression.type)) {
printf("movzx %s, %s\n", xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to->exprCast.what)); printf("movzx %s, %s\n", xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to->exprCast.what));
} else if(type_size(s->stmtAssign.what->expression.type) > type_size(s->stmtAssign.to->exprCast.what->expression.type)) {
/*
* LEMMA: For every register in x86, its lowest bits are accessible.
*/
assert(0 && "Not implemented.");
} else { } else {
const char *dest = xop_sz(cg->tlc, s->stmtAssign.what, x86_max_gpr_size()); const char *dest = xop_sz(cg->tlc, s->stmtAssign.what, x86_max_gpr_size());
@ -542,10 +572,17 @@ void cg_chunk(CGState *cg, AST *a) {
if(s->stmtReturn.val) { if(s->stmtReturn.val) {
assert(s->stmtReturn.val->nodeKind == AST_EXPR_VAR); assert(s->stmtReturn.val->nodeKind == AST_EXPR_VAR);
assert(s->stmtReturn.val->exprVar.thing->kind == SCOPEITEM_VAR); assert(s->stmtReturn.val->exprVar.thing->kind == SCOPEITEM_VAR);
//assert(s->stmtReturn.val->exprVar.thing->data.var.color == COLOR_EAX);
} }
if(a->chunk.stackReservation) { if(a->chunk.stackReservation) {
for(int i = 0; i < MAX_REGS_PER_CLASS && cg->calleeSaved.reg[i]; i++) {
if(x86_ia16()) {
printf("mov [bp + %li], %s\n", cg->calleeSaved.stackOffset[i] - a->chunk.stackReservation, cg->calleeSaved.reg[i]);
} else {
printf("mov [esp + %li], %s\n", cg->calleeSaved.stackOffset[i], cg->calleeSaved.reg[i]);
}
}
if(x86_ia16()) { if(x86_ia16()) {
printf("add sp, %lu\n", a->chunk.stackReservation); printf("add sp, %lu\n", a->chunk.stackReservation);
} else { } else {
@ -599,170 +636,40 @@ static bool var_collision(AST *tlc, ScopeItem *v1, ScopeItem *v2) {
return liveRangeIntersection && resourceIntersection; return liveRangeIntersection && resourceIntersection;
} }
struct CalleeSavedState { static void callee_saved(AST *tlc, struct CalleeSavedState *state) {
AST *targetTLC; bool ebxused = false, ediused = false, esiused = false;
ScopeItem *calleeUsers[MAX_REGS_PER_CLASS];
size_t calleeOffsets[MAX_REGS_PER_CLASS];
// To make sure we don't process the same return statement to infinity
AST *lastProcessedReturn;
};
static void callee_saved_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct CalleeSavedState *this = ud;
AST *n = *nptr;
if(tlc != this->targetTLC) {
// Don't do anything.
return;
}
if(n == tlc) {
// Function entry
for(int i = 0; i < MAX_REGS_PER_CLASS; i++) {
ScopeItem *vte = this->calleeUsers[i];
if(!vte) {
continue;
}
ASTExprStackPointer *stk = calloc(1, sizeof(*stk));
stk->nodeKind = AST_EXPR_STACK_POINTER;
stk->type = primitive_parse("u32");
ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
offset->nodeKind = AST_EXPR_PRIMITIVE;
offset->type = primitive_parse("u32");
offset->val = this->calleeOffsets[i];
offset->stackGrowth = true;
ASTExprBinaryOp *sum = calloc(1, sizeof(*sum));
sum->nodeKind = AST_EXPR_BINARY_OP;
sum->type = offset->type;
sum->operator = BINOP_ADD;
sum->operands[0] = (AST*) stk;
sum->operands[1] = (AST*) offset;
ASTExprUnaryOp *deref = calloc(1, sizeof(*deref));
deref->nodeKind = AST_EXPR_UNARY_OP;
deref->type = offset->type;
deref->operator = UNOP_DEREF;
deref->operand = (AST*) sum;
ASTExprVar *ev = calloc(1, sizeof(*ev));
ev->nodeKind = AST_EXPR_VAR;
ev->type = vte->type;
ev->thing = vte;
ASTStmtAssign *assign = calloc(1, sizeof(*assign));
assign->nodeKind = AST_STMT_ASSIGN;
assign->what = (AST*) deref;
assign->to = (AST*) ev;
assign->next = tlc->chunk.statementFirst;
tlc->chunk.statementFirst = (AST*) assign;
assert(tlc->chunk.statementLast != NULL);
}
} else if(n->nodeKind == AST_STMT_RETURN && n != this->lastProcessedReturn) {
// Function exit
this->lastProcessedReturn = n;
for(int i = 0; i < MAX_REGS_PER_CLASS; i++) {
ScopeItem *vte = this->calleeUsers[i];
if(!vte) {
continue;
}
ASTExprStackPointer *stk = calloc(1, sizeof(*stk));
stk->nodeKind = AST_EXPR_STACK_POINTER;
stk->type = primitive_parse("u32");
ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
offset->nodeKind = AST_EXPR_PRIMITIVE;
offset->type = primitive_parse("u32");
offset->val = this->calleeOffsets[i];
offset->stackGrowth = true;
ASTExprBinaryOp *sum = calloc(1, sizeof(*sum));
sum->nodeKind = AST_EXPR_BINARY_OP;
sum->type = offset->type;
sum->operator = BINOP_ADD;
sum->operands[0] = (AST*) stk;
sum->operands[1] = (AST*) offset;
ASTExprUnaryOp *deref = calloc(1, sizeof(*deref));
deref->nodeKind = AST_EXPR_UNARY_OP;
deref->type = offset->type;
deref->operator = UNOP_DEREF;
deref->operand = (AST*) sum;
ASTExprVar *ev = calloc(1, sizeof(*ev));
ev->nodeKind = AST_EXPR_VAR;
ev->type = vte->type;
ev->thing = vte;
ASTStmtAssign *assign = calloc(1, sizeof(*assign));
assign->nodeKind = AST_STMT_ASSIGN;
assign->what = (AST*) ev;
assign->to = (AST*) deref;
assign->next = stmt;
if(stmtPrev) {
stmtPrev->statement.next = (AST*) assign;
} else {
tlc->chunk.statementFirst = (AST*) assign;
}
stmtPrev = (AST*) assign;
}
}
}
static void callee_saved(AST *tlc) {
ScopeItem *ebxuser = NULL, *ediuser = NULL, *esiuser = NULL;
for(size_t v = 0; v < tlc->chunk.varCount; v++) { for(size_t v = 0; v < tlc->chunk.varCount; v++) {
if(is_reg_b(tlc->chunk.vars[v]->data.var.registerClass, tlc->chunk.vars[v]->data.var.color)) { size_t resource = REG_CLASSES[tlc->chunk.vars[v]->data.var.registerClass].rs[tlc->chunk.vars[v]->data.var.color];
ebxuser = tlc->chunk.vars[v];
}
if(is_reg_di(tlc->chunk.vars[v]->data.var.registerClass, tlc->chunk.vars[v]->data.var.color)) {
ediuser = tlc->chunk.vars[v];
}
if(is_reg_si(tlc->chunk.vars[v]->data.var.registerClass, tlc->chunk.vars[v]->data.var.color)) {
esiuser = tlc->chunk.vars[v];
}
}
struct CalleeSavedState state = {}; if(resource & HWR_EBX) {
state.targetTLC = tlc; ebxused = true;
}
if(resource & HWR_EDI) {
ediused = true;
}
if(resource & HWR_ESI) {
esiused = true;
}
}
size_t nextUser = 0; size_t nextUser = 0;
if(ebxuser) { if(ebxused) {
state.calleeOffsets[nextUser] = nextUser * 4; state->stackOffset[nextUser] = nextUser * x86_max_gpr_size();
state.calleeUsers[nextUser] = ebxuser; state->reg[nextUser] = x86_ia16() ? "bx" : "ebx";
nextUser++; nextUser++;
} }
if(esiuser) { if(esiused) {
state.calleeOffsets[nextUser] = nextUser * 4; state->stackOffset[nextUser] = nextUser * x86_max_gpr_size();
state.calleeUsers[nextUser] = esiuser; state->reg[nextUser] = x86_ia16() ? "si" : "esi";
nextUser++; nextUser++;
} }
if(ediuser) { if(ediused) {
state.calleeOffsets[nextUser] = nextUser * 4; state->stackOffset[nextUser] = nextUser * x86_max_gpr_size();
state.calleeUsers[nextUser] = ediuser; state->reg[nextUser] = x86_ia16() ? "di" : "edi";
nextUser++; nextUser++;
} }
ast_grow_stack_frame(tlc, nextUser * 4); ast_grow_stack_frame(tlc, nextUser * x86_max_gpr_size());
if(nextUser) {
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, callee_saved_visitor, NULL);
}
} }
static void determine_register_classes(AST *tlc) { static void determine_register_classes(AST *tlc) {
@ -945,14 +852,16 @@ cont:;
return 0; return 0;
} }
struct CalleeSavedState calleeSaved = {};
if(a->chunk.functionType) { if(a->chunk.functionType) {
callee_saved(a); callee_saved(a, &calleeSaved);
} }
CGState cg; CGState cg;
memset(&cg, 0, sizeof(cg)); memset(&cg, 0, sizeof(cg));
cg.tlc = a; cg.tlc = a;
cg.isFunction = !!a->chunk.functionType; cg.isFunction = !!a->chunk.functionType;
cg.calleeSaved = calleeSaved;
cg_chunk(&cg, a); cg_chunk(&cg, a);

View File

@ -83,6 +83,32 @@ static void mark_ptr(AST *a) {
} }
} }
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 { struct DumbenState {
AST *targetTLC; AST *targetTLC;
int effective; int effective;
@ -225,7 +251,7 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
} else if(s->nodeKind == AST_STMT_RETURN) { } else if(s->nodeKind == AST_STMT_RETURN) {
// ret specifically returns in eax always, so it needs to be in a precolored var // ret returns in eax always, so it needs to be in a precolored var
AST *retval = s->stmtReturn.val; 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'))))) { 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'))))) {
@ -233,7 +259,7 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
retval = s->stmtReturn.val = varify(tlc, chu, stmtPrev, s, retval); retval = s->stmtReturn.val = varify(tlc, chu, stmtPrev, s, retval);
this->effective = 1; this->effective = 1;
vte_precolor(retval->exprVar.thing, REG_CLASS_16_32, 1); mark_a(retval->exprVar.thing);
} }
@ -246,7 +272,7 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
s->stmtExpr.expr = varify(tlc, chu, stmtPrev, s, s->stmtExpr.expr); s->stmtExpr.expr = varify(tlc, chu, stmtPrev, s, s->stmtExpr.expr);
vte_precolor(s->stmtExpr.expr->exprVar.thing, REG_CLASS_16_32, 1); mark_a(s->stmtExpr.expr->exprVar.thing);
// Purge this statement entirely, otherwise we'd have // Purge this statement entirely, otherwise we'd have
// a = f(x); // a = f(x);
@ -273,7 +299,7 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
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)) { 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); ScopeItem *tmp = create_dumbtemp(tlc, s->stmtAssign.what->expression.type);
vte_precolor(tmp, REG_CLASS_16_32, 1); mark_a(tmp);
ASTExprVar *ev[2] = {calloc(1, sizeof(**ev)), calloc(1, sizeof(**ev))}; ASTExprVar *ev[2] = {calloc(1, sizeof(**ev)), calloc(1, sizeof(**ev))};
ev[0]->nodeKind = AST_EXPR_VAR; ev[0]->nodeKind = AST_EXPR_VAR;
@ -308,6 +334,12 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
this->effective = 1; 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) { } 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; ASTExprCall *call = &s->stmtAssign.to->exprCall;
@ -392,7 +424,7 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
AST *hihalf = varify(tlc, chu, stmtPrev->statement.next, s, mulhi); AST *hihalf = varify(tlc, chu, stmtPrev->statement.next, s, mulhi);
vte_precolor(hihalf->exprVar.thing, REG_CLASS_16_32, 7); mark_d(hihalf->exprVar.thing);
} }
s->stmtAssign.what = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]); s->stmtAssign.what = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]);
@ -405,7 +437,7 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
s->stmtAssign.next = (AST*) redest; s->stmtAssign.next = (AST*) redest;
vte_precolor(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, REG_CLASS_16_32, 1); mark_a(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing);
this->effective = 1; this->effective = 1;
} else assert(because == NOT_AT_ALL_IT || because == GUCCI); } else assert(because == NOT_AT_ALL_IT || because == GUCCI);
@ -452,11 +484,6 @@ 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) { static void pre_dumb_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *n = *nptr; AST *n = *nptr;
@ -474,7 +501,7 @@ static void pre_dumb_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, A
ASTExprPrimitive *offset = calloc(1, sizeof(*offset)); ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
offset->nodeKind = AST_EXPR_PRIMITIVE; offset->nodeKind = AST_EXPR_PRIMITIVE;
offset->type = type_u(x86_max_gpr_size()); offset->type = type_u(8 * x86_max_gpr_size());
offset->val = 4 + i * x86_max_gpr_size(); offset->val = 4 + i * x86_max_gpr_size();
offset->stackGrowth = true; offset->stackGrowth = true;
@ -500,7 +527,7 @@ static void pre_dumb_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, A
ass->nodeKind = AST_STMT_ASSIGN; ass->nodeKind = AST_STMT_ASSIGN;
ass->next = NULL; ass->next = NULL;
ass->what = (AST*) evar; ass->what = (AST*) evar;
ass->to = (AST*) deref; ass->to = ast_cast_expr((AST*) deref, vte->type); // Must cast because of "convention correctness"
ass->next = n->chunk.statementFirst; ass->next = n->chunk.statementFirst;
if(n->chunk.statementFirst) { if(n->chunk.statementFirst) {
@ -567,6 +594,11 @@ 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; 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) { static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct DenoopState *state = ud; struct DenoopState *state = ud;
@ -579,6 +611,9 @@ static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST
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) { 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` // 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; *nptr = n->exprUnOp.operand->exprUnOp.operand;
*success = true; *success = true;
@ -603,6 +638,15 @@ static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST
*nptr = n->exprBinOp.operands[0]; *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; *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) { } 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)` // Turn `!(a op b)` to `(a !op b)`
@ -621,8 +665,31 @@ static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST
} else if(n->nodeKind == AST_EXPR_CAST && n->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && n->exprCast.to->type == TYPE_TYPE_POINTER) { } 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 // 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; *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; *success = true;
} else if(n->nodeKind == AST_EXPR_EXT_SIZEOF) { } else if(n->nodeKind == AST_EXPR_EXT_SIZEOF) {
ASTExprPrimitive *prim = calloc(1, sizeof(*prim)); ASTExprPrimitive *prim = calloc(1, sizeof(*prim));
@ -676,7 +743,40 @@ void ast_denoop(AST *tlc, AST **node) {
} while(state.success); } 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) { 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, pre_dumb_visitor, NULL);
generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, decompose_symbol_record_field_access, NULL); generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, decompose_symbol_record_field_access, NULL);