Compare commits

...

6 Commits

Author SHA1 Message Date
Mid
8caadf9af1 Function definition example 2024-11-26 18:43:34 +02:00
Mid
d6d2b228ef Implement function definitions 2024-11-26 18:42:20 +02:00
Mid
6789984bd1 Codegen casts 2024-11-25 18:35:11 +02:00
Mid
fe0baa26a0 Spilling 2024-11-25 17:36:03 +02:00
Mid
9e04938065 Partial compilation 2024-11-25 17:33:46 +02:00
Mid
ba6f9fb6c1 Safety commit 2024-11-20 16:36:17 +02:00
19 changed files with 1026 additions and 433 deletions

View File

@ -3,19 +3,25 @@ rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
SOURCES := $(call rwildcard,src/,*.c) SOURCES := $(call rwildcard,src/,*.c)
HEADERS := $(call rwildcard,src/,*.h) HEADERS := $(call rwildcard,src/,*.h)
OBJECTS := $(patsubst src/%.c,build/%.o,$(SOURCES))
DEPS := $(patsubst build/%.o,build/%.d,$(OBJECTS))
PREFIX = /usr/local PREFIX = /usr/local
CFLAGS = $(if $(DEBUGA),-DDEBUG=1,) -Wall $(if $(ASAN),-fsanitize=address,) -fno-PIE -no-pie -std=gnu11 $(if $(DEBUG),-O0 -g,-Os -s) -fms-extensions -Isrc -Wno-array-bounds -MMD -MP -flto
.PHONY: install clean .PHONY: install clean
ntc: $(SOURCES) $(HEADERS) build/%.o: src/%.c
ifdef OW cc -c $(CFLAGS) -o $@ $<
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 ntc: $(OBJECTS)
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 $(CFLAGS) -o ntc $(OBJECTS)
endif
install: ntc install: ntc
mv ./ntc $(PREFIX)/bin mv ./ntc $(PREFIX)/bin
clean: clean:
rm ./ntc rm ./ntc
-include $(DEPS)

480
src/ast.c
View File

@ -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) {
@ -55,3 +65,469 @@ 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 = NULL;
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;
default:
abort();
}
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;
case BINOP_NEQUAL:
op = "!=";
break;
default:
abort();
}
char *r = malp("(%s %s %s)", a, op, b);
free(a);
free(b);
return r;
} else if(e->nodeKind == AST_EXPR_STACK_POINTER) {
return malp("@stack");
}
return malp("@unimp:%s", AST_KIND_STR[e->nodeKind]);
}
char *ast_dump(AST *tlc);
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;
} else if(s->nodeKind == AST_STMT_LOOP) {
char *inner = ast_dump(s->stmtLoop.body);
char *c = malp("loop {\n%s}\n", inner);
free(inner);
return c;
} else if(s->nodeKind == AST_STMT_IF) {
char *cond = ast_dumpe(s->stmtIf.expression);
char *inner = ast_dump(s->stmtIf.then);
char *c = malp("if(%s) {\n%s}\n", cond, inner);
free(cond);
free(inner);
return c;
} else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_VAR) {
const char *name;
if(s->stmtExpr.expr->exprVar.thing->kind == VARTABLEENTRY_VAR) {
name = s->stmtExpr.expr->exprVar.thing->data.var.name;
} else {
name = s->stmtExpr.expr->exprVar.thing->data.symbol.name;
}
return malp("%s; /* loop guard */\n", name);
}
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();
}

View File

@ -18,29 +18,36 @@
#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_EXPR_FUNC) \
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 +133,16 @@ typedef struct {
char *data; char *data;
} ASTExprStringLiteral; } ASTExprStringLiteral;
typedef struct {
ASTExpr;
} ASTExprStackPointer;
typedef struct {
ASTExpr;
union AST *chunk;
} ASTExprFunc;
typedef struct { typedef struct {
ASTKind nodeKind; ASTKind nodeKind;
@ -167,6 +184,8 @@ typedef struct {
union AST *statementFirst; union AST *statementFirst;
union AST *statementLast; union AST *statementLast;
size_t stackReservation;
} ASTChunk; } ASTChunk;
typedef struct { typedef struct {
@ -259,6 +278,7 @@ typedef union AST {
ASTExprStringLiteral exprStrLit; ASTExprStringLiteral exprStrLit;
ASTExprCast exprCast; ASTExprCast exprCast;
ASTExprArray exprArray; ASTExprArray exprArray;
ASTExprFunc exprFunc;
ASTStmtExtOrg stmtExtOrg; ASTStmtExtOrg stmtExtOrg;
ASTStmtExtSection stmtExtSection; ASTStmtExtSection stmtExtSection;
} AST; } AST;
@ -270,4 +290,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

267
src/cg.c
View File

@ -8,19 +8,28 @@
#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] = {
[COLOR_EAX] = {"al", "ax", "eax"},
[COLOR_EBX] = {"bl", "bx", "ebx"},
[COLOR_ECX] = {"cl", "cx", "ecx"},
[COLOR_EDX] = {"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"};
static size_t nextLocalLabel = 0; static size_t nextLocalLabel = 0;
typedef struct {
#define LOOPSTACKSIZE 96 #define LOOPSTACKSIZE 96
static size_t loopStackStart[LOOPSTACKSIZE]; size_t loopStackStart[LOOPSTACKSIZE];
static size_t loopStackEnd[LOOPSTACKSIZE]; size_t loopStackEnd[LOOPSTACKSIZE];
static size_t loopStackIdx; size_t loopStackIdx;
} CGState;
static const char *direct(int size) { static const char *direct(int size) {
switch(size) { switch(size) {
case 0:
case 1: return "db"; case 1: return "db";
case 2: return "dw"; case 2: return "dw";
case 4: return "dd"; case 4: return "dd";
@ -31,6 +40,7 @@ static const char *direct(int size) {
static const char *spec(int size) { static const char *spec(int size) {
switch(size) { switch(size) {
case 0:
case 1: return "byte"; case 1: return "byte";
case 2: return "word"; case 2: return "word";
case 4: return "dword"; case 4: return "dword";
@ -41,6 +51,7 @@ static const char *spec(int size) {
static int log_size(int size) { static int log_size(int size) {
switch(size) { switch(size) {
case 0:
case 1: return 0; case 1: return 0;
case 2: return 1; case 2: return 1;
case 4: return 2; case 4: return 2;
@ -94,13 +105,15 @@ 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) {
return xv_sz(v, sz); return xv_sz(v, sz);
} else if(v->kind == VARTABLEENTRY_SYMBOL) { } else if(v->kind == VARTABLEENTRY_SYMBOL) {
snprintf(ret, XOPBUFSZ, "[%s]", v->data.symbol.name); snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), v->data.symbol.name);
} else abort(); } else abort();
} else if(e->nodeKind == AST_EXPR_PRIMITIVE) { } else if(e->nodeKind == AST_EXPR_PRIMITIVE) {
snprintf(ret, XOPBUFSZ, "%i", e->exprPrim.val); snprintf(ret, XOPBUFSZ, "%i", e->exprPrim.val);
@ -124,6 +137,8 @@ static const char *xop_sz(AST *e, int sz) {
snprintf(ret, XOPBUFSZ, "%s", e->exprUnOp.operand->exprVar.thing->data.symbol.name); snprintf(ret, XOPBUFSZ, "%s", e->exprUnOp.operand->exprVar.thing->data.symbol.name);
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_VAR) { } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(e->exprUnOp.operand->exprVar.thing, 4)); snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(e->exprUnOp.operand->exprVar.thing, 4));
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == UNOP_DEREF && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_STACK_POINTER && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
snprintf(ret, XOPBUFSZ, "[esp + %i]", e->exprUnOp.operand->exprBinOp.operands[1]->exprPrim.val);
} else { } else {
return NULL; return NULL;
} }
@ -137,9 +152,13 @@ static const char *xop(AST *e) {
return xop_sz(e, type_size(e->expression.type)); return xop_sz(e, type_size(e->expression.type));
} }
void cg_chunk(AST *a) { void cg_chunk(CGState *cg, AST *a) {
AST *s = a->chunk.statementFirst; AST *s = a->chunk.statementFirst;
if(a->chunk.stackReservation) {
printf("sub esp, %lu\n", a->chunk.stackReservation);
}
// Potentially complex pattern matching // Potentially complex pattern matching
while(s) { while(s) {
if(s->nodeKind == AST_STMT_EXT_SECTION) { if(s->nodeKind == AST_STMT_EXT_SECTION) {
@ -186,7 +205,15 @@ void cg_chunk(AST *a) {
printf("%i,", s->stmtDecl.expression->exprArray.items[i]->exprPrim.val); printf("%i,", s->stmtDecl.expression->exprArray.items[i]->exprPrim.val);
} }
} else printf("A"); } else if(v->type->type == TYPE_TYPE_FUNCTION) {
putchar('\n');
assert(s->stmtDecl.expression->nodeKind == AST_EXPR_FUNC);
cg_go(s->stmtDecl.expression->exprFunc.chunk);
} else abort();
putchar('\n'); putchar('\n');
} else { } else {
@ -194,9 +221,37 @@ void cg_chunk(AST *a) {
} }
} }
} else if(s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_CALL) {
AST *e = s->stmtAssign.to;
puts("push ecx");
puts("push edx");
int argCount = e->exprCall.what->expression.type->function.argCount;
size_t argSize = 0;
for(int i = argCount - 1; i >= 0; i--) {
printf("push %s\n", xop_sz(e->exprCall.args[i], 4));
argSize += (type_size(e->exprCall.args[i]->expression.type) + 3) & ~3;
}
assert(e->exprCall.what->nodeKind == AST_EXPR_VAR && e->exprCall.what->exprVar.thing->kind == VARTABLEENTRY_SYMBOL);
printf("call %s\n", e->exprCall.what->exprVar.thing->data.symbol.name);
if(argSize) printf("add esp, %lu\n", argSize);
puts("pop edx");
puts("pop ecx");
} else if(s->nodeKind == AST_STMT_ASSIGN) { } else if(s->nodeKind == AST_STMT_ASSIGN) {
if(s->stmtAssign.to) { if(is_xop(s->stmtAssign.what) == XOP_NOT_MEM && is_xop(s->stmtAssign.to) == XOP_NOT_MEM && !strcmp(xop(s->stmtAssign.what), xop(s->stmtAssign.to))) {
// It's a noop
} else if(s->stmtAssign.to) {
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_ADD || s->stmtAssign.to->exprBinOp.operator == BINOP_SUB) && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val == 1) { 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_ADD || s->stmtAssign.to->exprBinOp.operator == BINOP_SUB) && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val == 1) {
// inc or dec // inc or dec
@ -204,6 +259,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",
@ -229,6 +288,10 @@ void cg_chunk(AST *a) {
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 {
if(is_xop(s->stmtAssign.to) == XOP_MEM && type_size(s->stmtAssign.what->expression.type) != type_size(s->stmtAssign.to->expression.type)) { if(is_xop(s->stmtAssign.to) == XOP_MEM && type_size(s->stmtAssign.what->expression.type) != type_size(s->stmtAssign.to->expression.type)) {
@ -245,27 +308,27 @@ void cg_chunk(AST *a) {
size_t lbl0 = nextLocalLabel++; size_t lbl0 = nextLocalLabel++;
size_t lbl1 = nextLocalLabel++; size_t lbl1 = nextLocalLabel++;
loopStackStart[loopStackIdx] = lbl0; cg->loopStackStart[cg->loopStackIdx] = lbl0;
loopStackEnd[loopStackIdx] = lbl1; cg->loopStackEnd[cg->loopStackIdx] = lbl1;
loopStackIdx++; cg->loopStackIdx++;
printf(".L%lu:\n", lbl0); printf(".L%lu:\n", lbl0);
cg_chunk(s->stmtLoop.body); cg_chunk(cg, s->stmtLoop.body);
printf("jmp .L%lu\n", lbl0); printf("jmp .L%lu\n", lbl0);
printf(".L%lu:\n", lbl1); printf(".L%lu:\n", lbl1);
loopStackIdx--; cg->loopStackIdx--;
} else if(s->nodeKind == AST_STMT_BREAK) { } else if(s->nodeKind == AST_STMT_BREAK) {
printf("jmp .L%lu\n", loopStackEnd[loopStackIdx - 1]); printf("jmp .L%lu\n", cg->loopStackEnd[cg->loopStackIdx - 1]);
} else if(s->nodeKind == AST_STMT_CONTINUE) { } else if(s->nodeKind == AST_STMT_CONTINUE) {
printf("jmp .L%lu\n", loopStackStart[loopStackIdx - 1]); printf("jmp .L%lu\n", cg->loopStackStart[cg->loopStackIdx - 1]);
} else if(s->nodeKind == AST_STMT_IF) { } else if(s->nodeKind == AST_STMT_IF) {
@ -276,44 +339,106 @@ void cg_chunk(AST *a) {
printf("cmp %s, %s\n", xop(s->stmtIf.expression->exprBinOp.operands[0]), xop(s->stmtIf.expression->exprBinOp.operands[1])); printf("cmp %s, %s\n", xop(s->stmtIf.expression->exprBinOp.operands[0]), xop(s->stmtIf.expression->exprBinOp.operands[1]));
printf("j%s .L%lu\n", xj(binop_comp_opposite(s->stmtIf.expression->exprBinOp.operator)), lbl); printf("j%s .L%lu\n", xj(binop_comp_opposite(s->stmtIf.expression->exprBinOp.operator)), lbl);
cg_chunk(s->stmtIf.then); cg_chunk(cg, s->stmtIf.then);
printf(".L%lu:\n", lbl); printf(".L%lu:\n", lbl);
} else if(s->nodeKind == AST_STMT_EXPR) {
AST *e = s->stmtExpr.expr;
if(e->nodeKind == AST_EXPR_CALL) {
puts("push eax");
puts("push ecx");
puts("push edx");
int argCount = e->exprCall.what->expression.type->function.argCount;
size_t argSize = 0;
for(int i = argCount - 1; i >= 0; i--) {
printf("push %s\n", xop(e->exprCall.args[i]));
argSize += (type_size(e->exprCall.args[i]->expression.type) + 3) & ~3;
}
assert(e->exprCall.what->nodeKind == AST_EXPR_VAR && e->exprCall.what->exprVar.thing->kind == VARTABLEENTRY_SYMBOL);
printf("call %s\n", e->exprCall.what->exprVar.thing->data.symbol.name);
printf("add esp, %lu\n", argSize);
puts("pop edx");
puts("pop ecx");
puts("pop eax");
}
} }
s = s->statement.next; s = s->statement.next;
} }
if(a->chunk.stackReservation) {
printf("add esp, %lu\n", a->chunk.stackReservation);
}
}
// MUST FIRST CALL with *aptr == tlc SO stackReservation IS UPDATED!!
static void spill_pass(AST *tlc, AST **aptr, VarTableEntry *vte) {
AST *a = *aptr;
if(a->nodeKind == AST_CHUNK) {
for(AST **s = &a->chunk.statementFirst; *s; s = &((*s)->statement.next)) {
spill_pass(tlc, s, vte);
}
tlc->chunk.stackReservation += 4;
} else if(a->nodeKind == AST_STMT_IF) {
spill_pass(tlc, &a->stmtIf.expression, vte);
spill_pass(tlc, &a->stmtIf.then, vte);
} else if(a->nodeKind == AST_STMT_LOOP) {
spill_pass(tlc, &a->stmtLoop.body, vte);
} else if(a->nodeKind == AST_STMT_ASSIGN) {
spill_pass(tlc, &a->stmtAssign.what, vte);
if(a->stmtAssign.to) {
spill_pass(tlc, &a->stmtAssign.to, vte);
}
} else if(a->nodeKind == AST_STMT_EXPR) {
spill_pass(tlc, &a->stmtExpr.expr, vte);
} else if(a->nodeKind == AST_EXPR_VAR) {
if(a->exprVar.thing == vte) {
// FINALLY SPILL
ASTExprStackPointer *rsp = malloc(sizeof(*rsp));
rsp->nodeKind = AST_EXPR_STACK_POINTER;
rsp->type = primitive_parse("u32");
ASTExprPrimitive *offset = malloc(sizeof(*offset));
offset->nodeKind = AST_EXPR_PRIMITIVE;
offset->type = rsp->type;
offset->val = -tlc->chunk.stackReservation;
ASTExprBinaryOp *bop = malloc(sizeof(*bop));
bop->nodeKind = AST_EXPR_BINARY_OP;
bop->type = rsp->type;
bop->operator = BINOP_ADD;
bop->operands[0] = (AST*) rsp;
bop->operands[1] = (AST*) offset;
ASTExprUnaryOp *deref = malloc(sizeof(*deref));
deref->nodeKind = AST_EXPR_UNARY_OP;
deref->type = a->expression.type;
deref->operator = UNOP_DEREF;
deref->operand = (AST*) bop;
*aptr = (AST*) deref;
}
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
spill_pass(tlc, &a->exprBinOp.operands[0], vte);
spill_pass(tlc, &a->exprBinOp.operands[1], vte);
} else if(a->nodeKind == AST_EXPR_UNARY_OP) {
spill_pass(tlc, &a->exprUnOp.operand, vte);
} else if(a->nodeKind == AST_EXPR_CALL) {
spill_pass(tlc, &a->exprCall.what, vte);
for(size_t p = 0; p < a->exprCall.what->expression.type->function.argCount; p++) {
spill_pass(tlc, &a->exprCall.args[p], vte);
}
} else if(a->nodeKind == AST_EXPR_PRIMITIVE) {
} else if(a->nodeKind == AST_EXPR_STRING_LITERAL) {
} else if(a->nodeKind == AST_EXPR_CAST) {
spill_pass(tlc, &a->exprCast.what, vte);
} 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();
}
}
static int ud_empty(VarTableEntry *a) {
assert(a->kind == VARTABLEENTRY_VAR);
assert(!a->data.var.usedefFirst == !a->data.var.usedefLast);
return !a->data.var.usedefFirst;
} }
/* Welsh-Powell graph coloring */ /* Welsh-Powell graph coloring */
@ -322,14 +447,22 @@ static int comparator(const void *A, const void *B) {
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 = calloc(adjCount, sizeof(*adjs));
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;
}
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;
@ -338,15 +471,13 @@ void cg_go(AST *a) {
VarTableEntry *v2 = vars[v2i]; VarTableEntry *v2 = vars[v2i];
/* 1D intersection test */ /* 1D intersection test */
// if((v1->data.var.start >= v2->data.var.start && v1->data.var.start <= v2->data.var.end) if(!ud_empty(v1) && !ud_empty(v2) && (
// || (v1->data.var.end >= v2->data.var.start && v1->data.var.end <= v2->data.var.end)) {
if(
(ast_stmt_is_after(a, v1->data.var.usedefFirst->stmt, v2->data.var.usedefFirst->stmt) == 1 (ast_stmt_is_after(a, v1->data.var.usedefFirst->stmt, v2->data.var.usedefFirst->stmt) == 1
&& ast_stmt_is_after(a, v2->data.var.usedefLast->stmt, v1->data.var.usedefFirst->stmt) == 1) && ast_stmt_is_after(a, v2->data.var.usedefLast->stmt, v1->data.var.usedefFirst->stmt) == 1)
|| ||
(ast_stmt_is_after(a, v1->data.var.usedefLast->stmt, v2->data.var.usedefFirst->stmt) == 1 (ast_stmt_is_after(a, v1->data.var.usedefLast->stmt, v2->data.var.usedefFirst->stmt) == 1
&& ast_stmt_is_after(a, v2->data.var.usedefLast->stmt, v1->data.var.usedefLast->stmt) == 1) && ast_stmt_is_after(a, v2->data.var.usedefLast->stmt, v1->data.var.usedefLast->stmt) == 1)
) { )) {
VarTableEntry *min = v1 < v2 ? v1 : v2; VarTableEntry *min = v1 < v2 ? v1 : v2;
VarTableEntry *max = v1 < v2 ? v2 : v1; VarTableEntry *max = v1 < v2 ? v2 : v1;
@ -372,18 +503,28 @@ 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++) {
if(vars[v]->data.var.color != -1) {
// Already assigned.
continue;
}
for(int c = 0;; c++) { for(int c = 0;; c++) {
for(int a = 0; a < adjCount; a++) { for(int a = 0; a < adjCount; a++) {
if(adjs[a][0] == vars[v] && adjs[a][1]->data.var.color == c) { if(adjs[a][0] == vars[v] && adjs[a][1]->data.var.color != -1 && adjs[a][1]->data.var.color == c) {
goto nextColor; goto nextColor;
} else if(adjs[a][1] == vars[v] && adjs[a][0]->data.var.color == c) { } else if(adjs[a][1] == vars[v] && adjs[a][0]->data.var.color != -1 && adjs[a][0]->data.var.color == c) {
goto nextColor; goto nextColor;
} }
} }
vars[v]->data.var.color = c; vars[v]->data.var.color = c;
lastColor = lastColor < c ? c : lastColor;
break; break;
nextColor:; nextColor:;
@ -392,7 +533,19 @@ nextColor:;
free(adjs); free(adjs);
cg_chunk(a); if(lastColor >= 4) {
// Spill node with highest degree
free(vars); spill_pass(a, &a, vars[a->chunk.varCount - 1]);
return 0;
}
CGState cg;
memset(&cg, 0, sizeof(cg));
cg_chunk(&cg, a);
return 1;
} }

View File

@ -3,6 +3,6 @@
#include"ast.h" #include"ast.h"
void cg_go(union AST*); int cg_go(union AST*);
#endif #endif

View File

@ -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));
@ -98,6 +22,7 @@ static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
vte->data.var.degree = 0; vte->data.var.degree = 0;
vte->data.var.priority = 0; vte->data.var.priority = 0;
vte->data.var.reachingDefs = NULL; vte->data.var.reachingDefs = NULL;
vte->data.var.name = strdup("googoo");
// Add to var array // Add to var array
tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount)); tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount));
@ -125,28 +50,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 +62,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;
@ -223,31 +108,39 @@ static int dumben_chunk(AST *tlc, AST *chu) {
} else if(s->nodeKind == AST_STMT_ASSIGN) { } else if(s->nodeKind == AST_STMT_ASSIGN) {
if(ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to) && sPrev) {
// Remove this statement from AST
sPrev->statement.next = s->statement.next;
effective = 1;
} else {
if(s->stmtAssign.what->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.what->exprUnOp.operator == UNOP_DEREF if(s->stmtAssign.what->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.what->exprUnOp.operator == UNOP_DEREF
&& s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_DEREF) { && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_DEREF) {
s->stmtAssign.to = varify(tlc, chu, sPrev, s, s->stmtAssign.to); s->stmtAssign.to = varify(tlc, chu, sPrev, s, s->stmtAssign.to);
effective = 1; effective = 1;
} } else if(s->stmtAssign.what && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_CALL) {
ASTExprCall *call = &s->stmtAssign.to->exprCall;
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)) { int argCount = call->what->expression.type->function.argCount;
for(int i = 0; i < argCount; i++) {
if(is_xop(call->args[i]) == XOP_NOT_XOP) {
call->args[i] = xopify(tlc, chu, sPrev, s, call->args[i]);
effective = 1;
}
}
} else if(s->stmtAssign.to && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_NEGATE && !ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprUnOp.operand)) {
// Turn this: // 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;
@ -262,35 +155,37 @@ 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); effective = 1;
} else if(is_xop(s->stmtAssign.to) == XOP_NOT_XOP) {
if(s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_DEREF) {
s->stmtAssign.to->exprUnOp.operand = varify(tlc, chu, sPrev, s, s->stmtAssign.to->exprUnOp.operand);
UseDef *ud[2]; effective = 1;
ud[0] = malloc(sizeof(UseDef)); } 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])) {
ud[1] = malloc(sizeof(UseDef)); // Turn this:
// a = b op c
// into
// a = b
// a = a op c
ud[0]->stmt = assign2; AST *assign2 = malloc(sizeof(ASTStmtAssign));
ud[0]->use = (AST*) ev[1]; assign2->nodeKind = AST_STMT_ASSIGN;
ud[0]->def = s; assign2->stmtAssign.what = ast_deep_copy(s->stmtAssign.what);
ud[0]->next = ud[1]; assign2->stmtAssign.to = s->stmtAssign.to->exprBinOp.operands[0];
ud[1]->stmt = assign2; sPrev->statement.next = assign2;
ud[1]->use = (AST*) ev[0]; assign2->statement.next = s;
ud[1]->def = s;
ud[1]->next = link->next;
link->next = ud[0]; 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]);
effective = 1;
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && !is_xop(s->stmtAssign.to->exprBinOp.operands[1])) {
s->stmtAssign.to->exprBinOp.operands[1] = xopify(tlc, chu, sPrev, s, s->stmtAssign.to->exprBinOp.operands[1]);
effective = 1; effective = 1;
} }
} 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;
for(int i = 0; i < argCount; i++) {
if(is_xop(s->stmtExpr.expr->exprCall.args[i]) == XOP_NOT_XOP) {
s->stmtExpr.expr->exprCall.args[i] = xopify(tlc, chu, sPrev, s, s->stmtExpr.expr->exprCall.args[i]);
effective = 1;
} }
} }
@ -304,5 +199,12 @@ static int dumben_chunk(AST *tlc, AST *chu) {
} }
void dumben_go(AST* tlc) { void dumben_go(AST* tlc) {
while(dumben_chunk(tlc, tlc)); size_t i = 0;
while(1) {
fprintf(stderr, "// Dumbing down %lu...\n", i++);
int successful = dumben_chunk(tlc, tlc);
if(!successful) break;
}
} }

View File

@ -36,7 +36,8 @@ char *TOKEN_NAMES[] = {
"'?'", "'?'",
"string" "string"
"'!='", "'!='",
"'!'" "'!'",
"'continue'",
}; };
static int isAlpha(int c) { static int isAlpha(int c) {

View File

@ -37,9 +37,17 @@ int main(int argc_, char **argv_) {
free(tokens); free(tokens);
fputs("/* === Original AST === */\n", stderr);
fputs(ast_dump(chunk), stderr);
fputc('\n', stderr);
dumben_go(chunk); dumben_go(chunk);
cg_go(chunk); fputs("\n/* === Dumbified === */\n", stderr);
fputs(ast_dump(chunk), stderr);
fputc('\n', stderr);
while(!cg_go(chunk));
return 0; return 0;
} }

View File

@ -8,6 +8,7 @@
#include"reporting.h" #include"reporting.h"
#include<stdint.h> #include<stdint.h>
#include<signal.h> #include<signal.h>
#include"x86.h"
#ifndef __GNUC__ #ifndef __GNUC__
static inline int __builtin_clzl(unsigned long x) { static inline int __builtin_clzl(unsigned long x) {
@ -24,11 +25,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 +37,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 +80,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 +111,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 +130,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 +138,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)!!!!!!!!!
@ -247,16 +213,19 @@ AST *nct_cast_expr(AST *what, Type *to) {
return NULL; return NULL;
} }
if(what->nodeKind == AST_EXPR_PRIMITIVE && to->type == TYPE_TYPE_PRIMITIVE) { if(what->nodeKind == AST_EXPR_PRIMITIVE && (to->type == TYPE_TYPE_PRIMITIVE || to->type == TYPE_TYPE_POINTER)) {
ASTExprPrimitive *ret = malloc(sizeof(*ret)); ASTExprPrimitive *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_PRIMITIVE; ret->nodeKind = AST_EXPR_PRIMITIVE;
ret->type = to; ret->type = to;
ret->val = what->exprPrim.val & (((int64_t) 1 << to->primitive.width) - 1);
if(to->type == TYPE_TYPE_PRIMITIVE) {
ret->val = what->exprPrim.val & ((1UL << to->primitive.width) - 1);
} else {
ret->val = what->exprPrim.val & ((1UL << (8 * type_size(to))) - 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;
@ -268,11 +237,43 @@ AST *nct_cast_expr(AST *what, Type *to) {
abort(); abort();
} }
ASTChunk *nct_parse_chunk(Parser*, int, int);
Type *nct_parse_typename(Parser *P);
AST *nct_parse_expression(Parser *P, int lOP) { AST *nct_parse_expression(Parser *P, int lOP) {
if(lOP == 0) {
// Test if this is an anonymous function
Type *ft = nct_parse_typename(P);
if(ft) {
assert(ft->type == TYPE_TYPE_FUNCTION);
ASTExprFunc *e = malloc(sizeof(*e));
e->nodeKind = AST_EXPR_FUNC;
e->type = ft;
maybe(P, TOKEN_SQUIGGLY_L);
e->chunk = (AST*) nct_parse_chunk(P, 1, 0);
maybe(P, TOKEN_SQUIGGLY_R);
return (AST*) e;
}
}
if(lOP == 5) { if(lOP == 5) {
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 (AST*) 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));
@ -326,8 +327,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
AST *child = nct_parse_expression(P, lOP); AST *child = nct_parse_expression(P, lOP);
if(child->nodeKind == AST_EXPR_PRIMITIVE) { if(child->nodeKind == AST_EXPR_PRIMITIVE) {
child->exprPrim.val = \ child->exprPrim.val = ~child->exprPrim.val;
~child->exprPrim.val;
return child; return child;
} else { } else {
ASTExprUnaryOp *astop = malloc(sizeof(*astop)); ASTExprUnaryOp *astop = malloc(sizeof(*astop));
@ -348,12 +348,20 @@ AST *nct_parse_expression(Parser *P, int lOP) {
stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Only function types may be called."); stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Only function types may be called.");
} }
VarTableEntry *tempo = calloc(1, sizeof(*tempo));
tempo->kind = VARTABLEENTRY_VAR;
tempo->type = ret->expression.type->function.ret;
tempo->data.var.name = "$temp";
tempo->data.var.color = COLOR_EAX;
P->topLevel->vars = realloc(P->topLevel->vars, sizeof(*P->topLevel->vars) * (++P->topLevel->varCount));
P->topLevel->vars[P->topLevel->varCount - 1] = tempo;
ASTExprCall *call = malloc(sizeof(*call)); ASTExprCall *call = malloc(sizeof(*call));
call->nodeKind = AST_EXPR_CALL; call->nodeKind = AST_EXPR_CALL;
call->type = ret->expression.type->function.ret; call->type = ret->expression.type->function.ret;
call->what = ret; call->what = ret;
call->args = NULL; call->args = NULL;
ret = (AST*) call;
int argCount = 0; int argCount = 0;
@ -370,6 +378,14 @@ AST *nct_parse_expression(Parser *P, int lOP) {
} }
} }
ASTStmtAssign *assign = calloc(1, sizeof(*assign));
assign->nodeKind = AST_STMT_ASSIGN;
assign->what = exprvar(P, tempo);
assign->to = call;
pushstat(P, assign);
ret = exprvar(P, tempo);
/* TODO: Check argument count. */ /* TODO: Check argument count. */
} else if(maybe(P, TOKEN_SQUAREN_L)) { } else if(maybe(P, TOKEN_SQUAREN_L)) {
ASTExprUnaryOp *ref = malloc(sizeof(*ref)); ASTExprUnaryOp *ref = malloc(sizeof(*ref));
@ -395,10 +411,10 @@ AST *nct_parse_expression(Parser *P, int lOP) {
ASTExprBinaryOp *mul = malloc(sizeof(*mul)); ASTExprBinaryOp *mul = malloc(sizeof(*mul));
mul->nodeKind = AST_EXPR_BINARY_OP; mul->nodeKind = AST_EXPR_BINARY_OP;
mul->operator = BINOP_MUL; mul->operator = BINOP_MUL;
mul->operands[0] = scale; mul->operands[0] = (AST*) scale;
mul->operands[1] = child->operands[1]; mul->operands[1] = child->operands[1];
child->operands[1] = mul; child->operands[1] = (AST*) mul;
} }
ASTExprUnaryOp *unop = malloc(sizeof(*unop)); ASTExprUnaryOp *unop = malloc(sizeof(*unop));
@ -478,8 +494,18 @@ 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) {
@ -650,13 +676,14 @@ static AST *parse_declaration(Parser *P) {
entry->kind = VARTABLEENTRY_VAR; entry->kind = VARTABLEENTRY_VAR;
entry->data.var.priority = 1; entry->data.var.priority = 1;
entry->data.var.color = -1;
ASTStmtAssign *assign = malloc(sizeof(*assign)); ASTStmtAssign *assign = malloc(sizeof(*assign));
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,78 +737,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);
void nct_parse_statement(Parser *P) { void nct_parse_statement(Parser *P) {
if(maybe(P, TOKEN_IF)) { if(maybe(P, TOKEN_IF)) {
expect(P, TOKEN_PAREN_L); expect(P, TOKEN_PAREN_L);
@ -815,22 +770,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 = (AST*) 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 +799,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 +809,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 +881,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,22 +903,24 @@ 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;
ret->chunk.vars = NULL; ret->chunk.vars = NULL;
ret->chunk.stackReservation = 0;
AST *oldChunk = P->currentChunk; AST *oldChunk = (AST*) P->currentChunk;
P->currentChunk = (AST*) ret; P->currentChunk = &ret->chunk;
P->scope = vartable_new(P->scope); P->scope = vartable_new(P->scope);
ASTChunk *oldTopLevel = P->topLevel;
if(isTopLevel) { if(isTopLevel) {
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 */
@ -983,7 +946,7 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) {
/* Move back to beginning of declaration. */ /* Move back to beginning of declaration. */
do { do {
P->i--; P->i--;
} while(P->i >= 0 && P->tokens[P->i].type != TOKEN_SEMICOLON && P->tokens[P->i].type != TOKEN_SQUIGGLY_R && P->tokens[P->i].type != TOKEN_PAREN_R); } while(P->i >= 0 && P->tokens[P->i].type != TOKEN_SEMICOLON && P->tokens[P->i].type != TOKEN_SQUIGGLY_R);
P->i++; P->i++;
AST *d = parse_declaration(P); AST *d = parse_declaration(P);
@ -997,11 +960,11 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) {
/* Now actual parsing. */ /* Now actual parsing. */
while(peek(P, 0).type != (isTopLevel ? TOKEN_EOF : TOKEN_SQUIGGLY_R)) { while(peek(P, 0).type != TOKEN_EOF && peek(P, 0).type != TOKEN_SQUIGGLY_R) {
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++) {
@ -1017,7 +980,7 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) {
for(size_t i = 0; i < P->scope->count; i++) { for(size_t i = 0; i < P->scope->count; i++) {
if(P->scope->data[i]->kind == VARTABLEENTRY_VAR) { if(P->scope->data[i]->kind == VARTABLEENTRY_VAR) {
P->topLevel->vars[P->topLevel->varCount++] = P->scope->data[i]; P->topLevel->vars[P->topLevel->varCount++] = P->scope->data[i];
P->scope->data[i]->owner = P->topLevel; //P->scope->data[i]->owner = P->topLevel; // not sure why this line ever existed, it makes no sense
} }
} }
@ -1025,6 +988,10 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize) {
P->currentChunk = oldChunk; P->currentChunk = oldChunk;
if(isTopLevel) {
P->topLevel = oldTopLevel;
}
return &ret->chunk; return &ret->chunk;
} }

View File

@ -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

View File

@ -117,6 +117,22 @@ int type_equal(Type *O, Type *T) {
return type_equal(O->pointer.of, T->pointer.of); return type_equal(O->pointer.of, T->pointer.of);
} else if(O->type == TYPE_TYPE_ARRAY) { } else if(O->type == TYPE_TYPE_ARRAY) {
return type_equal(O->array.of, T->array.of) && O->array.length == T->array.length; return type_equal(O->array.of, T->array.of) && O->array.length == T->array.length;
} else if(O->type == TYPE_TYPE_FUNCTION) {
if(!type_equal(O->function.ret, T->function.ret)) {
return 0;
}
if(O->function.argCount != T->function.argCount) {
return 0;
}
for(int i = 0; i < O->function.argCount; i++) {
if(!type_equal(O->function.args[i], T->function.args[i])) {
return 0;
}
}
return 1;
} }
/* Consider nominal typing. */ /* Consider nominal typing. */
@ -132,6 +148,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;

View File

@ -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

View File

@ -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,9 +50,9 @@ 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; int16_t color, degree;
// Used during parsing // Used during parsing

View File

@ -1,5 +1,10 @@
#pragma once #pragma once
#define COLOR_EAX 0
#define COLOR_EBX 1
#define COLOR_ECX 2
#define COLOR_EDX 3
// Can expression be expressed as a single x86 operand? // Can expression be expressed as a single x86 operand?
#define XOP_NOT_XOP 0 #define XOP_NOT_XOP 0
#define XOP_NOT_MEM 1 #define XOP_NOT_MEM 1
@ -9,7 +14,7 @@ static inline int is_xop(AST *e) {
return XOP_NOT_MEM; return XOP_NOT_MEM;
} else if(e->nodeKind == AST_EXPR_VAR) { } else if(e->nodeKind == AST_EXPR_VAR) {
return e->exprVar.thing->kind == VARTABLEENTRY_VAR ? XOP_NOT_MEM : XOP_MEM; return e->exprVar.thing->kind == VARTABLEENTRY_VAR ? XOP_NOT_MEM : XOP_MEM;
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) { } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && is_xop(e->exprUnOp.operand->exprBinOp.operands[0]) == XOP_NOT_MEM && is_xop(e->exprUnOp.operand->exprBinOp.operands[1]) == XOP_NOT_MEM) {
return XOP_MEM; return XOP_MEM;
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) { } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) {
return XOP_NOT_MEM; return XOP_NOT_MEM;
@ -29,6 +34,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;

View File

@ -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;

7
tests/funcdefs.nct Normal file
View File

@ -0,0 +1,7 @@
/* Currently return isn't implemented (and no ret instructions are output either) */
/* Oh and arguments aren't supported yet either */
/* But functions work :) */
u0() foo: u0() {
u8 a = 5;
};

View File

@ -1,9 +1,12 @@
extern u32() getchar; extern s32() getchar;
extern void(u32) putchar; extern u0(s32) putchar;
loop { loop {
u8 a = getchar(); s32 z = 5;
if(a - 48) { s32 a = getchar();
putchar(a); if(a == -1) {
break;
} }
putchar(a);
putchar(z);
} }

6
tests/spill.nct Normal file
View 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
View File

@ -0,0 +1 @@
@stack = @stack - 123;