Compare commits

..

10 Commits

Author SHA1 Message Date
mid
07f6d57aba Whoopsies, I dropped this 2024-06-13 09:56:30 +03:00
mid
012320569e Support negation, parentheses. Always zero-extend registers for bugless memory operand usage 2024-06-12 11:17:09 +03:00
mid
fa40a78546 Don't specify immediate operand size 2024-06-11 17:18:45 +03:00
Mid
8e4cb71924 BF doesn't need loop guards anymore 2024-02-15 22:33:27 +02:00
Mid
77a459ffd3 Loop guards 2024-02-15 22:33:06 +02:00
Mid
5ec2349336 Update README.md 2024-02-13 22:43:28 +02:00
Mid
55bfa2289e Iterative dumbification 2024-02-13 22:43:04 +02:00
Mid
a1077f7c03 Oh who gives a fuck? 2024-02-13 21:33:49 +02:00
Mid
83e0771f2c Use wcl386 2024-02-13 21:30:42 +02:00
Mid
4d182e1685 Closer to OW-compliance + removed some leaks 2023-08-31 21:24:46 +03:00
21 changed files with 718 additions and 90 deletions

View File

@ -9,7 +9,7 @@ PREFIX = /usr/local
ntc: $(SOURCES) $(HEADERS) ntc: $(SOURCES) $(HEADERS)
ifdef OW ifdef OW
wcl $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUG),-DDEBUG=1,) -fe="ntc.exe" -0 -bcl=dos -mt $(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 $(DEBUG),-DDEBUG=1,) -Wall -o ntc -fno-PIE -no-pie -std=gnu11 $(if $(DEBUG),-O0 -g,-Os -s) -fms-extensions -Isrc $(SOURCES)
endif endif

View File

@ -1,11 +1,19 @@
# N19 Reference Compiler # N19 Reference Compiler
Made to compile fast and produce not great, but acceptable output. Currently only 386 output supported (protected and partially real mode). Made to compile fast and produce acceptable output. Currently only 386 output supported (protected and partially real mode).
Composed of the following passes:
1. Lexing
2. Parsing & loop\_second\_pass
3. Dumbification
4. Codegen
UD-chains are generated during parsing. Codegen uses on primitive patterns within the AST, possible thanks to dumbification. This technique is applicable owing to Nectar's already low-level nature
# Installation # Installation
make make
sudo make install sudo make install
# Command-line usage # Command-line usage

View File

@ -4,14 +4,6 @@
#include<string.h> #include<string.h>
#include<stdlib.h> #include<stdlib.h>
int BINOP_COMMUTATIVE[] = {
[BINOP_ADD] = 1,
[BINOP_SUB] = 0,
[BINOP_MUL] = 1,
[BINOP_DIV] = 0
};
AST *ast_expression_optimize(AST *ast) { AST *ast_expression_optimize(AST *ast) {
return ast; return ast;
} }
@ -37,6 +29,13 @@ 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(s) {
if(s->nodeKind == AST_STMT_LOOP) {
int i = ast_stmt_is_after(s->stmtLoop.body, s1, s2);
if(i != -1) {
return i;
}
}
if(s == s1) { if(s == s1) {
return 0; return 0;
} }
@ -49,11 +48,6 @@ int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) {
if(i != -1) { if(i != -1) {
return i; return i;
} }
} else if(s->nodeKind == AST_STMT_LOOP) {
int i = ast_stmt_is_after(s->stmtLoop.body, s1, s2);
if(i != -1) {
return i;
}
} }
s = s->statement.next; s = s->statement.next;

View File

@ -5,7 +5,20 @@
#include"lexer.h" #include"lexer.h"
#include"vartable.h" #include"vartable.h"
typedef enum { // VISITORS APPEAR IN varify_change_usedefs,ast_expr_is_equal,ast_stmt_is_after,
// dumben_chunk, cg_go, loop_second_pass AND OTHERS
//
// THEY ***MUST*** BE CHANGED ACCORDINGLY IF AST IS ALTERED
#pragma pack(push, 1)
#ifdef __GNUC__
#define ENUMPAK __attribute__((packed))
#else
#define ENUMPAK
#endif
typedef enum ENUMPAK {
AST_CHUNK, AST_CHUNK,
AST_STMT_DECL, AST_STMT_DECL,
AST_TYPE_IDENTIFIER, AST_TYPE_IDENTIFIER,
@ -29,7 +42,7 @@ typedef enum {
AST_STMT_EXT_SECTION, AST_STMT_EXT_SECTION,
} ASTKind; } ASTKind;
typedef enum { typedef enum ENUMPAK {
BINOP_ADD = 0, BINOP_ADD = 0,
BINOP_SUB = 1, BINOP_SUB = 1,
BINOP_BITWISE_AND = 2, BINOP_BITWISE_AND = 2,
@ -44,7 +57,6 @@ typedef enum {
BINOP_WTF = 999, BINOP_WTF = 999,
} BinaryOp; } BinaryOp;
extern int BINOP_COMMUTATIVE[];
static inline int binop_is_comparison(BinaryOp op) { static inline int binop_is_comparison(BinaryOp op) {
return op == BINOP_EQUAL || op == BINOP_NEQUAL; return op == BINOP_EQUAL || op == BINOP_NEQUAL;
@ -59,7 +71,7 @@ static inline BinaryOp binop_comp_opposite(BinaryOp op) {
return BINOP_WTF; return BINOP_WTF;
} }
typedef enum { typedef enum ENUMPAK {
UNOP_DEREF = 0, UNOP_DEREF = 0,
UNOP_NEGATE = 1, UNOP_NEGATE = 1,
UNOP_BITWISE_NOT = 2, UNOP_BITWISE_NOT = 2,
@ -251,6 +263,8 @@ typedef union AST {
ASTStmtExtSection stmtExtSection; ASTStmtExtSection stmtExtSection;
} AST; } AST;
#pragma pack(pop)
AST *ast_expression_optimize(AST*); AST *ast_expression_optimize(AST*);
int ast_expression_equal(AST*, AST*); int ast_expression_equal(AST*, AST*);

129
src/cg.c
View File

@ -5,14 +5,16 @@
#include<string.h> #include<string.h>
#include<assert.h> #include<assert.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"}, {"sil", "si", "esi"}, {"dil", "di", "edi"}}; static const char *regs[REGS][3] = {{"al", "ax", "eax"}, {"bl", "bx", "ebx"}, {"cl", "cx", "ecx"}, {"dl", "dx", "edx"}};
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;
#define LOOPSTACKSIZE 64 #define LOOPSTACKSIZE 96
static size_t loopStackStart[LOOPSTACKSIZE]; static size_t loopStackStart[LOOPSTACKSIZE];
static size_t loopStackEnd[LOOPSTACKSIZE]; static size_t loopStackEnd[LOOPSTACKSIZE];
static size_t loopStackIdx; static size_t loopStackIdx;
@ -37,11 +39,21 @@ static const char *spec(int size) {
abort(); abort();
} }
static int log_size(int size) {
switch(size) {
case 1: return 0;
case 2: return 1;
case 4: return 2;
case 8: return 3;
}
abort();
}
static const char *specexpr(AST *e) { static const char *specexpr(AST *e) {
return spec(type_size(e->expression.type)); return spec(type_size(e->expression.type));
} }
static const char *xv(VarTableEntry *v) { static const char *xv_sz(VarTableEntry *v, int sz) {
assert(v->kind == VARTABLEENTRY_VAR); assert(v->kind == VARTABLEENTRY_VAR);
#define XVBUFS 8 #define XVBUFS 8
@ -54,7 +66,7 @@ static const char *xv(VarTableEntry *v) {
#ifdef DEBUG #ifdef DEBUG
snprintf(ret, XVBUFSZ, "@%i", v->data.var.color); snprintf(ret, XVBUFSZ, "@%i", v->data.var.color);
#else #else
snprintf(ret, XVBUFSZ, "%s", regs[v->data.var.color][2]); snprintf(ret, XVBUFSZ, "%s", regs[v->data.var.color][log_size(sz)]);
#endif #endif
bufidx = (bufidx + 1) % XVBUFS; bufidx = (bufidx + 1) % XVBUFS;
@ -62,6 +74,10 @@ static const char *xv(VarTableEntry *v) {
return ret; return ret;
} }
static const char *xv(VarTableEntry *v) {
return xv_sz(v, type_size(v->type));
}
static const char *xj(BinaryOp op) { static const char *xj(BinaryOp op) {
switch(op) { switch(op) {
case BINOP_EQUAL: return "e"; case BINOP_EQUAL: return "e";
@ -70,7 +86,7 @@ static const char *xj(BinaryOp op) {
} }
} }
static const char *xop(AST *e) { static const char *xop_sz(AST *e, int sz) {
#define XOPBUFS 16 #define XOPBUFS 16
#define XOPBUFSZ 24 #define XOPBUFSZ 24
static char bufs[XOPBUFS][XOPBUFSZ]; static char bufs[XOPBUFS][XOPBUFSZ];
@ -82,25 +98,32 @@ static const char *xop(AST *e) {
VarTableEntry *v = e->exprVar.thing; VarTableEntry *v = e->exprVar.thing;
if(v->kind == VARTABLEENTRY_VAR) { if(v->kind == VARTABLEENTRY_VAR) {
return xv(v); 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]", 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);
} 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) {
snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
xv_sz(e->exprUnOp.operand->exprBinOp.operands[0]->exprVar.thing, 4),
xv_sz(e->exprUnOp.operand->exprBinOp.operands[1]->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 == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && 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 && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "[%s + %s]", snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name, e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
xv(e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing)); xv_sz(e->exprUnOp.operand->exprBinOp.operands[1]->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 == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && e->exprUnOp.operand->exprBinOp.operands[1]->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 && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "[%s + %i * %s]", snprintf(ret, XOPBUFSZ, "%s [%s + %i * %s]",
spec(sz),
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name, e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val, e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val,
xv(e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing)); xv_sz(e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing, 4));
} 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) {
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]", xv(e->exprUnOp.operand->exprVar.thing)); snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(e->exprUnOp.operand->exprVar.thing, 4));
} else { } else {
return NULL; return NULL;
} }
@ -110,6 +133,10 @@ static const char *xop(AST *e) {
return ret; return ret;
} }
static const char *xop(AST *e) {
return xop_sz(e, type_size(e->expression.type));
}
void cg_chunk(AST *a) { void cg_chunk(AST *a) {
AST *s = a->chunk.statementFirst; AST *s = a->chunk.statementFirst;
@ -124,6 +151,16 @@ void cg_chunk(AST *a) {
printf("org %lu\n", s->stmtExtOrg.val); printf("org %lu\n", s->stmtExtOrg.val);
} else if(s->nodeKind == AST_STMT_EXT_ALIGN) {
uint32_t val = s->stmtExtAlign.val;
if((val & (val - 1))) {
// nasm does not support non-PoT alignments, so pad manually
printf("times ($ - $$ + %u) / %u * %u - ($ - $$) db 0\n", val - 1, val, val);
} else {
printf("align %u\n", val);
}
} else if(s->nodeKind == AST_STMT_DECL && s->stmtDecl.thing->kind == VARTABLEENTRY_SYMBOL) { } else if(s->nodeKind == AST_STMT_DECL && s->stmtDecl.thing->kind == VARTABLEENTRY_SYMBOL) {
VarTableEntry *v = s->stmtDecl.thing; VarTableEntry *v = s->stmtDecl.thing;
@ -135,38 +172,72 @@ void cg_chunk(AST *a) {
} }
if(s->stmtDecl.expression) { if(s->stmtDecl.expression) {
puts("A"); printf("%s:", v->data.symbol.name);
if(v->type->type == TYPE_TYPE_PRIMITIVE) {
assert(s->stmtDecl.expression->nodeKind == AST_EXPR_PRIMITIVE);
printf("%s %i", direct(type_size(v->type)), s->stmtDecl.expression->exprPrim.val);
} else if(v->type->type == TYPE_TYPE_ARRAY && v->type->array.of->type == TYPE_TYPE_PRIMITIVE) {
printf("%s ", direct(type_size(v->type->array.of)));
for(size_t i = 0; i < v->type->array.length; i++) {
printf("%i,", s->stmtDecl.expression->exprArray.items[i]->exprPrim.val);
}
} else printf("A");
putchar('\n');
} else { } else {
printf("%s resb %lu\n", v->data.symbol.name, type_size(s->stmtDecl.thing->type)); printf("%s resb %lu\n", v->data.symbol.name, type_size(s->stmtDecl.thing->type));
} }
} }
} else if(s->nodeKind == AST_STMT_ASSIGN) { } else if(s->nodeKind == AST_STMT_ASSIGN) {
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) {
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
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.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",
xv(s->stmtAssign.what->exprVar.thing), xv(s->stmtAssign.what->exprVar.thing),
xv(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing), xv(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing),
xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing)); xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing));
} 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_UNARY_OP && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && 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_UNARY_OP && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
printf("lea %s, [%s + %s]\n", printf("lea %s, [%s + %s]\n",
xv(s->stmtAssign.what->exprVar.thing), xv(s->stmtAssign.what->exprVar.thing),
s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name, s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing)); xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing));
} else { } else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
printf("mov %s, %s\n", xop(s->stmtAssign.what), xop(s->stmtAssign.to)); printf("lea %s, [%s + %i]\n",
xv_sz(s->stmtAssign.what->exprVar.thing, 4),
xv_sz(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, 4),
s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val);
} 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));
} else {
if(is_xop(s->stmtAssign.to) == XOP_MEM && type_size(s->stmtAssign.what->expression.type) != type_size(s->stmtAssign.to->expression.type)) {
printf("movzx %s, %s\n", xop_sz(s->stmtAssign.what, 4), xop_sz(s->stmtAssign.to, type_size(s->stmtAssign.what->expression.type)));
} else {
printf("mov %s, %s\n", xop(s->stmtAssign.what), xop_sz(s->stmtAssign.to, type_size(s->stmtAssign.what->expression.type)));
}
}
} }
} else if(s->nodeKind == AST_STMT_LOOP) { } else if(s->nodeKind == AST_STMT_LOOP) {
@ -202,7 +273,7 @@ void cg_chunk(AST *a) {
size_t lbl = nextLocalLabel++; size_t lbl = nextLocalLabel++;
printf("cmp %s %s, %s\n", specexpr(s->stmtIf.expression->exprBinOp.operands[0]), 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(s->stmtIf.then);

308
src/dumberdowner.c Normal file
View File

@ -0,0 +1,308 @@
#include"dumberdowner.h"
#include<stdlib.h>
#include<assert.h>
// This is the dumbing down pass.
//
// Complex expressions are to be "broken down" into simpler ones until the AST
// can be trivially translated to the target architecture.
//
// This pass, along with CG is strictly dependent on x86 and will fail for
// any other architecture.
#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 */
static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
VarTableEntry *vte = malloc(sizeof(*vte));
vte->kind = VARTABLEENTRY_VAR;
vte->type = e->expression.type;
vte->data.var.color = 0;
vte->data.var.degree = 0;
vte->data.var.priority = 0;
vte->data.var.reachingDefs = NULL;
// Add to var array
tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount));
tlc->chunk.vars[tlc->chunk.varCount - 1] = vte;
// Alter AST
ASTExprVar *ev[2];
for(int i = 0; i < 2; i++) {
ev[i] = malloc(sizeof(ASTExprVar));
ev[i]->nodeKind = AST_EXPR_VAR;
ev[i]->type = e->expression.type;
ev[i]->thing = vte;
}
ASTStmtAssign *assign = malloc(sizeof(*assign));
assign->nodeKind = AST_STMT_ASSIGN;
assign->what = (AST*) ev[0];
assign->to = e;
if(stmtPrev) {
stmtPrev->statement.next = (AST*) assign;
} else {
chunk->chunk.statementFirst = (AST*) assign;
}
assign->next = stmt;
// Assemble ud-chain
UseDef *ud[2];
ud[0] = malloc(sizeof(UseDef));
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];
}
static AST *xopify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *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) {
AST *sPrev = NULL;
AST *s = chu->chunk.statementFirst;
int effective = 0;
while(s) {
if(s->nodeKind == AST_STMT_IF) {
AST *e = s->stmtIf.expression;
if(e->nodeKind == AST_EXPR_BINARY_OP && binop_is_comparison(e->exprBinOp.operator)) {
if(is_xop(e->exprBinOp.operands[0]) == XOP_NOT_XOP) {
e->exprBinOp.operands[0] = xopify(tlc, chu, sPrev, s, e->exprBinOp.operands[0]);
effective = 1;
}
if(is_xop(e->exprBinOp.operands[1]) == XOP_NOT_XOP) {
e->exprBinOp.operands[1] = xopify(tlc, chu, sPrev, s, e->exprBinOp.operands[1]);
effective = 1;
}
if(is_xop(e->exprBinOp.operands[0]) == XOP_MEM && is_xop(e->exprBinOp.operands[1]) == XOP_MEM) {
// Can't have two mems; put one in var
e->exprBinOp.operands[1] = varify(tlc, chu, sPrev, s, e->exprBinOp.operands[1]);
effective = 1;
}
} else {
s->stmtIf.expression = varify(tlc, chu, sPrev, s, e);
effective = 1;
}
effective |= dumben_chunk(tlc, s->stmtIf.then);
} else if(s->nodeKind == AST_STMT_LOOP) {
effective |= dumben_chunk(tlc, s->stmtLoop.body);
} else if(s->nodeKind == AST_STMT_ASSIGN) {
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 = varify(tlc, chu, sPrev, s, s->stmtAssign.to);
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)) {
// Turn this:
// a = -b
// into
// a = b
// 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.
// TODO: Scrap incremental UD-chain modifications. Simply rebuild them after each pass.
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;
s->stmtAssign.to = negation->exprUnOp.operand;
negation->exprUnOp.operand = ev[0];
AST *assign2 = malloc(sizeof(ASTStmtAssign));
assign2->nodeKind = AST_STMT_ASSIGN;
assign2->stmtAssign.what = ev[1];
assign2->stmtAssign.to = negation;
assign2->statement.next = s->statement.next;
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;
}
} 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;
}
}
}
sPrev = s;
s = s->statement.next;
}
return effective;
}
void dumben_go(AST* tlc) {
while(dumben_chunk(tlc, tlc));
}

5
src/dumberdowner.h Normal file
View File

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

View File

@ -318,3 +318,10 @@ Token *nct_lex(FILE *f) {
return NULL; /* Doesn't reach here. */ return NULL; /* Doesn't reach here. */
} }
void nct_lex_free(Token *tokens) {
for(Token *t = tokens; t->type != TOKEN_EOF; t++) {
if(t->content) free(t->content);
}
free(tokens);
}

View File

@ -50,5 +50,6 @@ typedef struct {
Token nct_tokenize(FILE*); Token nct_tokenize(FILE*);
Token *nct_lex(FILE*); Token *nct_lex(FILE*);
void nct_lex_free(Token *);
#endif #endif

View File

@ -7,6 +7,7 @@
#include"ntc.h" #include"ntc.h"
#include"reporting.h" #include"reporting.h"
#include"cg.h" #include"cg.h"
#include"dumberdowner.h"
static int argc; static int argc;
static char **argv; static char **argv;
@ -36,6 +37,8 @@ int main(int argc_, char **argv_) {
free(tokens); free(tokens);
dumben_go(chunk);
cg_go(chunk); cg_go(chunk);
return 0; return 0;

View File

@ -9,6 +9,21 @@
#include<stdint.h> #include<stdint.h>
#include<signal.h> #include<signal.h>
#ifndef __GNUC__
static inline int __builtin_clzl(unsigned long x) {
unsigned long n = 32;
unsigned long y;
y = x >>16; if (y != 0) { n = n -16; x = y; }
y = x >> 8; if (y != 0) { n = n - 8; x = y; }
y = x >> 4; if (y != 0) { n = n - 4; x = y; }
y = x >> 2; if (y != 0) { n = n - 2; x = y; }
y = x >> 1; if (y != 0) return n - 2;
return n - x;
}
#endif
typedef struct { typedef struct {
UseDef ud; UseDef ud;
VarTableEntry *to; VarTableEntry *to;
@ -16,7 +31,7 @@ typedef struct {
typedef struct { typedef struct {
Token *tokens; Token *tokens;
ssize_t i; intmax_t i;
VarTable *scope; VarTable *scope;
@ -29,6 +44,11 @@ typedef struct {
// Used by pushstmt to assemble use-def chain // Used by pushstmt to assemble use-def chain
size_t udsToAddCount; size_t udsToAddCount;
UseDefToAdd *udsToAdd; UseDefToAdd *udsToAdd;
// Used to place guard variable uses after loops to stop reg allocation from fucking up
VarTable *loopScope;
size_t guardedVarCount;
ASTExprVar **guardedVars;
} Parser; } Parser;
static Token get(Parser *P) { static Token get(Parser *P) {
@ -75,6 +95,11 @@ static void pushstat(Parser *P, void *a) {
memcpy(ud, &P->udsToAdd[i].ud, sizeof(*ud)); memcpy(ud, &P->udsToAdd[i].ud, sizeof(*ud));
ud->stmt = a; 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) { if(to->data.var.usedefFirst) {
to->data.var.usedefLast->next = ud; to->data.var.usedefLast->next = ud;
to->data.var.usedefLast = ud; to->data.var.usedefLast = ud;
@ -96,7 +121,6 @@ static void pushstat(Parser *P, void *a) {
static ASTExprPrimitive *parse_prim(Parser *P) { static ASTExprPrimitive *parse_prim(Parser *P) {
ASTExprPrimitive *ret = malloc(sizeof(*ret)); ASTExprPrimitive *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_PRIMITIVE; ret->nodeKind = AST_EXPR_PRIMITIVE;
ret->type = (Type*) primitive_parse("s16");
Token tok = get(P); Token tok = get(P);
@ -109,21 +133,32 @@ static ASTExprPrimitive *parse_prim(Parser *P) {
ret->val = strtol(str, NULL, base); ret->val = strtol(str, NULL, base);
// Smallest integer type to store number
char buf[8];
snprintf(buf, sizeof(buf), "s%i", ret->val ? (64 - __builtin_clzl(ret->val - 1)) : 1);
ret->type = (Type*) primitive_parse(buf);
return ret; return ret;
} }
static void newusedef(Parser *P, VarTableEntry *v, AST *expr) { static void newusedef(Parser *P, VarTableEntry *v, AST *expr) {
for(size_t i = 0; i < v->data.var.reachingDefs->defCount; i++) { ReachingDefs *defs = v->data.var.reachingDefs;
P->udsToAdd = realloc(P->udsToAdd, sizeof(*P->udsToAdd) * (++P->udsToAddCount));
P->udsToAdd[P->udsToAddCount - 1] = (UseDefToAdd) { while(defs) {
.ud = { for(size_t i = 0; i < defs->defCount; i++) {
.def = v->data.var.reachingDefs->defs[i], P->udsToAdd = realloc(P->udsToAdd, sizeof(*P->udsToAdd) * (++P->udsToAddCount));
.use = expr, P->udsToAdd[P->udsToAddCount - 1].ud.def = defs->defs[i];
.stmt = NULL, // set by pushstmt P->udsToAdd[P->udsToAddCount - 1].ud.use = expr;
.next = NULL, P->udsToAdd[P->udsToAddCount - 1].ud.stmt = NULL; // set by pushstmt
}, P->udsToAdd[P->udsToAddCount - 1].ud.next = NULL;
.to = v P->udsToAdd[P->udsToAddCount - 1].to = v;
}; }
if(defs->excludeParent) {
break;
}
defs = defs->parent;
} }
} }
@ -137,6 +172,36 @@ static AST *exprvar(Parser *P, VarTableEntry *v) {
newusedef(P, v, a); newusedef(P, v, a);
} }
if(P->loopScope) {
// XXX: O(n)!!!!!!!!!
int inloop = 0;
for(VarTable *vt = v->owner; vt; vt = vt->parent) {
if(vt->parent == P->loopScope) {
inloop = 1;
break;
}
}
if(!inloop) {
int alreadyAdded = 0;
for(size_t i = 0; i < P->guardedVarCount; i++) {
if(P->guardedVars[i]->thing == v) {
alreadyAdded = 1;
break;
}
}
if(!alreadyAdded) {
ASTExprVar *ev = malloc(sizeof(*ev));
memcpy(ev, a, sizeof(*ev));
P->guardedVars = realloc(P->guardedVars, sizeof(*P->guardedVars) * (P->guardedVarCount + 1));
P->guardedVars[P->guardedVarCount++] = ev;
}
}
}
return a; return a;
} }
@ -163,10 +228,14 @@ AST *nct_cast_expr(AST *what, Type *to) {
} }
return (AST*) ret; return (AST*) ret;
} else if(to->type == TYPE_TYPE_PRIMITIVE && to->primitive.width == 32) { } else if(to->type == TYPE_TYPE_PRIMITIVE) {
if(to->primitive.width != what->exprStrLit.length * 8) {
stahp(0, 0, "Size mismatch between string literal and target type");
}
ASTExprPrimitive *ret = malloc(sizeof(*ret)); ASTExprPrimitive *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_PRIMITIVE; ret->nodeKind = AST_EXPR_PRIMITIVE;
ret->type = primitive_parse("u32"); ret->type = to;
memcpy(&ret->val, what->exprStrLit.data, sizeof(ret->val)); memcpy(&ret->val, what->exprStrLit.data, sizeof(ret->val));
return (AST*) ret; return (AST*) ret;
} else abort(); } else abort();
@ -185,6 +254,9 @@ 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;
@ -213,6 +285,10 @@ AST *nct_parse_expression(Parser *P, int lOP) {
ret->length = tok.length; ret->length = tok.length;
return (AST*) ret; return (AST*) ret;
} else if(maybe(P, TOKEN_PAREN_L)) {
AST *e = nct_parse_expression(P, 0);
expect(P, TOKEN_PAREN_R);
return e;
} }
} else if(lOP == 4) { } else if(lOP == 4) {
if(maybe(P, TOKEN_STAR)) { if(maybe(P, TOKEN_STAR)) {
@ -577,6 +653,7 @@ static AST *parse_declaration(Parser *P) {
ASTStmtAssign *assign = malloc(sizeof(*assign)); ASTStmtAssign *assign = malloc(sizeof(*assign));
assign->nodeKind = AST_STMT_ASSIGN; assign->nodeKind = AST_STMT_ASSIGN;
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);
@ -633,6 +710,77 @@ 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)) {
@ -657,10 +805,31 @@ void nct_parse_statement(Parser *P) {
ret->nodeKind = AST_STMT_LOOP; ret->nodeKind = AST_STMT_LOOP;
ret->next = NULL; ret->next = NULL;
int isFirstLoop = P->loopScope == NULL;
if(isFirstLoop) {
P->loopScope = P->scope;
}
expect(P, TOKEN_SQUIGGLY_L); expect(P, TOKEN_SQUIGGLY_L);
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);
if(isFirstLoop) {
P->loopScope = NULL;
for(size_t i = 0; i < P->guardedVarCount; i++) {
ASTExprVar *ev = P->guardedVars[i];
newusedef(P, ev->thing, ev);
}
P->guardedVarCount = 0;
free(P->guardedVars);
P->guardedVars = NULL;
}
loop_second_pass(ret->body);
pushstat(P, ret); pushstat(P, ret);
return; return;
} else if(maybe(P, TOKEN_BREAK)) { } else if(maybe(P, TOKEN_BREAK)) {
@ -749,7 +918,7 @@ void nct_parse_statement(Parser *P) {
ret->nodeKind = AST_STMT_ASSIGN; ret->nodeKind = AST_STMT_ASSIGN;
ret->next = NULL; ret->next = NULL;
ret->what = e; ret->what = e;
ret->to = nct_parse_expression(P, 0);//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);
@ -773,9 +942,6 @@ 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) {
if(P->scope) {
}
AST *ret = malloc(sizeof(ASTChunk)); AST *ret = malloc(sizeof(ASTChunk));
ret->nodeKind = AST_CHUNK; ret->nodeKind = AST_CHUNK;
ret->chunk.statementFirst = ret->chunk.statementLast = NULL; ret->chunk.statementFirst = ret->chunk.statementLast = NULL;
@ -851,6 +1017,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;
} }
} }

View File

@ -22,7 +22,7 @@ Type *primitive_parse(const char *src) {
TypePrimitive *ret = malloc(sizeof(*ret)); TypePrimitive *ret = malloc(sizeof(*ret));
ret->type = TYPE_TYPE_PRIMITIVE; ret->type = TYPE_TYPE_PRIMITIVE;
ret->src = src; ret->src = strdup(src);
if(*src == 'n') { if(*src == 'n') {
src++; src++;

View File

@ -62,6 +62,7 @@ typedef union Type {
extern Type TYPE_ERROR; extern Type TYPE_ERROR;
Type *primitive_parse(const char*); Type *primitive_parse(const char*);
Type *primitive_make(uint16_t width);
size_t type_size(Type*); size_t type_size(Type*);
int type_equal(Type*, Type*); int type_equal(Type*, Type*);

View File

@ -26,6 +26,7 @@ void reachingdefs_set(struct ReachingDefs *this, union AST *def) {
this->defCount = 1; this->defCount = 1;
this->defs = realloc(this->defs, sizeof(*this->defs) * this->defCount); this->defs = realloc(this->defs, sizeof(*this->defs) * this->defCount);
this->defs[0] = def; this->defs[0] = def;
this->excludeParent = 1;
} }
VarTable *vartable_new(VarTable *parent) { VarTable *vartable_new(VarTable *parent) {
@ -64,6 +65,7 @@ VarTableEntry *vartable_set(VarTable *this, const char *name, VarTableEntry *e)
this->data[this->count] = e; this->data[this->count] = e;
this->count++; this->count++;
if(e->kind == VARTABLEENTRY_VAR) e->data.var.name = name; if(e->kind == VARTABLEENTRY_VAR) e->data.var.name = name;
e->owner = this;
return e; return e;
} }

View File

@ -20,15 +20,19 @@ typedef struct ReachingDefs {
size_t defCount; size_t defCount;
union AST **defs; union AST **defs;
int excludeParent;
struct ReachingDefs *parent; struct ReachingDefs *parent;
} ReachingDefs; } ReachingDefs;
struct ReachingDefs *reachingdefs_push(struct ReachingDefs*); struct ReachingDefs *reachingdefs_push(struct ReachingDefs*);
struct ReachingDefs *reachingdefs_coalesce(struct ReachingDefs*); struct ReachingDefs *reachingdefs_coalesce(struct ReachingDefs*);
void reachingdefs_set(struct ReachingDefs*, union AST*); void reachingdefs_set(struct ReachingDefs*, union AST*);
struct VarTable;
typedef struct VarTableEntry { typedef struct VarTableEntry {
Type *type; Type *type;
struct VarTable *owner;
VarTableEntryKind kind; VarTableEntryKind kind;
struct { struct {
union { union {

35
src/x86.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
// Can expression be expressed as a single x86 operand?
#define XOP_NOT_XOP 0
#define XOP_NOT_MEM 1
#define XOP_MEM 2
static inline int is_xop(AST *e) {
if(e->nodeKind == AST_EXPR_PRIMITIVE) {
return XOP_NOT_MEM;
} else if(e->nodeKind == AST_EXPR_VAR) {
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) {
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) {
return XOP_NOT_MEM;
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF) {
AST *c = e->exprUnOp.operand;
if(c->nodeKind == AST_EXPR_VAR && c->exprVar.thing->kind == VARTABLEENTRY_VAR) {
return XOP_MEM;
} else if(c->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operator == BINOP_ADD && c->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && c->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && c->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR) {
if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) {
return XOP_MEM;
} else if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && c->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && c->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) {
int scale = c->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val;
if(scale == 1 || scale == 2 || scale == 4 || scale == 8) {
return XOP_MEM;
}
}
}
}
return XOP_NOT_XOP;
}

View File

@ -30,12 +30,10 @@ loop {
data[dataPtr] = data[dataPtr] - 1; data[dataPtr] = data[dataPtr] - 1;
} }
if(code[codePtr] == 46) { if(code[codePtr] == 46) {
u32 z = &data + dataPtr; write(1, &data + dataPtr, 1);
write(1, z, 1);
} }
if(code[codePtr] == 44) { if(code[codePtr] == 44) {
u32 z = &data + dataPtr; read(0, &data + dataPtr, 1);
read(0, z, 1);
} }
if(code[codePtr] == 91) { if(code[codePtr] == 91) {
if(data[dataPtr] == 0) { if(data[dataPtr] == 0) {
@ -75,7 +73,3 @@ loop {
codePtr = codePtr + 1; codePtr = codePtr + 1;
} }
codePtr;
dataPtr;
stckPtr;

View File

@ -1,2 +1,6 @@
u32 x: 123; u32 x: 123;
u33 y: 5; u33 y: 5;
u3 *o = 5000;
u3 z = *o;
u3 p = *(o + z);

View File

@ -2,7 +2,8 @@ u16 x: 5;
loop { loop {
u16* y = 257; u16* y = 257;
u9 w = -4; u9 w = -4;
u4 z = 3 + *y; u16 p = *y;
u4 z = p + 3;
u2 o = -w; u2 o = -w;
if(x != 0) { if(x != 0) {

9
tests/loopregalloc.nct Normal file
View File

@ -0,0 +1,9 @@
u8 a;
loop {
a = 3;
u8 b = 1;
u8 c = 2;
}

View File

@ -1,5 +1,5 @@
u8 a = 5; u8 a = 5;
if(a) { if(a != 0) {
u8 a = 10; /* Should not cause scoping errors. */ u8 a = 10; /* Should not cause scoping errors. */
} }
u8 b = 15; /* `a` in the if statement scope should be free'd. */ u8 b = 15; /* `a` in the if statement scope should be free'd. */