Compare commits

..

No commits in common. "2c6033e501993e8be98fed3355b556a2004b0a8f" and "9dc5bddfef10f12031b7daf5af2c8f9e19c560c7" have entirely different histories.

14 changed files with 306 additions and 1021 deletions

View File

@ -1,10 +1,10 @@
# Nectar Reference Compiler Source Documentation
When writing a program, I usually make the most primitive and smallest code I can that does the job. If it turns out I miscalculated the complexity, or I must add some feature that isn't compatible with the codebase, I'll obviously have to refactor it. Still, I've been programming this way for probably my entire life.
When writing a program, I usually make the most primitive and smallest code I can that does the job. If it turns out I miscalculated the complexity, or I must add some feature that isn't compatible with the codebase, I'll obviously have to refactor it. Still, I've been using this method of programming for probably my entire life.
That being said, if you know this compiler took since 2019 to get to its current state, you will correctly guess that I DO NOT KNOW WHAT I AM DOING. Compiler literature, and online discussion, is abstract to the point where it is not useful for real-world processors. Imagine how long it took me to realize real-world IRs are actually not at all generic, and are actually quite close to their target architectures. As a result, much of what you see in the source is the result of a lot of experimentation. There's definitely better ways to do the things I show here, but I figured it's better to have at least some resource on how a "real" compiler works.
That being said, if you know this compiler took since 2019 to get to its current state, you will correctly guess that I DO NOT KNOW WHAT I AM DOING. Compiler literature, and online discussion, is abstract to the point where it is not useful for real-world processors. As a result, much of what you see in the source is the result of a lot of experimentation. There's definitely better ways to do the things I show here, but I figured it's better to have at least some resource on how a "real" compiler works.
The core idea behind the compiler is to progressively iterate through the AST, turning it into a more primitive form step by step. Once this primitivization ends, the code generator is given the code in a form it will understand. Doing it this way is necessary because machine code itself is primitive, and instructions typically have 0-3 operands. Thanks to both this, and Nectar itself being highly low-level, the need for an IR disappears. On the other hand, making sure the AST is in a correct state between steps is the prime source of bugs.
Basically, the compiler works by progressively iterating through the AST, turning it into a more primitive form step by step. This is necessary because machine code itself is primitive, and instructions typically have 0-3 operands. Thanks to both this, and Nectar itself being highly low-level, the need for an IR disappears. On the other hand, making sure the AST is in a correct state between steps is the prime source of bugs.
Currently the compiler is designed with only i386+ processors in mind. I intend to add support for i286- and other exotic processors, but I honestly don't see it happening ever, especially if this remains a solo project. More RISC architectures with regular register files will be easier to add support for, but they're also the kind for which the advantages of this programming language aren't worth the squeeze.
@ -28,21 +28,9 @@ There's enough types of passes to push us to have a generic way to invoke the vi
If the current node is within a statement (most are), `stmt` is equal to that statement. `stmtPrev` is the previous statement. This is necessary for patching in the linked list of statements within a chunk during modification passes. If there is no previous statement, then the head pointer of the singly-linked list must be patched through the `chu` node. The `tlc` is the top-level chunk, which may be equal to `chu`.
## Pre-dumbification
Before dumbification we need to make sure the code at least matches the semantics of the x86 architecture.
For one thing, arguments are not magically loaded with the correct values. The `pre_dumb_visitor` pass inserts assignment statements at the beginning of the function to load the values from the stack, as per the C ABI.
Then we have structures which, of course, x86 doesn't support. There must be no by-value use of a structure anywhere. The `decompose_symbol_record_field_access` pass decomposes all references to a structure's field to pointer-like accesses. For example, `a.b` becomes `*((&a + x) as B*)`, where `B` is the type of `a.b`, and `x` is the memory offset of the field `a.b`. The same pattern is specially recognized by the code generator for outputting an x86 address mode like `[eax + x]`.
Afterward, local structs must be either spilled to global memory or to the stack. They may also be decomposed in some cases, but that is currently unsupported. This is done by `spill2stack_visitor`, with `ast_spill_to_stack` as the public interface. Since spilling causes the stack frame of the function to grow, all references to the stack must be updated accordingly. This is also done by `spill2stack_visitor`, and in such a way so as to not require more dumbification.
Now a deeper structure access like `a.b.c` (`*((&(*((&a + x) as B*)) + y) as C*)`) is not recognized by the code generator. We must rely on optimizations passes that are guaranteed to happen: `*&p` and `&*p` should become `p`, `p as A* as B*` should become `p as B*` and `(p + x) as A* + y` should become `(p + (x + y)) as A*`. These are done by `denoop_visitor`.
## Dumbification
The idea of turning the AST progressively primitive is called "dumbification" in the source. The most simple example would be the following:
Once the AST is parsed, we move to machine-specific passes (in this case, i386). The idea of turning the AST progressively primitive is called "dumbification" in the source. The most simple example would be the following:
a = -b
@ -57,6 +45,8 @@ Another rule is to extract function arguments and place them into local variable
Dumbification must be repeated until there are no more changes. The dumbification part of the source is responsible for making sure the resulting AST is "trivially compilable" to the machine code. For example, `a = a + b` is trivially compilable, because we have the `add reg, reg` instruction. What is trivially compilable depends on which registers are used in the end (a variable colored as `edi`, `esi` or `ebp` cannot be used for 8-bit stores/loads). These details are not taken into account by dumbification.
Before dumbification is a single-use pass called pre-dumbification, which takes a top-level chunk, and inserts loads for the function arguments. Such unconditional instructions are not efficient, but they work.
Putting all of this together, here is an example of nctref's dumbification of the following Fibonacci implementation, as of writing. Here is the main Nectar source code:
fibonacci: u32(u32 n) -> {
@ -146,7 +136,7 @@ That's one problem, but there's another:
Despite appearing later in the source, `x = x + 1` is a potential definition for `f(x)`! This means the UD-chain generator must go through loops twice -- once with the upper definitions, and once with definitions from within the loop. Additionally, the UD-chain is assumed to be ordered by appearence in the source, so insertion in the second pass must consider that.
Now, why did I choose UD chains? Why, simplicity, obviously...
Now, why did I choose UD chains? Why, simplicity, obviously.
## Coloring
@ -209,35 +199,6 @@ Using the same Fibonacci example as above, this is the result.
mov eax, ecx
ret
## Other problems with this approach
Short-circuit evaluation is when the evaluation of an expression is guaranteed to stop once the output is already known. For example, if in `A || B` `A` is already truthy, then `B` is not evaluated. This is not just an optimization, but an important semantical feature, as evaluation of the operands may have side-effects.
Let us write `if(x == 1 || y == 1) { do stuff; }` in x86:
cmp eax, 1
je .L1
cmp ebx, 1
jne .L2
.L1:
; do stuff
.L2:
Note that the two jump instructions are basically goto statements. As the Nectar IR is defined without gotos, it is practically impossible for the compiler to output the neat code shown above. You could insert special logic for this case, but in general it'll fail.
Even worse, the dumbification pass will try to move the condition into a variable and dumbify it further, creating the following:
u1 z = x == 1;
u1 w = y == 1;
u1 k = z || w;
if(k) {
do stuff;
}
And now suddenly we need 2 new registers for no reason..
In conclusion, an optimized IR should not use self-contained blocks, but should actually be flat, like the real thing, and have goto statements. Fixing this in nctref will require extreme refactoring, as the notion of blocks forming a tree is ingrained. Also, statements within a block form a singly-linked list. Even if we there were a simple `ASTStmtGoto` node, it cannot be a simple pointer to a statement, because passes need to modify the AST. For the time being, I have given up on short-circuit evaluation, and I do not actually support neither `||` nor `&&`.
## Adding a Feature
When adding a feature, first write it out in Nectar in the ideal dumbified form. Make sure this compiles correctly. Afterward, implement dumbification rules so that code can be written in any fashion. If specific colorings are required, then the pre-coloring and spill2var passes must be updated. The following is an example with multiplication, as this is what I'm adding as of writing.
@ -250,16 +211,9 @@ To account for this, we can have a second assignment statement right next to the
w = z *^ y;
z = z * y;
But this is without pre-coloring. We want precolored nodes to live as little as possible, because separately solving pre-coloring collisions whilst also keeping the code dumbified *and* not horrible is pretty much impossible. I've tried.
Now we must modify the pre-coloring pass to make sure `z` is marked as A and `w` as D. In case such pre-coloring is impossible, the `spill2var` pass must also be modified to spill whatever variables prevent this coloring into another variable. If `z` is chosen for spilling, then the pre-coloring pass must change both the mulhi and mul statements. If `w` is chosen for spilling, then.. If this is the last use of `y`, then it is fine for `y` to be assigned to D, although this fact is ignored in the code.
k = x;
w = k *^ y;
k = k * y;
z = k;
Where `k` and `w` are the VTEs that must live and die immediately. The dumbifier checks if `w` and `k` are already precolored to not repeat forever, but it would be better to check the UD-chain.
Lastly, the codegen pass must recognize the above sequence as a multiplication and emit a single `mul` instruction.
Lastly, the codegen pass must recognize the sequence `w = z *^ y; z = z * y;` and emit a single `mul` instruction.
In `cg.c` is a function called `xop`, which returns an x86 operand string, given a trivially compilable Nectar expression. Because we've guaranteed the other operand may not be a constant, we do not need to check the XOP type, but it's a good idea to insert `assert`s and `abort`s everywhere to prevent hard-to-find bugs.

175
src/ast.c
View File

@ -26,9 +26,7 @@ void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, v
}
} else if(n->nodeKind == AST_STMT_ASSIGN) {
generic_visitor(&n->stmtAssign.what, stmt, stmtPrev, chu, tlc, ud, handler);
if(n->stmtAssign.to) {
generic_visitor(&n->stmtAssign.to, stmt, stmtPrev, chu, tlc, ud, handler);
}
generic_visitor(&n->stmtAssign.to, stmt, stmtPrev, chu, tlc, ud, handler);
} else if(n->nodeKind == AST_STMT_IF) {
generic_visitor(&n->stmtIf.expression, stmt, stmtPrev, chu, tlc, ud, handler);
generic_visitor(&n->stmtIf.then, stmt, stmtPrev, chu, tlc, ud, handler);
@ -75,9 +73,6 @@ void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, v
for(size_t i = 0; i < n->expression.type->array.length; i++) {
generic_visitor(&n->exprArray.items[i], stmt, stmtPrev, chu, tlc, ud, handler);
}
} else if(n->nodeKind == AST_EXPR_EXT_SALLOC) {
} else if(n->nodeKind == AST_EXPR_DOT) {
generic_visitor(&n->exprDot.a, stmt, stmtPrev, chu, tlc, ud, handler);
} else {
abort();
}
@ -224,15 +219,9 @@ static void adduse(VarTableEntry *vte, AST *use, AST *whole) {
assert(vte->data.var.reachingDefs != NULL);
ReachingDefs *rd = vte->data.var.reachingDefs;
while(rd && rd->defCount == 0) rd = rd->parent;
if(!rd) return;
for(size_t d = 0; d < rd->defCount; d++) {
for(size_t d = 0; d < vte->data.var.reachingDefs->defCount; d++) {
UseDef *ud = malloc(sizeof(*ud));
ud->def = rd->defs[d];
ud->def = vte->data.var.reachingDefs->defs[d];
ud->use = use;
ud->stmt = whole;
ud->next = NULL;
@ -382,7 +371,6 @@ static void ast_usedef_pass(AST *tlc, AST *a, AST *wholestmt) {
} 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_EXPR_EXT_SALLOC) {
} else if(a->nodeKind == AST_STMT_BREAK) {
} else if(a->nodeKind == AST_STMT_CONTINUE) {
} else if(a->nodeKind == AST_STMT_EXT_ALIGN) {
@ -453,8 +441,6 @@ char *type_to_string(Type *t) {
char *r = malp("%s*", c);
free(c);
return r;
} else if(t->type == TYPE_TYPE_RECORD) {
return malp("%s", t->record.name);
}
return strdup("@unimp");
@ -537,9 +523,6 @@ static char *ast_dumpe(AST *e) {
case BINOP_GEQUAL:
op = ">=";
break;
case BINOP_MULHI:
op = "*^";
break;
default:
abort();
}
@ -588,23 +571,6 @@ static char *ast_dumpe(AST *e) {
out = out2;
}
return out;
} else if(e->nodeKind == AST_EXPR_EXT_SALLOC) {
char *w = type_to_string(e->exprExtSalloc.size);
char *out = malp("@salloc(%s)", w);
free(w);
return out;
} else if(e->nodeKind == AST_EXPR_CAST) {
char *a = ast_dumpe(e->exprCast.what);
char *b = type_to_string(e->exprCast.to);
char *out = malp("(%s as %s)", a, b);
free(a);
free(b);
return out;
} else if(e->nodeKind == AST_EXPR_DOT) {
char *a = ast_dumpe(e->exprDot.a);
char *out = malp(e->nodeKind == AST_EXPR_BINARY_OP ? "(%s).%s" : "%s.%s", a, e->exprDot.b);
free(a);
return out;
}
return malp("@unimp:%s", AST_KIND_STR[e->nodeKind]);
@ -705,138 +671,3 @@ AST *ast_deep_copy(AST *src) {
abort();
}
AST *ast_cast_expr(AST *what, Type *to) {
if(what == NULL) goto fail;
/* Only exists at parse-time, hence not part of type system and is handled separately */
if(what->nodeKind == AST_EXPR_STRING_LITERAL) {
if(to->type == TYPE_TYPE_ARRAY && type_equal(primitive_parse("u8"), to->array.of) && to->array.length == what->exprStrLit.length) {
ASTExprArray *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_ARRAY;
ret->items = malloc(sizeof(*ret->items) * to->array.length);
ret->type = to;
for(int i = 0; i < to->array.length; i++) {
uint8_t bajt = what->exprStrLit.data[i];
ASTExprPrimitive *item = malloc(sizeof(*item));
item->nodeKind = AST_EXPR_PRIMITIVE;
item->type = to->array.of;
item->val = bajt;
ret->items[i] = (AST*) item;
}
return (AST*) ret;
} 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));
ret->nodeKind = AST_EXPR_PRIMITIVE;
ret->type = to;
memcpy(&ret->val, what->exprStrLit.data, sizeof(ret->val));
return (AST*) ret;
} else abort();
}
if(type_equal(what->expression.type, to)) return what;
if(!type_is_castable(what->expression.type, to)) {
goto fail;
}
if(what->nodeKind == AST_EXPR_PRIMITIVE && (to->type == TYPE_TYPE_PRIMITIVE || to->type == TYPE_TYPE_POINTER)) {
ASTExprPrimitive *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_PRIMITIVE;
ret->type = to;
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;
} else {
ASTExprCast *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_CAST;
ret->type = to;
ret->what = what;
ret->to = to;
return (AST*) ret;
}
fail:
stahp(0, 0, "Cannot cast type %s into %s", type_to_string(what->expression.type), type_to_string(to));
}
struct Spill2StackState {
AST *targetTLC;
VarTableEntry *target;
size_t stackGrowth;
};
static void spill2stack_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct Spill2StackState *this = ud;
if(tlc != this->targetTLC) {
// Don't do anything.
return;
}
AST *a = *aptr;
if(a == tlc) {
a->chunk.stackReservation += this->stackGrowth;
} else if(a->nodeKind == AST_EXPR_VAR) {
if(a->exprVar.thing == this->target) {
// DO THE 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 = -this->stackGrowth; // This will be affected by the other part of this pass, so we must reverse
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 && a->exprBinOp.operands[0]->nodeKind == AST_EXPR_STACK_POINTER && a->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
// Guaranteed to not require more dumbification
a->exprBinOp.operands[1]->exprPrim.val += this->stackGrowth;
}
}
void ast_spill_to_stack(AST *tlc, VarTableEntry *vte) {
assert(vte != NULL);
assert(tlc->nodeKind == AST_CHUNK);
struct Spill2StackState state;
memset(&state, 0, sizeof(state));
state.target = vte;
state.targetTLC = tlc;
state.stackGrowth = (type_size(vte->type) + 7) & ~7;
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, spill2stack_visitor);
}

View File

@ -45,9 +45,7 @@
K(AST_EXPR_FUNC) \
K(AST_STMT_EXT_ORG) \
K(AST_STMT_EXT_SECTION) \
K(AST_STMT_RETURN) \
K(AST_EXPR_EXT_SALLOC) \
K(AST_EXPR_DOT)
K(AST_STMT_RETURN)
typedef enum ENUMPAK { AST_KINDS(GEN_ENUM) } ASTKind;
extern const char *AST_KIND_STR[];
@ -58,23 +56,17 @@ typedef enum ENUMPAK {
BINOP_BITWISE_AND = 2,
BINOP_BITWISE_OR = 3,
BINOP_BITWISE_XOR = 4,
BINOP_SIMPLES = 5,
BINOP_MUL = 5,
BINOP_DIV = 6,
BINOP_2OPS = 7,
BINOP_MULHI = 7,
BINOP_MOD = 8,
BINOP_EQUAL = 9,
BINOP_NEQUAL = 10,
BINOP_LESS = 11,
BINOP_GREATER = 12,
BINOP_LEQUAL = 13,
BINOP_GEQUAL = 14,
BINOP_EQUAL = 7,
BINOP_NEQUAL = 8,
BINOP_LESS = 9,
BINOP_GREATER = 10,
BINOP_LEQUAL = 11,
BINOP_GEQUAL = 12,
BINOP_WTF = 999,
} BinaryOp;
@ -269,19 +261,6 @@ typedef struct {
union AST **items;
} ASTExprArray;
typedef struct {
ASTExpr;
Type *size;
} ASTExprExtSalloc;
typedef struct {
ASTExpr;
union AST *a;
const char *b;
} ASTExprDot;
typedef struct {
ASTStmt;
@ -324,8 +303,6 @@ typedef union AST {
ASTExprCast exprCast;
ASTExprArray exprArray;
ASTExprFunc exprFunc;
ASTExprDot exprDot;
ASTExprExtSalloc exprExtSalloc;
ASTStmtExtOrg stmtExtOrg;
ASTStmtExtSection stmtExtSection;
} AST;
@ -345,10 +322,6 @@ char *ast_dump(AST *tlc);
AST *ast_deep_copy(AST*);
AST *ast_cast_expr(AST *what, Type *to);
void ast_spill_to_stack(AST *tlc, VarTableEntry *vte);
__attribute__((format(printf, 1, 2))) char *malp(const char *fmt, ...);
#endif

267
src/cg.c
View File

@ -101,28 +101,10 @@ static const char *xj(BinaryOp op) {
case BINOP_GREATER: return "a";
case BINOP_LEQUAL: return "be";
case BINOP_GEQUAL: return "ae";
default: abort(); return NULL;
default: return "wtf";
}
}
static AST *is_field_access(AST *e) {
if(e->nodeKind != AST_EXPR_UNARY_OP || e->exprUnOp.operator != UNOP_DEREF) {
return NULL;
}
e = e->exprUnOp.operand;
if(e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER) {
e = e->exprCast.what;
}
if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_ADD && e->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && e->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) {
return e;
}
return NULL;
}
static const char *xop_sz(AST *tlc, AST *e, int sz) {
#define XOPBUFS 16
#define XOPBUFSZ 24
@ -131,48 +113,7 @@ static const char *xop_sz(AST *tlc, AST *e, int sz) {
char *ret = bufs[bufidx];
if(e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER) {
e = e->exprCast.what;
}
if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF) {
AST *p = e->exprUnOp.operand;
if(p->nodeKind == AST_EXPR_CAST && p->exprCast.to->type == TYPE_TYPE_POINTER) {
p = p->exprCast.what;
}
if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR && p->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
xv_sz(p->exprBinOp.operands[0]->exprVar.thing, 4),
xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 4));
} else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && p->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && p->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 4));
} else if(is_field_access(e)) {
e = is_field_access(e);
snprintf(ret, XOPBUFSZ, "%s [%s + %i]",
spec(sz),
e->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
e->exprBinOp.operands[1]->exprPrim.val);
} else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && p->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && p->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && p->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && p->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s + %i * %s]",
spec(sz),
p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
p->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val,
xv_sz(p->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing, 4));
} else if(p->nodeKind == AST_EXPR_VAR && p->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(p->exprVar.thing, 4));
} else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_STACK_POINTER && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
snprintf(ret, XOPBUFSZ, "[esp + %i]", p->exprBinOp.operands[1]->exprPrim.val);
} else {
return NULL;
}
} else if(e->nodeKind == AST_EXPR_STACK_POINTER) {
if(e->nodeKind == AST_EXPR_STACK_POINTER) {
snprintf(ret, XOPBUFSZ, "esp", tlc->chunk.stackReservation);
} else if(e->nodeKind == AST_EXPR_VAR) {
VarTableEntry *v = e->exprVar.thing;
@ -183,9 +124,34 @@ static const char *xop_sz(AST *tlc, AST *e, int sz) {
snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), v->data.symbol.name);
} else abort();
} else if(e->nodeKind == AST_EXPR_PRIMITIVE) {
snprintf(ret, XOPBUFSZ, "%s %i", spec(type_size(e->exprPrim.type)), 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) {
snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
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_PRIMITIVE && 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) {
snprintf(ret, XOPBUFSZ, "%s [%s + %i]",
spec(sz),
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
e->exprUnOp.operand->exprBinOp.operands[1]->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_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 [%s + %i * %s]",
spec(sz),
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val,
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) {
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) {
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 + tlc->chunk.stackReservation);
} else {
return NULL;
}
@ -309,37 +275,7 @@ void cg_chunk(CGState *cg, AST *a) {
if(s->stmtAssign.to && is_xop(s->stmtAssign.what) == XOP_NOT_MEM && is_xop(s->stmtAssign.to) == XOP_NOT_MEM && !strcmp(xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to))) {
// It's a noop
} else if(s->stmtAssign.to) {
if(x86_imul_supported() && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_MUL) {
assert(s->stmtAssign.what->nodeKind == AST_EXPR_VAR);
assert(s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR);
if(!strcmp(xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]))) {
printf("imul %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[1]));
} else {
printf("imul %s, %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]), xop(a, s->stmtAssign.to->exprBinOp.operands[1]));
}
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_MULHI) {
assert(s->stmtAssign.what->nodeKind == AST_EXPR_VAR);
assert(s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR);
assert(s->stmtAssign.what->exprVar.thing->data.var.color == COLOR_EDX);
assert(s->statement.next->nodeKind == AST_STMT_ASSIGN);
assert(s->statement.next->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP);
assert(s->statement.next->stmtAssign.to->exprBinOp.operator == BINOP_MUL);
AST *otherop = NULL;
if(ast_expression_equal(s->statement.next->stmtAssign.to->exprBinOp.operands[0], s->statement.next->stmtAssign.what)) {
otherop = s->statement.next->stmtAssign.to->exprBinOp.operands[1];
} else if(ast_expression_equal(s->statement.next->stmtAssign.to->exprBinOp.operands[1], s->statement.next->stmtAssign.what)) {
otherop = s->statement.next->stmtAssign.to->exprBinOp.operands[0];
} else abort();
printf("mul %s\n", xop(a, otherop));
// Skip next statement, because they come in a pair
s = s->statement.next;
} 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_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
@ -541,8 +477,57 @@ static void spill2var_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk,
}
}
struct Spill2StackState {
AST *targetTLC;
VarTableEntry *target;
};
static void spill2stack_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct Spill2StackState *this = ud;
if(tlc != this->targetTLC) {
// Don't do anything.
return;
}
AST *a = *aptr;
if(a == tlc) {
a->chunk.stackReservation += 4;
} else if(a->nodeKind == AST_EXPR_VAR) {
if(a->exprVar.thing == this->target) {
// DO THE 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;
}
}
}
struct PrecolorState {
VarTableEntry *mustBeSpilled;
ASTExprVar *mustBeSpilled;
};
static void precolor_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
struct PrecolorState *this = ud;
@ -561,21 +546,23 @@ static void precolor_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, A
}
}
if(n->nodeKind == AST_STMT_RETURN && n->stmtReturn.val) {
assert(n->stmtReturn.val->nodeKind == AST_EXPR_VAR && n->stmtReturn.val->exprVar.thing->kind == VARTABLEENTRY_VAR);
VarTableEntry *vte = n->stmtReturn.val->exprVar.thing;
const int requiredColor = COLOR_EAX;
if(vte->data.var.precolored && vte->data.var.color != requiredColor) {
// Precoloring collision!
this->mustBeSpilled = vte;
return;
if(n->nodeKind == AST_STMT_RETURN) {
if(n->stmtReturn.val) {
assert(n->stmtReturn.val->nodeKind == AST_EXPR_VAR && n->stmtReturn.val->exprVar.thing->kind == VARTABLEENTRY_VAR);
VarTableEntry *vte = n->stmtReturn.val->exprVar.thing;
const int requiredColor = COLOR_EAX;
if(vte->data.var.precolored && vte->data.var.color != requiredColor) {
// Precoloring collision!
this->mustBeSpilled = vte;
return;
}
vte->data.var.color = requiredColor;
vte->data.var.precolored = true;
}
vte->data.var.color = requiredColor;
vte->data.var.precolored = true;
} else if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.to->nodeKind == AST_EXPR_CALL) {
assert(n->stmtAssign.what->nodeKind == AST_EXPR_VAR && n->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR);
@ -589,34 +576,6 @@ static void precolor_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, A
return;
}
vte->data.var.color = requiredColor;
vte->data.var.precolored = true;
} else if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.what->nodeKind == AST_EXPR_VAR && n->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && n->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && n->stmtAssign.to->exprBinOp.operator == BINOP_MUL) {
VarTableEntry *vte = n->stmtAssign.what->exprVar.thing;
const int requiredColor = COLOR_EAX;
if(vte->data.var.precolored && vte->data.var.color != requiredColor) {
// Precoloring collision!
this->mustBeSpilled = vte;
return;
}
vte->data.var.color = requiredColor;
vte->data.var.precolored = true;
} else if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.what->nodeKind == AST_EXPR_VAR && n->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && n->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && n->stmtAssign.to->exprBinOp.operator == BINOP_MULHI) {
VarTableEntry *vte = n->stmtAssign.what->exprVar.thing;
const int requiredColor = COLOR_EDX;
if(vte->data.var.precolored && vte->data.var.color != requiredColor) {
// Precoloring collision!
this->mustBeSpilled = vte;
return;
}
vte->data.var.color = requiredColor;
vte->data.var.precolored = true;
}
@ -812,7 +771,27 @@ static int comparator(const void *A, const void *B) {
return ((*a)->data.var.degree * (*a)->data.var.priority) - ((*b)->data.var.degree * (*b)->data.var.priority);
}
int cg_go(AST *a) {
ast_usedef_reset(a);
do {
struct PrecolorState state = {};
generic_visitor(&a, NULL, NULL, a, a, &state, precolor_visitor);
ast_usedef_reset(a);
// Spill?
VarTableEntry *vte = state.mustBeSpilled; // set if precoloring demands DIFFERENT colors for the same variable
if(!vte) {
// Find variables with the same color
vte = get_precolor_spill_candidate(a);
}
if(vte) {
struct Spill2VarState state;
memset(&state, 0, sizeof(state));
state.target = vte;
generic_visitor(&a, NULL, NULL, a, a, &state, spill2var_visitor);
} else break;
} while(true);
size_t adjCount = 0;
Adjacency *adjs = calloc(adjCount, sizeof(*adjs));
@ -894,17 +873,11 @@ nextColor:;
if(lastColor >= 4) {
// Spill node with highest degree
VarTableEntry *chosen = NULL;
for(ssize_t i = a->chunk.varCount - 1; i >= 0; i--) {
if(!vars[i]->data.var.precolored) {
chosen = vars[i];
break;
}
}
struct Spill2StackState state;
memset(&state, 0, sizeof(state));
state.target = vars[a->chunk.varCount - 1];
assert(chosen != NULL);
ast_spill_to_stack(a, chosen);
generic_visitor(&a, NULL, NULL, a, a, &state, spill2stack_visitor);
return 0;
}

View File

@ -9,17 +9,18 @@
// can be trivially translated to the target architecture.
//
// This file along with CG is strictly for IA-32 and will fail for other
// architectures.
// architecture.
#include"x86.h"
#include<string.h>
static VarTableEntry *create_dumbtemp(AST *tlc, Type *itstype) {
/* Split away complex expression into a new local variable */
static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
static size_t vidx = 0;
VarTableEntry *vte = calloc(1, sizeof(*vte));
vte->kind = VARTABLEENTRY_VAR;
vte->type = itstype;
vte->type = e->expression.type;
vte->data.var.color = -1;
vte->data.var.precolored = false;
vte->data.var.degree = 0;
@ -31,13 +32,6 @@ static VarTableEntry *create_dumbtemp(AST *tlc, Type *itstype) {
tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount));
tlc->chunk.vars[tlc->chunk.varCount - 1] = vte;
return vte;
}
/* Split away complex expression into a new local variable */
static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
VarTableEntry *vte = create_dumbtemp(tlc, e->expression.type);
// Alter AST
ASTExprVar *ev[2];
@ -72,6 +66,18 @@ static AST *xopify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
return varify(tlc, chunk, stmtPrev, stmt, e);
}
static bool is_incomplete_mul(AST *stmt) {
if(stmt->nodeKind != AST_STMT_ASSIGN) {
return false;
}
if(stmt->stmtAssign.what->nodeKind != AST_EXPR_VAR) {
}
return true;
}
struct DumbenState {
int effective;
};
@ -115,16 +121,12 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
} else if(s->nodeKind == AST_STMT_RETURN) {
// ret specifically returns in eax always, so it needs to be in a precolored var
AST *retval = s->stmtReturn.val;
if(retval && (!is_xop(retval) || retval->nodeKind == AST_EXPR_PRIMITIVE || (retval->nodeKind == AST_EXPR_VAR && retval->exprVar.thing->kind == VARTABLEENTRY_VAR && retval->exprVar.thing->data.var.color != COLOR_EAX))) {
// ret specifically returns in eax always, so it needs to be in a var
if(s->stmtReturn.val && (!is_xop(s->stmtReturn.val) || s->stmtReturn.val->nodeKind == AST_EXPR_PRIMITIVE)) {
retval = s->stmtReturn.val = varify(tlc, chu, stmtPrev, s, retval);
s->stmtReturn.val = varify(tlc, chu, stmtPrev, s, s->stmtReturn.val);
this->effective = 1;
vte_precolor(retval->exprVar.thing, COLOR_EAX);
}
} else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_CALL) {
@ -134,55 +136,30 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
// into:
// a = f(x);
// So `a` would be colored as `eax`
s->stmtExpr.expr = varify(tlc, chu, stmtPrev, s, s->stmtExpr.expr);
vte_precolor(s->stmtExpr.expr->exprVar.thing, COLOR_EAX);
// Purge this statement entirely, otherwise we'd have
// Purge this statement entirely, otherwise it'd be
// a = f(x);
// a;
if(stmtPrev) {
assert(stmtPrev->statement.next->statement.next == s);
stmtPrev->statement.next->statement.next = s->statement.next;
} else {
assert(chu->chunk.statementFirst->statement.next == s);
assert(chu->chunk.statementFirst->statement.next = s);
chu->chunk.statementFirst->statement.next = s->statement.next;
}
this->effective = 1;
} else if(s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.to) {
} else if(s->nodeKind == AST_STMT_ASSIGN) {
if(ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to) && stmtPrev) {
// This is a NOP, remove it from the AST
// NOP; remove this statement from AST
stmtPrev->statement.next = s->statement.next;
this->effective = 1;
} else {
if(s->stmtAssign.to->nodeKind == AST_EXPR_CALL && (s->stmtAssign.what->nodeKind != AST_EXPR_VAR || s->stmtAssign.what->exprVar.thing->kind != VARTABLEENTRY_VAR)) {
VarTableEntry *tmp = create_dumbtemp(tlc, s->stmtAssign.what->expression.type);
ASTExprVar *ev[2] = {calloc(1, sizeof(**ev)), calloc(1, sizeof(**ev))};
ev[0]->nodeKind = AST_EXPR_VAR;
ev[0]->type = tmp->type;
ev[0]->thing = tmp;
memcpy(ev[1], ev[0], sizeof(**ev));
ASTStmtAssign *assign2 = calloc(1, sizeof(*assign2));
assign2->nodeKind = AST_STMT_ASSIGN;
assign2->what = (AST*) s->stmtAssign.what;
assign2->to = (AST*) ev[0];
s->stmtAssign.what = (AST*) ev[1];
assign2->next = s->stmtAssign.next;
s->stmtAssign.next = (AST*) assign2;
this->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 = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to);
@ -231,58 +208,16 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
this->effective = 1;
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_MUL) {
WhysItBad_Huh because = x86_test_mul(stmtPrev, s);
if(s->stmtAssign.what->nodeKind != AST_EXPR_VAR) {
s->stmtAssign.what = varify(tlc, chu, stmtPrev, s, s->stmtAssign.what);
this->effective = 1;
}
if(because == SRC0_IS_BAD_XOP) {
s->stmtAssign.to->exprBinOp.operands[0] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[0]);
/*if(!(s->statement.next && s->statement.next->nodeKind == AST_STMT_ASSIGN && s->statement.next->stmtAssign.)) {
this->effective = 1;
} else if(because == SRC1_IS_BAD_XOP) {
s->stmtAssign.to->exprBinOp.operands[1] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[1]);
this->effective = 1;
} else if(because == MISSING_MULHI_FOR_MUL || because == DEST_NOT_SRC0) {// !stmtPrev || stmtPrev->nodeKind != AST_STMT_ASSIGN || stmtPrev->stmtAssign.to->nodeKind != AST_EXPR_BINARY_OP || stmtPrev->stmtAssign.to->exprBinOp.operator != BINOP_MULHI) {
// Turn this:
// a = b * c
// Into this:
// d = b
// e = d *^ c
// d = d * c
// a = d
ASTExprVar *originalDestination = s->stmtAssign.what;
s->stmtAssign.to->exprBinOp.operands[0] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[0]);
assert(stmtPrev->statement.next->nodeKind == AST_STMT_ASSIGN && stmtPrev->statement.next->stmtAssign.what->nodeKind == AST_EXPR_VAR && ast_expression_equal(stmtPrev->statement.next->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0]));
if(!x86_imul_supported()) {
ASTExprBinaryOp *mulhi = calloc(1, sizeof(*mulhi));
mulhi->nodeKind = AST_EXPR_BINARY_OP;
mulhi->type = s->stmtAssign.to->expression.type;
mulhi->operator = BINOP_MULHI;
mulhi->operands[0] = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]);
mulhi->operands[1] = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[1]);
AST *hihalf = varify(tlc, chu, stmtPrev->statement.next, s, mulhi);
vte_precolor(hihalf->exprVar.thing, COLOR_EDX);
}
s->stmtAssign.what = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]);
ASTStmtAssign *redest = calloc(1, sizeof(*redest));
redest->nodeKind = AST_STMT_ASSIGN;
redest->what = originalDestination;
redest->to = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]);
redest->next = s->stmtAssign.next;
s->stmtAssign.next = (AST*) redest;
vte_precolor(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, COLOR_EAX);
this->effective = 1;
} else assert(because == NOT_AT_ALL_IT || because == GUCCI);
}*/
// TODO: implement multiplication
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator < BINOP_SIMPLES && !ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0])) {
// Turn this:
@ -375,118 +310,13 @@ static void pre_dumb_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, A
}
}
static void decompose_symbol_record_field_access(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *n = *nptr;
if(n->nodeKind == AST_EXPR_DOT) {
ASTExprUnaryOp *ref = calloc(1, sizeof(*ref));
ref->nodeKind = AST_EXPR_UNARY_OP;
ref->type = type_pointer_wrap(n->exprDot.a->expression.type);
ref->operator = UNOP_REF;
ref->operand = n->exprDot.a;
ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
offset->nodeKind = AST_EXPR_PRIMITIVE;
offset->type = primitive_parse("u32");
size_t f;
for(f = 0; f < n->exprDot.a->expression.type->record.fieldCount; f++) {
if(!strcmp(n->exprDot.a->expression.type->record.fieldNames[f], n->exprDot.b)) {
break;
}
}
assert(f < n->exprDot.a->expression.type->record.fieldCount);
offset->val = n->exprDot.a->expression.type->record.fieldOffsets[f];
ASTExprBinaryOp *sum = calloc(1, sizeof(*sum));
sum->nodeKind = AST_EXPR_BINARY_OP;
sum->type = ref->type;
sum->operator = BINOP_ADD;
sum->operands[0] = (AST*) ref;
sum->operands[1] = (AST*) offset;
AST *cast = ast_cast_expr((AST*) sum, type_pointer_wrap(n->exprDot.a->expression.type->record.fieldTypes[f]));
ASTExprUnaryOp *deref = calloc(1, sizeof(*deref));
deref->nodeKind = AST_EXPR_UNARY_OP;
deref->type = cast->expression.type->pointer.of;
deref->operator = UNOP_DEREF;
deref->operand = cast;
*nptr = (AST*) deref;
}
}
static bool is_pointer2pointer_cast(AST *e) {
return e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER;
}
static bool is_double_field_access(AST *e) {
return e->nodeKind == AST_EXPR_BINARY_OP && is_pointer2pointer_cast(e->exprBinOp.operands[0]) && e->exprBinOp.operands[0]->exprCast.what->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operands[0]->exprCast.what->exprBinOp.operator == e->exprBinOp.operator && e->exprBinOp.operator == BINOP_ADD && e->exprBinOp.operands[0]->exprCast.what->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && e->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE;
}
static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
AST *n = *nptr;
bool *success = ud;
if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_REF && n->exprUnOp.operand->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operand->exprUnOp.operator == UNOP_DEREF) {
// Turn `&*a` into `a`
*nptr = n->exprUnOp.operand->exprUnOp.operand;
*success = true;
} else if(is_double_field_access(n)) {
// Turn `(p + x) as C + y` into `(p + (x + y)) as C`
n->exprBinOp.operands[0]->exprCast.what->exprBinOp.operands[1]->exprPrim.val += n->exprBinOp.operands[1]->exprPrim.val;
*nptr = n->exprBinOp.operands[0];
*success = true;
} else if(n->nodeKind == AST_EXPR_CAST && n->exprCast.what->nodeKind == AST_EXPR_CAST) {
// Turn `(p as A) as B` to `p as B`
n->exprCast.what = n->exprCast.what->exprCast.what;
*success = true;
} else if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operands[0]->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[0]->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[0]->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
// Turn `(x + a) + b` into `x + (a + b)`
n->exprBinOp.operands[0]->exprBinOp.operands[1]->exprPrim.val += n->exprBinOp.operands[1]->exprPrim.val;
*nptr = n->exprBinOp.operands[0];
*success = true;
}
}
void dumben_pre(AST *tlc) {
generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, pre_dumb_visitor);
generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, decompose_symbol_record_field_access);
for(size_t t = 0; t < tlc->chunk.varCount; t++) {
if(tlc->chunk.vars[t]->type->type == TYPE_TYPE_RECORD) {
ast_spill_to_stack(tlc, tlc->chunk.vars[t]);
}
}
bool success;
do {
success = false;
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &success, denoop_visitor);
} while(success);
}
void dumben_go(AST* tlc) {
size_t i = 0;
while(1) {
if(i == 20000) {
puts("TOO MANY DUMBS");
abort();
}
fprintf(stderr, "; Dumbing down %lu...\n", i++);
struct DumbenState state;
@ -499,7 +329,5 @@ void dumben_go(AST* tlc) {
if(!successful) {
break;
}
fputs(ast_dump(tlc), stderr);
}
}

View File

@ -34,7 +34,7 @@ char *TOKEN_NAMES[] = {
"'['",
"']'",
"'?'",
"string",
"string"
"'!='",
"'!'",
"'continue'",
@ -44,10 +44,6 @@ char *TOKEN_NAMES[] = {
"'>='",
"'<'",
"'>'",
"'*^'",
"'record'",
"'.'",
"'as'",
};
static int isAlpha(int c) {
@ -66,7 +62,7 @@ static int isWS(int c) {
return c == ' ' || c == '\n' || c == '\r' || c == '\b' || c == '\t';
}
static size_t currentRow = 1;
static size_t currentRow = 0;
static size_t currentColumn = 0;
static int ungetted = EOF;
@ -134,10 +130,6 @@ Token nct_tokenize(FILE *f) {
return tok;
} else if(c == '*') {
tok.type = TOKEN_STAR;
int c = nextc(f);
if(c == '^') {
tok.type = TOKEN_STAR_CARET;
} else ungetc(c, f);
return tok;
} else if(c == '&') {
tok.type = TOKEN_AMPERSAND;
@ -205,9 +197,6 @@ Token nct_tokenize(FILE *f) {
} else if(c == ',') {
tok.type = TOKEN_COMMA;
return tok;
} else if(c == '.') {
tok.type = TOKEN_DOT;
return tok;
} else if(c == '"') {
int capacity = 5;
char *content = malloc(capacity);
@ -236,14 +225,14 @@ Token nct_tokenize(FILE *f) {
tok.content = content;
tok.length = i;
return tok;
} else if(isAlpha(c) || c == '@' || c == '_') {
} else if(isAlpha(c) || c == '@') {
int capacity = 5;
char *content = malloc(capacity);
size_t i = 0;
content[i++] = c;
while(c = nextc(f), (isAlphanum(c) || c == '@' || c == '_')) {
while(c = nextc(f), (isAlphanum(c) || c == '@')) {
if(i == capacity - 1) {
content = realloc(content, capacity += 4);
}
@ -282,14 +271,6 @@ Token nct_tokenize(FILE *f) {
free(content);
tok.type = TOKEN_RETURN;
return tok;
} else if(!strcmp(content, "record")) {
free(content);
tok.type = TOKEN_RECORD;
return tok;
} else if(!strcmp(content, "as")) {
free(content);
tok.type = TOKEN_AS;
return tok;
}
tok.type = TOKEN_IDENTIFIER;

View File

@ -44,10 +44,6 @@ typedef enum {
TOKEN_GEQUAL,
TOKEN_LESS,
TOKEN_GREATER,
TOKEN_STAR_CARET,
TOKEN_RECORD,
TOKEN_DOT,
TOKEN_AS,
} TokenKind;
typedef struct {

View File

@ -8,7 +8,6 @@
#include"reporting.h"
#include"cg.h"
#include"dumberdowner.h"
#include"x86.h"
static int argc;
static char **argv;
@ -26,10 +25,6 @@ int main(int argc_, char **argv_) {
argc = argc_;
argv = argv_;
if(x86_target() == IUNKNOWN86) {
stahp(0, 0, "Unknown architecture %s", ntc_get_arg("target"));
}
const char *in = ntc_get_arg("in");
FILE *f = in ? fopen(in, "rb") : stdin;
@ -55,10 +50,6 @@ int main(int argc_, char **argv_) {
while(!cg_go(chunk)) {
dumben_go(chunk);
fputs("\n; === Spill ===\n", stderr);
fputs(ast_dump(chunk), stderr);
fputc('\n', stderr);
}
return 0;

View File

@ -157,23 +157,75 @@ static AST *exprvar(Parser *P, VarTableEntry *v) {
return a;
}
AST *nct_cast_expr(AST *what, Type *to) {
if(what == NULL) return NULL;
/* Only exists at parse-time, hence not part of type system and is handled separately */
if(what->nodeKind == AST_EXPR_STRING_LITERAL) {
if(to->type == TYPE_TYPE_ARRAY && type_equal(primitive_parse("u8"), to->array.of) && to->array.length == what->exprStrLit.length) {
ASTExprArray *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_ARRAY;
ret->items = malloc(sizeof(*ret->items) * to->array.length);
ret->type = to;
for(int i = 0; i < to->array.length; i++) {
uint8_t bajt = what->exprStrLit.data[i];
ASTExprPrimitive *item = malloc(sizeof(*item));
item->nodeKind = AST_EXPR_PRIMITIVE;
item->type = to->array.of;
item->val = bajt;
ret->items[i] = (AST*) item;
}
return (AST*) ret;
} 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));
ret->nodeKind = AST_EXPR_PRIMITIVE;
ret->type = to;
memcpy(&ret->val, what->exprStrLit.data, sizeof(ret->val));
return (AST*) ret;
} else abort();
}
if(type_equal(what->expression.type, to)) return what;
if(!type_is_castable(what->expression.type, to)) {
return NULL;
}
if(what->nodeKind == AST_EXPR_PRIMITIVE && (to->type == TYPE_TYPE_PRIMITIVE || to->type == TYPE_TYPE_POINTER)) {
ASTExprPrimitive *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_PRIMITIVE;
ret->type = to;
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;
} else {
ASTExprCast *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_CAST;
ret->type = to;
ret->what = what;
ret->to = to;
return (AST*) ret;
}
abort();
}
ASTChunk *nct_parse_chunk(Parser*, int, int, VarTable*, Type *ft);
Type *nct_parse_typename(Parser *P);
static void binop_implicit_cast(/*Parser *P, */ASTExprBinaryOp *binop) {
if(type_size(binop->operands[0]->expression.type) < type_size(binop->operands[1]->expression.type)) {
binop->operands[0] = ast_cast_expr(binop->operands[0], binop->operands[1]->expression.type);
}
if(type_size(binop->operands[1]->expression.type) < type_size(binop->operands[0]->expression.type)) {
binop->operands[1] = ast_cast_expr(binop->operands[1], binop->operands[0]->expression.type);
}
if(!binop->type) {
binop->type = binop->operands[0]->expression.type;
}
}
AST *nct_parse_expression(Parser *P, int lOP) {
if(lOP == 0) {
// Test if this is an anonymous function
@ -207,44 +259,28 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
}
if(lOP == 6) {
AST *e = NULL;
if(lOP == 5) {
if(peek(P, 0).type == TOKEN_NUMBER) {
e = (AST*) parse_prim(P);
return (AST*) parse_prim(P);
} else if(peek(P, 0).type == TOKEN_IDENTIFIER) {
if(!strcmp(peek(P, 0).content, "@stack")) {
get(P);
ASTExprStackPointer *ret = calloc(1, sizeof(*ret));
ASTExprStackPointer *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_STACK_POINTER;
ret->type = primitive_parse("u32");
e = (AST*) ret;
} else if(!strcmp(peek(P, 0).content, "@salloc")) {
get(P);
expect(P, TOKEN_PAREN_L);
ASTExprExtSalloc *ret = calloc(1, sizeof(*ret));
ret->nodeKind = AST_EXPR_EXT_SALLOC;
ret->size = nct_parse_typename(P);
ret->type = type_pointer_wrap(ret->size);
expect(P, TOKEN_PAREN_R);
e = (AST*) ret;
} else {
Token varname = get(P);
VarTableEntry *vte = vartable_find(P->scope, varname.content);
if(!vte) {
stahp(varname.row, varname.column, "Unknown variable %s", varname.content);
}
e = (AST*) exprvar(P, vte);
return (AST*) ret;
}
Token varname = get(P);
VarTableEntry *vte = vartable_find(P->scope, varname.content);
if(!vte) {
stahp(varname.row, varname.column, "Unknown variable %s", varname.content);
}
return exprvar(P, vte);
} else if(peek(P, 0).type == TOKEN_STRING) {
ASTExprStringLiteral *ret = malloc(sizeof(*ret));
ret->nodeKind = AST_EXPR_STRING_LITERAL;
@ -255,42 +291,13 @@ AST *nct_parse_expression(Parser *P, int lOP) {
ret->data = tok.content;
ret->length = tok.length;
e = (AST*) ret;
return (AST*) ret;
} else if(maybe(P, TOKEN_PAREN_L)) {
e = nct_parse_expression(P, 0);
AST *e = nct_parse_expression(P, 0);
expect(P, TOKEN_PAREN_R);
return e;
}
while(maybe(P, TOKEN_DOT)) {
assert(e->expression.type->type == TYPE_TYPE_RECORD);
Token fieldTok = expect(P, TOKEN_IDENTIFIER);
ASTExprDot *d = calloc(1, sizeof(*d));
d->nodeKind = AST_EXPR_DOT;
d->a = (AST*) e;
bool foundField = false;
for(size_t f = 0; f < e->expression.type->record.fieldCount; f++) {
char *fieldName = e->expression.type->record.fieldNames[f];
if(!strcmp(fieldName, fieldTok.content)) {
foundField = true;
d->type = e->expression.type->record.fieldTypes[f];
d->b = strdup(fieldName);
}
}
if(!foundField) {
stahp(fieldTok.row, fieldTok.column, "Field %s does not exist.", fieldTok.content);
}
e = (AST*) d;
}
return e;
} else if(lOP == 5) {
} else if(lOP == 4) {
if(maybe(P, TOKEN_STAR)) {
ASTExprUnaryOp *astop = malloc(sizeof(*astop));
astop->nodeKind = AST_EXPR_UNARY_OP;
@ -338,7 +345,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
return (AST *) astop;
}
} else return nct_parse_expression(P, lOP + 1);
} else if(lOP == 4) {
} else if(lOP == 3) {
AST *ret = nct_parse_expression(P, lOP + 1);
while(peek(P, 0).type == TOKEN_PAREN_L || peek(P, 0).type == TOKEN_SQUAREN_L) {
@ -358,7 +365,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
if(!maybe(P, TOKEN_PAREN_R)) {
while(peek(P, 0).type != TOKEN_PAREN_R && peek(P, 0).type != TOKEN_COMMA) {
call->args = realloc(call->args, (argCount + 1) * sizeof(AST*));
call->args[argCount] = ast_cast_expr(nct_parse_expression(P, 0), ret->expression.type->function.args[argCount]);
call->args[argCount] = nct_parse_expression(P, 0);
argCount++;
@ -416,14 +423,13 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
return ret;
} else if(lOP == 3) {
} else if(lOP == 2) {
AST *ret = nct_parse_expression(P, lOP + 1);
if(peek(P, 0).type == TOKEN_STAR || peek(P, 0).type == TOKEN_SLASH || peek(P, 0).type == TOKEN_STAR_CARET) {
if(peek(P, 0).type == TOKEN_STAR || peek(P, 0).type == TOKEN_SLASH) {
while(1) {
BinaryOp op;
if(maybe(P, TOKEN_STAR)) op = BINOP_MUL;
else if(maybe(P, TOKEN_STAR_CARET)) op = BINOP_MULHI;
else if(maybe(P, TOKEN_SLASH)) op = BINOP_DIV;
else break;
@ -449,14 +455,12 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
}
binop_implicit_cast(astop);
ret = (AST*) astop;
}
}
return ret;
} else if(lOP == 2) {
} else if(lOP == 1) {
AST *ret = nct_parse_expression(P, lOP + 1);
if(
@ -477,7 +481,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
ASTExprBinaryOp *astop = malloc(sizeof(*astop));
astop->nodeKind = AST_EXPR_BINARY_OP;
astop->type = NULL;
astop->type = ret->expression.type;
astop->operator = op;
astop->operands[0] = ret;
@ -489,21 +493,26 @@ AST *nct_parse_expression(Parser *P, int lOP) {
stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Attempt to perform arithmetic on non-number types.");
}
binop_implicit_cast(astop);
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) {
astop->type = operand->type;
} else {
if(type_size(operand->type) > type_size(astop->type)) {
astop->type = operand->type;
}
}
ret = (AST*) astop;
}
}
return ret;
} else if(lOP == 1) {
AST *ret = nct_parse_expression(P, lOP + 1);
while(maybe(P, TOKEN_AS)) {
Type *castTo = nct_parse_typename(P);
ret = ast_cast_expr(ret, castTo);
}
return ret;
} else if(lOP == 0) {
AST *ret = nct_parse_expression(P, lOP + 1);
@ -531,7 +540,13 @@ AST *nct_parse_expression(Parser *P, int lOP) {
stahp(P->tokens[P->i].row, P->tokens[P->i].column, "Invalid combination of operator and operand types.");
}
binop_implicit_cast(astop);
if(!astop->type) {
astop->type = operand->type;
} else {
if(type_size(operand->type) > type_size(astop->type)) {
astop->type = operand->type;
}
}
ret = (AST*) astop;
}
@ -556,16 +571,7 @@ Type *nct_parse_typename(Parser *P) {
goto backtrack;
}
Type *ret = NULL;
Token id = expect(P, TOKEN_IDENTIFIER);
VarTableEntry *potentialVTE = vartable_find(P->scope, id.content);
if(potentialVTE && potentialVTE->kind == VARTABLEENTRY_TYPE) {
ret = potentialVTE->data.type.ptr;
} else {
ret = (Type*) primitive_parse(id.content);
}
Type *ret = (Type*) primitive_parse(expect(P, TOKEN_IDENTIFIER).content);
if(!ret) {
goto backtrack;
@ -683,12 +689,12 @@ static AST *parse_declaration(Parser *P) {
//reachingdefs_set(entry->data.var.reachingDefs, (AST*) assign);
assign->what = exprvar(P, entry);
assign->to = peek(P, 0).type == TOKEN_SEMICOLON ? NULL : ast_cast_expr(nct_parse_expression(P, 0), assign->what->expression.type);
assign->to = peek(P, 0).type == TOKEN_SEMICOLON ? NULL : nct_parse_expression(P, 0);
ret = (AST*) assign;
} else {
ASTStmtDecl *decl = calloc(1, sizeof(*ret));
ASTStmtDecl *decl = malloc(sizeof(*ret));
decl->nodeKind = AST_STMT_DECL;
decl->thing = entry;
decl->next = NULL;
@ -708,12 +714,16 @@ static AST *parse_declaration(Parser *P) {
decl->expression = nct_parse_expression(P, 0);
if(type) {
if(decl->expression) {
decl->expression = ast_cast_expr(decl->expression, type);
}
decl->expression = nct_cast_expr(decl->expression, type);
} else {
entry->type = decl->expression->expression.type;
}
if(decl->expression) {
//if(ret->expression->expression.constantType == EXPRESSION_NOT_CONSTANT) {
// stahp(1, 4142, "Symbol declaration may contain constant expressions only.");
//}
}
} else if(isExternal) {
entry->kind = VARTABLEENTRY_SYMBOL;
entry->data.symbol.isLocal = isLocal;
@ -827,24 +837,6 @@ void nct_parse_statement(Parser *P) {
pushstat(P, ret);
return;
} else if(maybe(P, TOKEN_RECORD)) {
// Do nothing. This is handled in nct_parse_chunk.
expect(P, TOKEN_IDENTIFIER);
expect(P, TOKEN_SQUIGGLY_L);
size_t depth = 1;
while(1) {
TokenKind tk = get(P).type;
if(tk == TOKEN_SQUIGGLY_L) {
depth++;
} else if(tk == TOKEN_SQUIGGLY_R) {
depth--;
if(depth == 0) {
break;
}
}
}
return;
} else if(peek(P, 0).type == TOKEN_IDENTIFIER) {
if(!strcmp(peek(P, 0).content, "@align")) {
@ -914,7 +906,7 @@ void nct_parse_statement(Parser *P) {
ret->nodeKind = AST_STMT_ASSIGN;
ret->next = NULL;
ret->what = e;
ret->to = ast_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) {
// reachingdefs_set(ret->what->exprVar.thing->data.var.reachingDefs, (AST*) ret);
@ -937,54 +929,6 @@ void nct_parse_statement(Parser *P) {
}
}
Type *nct_parse_record_definition(Parser *P) {
expect(P, TOKEN_RECORD);
Token name = expect(P, TOKEN_IDENTIFIER);
expect(P, TOKEN_SQUIGGLY_L);
Type *tr = calloc(1, sizeof(TypeRecord));
tr->type = TYPE_TYPE_RECORD;
tr->record.name = strndup(name.content, name.length);
size_t nextOffset = 0;
while(peek(P, 0).type != TOKEN_SQUIGGLY_R) {
if(peek(P, 0).type == TOKEN_NUMBER) {
ASTExprPrimitive *explicitOffset = parse_prim(P);
nextOffset = explicitOffset->val;
free(explicitOffset);
expect(P, TOKEN_COLON);
}
size_t fi = tr->record.fieldCount++;
tr->record.fieldOffsets = realloc(tr->record.fieldOffsets, sizeof(*tr->record.fieldOffsets) * tr->record.fieldCount);
tr->record.fieldTypes = realloc(tr->record.fieldTypes, sizeof(*tr->record.fieldTypes) * tr->record.fieldCount);
tr->record.fieldNames = realloc(tr->record.fieldNames, sizeof(*tr->record.fieldNames) * tr->record.fieldCount);
Type *fieldType = nct_parse_typename(P);
Token fieldName = expect(P, TOKEN_IDENTIFIER);
tr->record.fieldTypes[fi] = fieldType;
tr->record.fieldNames[fi] = strndup(fieldName.content, fieldName.length);
tr->record.fieldOffsets[fi] = nextOffset;
nextOffset += type_size(tr->record.fieldTypes[fi]);
if(tr->record.size < nextOffset) {
tr->record.size = nextOffset;
}
expect(P, TOKEN_SEMICOLON);
}
expect(P, TOKEN_SQUIGGLY_R);
return tr;
}
ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize, VarTable *toplevelParent, Type *ft) {
AST *ret = calloc(1, sizeof(ASTChunk));
ret->nodeKind = AST_CHUNK;
@ -1005,6 +949,8 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize, VarTable
P->topLevel = &ret->chunk;
}
//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 */
P->skimMode++;
@ -1037,16 +983,6 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize, VarTable
if(!d) abort();
free(d); /* We don't need it. */
} else if(k == TOKEN_RECORD) {
P->i--;
Type *tr = nct_parse_record_definition(P);
VarTableEntry *vte = calloc(1, sizeof(*vte));
vte->kind = VARTABLEENTRY_TYPE;
vte->data.type.ptr = tr;
vartable_set(P->scope, tr->record.name, vte);
}
}
P->i = oldIdx;
@ -1076,6 +1012,8 @@ ASTChunk *nct_parse_chunk(Parser *P, int isTopLevel, int varPrioritize, VarTable
nct_parse_statement(P);
}
//vartable_coalesce_reachingdefs_for_all_vars(P->scope);
size_t nonSymbols = 0;
for(size_t i = 0; i < P->scope->count; i++) {
if(P->scope->data[i]->kind == VARTABLEENTRY_VAR) {

View File

@ -72,11 +72,6 @@ Type *primitive_parse(const char *src) {
ret->vector = 1;
}
if(*src) {
free(ret);
return NULL;
}
ret->next = primitiveDatabase[hash];
primitiveDatabase[hash] = ret;
@ -99,8 +94,6 @@ size_t type_size(Type *t) {
return 4;
} else if(t->type == TYPE_TYPE_ARRAY) {
return type_size(t->array.of) * t->array.length;
} else if(t->type == TYPE_TYPE_RECORD) {
return t->record.size;
}
abort();

View File

@ -5,7 +5,7 @@
#include<stdint.h>
typedef enum {
TYPE_TYPE_PRIMITIVE, TYPE_TYPE_RECORD, TYPE_TYPE_POINTER, TYPE_TYPE_FUNCTION, TYPE_TYPE_ARRAY, TYPE_TYPE_ERROR
TYPE_TYPE_PRIMITIVE, TYPE_TYPE_COMPOUND, TYPE_TYPE_POINTER, TYPE_TYPE_FUNCTION, TYPE_TYPE_ARRAY, TYPE_TYPE_ERROR
} TypeType;
union Type;
@ -51,19 +51,6 @@ typedef struct TypeArray {
size_t length; /* 0 means unknown */
} TypeArray;
typedef struct TypeRecord {
TypeType type;
char *name;
size_t size;
union Type **fieldTypes;
size_t *fieldOffsets;
char **fieldNames;
size_t fieldCount;
} TypeRecord;
typedef union Type {
TypeType type;
@ -71,7 +58,6 @@ typedef union Type {
TypePointer pointer;
TypeFunction function;
TypeArray array;
TypeRecord record;
} Type;
extern Type TYPE_ERROR;

View File

@ -3,7 +3,6 @@
#include"utils.h"
#include<stdlib.h>
#include<string.h>
#include<assert.h>
struct ReachingDefs *reachingdefs_push(struct ReachingDefs *this) {
struct ReachingDefs *ret = calloc(1, sizeof(*ret));
@ -93,11 +92,3 @@ void vartable_coalesce_reachingdefs_for_all_vars(VarTable *this) {
vartable_coalesce_reachingdefs_for_all_vars(this->parent);
}
}
void vte_precolor(VarTableEntry *vte, int color) {
assert(vte->kind == VARTABLEENTRY_VAR);
assert(!vte->data.var.precolored);
vte->data.var.precolored = true;
vte->data.var.color = color;
}

View File

@ -5,7 +5,7 @@
#include<stdbool.h>
typedef enum {
VARTABLEENTRY_SYMBOL, VARTABLEENTRY_VAR, VARTABLEENTRY_TYPE
VARTABLEENTRY_SYMBOL, VARTABLEENTRY_STACK, VARTABLEENTRY_VAR
} VarTableEntryKind;
union AST;
@ -64,9 +64,6 @@ typedef struct VarTableEntry {
UseDef *usedefFirst;
UseDef *usedefLast;
} var;
struct {
Type *ptr;
} type;
};
} data;
} VarTableEntry;
@ -87,6 +84,4 @@ VarTableEntry *vartable_set(VarTable*, const char*, VarTableEntry*);
void vartable_new_reachingdefs_for_all_vars(VarTable*);
void vartable_coalesce_reachingdefs_for_all_vars(VarTable*);
void vte_precolor(VarTableEntry *vte, int color);
#endif

145
src/x86.h
View File

@ -1,9 +1,5 @@
#pragma once
#include<string.h>
#include"ntc.h"
#include<assert.h>
#define COLOR_EAX 0
#define COLOR_ECX 1
#define COLOR_EDX 2
@ -14,10 +10,6 @@
#define XOP_NOT_MEM 1
#define XOP_MEM 2
static inline int is_xop(AST *e) {
if(e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER) {
e = e->exprCast.what;
}
if(e->nodeKind == AST_EXPR_PRIMITIVE) {
return XOP_NOT_MEM;
} else if(e->nodeKind == AST_EXPR_VAR) {
@ -29,10 +21,6 @@ static inline int is_xop(AST *e) {
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF) {
AST *c = e->exprUnOp.operand;
if(c->nodeKind == AST_EXPR_CAST && c->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && c->exprCast.to->type == TYPE_TYPE_POINTER) {
c = c->exprCast.what;
}
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) {
@ -52,136 +40,3 @@ static inline int is_xop(AST *e) {
return XOP_NOT_XOP;
}
typedef enum {
NOT_AT_ALL_IT,
DEST_IS_BAD_XOP,
SRC0_IS_BAD_XOP,
SRC1_IS_BAD_XOP,
DEST_NOT_PRECOLORED,
SRC0_NOT_PRECOLORED,
SRC1_NOT_PRECOLORED,
DEST_NOT_SRC0,
MISSING_MULHI_FOR_MUL,
GUCCI,
} WhysItBad_Huh;
typedef enum {
I086 = 0,
I186 = 1,
I286 = 2,
I386 = 3,
IUNKNOWN86 = 255,
} X86Target;
static inline X86Target x86_target() {
const char *str = ntc_get_arg("target");
if(!str) return I386;
if(!strcmp(str, "086")) {
return I086;
}
if(!strcmp(str, "186")) {
return I186;
}
if(!strcmp(str, "286")) {
return I286;
}
if(!strcmp(str, "386")) {
return I386;
}
return IUNKNOWN86;
}
static inline size_t x86_max_gpr_size() {
switch(x86_target()) {
case I086:
case I186:
case I286:
return 2;
case I386:
return 4;
default: abort();
}
}
static inline bool x86_imul_supported() {
return x86_target() >= I186;
}
static inline WhysItBad_Huh x86_test_mul_matching_mulhi(AST *mulhi, AST *mul) {
assert(mulhi->statement.next == mul);
if(mulhi->stmtAssign.to->nodeKind != AST_EXPR_BINARY_OP) {
return NOT_AT_ALL_IT;
}
if(mulhi->stmtAssign.to->exprBinOp.operator != BINOP_MULHI) {
return NOT_AT_ALL_IT;
}
if(!ast_expression_equal(mulhi->stmtAssign.to->exprBinOp.operands[0], mul->stmtAssign.to->exprBinOp.operands[0])) {
return NOT_AT_ALL_IT;
}
if(!ast_expression_equal(mulhi->stmtAssign.to->exprBinOp.operands[1], mul->stmtAssign.to->exprBinOp.operands[1])) {
return NOT_AT_ALL_IT;
}
if(is_xop(mulhi->stmtAssign.what) != XOP_NOT_MEM) {
return DEST_IS_BAD_XOP;
}
return GUCCI;
}
static inline WhysItBad_Huh x86_test_mul(AST *stmtPrev, AST *stmt) {
if(stmt->nodeKind != AST_STMT_ASSIGN) {
return NOT_AT_ALL_IT;
}
if(stmt->stmtAssign.to->nodeKind != AST_EXPR_BINARY_OP) {
return NOT_AT_ALL_IT;
}
if(stmt->stmtAssign.to->exprBinOp.operator != BINOP_MUL) {
return NOT_AT_ALL_IT;
}
if(stmt->stmtAssign.what->nodeKind != AST_EXPR_VAR || stmt->stmtAssign.what->exprVar.thing->kind != VARTABLEENTRY_VAR) {
return DEST_IS_BAD_XOP;
}
if(x86_imul_supported()) {
return GUCCI;
} else if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
return SRC1_IS_BAD_XOP;
}
if(is_xop(stmt->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_XOP) {
return SRC1_IS_BAD_XOP;
}
/*if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && x86_get_target() < I186) {
return SRC1_IS_BAD_XOP;
}*/
if(!ast_expression_equal(stmt->stmtAssign.what, stmt->stmtAssign.to->exprBinOp.operands[0])) {
return DEST_NOT_SRC0;
}
if(!stmt->stmtAssign.what->exprVar.thing->data.var.precolored) {
return DEST_NOT_PRECOLORED;
}
if(x86_test_mul_matching_mulhi(stmtPrev, stmt) != GUCCI) {
return MISSING_MULHI_FOR_MUL;
}
return GUCCI;
}