Safety commit
This commit is contained in:
parent
07f6d57aba
commit
ba6f9fb6c1
2
Makefile
2
Makefile
@ -11,7 +11,7 @@ ntc: $(SOURCES) $(HEADERS)
|
|||||||
ifdef OW
|
ifdef OW
|
||||||
wcl386 -ml $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUG),-DDEBUG=1,) -fe="ntc.exe" -bt=dos -l=dos4g -ml $(if $(DEBUG),,-d0 -os -om -ob -oi -ol -ox) -lr -za99 -i=src $(SOURCES)
|
wcl386 -ml $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUG),-DDEBUG=1,) -fe="ntc.exe" -bt=dos -l=dos4g -ml $(if $(DEBUG),,-d0 -os -om -ob -oi -ol -ox) -lr -za99 -i=src $(SOURCES)
|
||||||
else
|
else
|
||||||
cc $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUG),-DDEBUG=1,) -Wall -o ntc -fno-PIE -no-pie -std=gnu11 $(if $(DEBUG),-O0 -g,-Os -s) -fms-extensions -Isrc $(SOURCES)
|
cc $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUGA),-DDEBUG=1,) -Wall -o ntc -fsanitize=address -fno-PIE -no-pie -std=gnu11 $(if $(DEBUG),-O0 -g,-Os -s) -fms-extensions -Isrc $(SOURCES) -Wno-array-bounds
|
||||||
endif
|
endif
|
||||||
|
|
||||||
install: ntc
|
install: ntc
|
||||||
|
451
src/ast.c
451
src/ast.c
@ -3,6 +3,12 @@
|
|||||||
#include<stdint.h>
|
#include<stdint.h>
|
||||||
#include<string.h>
|
#include<string.h>
|
||||||
#include<stdlib.h>
|
#include<stdlib.h>
|
||||||
|
#include<assert.h>
|
||||||
|
#include<stdarg.h>
|
||||||
|
|
||||||
|
const char *AST_KIND_STR[] = {
|
||||||
|
AST_KINDS(GEN_STRI)
|
||||||
|
};
|
||||||
|
|
||||||
AST *ast_expression_optimize(AST *ast) {
|
AST *ast_expression_optimize(AST *ast) {
|
||||||
return ast;
|
return ast;
|
||||||
@ -19,6 +25,8 @@ int ast_expression_equal(AST *a, AST *b) {
|
|||||||
return a->exprUnOp.operator == b->exprUnOp.operator && ast_expression_equal(a->exprUnOp.operand, b->exprUnOp.operand);
|
return a->exprUnOp.operator == b->exprUnOp.operator && ast_expression_equal(a->exprUnOp.operand, b->exprUnOp.operand);
|
||||||
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
|
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
|
||||||
return a->exprBinOp.operator == b->exprBinOp.operator && ast_expression_equal(a->exprBinOp.operands[0], b->exprBinOp.operands[0]) && ast_expression_equal(a->exprBinOp.operands[1], b->exprBinOp.operands[1]);
|
return a->exprBinOp.operator == b->exprBinOp.operator && ast_expression_equal(a->exprBinOp.operands[0], b->exprBinOp.operands[0]) && ast_expression_equal(a->exprBinOp.operands[1], b->exprBinOp.operands[1]);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -28,8 +36,8 @@ int ast_expression_equal(AST *a, AST *b) {
|
|||||||
int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) {
|
int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) {
|
||||||
const AST *s = chunk->chunk.statementFirst;
|
const AST *s = chunk->chunk.statementFirst;
|
||||||
|
|
||||||
while(s) {
|
while(1) {
|
||||||
if(s->nodeKind == AST_STMT_LOOP) {
|
if(s && s->nodeKind == AST_STMT_LOOP) {
|
||||||
int i = ast_stmt_is_after(s->stmtLoop.body, s1, s2);
|
int i = ast_stmt_is_after(s->stmtLoop.body, s1, s2);
|
||||||
if(i != -1) {
|
if(i != -1) {
|
||||||
return i;
|
return i;
|
||||||
@ -43,6 +51,8 @@ int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!s) break;
|
||||||
|
|
||||||
if(s->nodeKind == AST_STMT_IF) {
|
if(s->nodeKind == AST_STMT_IF) {
|
||||||
int i = ast_stmt_is_after(s->stmtIf.then, s1, s2);
|
int i = ast_stmt_is_after(s->stmtIf.then, s1, s2);
|
||||||
if(i != -1) {
|
if(i != -1) {
|
||||||
@ -54,4 +64,439 @@ int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This pass is necessary for the purposes of optimization and regalloc.
|
||||||
|
* Because an AST may hold outdated UD-chains, this pass MUST be called
|
||||||
|
* before using the UD-chains to make sure they are valid.
|
||||||
|
*
|
||||||
|
* Each local var (VTE of kind VARTABLEENTRY_VAR) holds its own UD-chain
|
||||||
|
* that specifies the exact nodes in the AST where:
|
||||||
|
* 1. It is used
|
||||||
|
* 2. The whole statement in which it is used
|
||||||
|
* 3. The definition that *might* be in use
|
||||||
|
*
|
||||||
|
* Because multiple definitions may be in use (reachable) at the point
|
||||||
|
* of the use, a unique UseDef for each possible definition is appended
|
||||||
|
* to the chain.
|
||||||
|
*
|
||||||
|
* Reachable definitions are kept track in a ReachingDefs, also held by
|
||||||
|
* each VTE. In the case of a single, simple block of code, we know
|
||||||
|
* exactly one definition (including undefined) can reach each variable,
|
||||||
|
* which would simplify the ReachingDefs structure to a single
|
||||||
|
* definition pointer.
|
||||||
|
*
|
||||||
|
* Unfortunately, conditional blocks and loops ruin this simplicity.
|
||||||
|
* If you have code like
|
||||||
|
* x = A
|
||||||
|
* if B {
|
||||||
|
* x = C
|
||||||
|
* }
|
||||||
|
* then afterward two definitions may apply to x.
|
||||||
|
*
|
||||||
|
* A solution here is to lay ReachingDefs as a graph, with each
|
||||||
|
* ReachingDefs having an optional parent. When we enter a new block of
|
||||||
|
* code, we create an empty ReachingDefs with the previous block as its
|
||||||
|
* parent. Any definitions replace the ones in the deepest
|
||||||
|
* ReachingDefs only.
|
||||||
|
*
|
||||||
|
* How we exit a block depends on its type. If it is conditional,
|
||||||
|
* the reaching definitions should join the parent (mergedefs).
|
||||||
|
* If the block is a loop, it is even worse. Given
|
||||||
|
* x = A
|
||||||
|
* loop {
|
||||||
|
* use x
|
||||||
|
* x = B
|
||||||
|
* }
|
||||||
|
* definitions can apply to uses that come before it!
|
||||||
|
*
|
||||||
|
* Also, a different case:
|
||||||
|
* x = A
|
||||||
|
* loop {
|
||||||
|
* use x
|
||||||
|
* y = B
|
||||||
|
* }
|
||||||
|
* Because, technically, the last use of x is before y = B, y and x may
|
||||||
|
* be assigned the same physical location, corrupting data as a result.
|
||||||
|
* To fix this, fake, "useless" statements are inserted during parsing
|
||||||
|
* that make the AST look as such:
|
||||||
|
* x = A
|
||||||
|
* loop {
|
||||||
|
* use x
|
||||||
|
* y = B
|
||||||
|
* }
|
||||||
|
* x;
|
||||||
|
* Until dead code removal is implemented, this will not be a problem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void rawadduse(VarTableEntry *vte, UseDef *ud) {
|
||||||
|
assert(vte->kind == VARTABLEENTRY_VAR);
|
||||||
|
|
||||||
|
assert(ud->next == NULL);
|
||||||
|
|
||||||
|
assert(!!vte->data.var.usedefFirst == !!vte->data.var.usedefLast);
|
||||||
|
|
||||||
|
if(!vte->data.var.usedefFirst) {
|
||||||
|
vte->data.var.usedefFirst = vte->data.var.usedefLast = ud;
|
||||||
|
} else {
|
||||||
|
vte->data.var.usedefLast->next = ud;
|
||||||
|
vte->data.var.usedefLast = ud;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adduse(VarTableEntry *vte, AST *use, AST *whole) {
|
||||||
|
assert(vte->kind == VARTABLEENTRY_VAR);
|
||||||
|
|
||||||
|
assert(vte->data.var.reachingDefs != NULL);
|
||||||
|
|
||||||
|
for(size_t d = 0; d < vte->data.var.reachingDefs->defCount; d++) {
|
||||||
|
UseDef *ud = malloc(sizeof(*ud));
|
||||||
|
ud->def = vte->data.var.reachingDefs->defs[d];
|
||||||
|
ud->use = use;
|
||||||
|
ud->stmt = whole;
|
||||||
|
ud->next = NULL;
|
||||||
|
|
||||||
|
rawadduse(vte, ud);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void overwritedefs(VarTableEntry *vte, AST *def) {
|
||||||
|
assert(vte->kind == VARTABLEENTRY_VAR);
|
||||||
|
|
||||||
|
if(!vte->data.var.reachingDefs) {
|
||||||
|
vte->data.var.reachingDefs = calloc(1, sizeof(*vte->data.var.reachingDefs));
|
||||||
|
}
|
||||||
|
|
||||||
|
vte->data.var.reachingDefs->defCount = 1;
|
||||||
|
|
||||||
|
if(!vte->data.var.reachingDefs->defs) {
|
||||||
|
vte->data.var.reachingDefs->defs = calloc(1, sizeof(*vte->data.var.reachingDefs->defs));
|
||||||
|
}
|
||||||
|
|
||||||
|
vte->data.var.reachingDefs->defs[0] = def;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mergedefs(VarTableEntry *vte) {
|
||||||
|
assert(vte->kind == VARTABLEENTRY_VAR);
|
||||||
|
|
||||||
|
ReachingDefs *rdefs = vte->data.var.reachingDefs;
|
||||||
|
|
||||||
|
assert(rdefs != NULL);
|
||||||
|
assert(rdefs->parent != NULL);
|
||||||
|
|
||||||
|
rdefs->parent->defs = realloc(rdefs->parent->defs, sizeof(*rdefs->parent->defs) * (rdefs->parent->defCount + rdefs->defCount));
|
||||||
|
memcpy(rdefs->parent->defs + rdefs->parent->defCount, rdefs->defs, rdefs->defCount * sizeof(*rdefs->defs));
|
||||||
|
|
||||||
|
vte->data.var.reachingDefs = rdefs->parent;
|
||||||
|
|
||||||
|
free(rdefs->defs);
|
||||||
|
free(rdefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pushdefs(VarTableEntry *vte) {
|
||||||
|
assert(vte->kind == VARTABLEENTRY_VAR);
|
||||||
|
|
||||||
|
ReachingDefs *rdefs = malloc(sizeof(*rdefs));
|
||||||
|
rdefs->defCount = 0;
|
||||||
|
rdefs->defs = NULL;
|
||||||
|
rdefs->excludeParent = 0;
|
||||||
|
rdefs->parent = vte->data.var.reachingDefs;
|
||||||
|
|
||||||
|
vte->data.var.reachingDefs = rdefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pushdefsall(AST *tlc) {
|
||||||
|
for(size_t i = 0; i < tlc->chunk.varCount; i++) {
|
||||||
|
pushdefs(tlc->chunk.vars[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mergedefsall(AST *tlc) {
|
||||||
|
for(size_t i = 0; i < tlc->chunk.varCount; i++) {
|
||||||
|
mergedefs(tlc->chunk.vars[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mergedefsloop(AST *tlc, VarTableEntry *vte, AST *daLoopStmt) {
|
||||||
|
assert(vte->kind == VARTABLEENTRY_VAR);
|
||||||
|
|
||||||
|
for(size_t d = 0; d < vte->data.var.reachingDefs->defCount; d++) {
|
||||||
|
UseDef *ud = vte->data.var.usedefFirst;
|
||||||
|
|
||||||
|
while(ud) {
|
||||||
|
if(ast_stmt_is_after(daLoopStmt->stmtLoop.body, NULL, ud->stmt) == 1 && ud->def != vte->data.var.reachingDefs->defs[d]) {
|
||||||
|
UseDef *udnew = calloc(1, sizeof(*udnew));
|
||||||
|
udnew->next = ud->next;
|
||||||
|
ud->next = udnew;
|
||||||
|
|
||||||
|
udnew->def = vte->data.var.reachingDefs->defs[d];
|
||||||
|
udnew->use = ud->use;
|
||||||
|
udnew->stmt = ud->stmt;
|
||||||
|
|
||||||
|
if(udnew->next == NULL) {
|
||||||
|
vte->data.var.usedefLast = udnew;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ud = ud->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mergedefs(vte);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mergedefsloopall(AST *tlc, AST *daLoopStmt) {
|
||||||
|
for(size_t i = 0; i < tlc->chunk.varCount; i++) {
|
||||||
|
mergedefsloop(tlc, tlc->chunk.vars[i], daLoopStmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ast_usedef_pass(AST *tlc, AST *a, AST *wholestmt) {
|
||||||
|
if(a->nodeKind == AST_CHUNK) {
|
||||||
|
for(AST *s = a->chunk.statementFirst; s; s = s->statement.next) {
|
||||||
|
ast_usedef_pass(tlc, s, s);
|
||||||
|
}
|
||||||
|
} else if(a->nodeKind == AST_STMT_IF) {
|
||||||
|
pushdefsall(tlc);
|
||||||
|
|
||||||
|
ast_usedef_pass(tlc, a->stmtIf.expression, wholestmt);
|
||||||
|
ast_usedef_pass(tlc, a->stmtIf.then, wholestmt);
|
||||||
|
|
||||||
|
mergedefsall(tlc);
|
||||||
|
} else if(a->nodeKind == AST_STMT_LOOP) {
|
||||||
|
pushdefsall(tlc);
|
||||||
|
|
||||||
|
ast_usedef_pass(tlc, a->stmtLoop.body, wholestmt);
|
||||||
|
|
||||||
|
mergedefsloopall(tlc, a);
|
||||||
|
} else if(a->nodeKind == AST_STMT_ASSIGN) {
|
||||||
|
if(a->stmtAssign.what->nodeKind == AST_EXPR_VAR && a->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
||||||
|
overwritedefs(a->stmtAssign.what->exprVar.thing, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_usedef_pass(tlc, a->stmtAssign.what, wholestmt);
|
||||||
|
|
||||||
|
if(a->stmtAssign.to) {
|
||||||
|
ast_usedef_pass(tlc, a->stmtAssign.to, wholestmt);
|
||||||
|
}
|
||||||
|
} else if(a->nodeKind == AST_STMT_EXPR) {
|
||||||
|
ast_usedef_pass(tlc, a->stmtExpr.expr, wholestmt);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_VAR) {
|
||||||
|
if(a->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
||||||
|
adduse(a->exprVar.thing, a, wholestmt);
|
||||||
|
}
|
||||||
|
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
|
||||||
|
ast_usedef_pass(tlc, a->exprBinOp.operands[0], wholestmt);
|
||||||
|
ast_usedef_pass(tlc, a->exprBinOp.operands[1], wholestmt);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_UNARY_OP) {
|
||||||
|
ast_usedef_pass(tlc, a->exprUnOp.operand, wholestmt);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_CALL) {
|
||||||
|
ast_usedef_pass(tlc, a->exprCall.what, wholestmt);
|
||||||
|
|
||||||
|
for(size_t p = 0; p < a->exprCall.what->expression.type->function.argCount; p++) {
|
||||||
|
ast_usedef_pass(tlc, a->exprCall.args[p], wholestmt);
|
||||||
|
}
|
||||||
|
} else if(a->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
} else if(a->nodeKind == AST_EXPR_STRING_LITERAL) {
|
||||||
|
} else if(a->nodeKind == AST_EXPR_CAST) {
|
||||||
|
ast_usedef_pass(tlc, a->exprCast.what, wholestmt);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_BREAK) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_CONTINUE) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_EXT_ALIGN) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_EXT_ORG) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_EXT_SECTION) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_DECL) {
|
||||||
|
assert(a->stmtDecl.thing->kind != VARTABLEENTRY_VAR || a->stmtDecl.expression);
|
||||||
|
} else {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ast_usedef_reset(AST *chu) {
|
||||||
|
for(size_t i = 0; i < chu->chunk.varCount; i++) {
|
||||||
|
VarTableEntry *vte = chu->chunk.vars[i];
|
||||||
|
|
||||||
|
assert(vte->kind == VARTABLEENTRY_VAR);
|
||||||
|
|
||||||
|
vte->data.var.reachingDefs = NULL;
|
||||||
|
vte->data.var.usedefFirst = NULL;
|
||||||
|
vte->data.var.usedefLast = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushdefsall(chu);
|
||||||
|
|
||||||
|
return ast_usedef_pass(chu, chu, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *cat(char *a, const char *b) {
|
||||||
|
if(!a) {
|
||||||
|
return strdup(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
a = realloc(a, strlen(a) + strlen(b) + 1);
|
||||||
|
strcpy(a + strlen(a), b);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((format(printf, 1, 2))) static 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] = {};
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
ret[i++] = t->primitive.isFloat ? 'f' : (t->primitive.isUnsigned ? 'u' : 'i');
|
||||||
|
snprintf(ret + i, sizeof(ret) - i, "%i", t->primitive.width);
|
||||||
|
|
||||||
|
return strdup(ret);
|
||||||
|
} else if(t->type == TYPE_TYPE_POINTER) {
|
||||||
|
char *c = type_to_string(t->pointer.of);
|
||||||
|
char *r = malp("%s*", c);
|
||||||
|
free(c);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strdup("@unimp");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *ast_dumpe(AST *e) {
|
||||||
|
if(e->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
return malp("%i", e->exprPrim.val);
|
||||||
|
} else if(e->nodeKind == AST_EXPR_VAR) {
|
||||||
|
VarTableEntry *vte = e->exprVar.thing;
|
||||||
|
|
||||||
|
if(vte->kind == VARTABLEENTRY_VAR) {
|
||||||
|
return strdup(vte->data.var.name);
|
||||||
|
} else if(vte->kind == VARTABLEENTRY_SYMBOL) {
|
||||||
|
return strdup(vte->data.symbol.name);
|
||||||
|
} else abort();
|
||||||
|
} else if(e->nodeKind == AST_EXPR_UNARY_OP) {
|
||||||
|
const char *op;
|
||||||
|
switch(e->exprUnOp.operator) {
|
||||||
|
case UNOP_REF:
|
||||||
|
op = "&";
|
||||||
|
break;
|
||||||
|
case UNOP_DEREF:
|
||||||
|
op = "*";
|
||||||
|
break;
|
||||||
|
case UNOP_BITWISE_NOT:
|
||||||
|
op = "~";
|
||||||
|
break;
|
||||||
|
case UNOP_NEGATE:
|
||||||
|
op = "-";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char *c = ast_dumpe(e->exprUnOp.operand);
|
||||||
|
char *r = malp("%s%s", op, c);
|
||||||
|
free(c);
|
||||||
|
return r;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_BINARY_OP) {
|
||||||
|
char *a = ast_dumpe(e->exprBinOp.operands[0]);
|
||||||
|
char *b = ast_dumpe(e->exprBinOp.operands[1]);
|
||||||
|
const char *op;
|
||||||
|
switch(e->exprBinOp.operator) {
|
||||||
|
case BINOP_ADD:
|
||||||
|
op = "+";
|
||||||
|
break;
|
||||||
|
case BINOP_SUB:
|
||||||
|
op = "-";
|
||||||
|
break;
|
||||||
|
case BINOP_MUL:
|
||||||
|
op = "*";
|
||||||
|
break;
|
||||||
|
case BINOP_DIV:
|
||||||
|
op = "/";
|
||||||
|
break;
|
||||||
|
case BINOP_BITWISE_AND:
|
||||||
|
op = "&";
|
||||||
|
break;
|
||||||
|
case BINOP_BITWISE_OR:
|
||||||
|
op = "|";
|
||||||
|
break;
|
||||||
|
case BINOP_BITWISE_XOR:
|
||||||
|
op = "^";
|
||||||
|
break;
|
||||||
|
case BINOP_EQUAL:
|
||||||
|
op = "==";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
char *r = malp("(%s %s %s)", a, op, b);
|
||||||
|
free(a);
|
||||||
|
free(b);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return malp("@unimp:%s", AST_KIND_STR[e->nodeKind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *ast_dumps(AST *s) {
|
||||||
|
if(s->nodeKind == AST_STMT_DECL) {
|
||||||
|
VarTableEntry *vte = s->stmtDecl.thing;
|
||||||
|
|
||||||
|
if(vte->kind == VARTABLEENTRY_SYMBOL) {
|
||||||
|
char *t = type_to_string(vte->type);
|
||||||
|
char *e = s->stmtDecl.expression ? ast_dumpe(s->stmtDecl.expression) : strdup("");
|
||||||
|
char *r = malp("%s%s %s: %s;\n", vte->data.symbol.isExternal ? "external " : "", t, vte->data.symbol.name, e);
|
||||||
|
free(t);
|
||||||
|
free(e);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} else if(s->nodeKind == AST_STMT_ASSIGN) {
|
||||||
|
char *a = ast_dumpe(s->stmtAssign.what);
|
||||||
|
char *b = ast_dumpe(s->stmtAssign.to);
|
||||||
|
char *r = malp("%s = %s;\n", a, b);
|
||||||
|
free(a);
|
||||||
|
free(b);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return malp("@unimp:%s\n", AST_KIND_STR[s->nodeKind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ast_dump(AST *tlc) {
|
||||||
|
AST *stmt = tlc->chunk.statementFirst;
|
||||||
|
|
||||||
|
char *ret = NULL;
|
||||||
|
|
||||||
|
#define CAT(s) do { char *b = s; ret = cat(ret, (b)); free(b); } while(0)
|
||||||
|
|
||||||
|
while(stmt) {
|
||||||
|
CAT(ast_dumps(stmt));
|
||||||
|
|
||||||
|
stmt = stmt->statement.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *memdup(void *a, size_t len) {
|
||||||
|
void *r = malloc(len);
|
||||||
|
memcpy(r, a, len);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST *ast_deep_copy(AST *src) {
|
||||||
|
if(src->nodeKind == AST_EXPR_VAR) {
|
||||||
|
return memdup(src, sizeof(ASTExprVar));
|
||||||
|
} else if(src->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
return memdup(src, sizeof(ASTExprPrimitive));
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
64
src/ast.h
64
src/ast.h
@ -18,29 +18,35 @@
|
|||||||
#define ENUMPAK
|
#define ENUMPAK
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum ENUMPAK {
|
#define GEN_ENUM(x) x,
|
||||||
AST_CHUNK,
|
#define GEN_STRI(x) #x,
|
||||||
AST_STMT_DECL,
|
|
||||||
AST_TYPE_IDENTIFIER,
|
#define AST_KINDS(K) \
|
||||||
AST_EXPR_PRIMITIVE,
|
K(AST_CHUNK) \
|
||||||
AST_STMT_IF,
|
K(AST_STMT_DECL) \
|
||||||
AST_EXPR_BINARY_OP,
|
K(AST_TYPE_IDENTIFIER) \
|
||||||
AST_EXPR_VAR,
|
K(AST_EXPR_PRIMITIVE) \
|
||||||
AST_TYPE_POINTER,
|
K(AST_STMT_IF) \
|
||||||
AST_EXPR_UNARY_OP,
|
K(AST_EXPR_BINARY_OP) \
|
||||||
AST_STMT_LOOP,
|
K(AST_EXPR_VAR) \
|
||||||
AST_STMT_BREAK,
|
K(AST_EXPR_STACK_POINTER) \
|
||||||
AST_STMT_CONTINUE,
|
K(AST_TYPE_POINTER) \
|
||||||
AST_EXPR_CALL,
|
K(AST_EXPR_UNARY_OP) \
|
||||||
AST_STMT_EXPR,
|
K(AST_STMT_LOOP) \
|
||||||
AST_STMT_ASSIGN,
|
K(AST_STMT_BREAK) \
|
||||||
AST_STMT_EXT_ALIGN,
|
K(AST_STMT_CONTINUE) \
|
||||||
AST_EXPR_STRING_LITERAL,
|
K(AST_EXPR_CALL) \
|
||||||
AST_EXPR_CAST,
|
K(AST_STMT_EXPR) \
|
||||||
AST_EXPR_ARRAY,
|
K(AST_STMT_ASSIGN) \
|
||||||
AST_STMT_EXT_ORG,
|
K(AST_STMT_EXT_ALIGN) \
|
||||||
AST_STMT_EXT_SECTION,
|
K(AST_EXPR_STRING_LITERAL) \
|
||||||
} ASTKind;
|
K(AST_EXPR_CAST) \
|
||||||
|
K(AST_EXPR_ARRAY) \
|
||||||
|
K(AST_STMT_EXT_ORG) \
|
||||||
|
K(AST_STMT_EXT_SECTION)
|
||||||
|
|
||||||
|
typedef enum ENUMPAK { AST_KINDS(GEN_ENUM) } ASTKind;
|
||||||
|
extern const char *AST_KIND_STR[];
|
||||||
|
|
||||||
typedef enum ENUMPAK {
|
typedef enum ENUMPAK {
|
||||||
BINOP_ADD = 0,
|
BINOP_ADD = 0,
|
||||||
@ -126,6 +132,10 @@ typedef struct {
|
|||||||
char *data;
|
char *data;
|
||||||
} ASTExprStringLiteral;
|
} ASTExprStringLiteral;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
} ASTExprStackPointer;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ASTKind nodeKind;
|
ASTKind nodeKind;
|
||||||
|
|
||||||
@ -167,6 +177,8 @@ typedef struct {
|
|||||||
|
|
||||||
union AST *statementFirst;
|
union AST *statementFirst;
|
||||||
union AST *statementLast;
|
union AST *statementLast;
|
||||||
|
|
||||||
|
size_t stackReservation;
|
||||||
} ASTChunk;
|
} ASTChunk;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -270,4 +282,10 @@ int ast_expression_equal(AST*, AST*);
|
|||||||
|
|
||||||
int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2);
|
int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2);
|
||||||
|
|
||||||
|
void ast_usedef_reset(AST *chu);
|
||||||
|
|
||||||
|
char *ast_dump(AST *tlc);
|
||||||
|
|
||||||
|
AST *ast_deep_copy(AST*);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
103
src/cg.c
103
src/cg.c
@ -8,7 +8,7 @@
|
|||||||
#include"x86.h"
|
#include"x86.h"
|
||||||
|
|
||||||
#define REGS 4
|
#define REGS 4
|
||||||
static const char *regs[REGS][3] = {{"al", "ax", "eax"}, {"bl", "bx", "ebx"}, {"cl", "cx", "ecx"}, {"dl", "dx", "edx"}};
|
static const char *regs[][3] = {{"al", "ax", "eax"}, {"bl", "bx", "ebx"}, {"cl", "cx", "ecx"}, {"dl", "dx", "edx"}, {"fu", "fk", "fck"}};
|
||||||
|
|
||||||
static const char *BINOP_SIMPLE_INSTRS[] = {[BINOP_ADD] = "add", [BINOP_SUB] = "sub", [BINOP_BITWISE_AND] = "and", [BINOP_BITWISE_OR] = "or", [BINOP_BITWISE_XOR] = "xor"};
|
static const char *BINOP_SIMPLE_INSTRS[] = {[BINOP_ADD] = "add", [BINOP_SUB] = "sub", [BINOP_BITWISE_AND] = "and", [BINOP_BITWISE_OR] = "or", [BINOP_BITWISE_XOR] = "xor"};
|
||||||
|
|
||||||
@ -94,7 +94,9 @@ static const char *xop_sz(AST *e, int sz) {
|
|||||||
|
|
||||||
char *ret = bufs[bufidx];
|
char *ret = bufs[bufidx];
|
||||||
|
|
||||||
if(e->nodeKind == AST_EXPR_VAR) {
|
if(e->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
snprintf(ret, XOPBUFSZ, "esp");
|
||||||
|
} else if(e->nodeKind == AST_EXPR_VAR) {
|
||||||
VarTableEntry *v = e->exprVar.thing;
|
VarTableEntry *v = e->exprVar.thing;
|
||||||
|
|
||||||
if(v->kind == VARTABLEENTRY_VAR) {
|
if(v->kind == VARTABLEENTRY_VAR) {
|
||||||
@ -204,6 +206,10 @@ void cg_chunk(AST *a) {
|
|||||||
static const char *instrs[] = {"inc", "dec"};
|
static const char *instrs[] = {"inc", "dec"};
|
||||||
printf("%s %s %s\n", instrs[s->stmtAssign.to->exprBinOp.operator == BINOP_SUB], specexpr(s->stmtAssign.what), xop(s->stmtAssign.what));
|
printf("%s %s %s\n", instrs[s->stmtAssign.to->exprBinOp.operator == BINOP_SUB], specexpr(s->stmtAssign.what), xop(s->stmtAssign.what));
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0]) && s->stmtAssign.to->exprBinOp.operator < BINOP_SIMPLES) {
|
||||||
|
|
||||||
|
printf("%s %s, %s\n", BINOP_SIMPLE_INSTRS[s->stmtAssign.to->exprBinOp.operator], xop(s->stmtAssign.what), xop(s->stmtAssign.to->exprBinOp.operands[1]));
|
||||||
|
|
||||||
} 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_VAR && s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
} 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_VAR && s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
||||||
|
|
||||||
printf("lea %s, [%s + %s]\n",
|
printf("lea %s, [%s + %s]\n",
|
||||||
@ -228,6 +234,10 @@ void cg_chunk(AST *a) {
|
|||||||
} else if(is_xop(s->stmtAssign.what) != NULL && 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)) {
|
} else if(is_xop(s->stmtAssign.what) != NULL && 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)) {
|
||||||
|
|
||||||
printf("neg %s\n", xop(s->stmtAssign.what));
|
printf("neg %s\n", xop(s->stmtAssign.what));
|
||||||
|
|
||||||
|
} else if(is_xop(s->stmtAssign.what) && s->stmtAssign.to->nodeKind == AST_EXPR_CAST) {
|
||||||
|
|
||||||
|
printf("movzx %s, %s\n", xop(s->stmtAssign.what), xop(s->stmtAssign.to->exprCast.what));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -316,20 +326,91 @@ void cg_chunk(AST *a) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spill_pass(AST *tlc, AST *a, VarTableEntry *vte) {
|
||||||
|
if(a->nodeKind == AST_CHUNK) {
|
||||||
|
for(AST *s = a->chunk.statementFirst; s; s = s->statement.next) {
|
||||||
|
ast_usedef_pass(tlc, s, s);
|
||||||
|
}
|
||||||
|
} else if(a->nodeKind == AST_STMT_IF) {
|
||||||
|
pushdefsall(tlc);
|
||||||
|
|
||||||
|
ast_usedef_pass(tlc, a->stmtIf.expression, wholestmt);
|
||||||
|
ast_usedef_pass(tlc, a->stmtIf.then, wholestmt);
|
||||||
|
|
||||||
|
mergedefsall(tlc);
|
||||||
|
} else if(a->nodeKind == AST_STMT_LOOP) {
|
||||||
|
pushdefsall(tlc);
|
||||||
|
|
||||||
|
ast_usedef_pass(tlc, a->stmtLoop.body, wholestmt);
|
||||||
|
|
||||||
|
mergedefsloopall(tlc, a);
|
||||||
|
} else if(a->nodeKind == AST_STMT_ASSIGN) {
|
||||||
|
if(a->stmtAssign.what->nodeKind == AST_EXPR_VAR && a->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
||||||
|
overwritedefs(a->stmtAssign.what->exprVar.thing, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_usedef_pass(tlc, a->stmtAssign.what, wholestmt);
|
||||||
|
|
||||||
|
if(a->stmtAssign.to) {
|
||||||
|
ast_usedef_pass(tlc, a->stmtAssign.to, wholestmt);
|
||||||
|
}
|
||||||
|
} else if(a->nodeKind == AST_STMT_EXPR) {
|
||||||
|
ast_usedef_pass(tlc, a->stmtExpr.expr, wholestmt);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_VAR) {
|
||||||
|
if(a->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
||||||
|
adduse(a->exprVar.thing, a, wholestmt);
|
||||||
|
}
|
||||||
|
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
|
||||||
|
ast_usedef_pass(tlc, a->exprBinOp.operands[0], wholestmt);
|
||||||
|
ast_usedef_pass(tlc, a->exprBinOp.operands[1], wholestmt);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_UNARY_OP) {
|
||||||
|
ast_usedef_pass(tlc, a->exprUnOp.operand, wholestmt);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_CALL) {
|
||||||
|
ast_usedef_pass(tlc, a->exprCall.what, wholestmt);
|
||||||
|
|
||||||
|
for(size_t p = 0; p < a->exprCall.what->expression.type->function.argCount; p++) {
|
||||||
|
ast_usedef_pass(tlc, a->exprCall.args[p], wholestmt);
|
||||||
|
}
|
||||||
|
} else if(a->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
} else if(a->nodeKind == AST_EXPR_STRING_LITERAL) {
|
||||||
|
} else if(a->nodeKind == AST_EXPR_CAST) {
|
||||||
|
ast_usedef_pass(tlc, a->exprCast.what, wholestmt);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_BREAK) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_CONTINUE) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_EXT_ALIGN) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_EXT_ORG) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_EXT_SECTION) {
|
||||||
|
} else if(a->nodeKind == AST_STMT_DECL) {
|
||||||
|
assert(a->stmtDecl.thing->kind != VARTABLEENTRY_VAR || a->stmtDecl.expression);
|
||||||
|
} else {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Welsh-Powell graph coloring */
|
/* Welsh-Powell graph coloring */
|
||||||
static int comparator(const void *A, const void *B) {
|
static int comparator(const void *A, const void *B) {
|
||||||
VarTableEntry *const *a = A;
|
VarTableEntry *const *a = A;
|
||||||
VarTableEntry *const *b = B;
|
VarTableEntry *const *b = B;
|
||||||
return ((*a)->data.var.degree * (*a)->data.var.priority) - ((*b)->data.var.degree * (*b)->data.var.priority);
|
return ((*a)->data.var.degree * (*a)->data.var.priority) - ((*b)->data.var.degree * (*b)->data.var.priority);
|
||||||
}
|
}
|
||||||
void cg_go(AST *a) {
|
int cg_go(AST *a) {
|
||||||
typedef VarTableEntry *Adjacency[2];
|
typedef VarTableEntry *Adjacency[2];
|
||||||
|
|
||||||
|
ast_usedef_reset(a);
|
||||||
|
|
||||||
size_t adjCount = 0;
|
size_t adjCount = 0;
|
||||||
Adjacency *adjs = malloc(sizeof(*adjs) * adjCount);
|
Adjacency *adjs = malloc(sizeof(*adjs) * adjCount);
|
||||||
|
|
||||||
VarTableEntry **vars = a->chunk.vars;
|
VarTableEntry **vars = a->chunk.vars;
|
||||||
|
|
||||||
|
for(size_t vi = 0; vi < a->chunk.varCount; vi++) {
|
||||||
|
vars[vi]->data.var.priority = 1;
|
||||||
|
vars[vi]->data.var.color = 0;
|
||||||
|
vars[vi]->data.var.degree = 0;
|
||||||
|
vars[vi]->data.var.markedForSpill = 0;
|
||||||
|
}
|
||||||
|
|
||||||
for(size_t v1i = 0; v1i < a->chunk.varCount; v1i++) {
|
for(size_t v1i = 0; v1i < a->chunk.varCount; v1i++) {
|
||||||
for(size_t v2i = 0; v2i < a->chunk.varCount; v2i++) {
|
for(size_t v2i = 0; v2i < a->chunk.varCount; v2i++) {
|
||||||
if(v1i == v2i) continue;
|
if(v1i == v2i) continue;
|
||||||
@ -372,6 +453,8 @@ cont:;
|
|||||||
|
|
||||||
qsort(vars, a->chunk.varCount, sizeof(*vars), comparator);
|
qsort(vars, a->chunk.varCount, sizeof(*vars), comparator);
|
||||||
|
|
||||||
|
int lastColor = 0;
|
||||||
|
|
||||||
/* Welsh plow my ass */
|
/* Welsh plow my ass */
|
||||||
for(int v = 0; v < a->chunk.varCount; v++) {
|
for(int v = 0; v < a->chunk.varCount; v++) {
|
||||||
for(int c = 0;; c++) {
|
for(int c = 0;; c++) {
|
||||||
@ -384,6 +467,9 @@ cont:;
|
|||||||
}
|
}
|
||||||
|
|
||||||
vars[v]->data.var.color = c;
|
vars[v]->data.var.color = c;
|
||||||
|
|
||||||
|
lastColor = lastColor < c ? c : lastColor;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
nextColor:;
|
nextColor:;
|
||||||
@ -392,7 +478,16 @@ nextColor:;
|
|||||||
|
|
||||||
free(adjs);
|
free(adjs);
|
||||||
|
|
||||||
|
if(lastColor >= 4) {
|
||||||
|
// Spill node with highest degree
|
||||||
|
|
||||||
|
spill_pass(a, a, vars[a->chunk.varCount - 1]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
cg_chunk(a);
|
cg_chunk(a);
|
||||||
|
|
||||||
free(vars);
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
src/cg.h
2
src/cg.h
@ -3,6 +3,6 @@
|
|||||||
|
|
||||||
#include"ast.h"
|
#include"ast.h"
|
||||||
|
|
||||||
void cg_go(union AST*);
|
int cg_go(union AST*);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -8,87 +8,11 @@
|
|||||||
// Complex expressions are to be "broken down" into simpler ones until the AST
|
// Complex expressions are to be "broken down" into simpler ones until the AST
|
||||||
// can be trivially translated to the target architecture.
|
// can be trivially translated to the target architecture.
|
||||||
//
|
//
|
||||||
// This pass, along with CG is strictly dependent on x86 and will fail for
|
// This file along with CG is strictly for IA-32 and will fail for other
|
||||||
// any other architecture.
|
// architecture.
|
||||||
|
|
||||||
#include"x86.h"
|
#include"x86.h"
|
||||||
|
|
||||||
static void varify_change_usedefs(AST *e, AST *oldStmt, AST *newStmt) {
|
|
||||||
if(e->nodeKind == AST_EXPR_VAR) {
|
|
||||||
if(e->exprVar.thing->kind != VARTABLEENTRY_VAR) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UseDef *swapWithPrev = NULL;
|
|
||||||
UseDef *swapWith = NULL;
|
|
||||||
|
|
||||||
UseDef *udPrev = NULL;
|
|
||||||
UseDef *ud = e->exprVar.thing->data.var.usedefFirst;
|
|
||||||
while(ud && ud->use != e) {
|
|
||||||
if(ud->stmt == oldStmt && !swapWith) {
|
|
||||||
swapWithPrev = udPrev;
|
|
||||||
swapWith = ud;
|
|
||||||
}
|
|
||||||
|
|
||||||
udPrev = ud;
|
|
||||||
ud = ud->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ud) {
|
|
||||||
assert(ud->stmt == oldStmt);
|
|
||||||
ud->stmt = newStmt;
|
|
||||||
|
|
||||||
// We must make sure all newStmt usedefs are before the oldStmt ones, so swap
|
|
||||||
if(swapWith) {
|
|
||||||
assert(!!swapWithPrev + !!udPrev != 0);
|
|
||||||
|
|
||||||
if(swapWithPrev) {
|
|
||||||
swapWithPrev->next = ud;
|
|
||||||
} else {
|
|
||||||
e->exprVar.thing->data.var.usedefFirst = ud;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(udPrev) {
|
|
||||||
udPrev->next = swapWith;
|
|
||||||
} else {
|
|
||||||
e->exprVar.thing->data.var.usedefFirst = swapWith;
|
|
||||||
}
|
|
||||||
|
|
||||||
UseDef *temp = ud->next;
|
|
||||||
ud->next = swapWith->next;
|
|
||||||
swapWith->next = temp;
|
|
||||||
|
|
||||||
assert(!!ud->next + !!swapWith->next != 0);
|
|
||||||
if(!swapWith->next) {
|
|
||||||
e->exprVar.thing->data.var.usedefLast = swapWith;
|
|
||||||
}
|
|
||||||
if(!ud->next) {
|
|
||||||
e->exprVar.thing->data.var.usedefLast = ud;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(e->nodeKind == AST_EXPR_BINARY_OP) {
|
|
||||||
varify_change_usedefs(e->exprBinOp.operands[0], oldStmt, newStmt);
|
|
||||||
varify_change_usedefs(e->exprBinOp.operands[1], oldStmt, newStmt);
|
|
||||||
} else if(e->nodeKind == AST_EXPR_UNARY_OP) {
|
|
||||||
varify_change_usedefs(e->exprUnOp.operand, oldStmt, newStmt);
|
|
||||||
} else if(e->nodeKind == AST_EXPR_CALL) {
|
|
||||||
varify_change_usedefs(e->exprCall.what, oldStmt, newStmt);
|
|
||||||
|
|
||||||
int argCount = e->exprCall.what->expression.type->function.argCount;
|
|
||||||
for(int i = 0; i < argCount; i++) {
|
|
||||||
varify_change_usedefs(e->exprCall.args[i], oldStmt, newStmt);
|
|
||||||
}
|
|
||||||
} else if(e->nodeKind == AST_EXPR_ARRAY) {
|
|
||||||
int sz = e->exprArray.type->array.length;
|
|
||||||
for(int i = 0; i < sz; i++) {
|
|
||||||
varify_change_usedefs(e->exprArray.items[i], oldStmt, newStmt);
|
|
||||||
}
|
|
||||||
} else if(e->nodeKind == AST_EXPR_CAST) {
|
|
||||||
varify_change_usedefs(e->exprCast.what, oldStmt, newStmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Split away complex expression into a new local variable */
|
/* Split away complex expression into a new local variable */
|
||||||
static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
|
static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
|
||||||
VarTableEntry *vte = malloc(sizeof(*vte));
|
VarTableEntry *vte = malloc(sizeof(*vte));
|
||||||
@ -125,28 +49,10 @@ static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
|
|||||||
}
|
}
|
||||||
assign->next = stmt;
|
assign->next = stmt;
|
||||||
|
|
||||||
// Assemble ud-chain
|
// Reset ud-chain
|
||||||
|
|
||||||
UseDef *ud[2];
|
vte->data.var.usedefFirst = NULL;
|
||||||
ud[0] = malloc(sizeof(UseDef));
|
vte->data.var.usedefLast = NULL;
|
||||||
ud[1] = malloc(sizeof(UseDef));
|
|
||||||
|
|
||||||
ud[0]->stmt = (AST*) assign;
|
|
||||||
ud[0]->use = assign->what;
|
|
||||||
ud[0]->def = (AST*) assign;
|
|
||||||
ud[0]->next = ud[1];
|
|
||||||
|
|
||||||
ud[1]->stmt = stmt;
|
|
||||||
ud[1]->use = (AST*) ev[1];
|
|
||||||
ud[1]->def = (AST*) assign;
|
|
||||||
ud[1]->next = NULL;
|
|
||||||
|
|
||||||
vte->data.var.usedefFirst = ud[0];
|
|
||||||
vte->data.var.usedefLast = ud[1];
|
|
||||||
|
|
||||||
// Correct the ud-chain of variables used in varified expression `e`
|
|
||||||
|
|
||||||
varify_change_usedefs(e, stmt, (AST*) assign);
|
|
||||||
|
|
||||||
return (AST*) ev[1];
|
return (AST*) ev[1];
|
||||||
}
|
}
|
||||||
@ -155,28 +61,6 @@ static AST *xopify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
|
|||||||
return varify(tlc, chunk, stmtPrev, stmt, e);
|
return varify(tlc, chunk, stmtPrev, stmt, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static UseDef *get_last_usedef_before_stmt(AST *tlc, VarTableEntry *v, AST *stmt) {
|
|
||||||
assert(v->kind == VARTABLEENTRY_VAR);
|
|
||||||
|
|
||||||
UseDef *ud = v->data.var.usedefFirst;
|
|
||||||
|
|
||||||
if(!ud) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
UseDef *udPrev = NULL;
|
|
||||||
|
|
||||||
while(ud) {
|
|
||||||
if(ast_stmt_is_after(tlc, ud->stmt, stmt)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
udPrev = ud;
|
|
||||||
ud = ud->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return udPrev;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dumben_chunk(AST *tlc, AST *chu) {
|
static int dumben_chunk(AST *tlc, AST *chu) {
|
||||||
AST *sPrev = NULL;
|
AST *sPrev = NULL;
|
||||||
AST *s = chu->chunk.statementFirst;
|
AST *s = chu->chunk.statementFirst;
|
||||||
@ -230,24 +114,17 @@ static int dumben_chunk(AST *tlc, AST *chu) {
|
|||||||
effective = 1;
|
effective = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_NEGATE && (s->stmtAssign.to->exprUnOp.operand->nodeKind != AST_EXPR_VAR || s->stmtAssign.what->exprVar.thing != s->stmtAssign.to->exprUnOp.operand->exprVar.thing)) {
|
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:
|
// Turn this:
|
||||||
// a = -b
|
// a = -b
|
||||||
// into
|
// into
|
||||||
// a = b
|
// a = b
|
||||||
// a = -a
|
// a = -a
|
||||||
// While keeping UD-chain valid.
|
|
||||||
|
|
||||||
// Currently only for variables. While this could work for any XOP, it requires the ability to deep-copy `a`, which is not trivial when taking into account UD-chains.
|
AST *ev[2] = {
|
||||||
// TODO: Scrap incremental UD-chain modifications. Simply rebuild them after each pass.
|
ast_deep_copy(s->stmtAssign.what),
|
||||||
|
ast_deep_copy(s->stmtAssign.what)
|
||||||
ASTExprVar *ev[2];
|
};
|
||||||
for(int i = 0; i < 2; i++) {
|
|
||||||
ev[i] = malloc(sizeof(ASTExprVar));
|
|
||||||
ev[i]->nodeKind = AST_EXPR_VAR;
|
|
||||||
ev[i]->type = s->stmtAssign.what->expression.type;
|
|
||||||
ev[i]->thing = s->stmtAssign.what->exprVar.thing;
|
|
||||||
}
|
|
||||||
|
|
||||||
AST *negation = s->stmtAssign.to;
|
AST *negation = s->stmtAssign.to;
|
||||||
|
|
||||||
@ -261,28 +138,40 @@ static int dumben_chunk(AST *tlc, AST *chu) {
|
|||||||
|
|
||||||
assign2->statement.next = s->statement.next;
|
assign2->statement.next = s->statement.next;
|
||||||
s->statement.next = assign2;
|
s->statement.next = assign2;
|
||||||
|
|
||||||
UseDef *link = get_last_usedef_before_stmt(tlc, ev[0]->thing, assign2->statement.next);
|
|
||||||
|
|
||||||
UseDef *ud[2];
|
|
||||||
ud[0] = malloc(sizeof(UseDef));
|
|
||||||
ud[1] = malloc(sizeof(UseDef));
|
|
||||||
|
|
||||||
ud[0]->stmt = assign2;
|
|
||||||
ud[0]->use = (AST*) ev[1];
|
|
||||||
ud[0]->def = s;
|
|
||||||
ud[0]->next = ud[1];
|
|
||||||
|
|
||||||
ud[1]->stmt = assign2;
|
|
||||||
ud[1]->use = (AST*) ev[0];
|
|
||||||
ud[1]->def = s;
|
|
||||||
ud[1]->next = link->next;
|
|
||||||
|
|
||||||
link->next = ud[0];
|
|
||||||
|
|
||||||
effective = 1;
|
effective = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, sPrev, s, s->stmtAssign.to->exprUnOp.operand);
|
||||||
|
|
||||||
|
effective = 1;
|
||||||
|
} 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 = malloc(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];
|
||||||
|
|
||||||
|
sPrev->statement.next = assign2;
|
||||||
|
assign2->statement.next = s;
|
||||||
|
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[0] = ast_deep_copy(s->stmtAssign.what);
|
||||||
|
|
||||||
|
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, sPrev, s, s->stmtAssign.to->exprBinOp.operands[0]);
|
||||||
|
} 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, sPrev, s, s->stmtAssign.to->exprBinOp.operands[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_CALL) {
|
} else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_CALL) {
|
||||||
|
|
||||||
int argCount = s->stmtExpr.expr->exprCall.what->expression.type->function.argCount;
|
int argCount = s->stmtExpr.expr->exprCall.what->expression.type->function.argCount;
|
||||||
|
@ -39,7 +39,12 @@ int main(int argc_, char **argv_) {
|
|||||||
|
|
||||||
dumben_go(chunk);
|
dumben_go(chunk);
|
||||||
|
|
||||||
cg_go(chunk);
|
while(!cg_go(chunk)) {
|
||||||
|
|
||||||
|
puts(ast_dump(chunk));
|
||||||
|
|
||||||
|
dumben_go(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
170
src/parse.c
170
src/parse.c
@ -24,11 +24,6 @@ static inline int __builtin_clzl(unsigned long x) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
UseDef ud;
|
|
||||||
VarTableEntry *to;
|
|
||||||
} UseDefToAdd;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Token *tokens;
|
Token *tokens;
|
||||||
intmax_t i;
|
intmax_t i;
|
||||||
@ -41,10 +36,6 @@ typedef struct {
|
|||||||
// Used by pushstat to add statements
|
// Used by pushstat to add statements
|
||||||
ASTChunk *currentChunk;
|
ASTChunk *currentChunk;
|
||||||
|
|
||||||
// Used by pushstmt to assemble use-def chain
|
|
||||||
size_t udsToAddCount;
|
|
||||||
UseDefToAdd *udsToAdd;
|
|
||||||
|
|
||||||
// Used to place guard variable uses after loops to stop reg allocation from fucking up
|
// Used to place guard variable uses after loops to stop reg allocation from fucking up
|
||||||
VarTable *loopScope;
|
VarTable *loopScope;
|
||||||
size_t guardedVarCount;
|
size_t guardedVarCount;
|
||||||
@ -88,28 +79,6 @@ static int maybe(Parser *P, TokenKind t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void pushstat(Parser *P, void *a) {
|
static void pushstat(Parser *P, void *a) {
|
||||||
for(size_t i = 0; i < P->udsToAddCount; i++) {
|
|
||||||
VarTableEntry *to = P->udsToAdd[i].to;
|
|
||||||
|
|
||||||
UseDef *ud = malloc(sizeof(*ud));
|
|
||||||
memcpy(ud, &P->udsToAdd[i].ud, sizeof(*ud));
|
|
||||||
ud->stmt = a;
|
|
||||||
|
|
||||||
/*if(ud->stmt->nodeKind == AST_STMT_ASSIGN && ud->stmt->stmtAssign.what == ud->use) {
|
|
||||||
// Forget. The x in `x = y` is not a use of x.
|
|
||||||
continue;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if(to->data.var.usedefFirst) {
|
|
||||||
to->data.var.usedefLast->next = ud;
|
|
||||||
to->data.var.usedefLast = ud;
|
|
||||||
} else {
|
|
||||||
to->data.var.usedefFirst = to->data.var.usedefLast = ud;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
P->udsToAddCount = 0;
|
|
||||||
|
|
||||||
if(P->currentChunk->statementFirst) {
|
if(P->currentChunk->statementFirst) {
|
||||||
P->currentChunk->statementLast->statement.next = a;
|
P->currentChunk->statementLast->statement.next = a;
|
||||||
P->currentChunk->statementLast = a;
|
P->currentChunk->statementLast = a;
|
||||||
@ -141,7 +110,7 @@ static ASTExprPrimitive *parse_prim(Parser *P) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void newusedef(Parser *P, VarTableEntry *v, AST *expr) {
|
/*static void newusedef(Parser *P, VarTableEntry *v, AST *expr) {
|
||||||
ReachingDefs *defs = v->data.var.reachingDefs;
|
ReachingDefs *defs = v->data.var.reachingDefs;
|
||||||
|
|
||||||
while(defs) {
|
while(defs) {
|
||||||
@ -160,7 +129,7 @@ static void newusedef(Parser *P, VarTableEntry *v, AST *expr) {
|
|||||||
|
|
||||||
defs = defs->parent;
|
defs = defs->parent;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
static AST *exprvar(Parser *P, VarTableEntry *v) {
|
static AST *exprvar(Parser *P, VarTableEntry *v) {
|
||||||
AST *a = malloc(sizeof(ASTExprVar));
|
AST *a = malloc(sizeof(ASTExprVar));
|
||||||
@ -168,10 +137,6 @@ static AST *exprvar(Parser *P, VarTableEntry *v) {
|
|||||||
a->exprVar.type = v->type;
|
a->exprVar.type = v->type;
|
||||||
a->exprVar.thing = v;
|
a->exprVar.thing = v;
|
||||||
|
|
||||||
if(v->kind == VARTABLEENTRY_VAR) {
|
|
||||||
newusedef(P, v, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(P->loopScope) {
|
if(P->loopScope) {
|
||||||
// XXX: O(n)!!!!!!!!!
|
// XXX: O(n)!!!!!!!!!
|
||||||
|
|
||||||
@ -254,9 +219,6 @@ AST *nct_cast_expr(AST *what, Type *to) {
|
|||||||
ret->val = what->exprPrim.val & (((int64_t) 1 << to->primitive.width) - 1);
|
ret->val = what->exprPrim.val & (((int64_t) 1 << to->primitive.width) - 1);
|
||||||
return (AST*) ret;
|
return (AST*) ret;
|
||||||
} else {
|
} else {
|
||||||
// Silently fail :)
|
|
||||||
return what;
|
|
||||||
|
|
||||||
ASTExprCast *ret = malloc(sizeof(*ret));
|
ASTExprCast *ret = malloc(sizeof(*ret));
|
||||||
ret->nodeKind = AST_EXPR_CAST;
|
ret->nodeKind = AST_EXPR_CAST;
|
||||||
ret->type = to;
|
ret->type = to;
|
||||||
@ -273,6 +235,15 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
|||||||
if(peek(P, 0).type == TOKEN_NUMBER) {
|
if(peek(P, 0).type == TOKEN_NUMBER) {
|
||||||
return (AST*) parse_prim(P);
|
return (AST*) parse_prim(P);
|
||||||
} else if(peek(P, 0).type == TOKEN_IDENTIFIER) {
|
} else if(peek(P, 0).type == TOKEN_IDENTIFIER) {
|
||||||
|
if(!strcmp(peek(P, 0).content, "@stack")) {
|
||||||
|
get(P);
|
||||||
|
|
||||||
|
ASTExprStackPointer *ret = malloc(sizeof(*ret));
|
||||||
|
ret->nodeKind = AST_EXPR_STACK_POINTER;
|
||||||
|
ret->type = primitive_parse("u32");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return exprvar(P, vartable_find(P->scope, get(P).content));
|
return exprvar(P, vartable_find(P->scope, get(P).content));
|
||||||
} else if(peek(P, 0).type == TOKEN_STRING) {
|
} else if(peek(P, 0).type == TOKEN_STRING) {
|
||||||
ASTExprStringLiteral *ret = malloc(sizeof(*ret));
|
ASTExprStringLiteral *ret = malloc(sizeof(*ret));
|
||||||
@ -478,10 +449,20 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
|||||||
|
|
||||||
ASTExpr *operand = &(astop->operands[1] = nct_parse_expression(P, lOP + 1))->expression;
|
ASTExpr *operand = &(astop->operands[1] = nct_parse_expression(P, lOP + 1))->expression;
|
||||||
|
|
||||||
if(operand->type->type != TYPE_TYPE_PRIMITIVE) {
|
if(!type_is_number(astop->operands[0]->expression.type)
|
||||||
stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Invalid combination of operator and operand types.");
|
|| !type_is_number(astop->operands[1]->expression.type)) {
|
||||||
}
|
|
||||||
|
stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Attempt to perform arithmetic on non-number types.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type_size(astop->operands[0]->expression.type) < type_size(astop->operands[1]->expression.type)) {
|
||||||
|
astop->operands[0] = nct_cast_expr(astop->operands[0], astop->operands[1]->expression.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type_size(astop->operands[1]->expression.type) < type_size(astop->operands[0]->expression.type)) {
|
||||||
|
astop->operands[1] = nct_cast_expr(astop->operands[1], astop->operands[0]->expression.type);
|
||||||
|
}
|
||||||
|
|
||||||
if(!astop->type) {
|
if(!astop->type) {
|
||||||
astop->type = operand->type;
|
astop->type = operand->type;
|
||||||
} else {
|
} else {
|
||||||
@ -655,8 +636,8 @@ static AST *parse_declaration(Parser *P) {
|
|||||||
assign->nodeKind = AST_STMT_ASSIGN;
|
assign->nodeKind = AST_STMT_ASSIGN;
|
||||||
assign->next = NULL;
|
assign->next = NULL;
|
||||||
|
|
||||||
entry->data.var.reachingDefs = reachingdefs_push(NULL);
|
//entry->data.var.reachingDefs = reachingdefs_push(NULL);
|
||||||
reachingdefs_set(entry->data.var.reachingDefs, (AST*) assign);
|
//reachingdefs_set(entry->data.var.reachingDefs, (AST*) assign);
|
||||||
|
|
||||||
assign->what = exprvar(P, entry);
|
assign->what = exprvar(P, entry);
|
||||||
assign->to = peek(P, 0).type == TOKEN_SEMICOLON ? NULL : nct_parse_expression(P, 0);
|
assign->to = peek(P, 0).type == TOKEN_SEMICOLON ? NULL : nct_parse_expression(P, 0);
|
||||||
@ -710,77 +691,6 @@ backtrack:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Imagine the following pseudocode:
|
|
||||||
x = ...
|
|
||||||
while ... {
|
|
||||||
(do something with x)
|
|
||||||
x = ...
|
|
||||||
}
|
|
||||||
The second definition of x reaches the code *before* it.
|
|
||||||
Therefore we must go over the loop body a second time with known reaching definitions and add the necessary usedefs.*/
|
|
||||||
static void loop_second_pass(AST *a) {
|
|
||||||
if(a->nodeKind == AST_CHUNK) {
|
|
||||||
for(AST *s = a->chunk.statementFirst; s; s = s->statement.next) {
|
|
||||||
loop_second_pass(s);
|
|
||||||
}
|
|
||||||
} else if(a->nodeKind == AST_STMT_IF) {
|
|
||||||
loop_second_pass(a->stmtIf.expression);
|
|
||||||
loop_second_pass(a->stmtIf.then);
|
|
||||||
} else if(a->nodeKind == AST_STMT_LOOP) {
|
|
||||||
loop_second_pass(a->stmtLoop.body);
|
|
||||||
} else if(a->nodeKind == AST_STMT_ASSIGN) {
|
|
||||||
loop_second_pass(a->stmtAssign.what);
|
|
||||||
loop_second_pass(a->stmtAssign.to);
|
|
||||||
|
|
||||||
/* TODO: Per-variable flag.to cease additional usedefs after assignment?
|
|
||||||
It is okay to have too many usedefs, so this isn't high priority.*/
|
|
||||||
} else if(a->nodeKind == AST_STMT_EXPR) {
|
|
||||||
loop_second_pass(a->stmtExpr.expr);
|
|
||||||
} else if(a->nodeKind == AST_EXPR_VAR) {
|
|
||||||
UseDef *udPrev = NULL;
|
|
||||||
UseDef *ud = a->exprVar.thing->data.var.usedefFirst;
|
|
||||||
while(ud && ud->use != a) {
|
|
||||||
udPrev = ud;
|
|
||||||
ud = ud->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ud) {
|
|
||||||
ReachingDefs *defs = a->exprVar.thing->data.var.reachingDefs;
|
|
||||||
while(defs) {
|
|
||||||
for(size_t i = 0; i < defs->defCount; i++) {
|
|
||||||
UseDef *newud = calloc(1, sizeof(*newud));
|
|
||||||
memcpy(newud, ud, sizeof(*ud));
|
|
||||||
newud->def = defs->defs[i];
|
|
||||||
|
|
||||||
udPrev->next = newud;
|
|
||||||
newud->next = ud;
|
|
||||||
ud = newud;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(defs->excludeParent) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
defs = defs->parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
|
|
||||||
loop_second_pass(a->exprBinOp.operands[0]);
|
|
||||||
loop_second_pass(a->exprBinOp.operands[1]);
|
|
||||||
} else if(a->nodeKind == AST_EXPR_UNARY_OP) {
|
|
||||||
loop_second_pass(a->exprUnOp.operand);
|
|
||||||
} else if(a->nodeKind == AST_EXPR_CAST) {
|
|
||||||
loop_second_pass(a->exprCast.what);
|
|
||||||
} else if(a->nodeKind == AST_EXPR_CALL) {
|
|
||||||
loop_second_pass(a->exprCall.what);
|
|
||||||
|
|
||||||
size_t argCount = a->exprCall.what->expression.type->function.argCount;
|
|
||||||
for(size_t i = 0; i < argCount; i++) {
|
|
||||||
loop_second_pass(a->exprCall.args[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTChunk *nct_parse_chunk(Parser*, int, int);
|
ASTChunk *nct_parse_chunk(Parser*, int, int);
|
||||||
void nct_parse_statement(Parser *P) {
|
void nct_parse_statement(Parser *P) {
|
||||||
if(maybe(P, TOKEN_IF)) {
|
if(maybe(P, TOKEN_IF)) {
|
||||||
@ -815,22 +725,26 @@ void nct_parse_statement(Parser *P) {
|
|||||||
ret->body = (AST*) nct_parse_chunk(P, 0, 1);
|
ret->body = (AST*) nct_parse_chunk(P, 0, 1);
|
||||||
expect(P, TOKEN_SQUIGGLY_R);
|
expect(P, TOKEN_SQUIGGLY_R);
|
||||||
|
|
||||||
|
pushstat(P, ret);
|
||||||
|
|
||||||
if(isFirstLoop) {
|
if(isFirstLoop) {
|
||||||
P->loopScope = NULL;
|
P->loopScope = NULL;
|
||||||
|
|
||||||
for(size_t i = 0; i < P->guardedVarCount; i++) {
|
for(size_t i = 0; i < P->guardedVarCount; i++) {
|
||||||
ASTExprVar *ev = P->guardedVars[i];
|
ASTExprVar *ev = P->guardedVars[i];
|
||||||
|
|
||||||
newusedef(P, ev->thing, ev);
|
AST *es = calloc(1, sizeof(ASTStmtExpr));
|
||||||
|
es->nodeKind = AST_STMT_EXPR;
|
||||||
|
es->stmtExpr.expr = ev;
|
||||||
|
|
||||||
|
pushstat(P, es);
|
||||||
}
|
}
|
||||||
|
|
||||||
P->guardedVarCount = 0;
|
P->guardedVarCount = 0;
|
||||||
free(P->guardedVars);
|
free(P->guardedVars);
|
||||||
P->guardedVars = NULL;
|
P->guardedVars = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
loop_second_pass(ret->body);
|
|
||||||
|
|
||||||
pushstat(P, ret);
|
|
||||||
return;
|
return;
|
||||||
} else if(maybe(P, TOKEN_BREAK)) {
|
} else if(maybe(P, TOKEN_BREAK)) {
|
||||||
ASTStmtBreak *ret = malloc(sizeof(*ret));
|
ASTStmtBreak *ret = malloc(sizeof(*ret));
|
||||||
@ -840,6 +754,7 @@ void nct_parse_statement(Parser *P) {
|
|||||||
expect(P, TOKEN_SEMICOLON);
|
expect(P, TOKEN_SEMICOLON);
|
||||||
|
|
||||||
pushstat(P, ret);
|
pushstat(P, ret);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else if(maybe(P, TOKEN_CONTINUE)) {
|
} else if(maybe(P, TOKEN_CONTINUE)) {
|
||||||
ASTStmtContinue *ret = malloc(sizeof(*ret));
|
ASTStmtContinue *ret = malloc(sizeof(*ret));
|
||||||
@ -849,6 +764,7 @@ void nct_parse_statement(Parser *P) {
|
|||||||
expect(P, TOKEN_SEMICOLON);
|
expect(P, TOKEN_SEMICOLON);
|
||||||
|
|
||||||
pushstat(P, ret);
|
pushstat(P, ret);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else if(peek(P, 0).type == TOKEN_IDENTIFIER) {
|
} else if(peek(P, 0).type == TOKEN_IDENTIFIER) {
|
||||||
if(!strcmp(peek(P, 0).content, "@align")) {
|
if(!strcmp(peek(P, 0).content, "@align")) {
|
||||||
@ -920,9 +836,9 @@ void nct_parse_statement(Parser *P) {
|
|||||||
ret->what = e;
|
ret->what = e;
|
||||||
ret->to = nct_cast_expr(nct_parse_expression(P, 0), ret->what->expression.type);
|
ret->to = nct_cast_expr(nct_parse_expression(P, 0), ret->what->expression.type);
|
||||||
|
|
||||||
if(ret->what->nodeKind == AST_EXPR_VAR) {
|
//if(ret->what->nodeKind == AST_EXPR_VAR) {
|
||||||
reachingdefs_set(ret->what->exprVar.thing->data.var.reachingDefs, (AST*) ret);
|
// reachingdefs_set(ret->what->exprVar.thing->data.var.reachingDefs, (AST*) ret);
|
||||||
}
|
//}
|
||||||
|
|
||||||
expect(P, TOKEN_SEMICOLON);
|
expect(P, TOKEN_SEMICOLON);
|
||||||
|
|
||||||
@ -942,7 +858,7 @@ void nct_parse_statement(Parser *P) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) {
|
ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) {
|
||||||
AST *ret = malloc(sizeof(ASTChunk));
|
AST *ret = calloc(1, sizeof(ASTChunk));
|
||||||
ret->nodeKind = AST_CHUNK;
|
ret->nodeKind = AST_CHUNK;
|
||||||
ret->chunk.statementFirst = ret->chunk.statementLast = NULL;
|
ret->chunk.statementFirst = ret->chunk.statementLast = NULL;
|
||||||
ret->chunk.varCount = 0;
|
ret->chunk.varCount = 0;
|
||||||
@ -957,7 +873,7 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) {
|
|||||||
P->topLevel = &ret->chunk;
|
P->topLevel = &ret->chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
vartable_new_reachingdefs_for_all_vars(P->scope);
|
//vartable_new_reachingdefs_for_all_vars(P->scope);
|
||||||
|
|
||||||
/* Find all symbol names and struct types ahead of time. Searches for colons as those can only mean symbol declarations */
|
/* Find all symbol names and struct types ahead of time. Searches for colons as those can only mean symbol declarations */
|
||||||
|
|
||||||
@ -1001,7 +917,7 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) {
|
|||||||
nct_parse_statement(P);
|
nct_parse_statement(P);
|
||||||
}
|
}
|
||||||
|
|
||||||
vartable_coalesce_reachingdefs_for_all_vars(P->scope);
|
//vartable_coalesce_reachingdefs_for_all_vars(P->scope);
|
||||||
|
|
||||||
size_t nonSymbols = 0;
|
size_t nonSymbols = 0;
|
||||||
for(size_t i = 0; i < P->scope->count; i++) {
|
for(size_t i = 0; i < P->scope->count; i++) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef NCTREF_REPORTING_H
|
#ifndef NCTREF_REPORTING_H
|
||||||
#define NCTREF_REPORTING_H
|
#define NCTREF_REPORTING_H
|
||||||
|
|
||||||
#ifndef _GNUC
|
#ifndef __GNUC__
|
||||||
#define __attribute__(x)
|
#define __attribute__(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -132,6 +132,10 @@ Type *type_pointer_wrap(Type *t) {
|
|||||||
return (Type*) ret;
|
return (Type*) ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int type_is_number(Type *t) {
|
||||||
|
return t->type == TYPE_TYPE_POINTER || t->type == TYPE_TYPE_PRIMITIVE;
|
||||||
|
}
|
||||||
|
|
||||||
int type_is_castable(Type *from, Type *to) {
|
int type_is_castable(Type *from, Type *to) {
|
||||||
if(type_equal(from, to)) return 2;
|
if(type_equal(from, to)) return 2;
|
||||||
|
|
||||||
@ -152,4 +156,4 @@ int type_is_castable(Type *from, Type *to) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -71,4 +71,8 @@ Type *type_pointer_wrap(Type*);
|
|||||||
/* 0 = not castable, 1 = explicitly castable, 2 = implicitly castable */
|
/* 0 = not castable, 1 = explicitly castable, 2 = implicitly castable */
|
||||||
int type_is_castable(Type *from, Type *to);
|
int type_is_castable(Type *from, Type *to);
|
||||||
|
|
||||||
|
int type_is_number(Type *t);
|
||||||
|
|
||||||
|
char *type_to_string(Type*);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
#define NCTREF_VARTABLE_H
|
#define NCTREF_VARTABLE_H
|
||||||
|
|
||||||
#include"types.h"
|
#include"types.h"
|
||||||
|
#include<stdbool.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VARTABLEENTRY_SYMBOL, VARTABLEENTRY_TYPE, VARTABLEENTRY_VAR
|
VARTABLEENTRY_SYMBOL, VARTABLEENTRY_STACK, VARTABLEENTRY_VAR
|
||||||
} VarTableEntryKind;
|
} VarTableEntryKind;
|
||||||
|
|
||||||
union AST;
|
union AST;
|
||||||
@ -49,7 +50,7 @@ typedef struct VarTableEntry {
|
|||||||
// Register allocation
|
// Register allocation
|
||||||
|
|
||||||
// vars in loops have higher priority
|
// vars in loops have higher priority
|
||||||
// a more advanced approach would be to use weights for different colors (e.g. vars for mul "should" be in eax)
|
// a more advanced approach would be to use weights for different colors (e.g. multipliers "should" be in eax)
|
||||||
uint8_t priority;
|
uint8_t priority;
|
||||||
uint16_t color, degree;
|
uint16_t color, degree;
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ static inline int is_xop(AST *e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if(e->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
return XOP_NOT_MEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
return XOP_NOT_XOP;
|
return XOP_NOT_XOP;
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
u32 x: 123;
|
u32 x: 123;
|
||||||
u33 y: 5;
|
u33 y: 5;
|
||||||
|
|
||||||
u3 *o = 5000;
|
u3* o = 5000;
|
||||||
u3 z = *o;
|
u32 z = *o;
|
||||||
u3 p = *(o + z);
|
u3 p = *(o + z);
|
||||||
|
u3 *g = o - z;
|
||||||
|
p = *g;
|
||||||
|
|
||||||
|
u32 c = 566;
|
||||||
|
u16 j = c;
|
||||||
|
6
tests/spill.nct
Normal file
6
tests/spill.nct
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
u8 a = 4;
|
||||||
|
u8 b = 1;
|
||||||
|
u8 c = 9;
|
||||||
|
u8 d = 5;
|
||||||
|
u8 e = 22;
|
||||||
|
u8 f = a + b + c + d + e;
|
1
tests/stack.nct
Normal file
1
tests/stack.nct
Normal file
@ -0,0 +1 @@
|
|||||||
|
@stack = @stack - 123;
|
Loading…
Reference in New Issue
Block a user