Compare commits
No commits in common. "cdcc1f6d3d707d9facb732f4e606b02114cb3536" and "6b7fefc5e7274e2fe86130eae95c5a81a1cebba0" have entirely different histories.
cdcc1f6d3d
...
6b7fefc5e7
@ -12,7 +12,7 @@ Currently the compiler is designed with only i386+ processors in mind. I intend
|
||||
|
||||
## AST structure
|
||||
|
||||
Starting with a Nectar source file, the compiler begins with the two common passes: lexing and parsing. The skimming in-between exploits Nectar's syntax quirks, and may jump back and forth multiple times to find all declared symbols and types in advance. This is necessary to avoid having to forward declare items. At the end, parsing returns what is called an AST in the source.
|
||||
Starting with a Nectar source file, the compiler begins with the two common passes: lexing and parsing. Parsing exploits Nectar's syntax quirks, and may jump back and forth multiple times to fully parse a source file. This is necessary to avoid having to forward declare items. At the end, parsing returns what is called an AST in the source, although formally speaking the term is incorrectly used.
|
||||
|
||||
An AST node may not be shared by multiple parent nodes. Also, the internal Nectar AST does not have scaling for pointer arithmetic; all pointers behave as `u8*`. This is the first of many simplifications.
|
||||
|
||||
@ -32,12 +32,6 @@ If the current node is within a statement (most are), `stmt` is equal to that st
|
||||
|
||||
A handler may be called before or after delving deeper into the tree (hence the pre and post handlers). Most passes use the prehandler, but type checking will be better with a posthandler, since we want type checks to happen bottom to top.
|
||||
|
||||
## Desegmentation
|
||||
|
||||
Pointers in Nectar might not be regular integers. The basic ones (called near) are, but we also have far pointers to support x86 segmentation. Internally these are record types with two fields: a `u16` segment and a `T*` near pointer.
|
||||
|
||||
## SRoA
|
||||
|
||||
## Pre-dumbification
|
||||
|
||||
Before dumbification we need to make sure the code at least matches the semantics of the x86 architecture.
|
||||
@ -179,7 +173,7 @@ It is insufficient for a compiler to assume these are in any way comparable. The
|
||||
|
||||
From these we form sets of registers called "register classes", which can be thought of as "ways in which a register can be used". The resource mask of a register class is the union (bitwise OR) of all bits used by all of its registers.
|
||||
|
||||
This compiler currently considers 3 register classes: `REG_CLASS_8`, for `al`, `ah`, `bl`, `bh`, `cl`, `ch`, `dl`, `dh`; `REG_CLASS_NOT_8` for `ax`, `eax`, `bx`, `ebx`, `cx`, `ecx`, `dx`, `edx`, `di`, `edi`, `si`, `esi`; `REG_CLASS_IA16_PTRS` for `di`, `si`, `bx`. It can be seen registers are not unique under this abstraction, but this is necessary as we gave up finding any patterns and assume the CPU to be a soup.
|
||||
This compiler currently considers 3 register classes: `REG_CLASS_8`, for `al`, `ah`, `bl`, `bh`, `cl`, `ch`, `dl`, `dh`; `REG_CLASS_NOT_8` for `ax`, `eax`, `bx`, `ebx`, `cx`, `ecx`, `dx`, `edx`, `di`, `edi`, `si`, `esi`; `REG_CLASS_IA16_PTRS` for `di`, `si`, `bx`. It can be seen registers are not unique under this abstraction, but this is necessary as this abstraction assumes the CPU to be a soup.
|
||||
|
||||
(64-bit is not considered)
|
||||
|
||||
|
@ -1,6 +0,0 @@
|
||||
u16 x = 0;
|
||||
u16 y = 5;
|
||||
|
||||
if(x && y) {
|
||||
u16 z = 9;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
u16 x = 0;
|
||||
u16 y = 5;
|
||||
u16 z = 9;
|
||||
|
||||
if(x && y || z) {
|
||||
u16 w = 15;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
u16 x = 0;
|
||||
u16 y = 5;
|
||||
|
||||
if(x || y) {
|
||||
u16 z = 9;
|
||||
}
|
@ -182,12 +182,6 @@ AST *ast_deep_copy(AST *src) {
|
||||
return memdup(src, sizeof(ASTExprVar));
|
||||
} else if(src->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||
return memdup(src, sizeof(ASTExprPrimitive));
|
||||
} else if(src->nodeKind == AST_EXPR_VAR) {
|
||||
return memdup(src, sizeof(ASTExprVar));
|
||||
} else if(src->nodeKind == AST_EXPR_UNARY_OP) {
|
||||
ASTExprUnaryOp *n = memdup(src, sizeof(ASTExprUnaryOp));
|
||||
n->operand = ast_deep_copy(n->operand);
|
||||
return n;
|
||||
}
|
||||
|
||||
abort();
|
||||
|
@ -73,9 +73,6 @@ typedef enum ENUMPAK {
|
||||
BINOP_LEQUAL = 13,
|
||||
BINOP_GEQUAL = 14,
|
||||
|
||||
BINOP_LOGICAL_AND = 15,
|
||||
BINOP_LOGICAL_OR = 16,
|
||||
|
||||
BINOP_WTF = 999,
|
||||
} BinaryOp;
|
||||
|
||||
|
@ -129,12 +129,6 @@ static char *ast_dumpe(AST *tlc, AST *e) {
|
||||
case BINOP_MULHI:
|
||||
op = "*^";
|
||||
break;
|
||||
case BINOP_LOGICAL_AND:
|
||||
op = "&&";
|
||||
break;
|
||||
case BINOP_LOGICAL_OR:
|
||||
op = "||";
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include"scope.h"
|
||||
#include<assert.h>
|
||||
#include<stdlib.h>
|
||||
#include"x86/arch.h"
|
||||
|
||||
struct Spill2StackState {
|
||||
AST *targetTLC;
|
||||
@ -31,7 +30,7 @@ static void spill2stack_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk
|
||||
|
||||
ASTExprStackPointer *rsp = calloc(1, sizeof(*rsp));
|
||||
rsp->nodeKind = AST_EXPR_STACK_POINTER;
|
||||
rsp->type = type_u(arch_gpr_size());
|
||||
rsp->type = primitive_parse("u32");
|
||||
|
||||
ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
|
||||
offset->nodeKind = AST_EXPR_PRIMITIVE;
|
||||
|
@ -153,10 +153,6 @@ void ast_usedef_reset(AST *chu) {
|
||||
}
|
||||
|
||||
s->statement.dirty = true;
|
||||
|
||||
s->statement.rd.defCount = 0;
|
||||
free(s->statement.rd.defs);
|
||||
s->statement.rd.defs = NULL;
|
||||
}
|
||||
|
||||
for(size_t rdsteps = 0;; rdsteps++) {
|
||||
|
@ -143,17 +143,9 @@ Token nct_tokenize(FILE *f) {
|
||||
return tok;
|
||||
} else if(c == '&') {
|
||||
tok.type = TOKEN_AMPERSAND;
|
||||
int c = nextc(f);
|
||||
if(c == '&') {
|
||||
tok.type = TOKEN_DOUBLE_AMPERSAND;
|
||||
} else ungetc(c, f);
|
||||
return tok;
|
||||
} else if(c == '|') {
|
||||
tok.type = TOKEN_VERTICAL_BAR;
|
||||
int c = nextc(f);
|
||||
if(c == '|') {
|
||||
tok.type = TOKEN_DOUBLE_VERTICAL_BAR;
|
||||
} else ungetc(c, f);
|
||||
return tok;
|
||||
} else if(c == '^') {
|
||||
tok.type = TOKEN_CARET;
|
||||
|
@ -27,9 +27,7 @@ typedef enum {
|
||||
TOKEN_BREAK,
|
||||
TOKEN_COMMA,
|
||||
TOKEN_AMPERSAND,
|
||||
TOKEN_DOUBLE_AMPERSAND,
|
||||
TOKEN_VERTICAL_BAR,
|
||||
TOKEN_DOUBLE_VERTICAL_BAR,
|
||||
TOKEN_CARET,
|
||||
TOKEN_TILDE,
|
||||
TOKEN_DOUBLE_EQUALS,
|
||||
|
@ -80,8 +80,6 @@ int main(int argc_, char **argv_) {
|
||||
stahp(0, 0, "Failed to read input (%s)", strerror(errno));
|
||||
}
|
||||
|
||||
arch_init();
|
||||
|
||||
Token *tokens = nct_lex(f);
|
||||
|
||||
if(in) fclose(f);
|
||||
|
96
src/parse.c
96
src/parse.c
@ -38,6 +38,9 @@ typedef struct {
|
||||
// Used by pushstat to add statements
|
||||
ASTChunk *currentChunk;
|
||||
|
||||
// Used to place guard variable uses after loops to stop reg allocation from fucking up
|
||||
Scope *loopScope;
|
||||
|
||||
// Used for generating statements that load & store arguments
|
||||
int isInFunction;
|
||||
|
||||
@ -140,6 +143,36 @@ static AST *exprvar(Parser *P, ScopeItem *v) {
|
||||
a->exprVar.type = v->type;
|
||||
a->exprVar.thing = v;
|
||||
|
||||
/*if(P->loopScope) {
|
||||
// XXX: O(n)!!!!!!!!!
|
||||
|
||||
int inloop = 0;
|
||||
for(Scope *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 = alloc_node(P, 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;
|
||||
}
|
||||
|
||||
@ -217,7 +250,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
}
|
||||
}
|
||||
|
||||
if(lOP == 7) {
|
||||
if(lOP == 6) {
|
||||
AST *e = NULL;
|
||||
|
||||
if(peek(P, 0).type == TOKEN_NUMBER) {
|
||||
@ -228,7 +261,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
|
||||
ASTExprStackPointer *ret = alloc_node(P, sizeof(*ret));
|
||||
ret->nodeKind = AST_EXPR_STACK_POINTER;
|
||||
ret->type = type_u(arch_sp_size());
|
||||
ret->type = primitive_parse("u32");
|
||||
|
||||
e = (AST*) ret;
|
||||
} else if(!strcmp(peek(P, 0).content, "@salloc")) {
|
||||
@ -255,7 +288,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
ret->ofExpr = nct_parse_expression(P, lOP - 1);
|
||||
}
|
||||
|
||||
ret->type = type_u(arch_gpr_size());
|
||||
ret->type = primitive_parse("u32");
|
||||
|
||||
e = (AST*) ret;
|
||||
} else {
|
||||
@ -314,7 +347,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
}
|
||||
|
||||
return e;
|
||||
} else if(lOP == 6) {
|
||||
} else if(lOP == 5) {
|
||||
if(maybe(P, TOKEN_STAR)) {
|
||||
ASTExprUnaryOp *astop = alloc_node(P, sizeof(*astop));
|
||||
astop->nodeKind = AST_EXPR_UNARY_OP;
|
||||
@ -368,7 +401,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
return (AST *) astop;
|
||||
}
|
||||
} else return nct_parse_expression(P, lOP + 1);
|
||||
} else if(lOP == 5) {
|
||||
} else if(lOP == 4) {
|
||||
AST *ret = nct_parse_expression(P, lOP + 1);
|
||||
|
||||
while(peek(P, 0).type == TOKEN_PAREN_L || peek(P, 0).type == TOKEN_SQUAREN_L) {
|
||||
@ -461,7 +494,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
if(typesize != 1) {
|
||||
ASTExprPrimitive *scale = alloc_node(P, sizeof(*scale));
|
||||
scale->nodeKind = AST_EXPR_PRIMITIVE;
|
||||
scale->type = type_u(arch_gpr_size());
|
||||
scale->type = primitive_parse("u32");
|
||||
scale->val = typesize;
|
||||
|
||||
ASTExprBinaryOp *mul = alloc_node(P, sizeof(*mul));
|
||||
@ -491,7 +524,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
}
|
||||
|
||||
return ret;
|
||||
} else if(lOP == 4) {
|
||||
} else if(lOP == 3) {
|
||||
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) {
|
||||
@ -531,7 +564,7 @@ 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(
|
||||
@ -570,7 +603,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
}
|
||||
|
||||
return ret;
|
||||
} else if(lOP == 2) {
|
||||
} else if(lOP == 1) {
|
||||
AST *ret = nct_parse_expression(P, lOP + 1);
|
||||
|
||||
while(maybe(P, TOKEN_AS)) {
|
||||
@ -578,35 +611,6 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
ret = ast_cast_expr(ret, castTo);
|
||||
}
|
||||
|
||||
return ret;
|
||||
} else if(lOP == 1) {
|
||||
AST *ret = nct_parse_expression(P, lOP + 1);
|
||||
|
||||
if(peek(P, 0).type == TOKEN_DOUBLE_AMPERSAND || peek(P, 0).type == TOKEN_DOUBLE_VERTICAL_BAR) {
|
||||
while(1) {
|
||||
BinaryOp op;
|
||||
if(maybe(P, TOKEN_DOUBLE_AMPERSAND)) op = BINOP_LOGICAL_AND;
|
||||
else if(maybe(P, TOKEN_DOUBLE_VERTICAL_BAR)) op = BINOP_LOGICAL_OR;
|
||||
else break;
|
||||
|
||||
ASTExprBinaryOp *astop = alloc_node(P, sizeof(*astop));
|
||||
astop->nodeKind = AST_EXPR_BINARY_OP;
|
||||
astop->type = NULL;
|
||||
astop->operator = op;
|
||||
astop->operands[0] = ret;
|
||||
|
||||
ASTExpr *operand = &(astop->operands[1] = nct_parse_expression(P, lOP + 1))->expression;
|
||||
|
||||
if(operand->type->type != TYPE_TYPE_PRIMITIVE) {
|
||||
stahp_token(&P->tokens[P->i], "Invalid combination of operator and operand types.");
|
||||
}
|
||||
|
||||
binop_implicit_cast(astop);
|
||||
|
||||
ret = (AST*) astop;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
} else if(lOP == 0) {
|
||||
AST *ret = nct_parse_expression(P, lOP + 1);
|
||||
@ -640,6 +644,8 @@ AST *nct_parse_expression(Parser *P, int lOP) {
|
||||
}
|
||||
}
|
||||
|
||||
//ret = ast_expression_optimize(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
@ -1102,11 +1108,11 @@ static void nct_parse_statement(Parser *P) {
|
||||
ret->nodeKind = AST_STMT_LOOP;
|
||||
ret->next = NULL;
|
||||
|
||||
//int isFirstLoop = P->loopScope == NULL;
|
||||
int isFirstLoop = P->loopScope == NULL;
|
||||
|
||||
//if(isFirstLoop) {
|
||||
//P->loopScope = P->scope;
|
||||
//}
|
||||
if(isFirstLoop) {
|
||||
P->loopScope = P->scope;
|
||||
}
|
||||
|
||||
expect(P, TOKEN_SQUIGGLY_L);
|
||||
ret->body = (AST*) nct_parse_chunk(P, 0, 1, NULL, NULL);
|
||||
@ -1114,8 +1120,8 @@ static void nct_parse_statement(Parser *P) {
|
||||
|
||||
pushstat(P, ret);
|
||||
|
||||
//if(isFirstLoop) {
|
||||
//P->loopScope = NULL;
|
||||
if(isFirstLoop) {
|
||||
P->loopScope = NULL;
|
||||
|
||||
/*for(size_t i = 0; i < P->guardedVarCount; i++) {
|
||||
ASTExprVar *ev = P->guardedVars[i];
|
||||
@ -1130,7 +1136,7 @@ static void nct_parse_statement(Parser *P) {
|
||||
P->guardedVarCount = 0;
|
||||
free(P->guardedVars);
|
||||
P->guardedVars = NULL;*/
|
||||
//}
|
||||
}
|
||||
|
||||
return;
|
||||
} else if(maybe(P, TOKEN_BREAK)) {
|
||||
|
@ -19,6 +19,7 @@ typedef struct UseDef {
|
||||
struct UseDef *next;
|
||||
} UseDef;
|
||||
|
||||
// Stack, necessary for "possible reaching defs" such as from if statements
|
||||
typedef struct ReachingDefs {
|
||||
size_t defCount;
|
||||
union AST **defs;
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include<stdint.h>
|
||||
#include<stdbool.h>
|
||||
#include<string.h>
|
||||
#include<stdio.h>
|
||||
|
||||
typedef enum {
|
||||
TYPE_TYPE_PRIMITIVE, TYPE_TYPE_RECORD, TYPE_TYPE_POINTER, TYPE_TYPE_FUNCTION, TYPE_TYPE_ARRAY, TYPE_TYPE_GENERIC, TYPE_TYPE_ERROR
|
||||
@ -115,10 +114,4 @@ static inline bool type_is_segmented_pointer(Type *type) {
|
||||
return type->type == TYPE_TYPE_RECORD && !!strstr(type->record.name, " @far*");
|
||||
}
|
||||
|
||||
static inline Type *type_u(size_t size) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "u%lu", size);
|
||||
return primitive_parse(buf);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -27,15 +27,9 @@ RegisterClass REG_CLASSES[] = {
|
||||
},
|
||||
[REG_CLASS_IA16_PTRS] = {
|
||||
.rMask = HWR_IND | HWR_BX,
|
||||
.rs = {HWR_DI, HWR_SI, HWR_BX},
|
||||
.rsN = {"di", "si", "bx"},
|
||||
.rsS = {2, 2, 2},
|
||||
},
|
||||
[REG_CLASS_IA16_USEABLE]= {
|
||||
.rMask = HWR_IND | HWR_GPR,
|
||||
.rs = {HWR_AX, HWR_CX, HWR_DX, HWR_DI, HWR_SI, HWR_BX},
|
||||
.rsN = {"ax", "cx", "dx", "di", "si", "bx"},
|
||||
.rsS = {2, 2, 2, 2, 2, 2},
|
||||
.rs = {HWR_DI, HWR_SI, HWR_BX, HWR_BP},
|
||||
.rsN = {"di", "si", "bx", "bp"},
|
||||
.rsS = {2, 2, 2, 2},
|
||||
},
|
||||
[REG_CLASS_DATASEGS] = {
|
||||
.rMask = HWR_SEGREGS,
|
||||
@ -56,7 +50,3 @@ bool arch_verify_target() {
|
||||
int arch_ptr_size() {
|
||||
return x86_ia16() ? 2 : 4;
|
||||
}
|
||||
|
||||
void arch_init() {
|
||||
|
||||
}
|
||||
|
107
src/x86/arch.h
107
src/x86/arch.h
@ -60,10 +60,45 @@ typedef struct RegisterClass {
|
||||
#define REG_CLASS_16_32 2
|
||||
#define REG_CLASS_IND 3
|
||||
#define REG_CLASS_IA16_PTRS 4
|
||||
#define REG_CLASS_IA16_USEABLE 5
|
||||
#define REG_CLASS_DATASEGS 6
|
||||
#define REG_CLASS_DATASEGS 5
|
||||
extern RegisterClass REG_CLASSES[];
|
||||
|
||||
// lol
|
||||
static inline bool is_reg_b(int cls, int res) {
|
||||
const char *name = REG_CLASSES[cls].rsN[res];
|
||||
return !strcmp(name, "bl") || !strcmp(name, "bh") || !!strstr(name, "bx");
|
||||
}
|
||||
static inline bool is_reg_di(int cls, int res) {
|
||||
const char *name = REG_CLASSES[cls].rsN[res];
|
||||
return !!strstr(name, "di");
|
||||
}
|
||||
static inline bool is_reg_si(int cls, int res) {
|
||||
const char *name = REG_CLASSES[cls].rsN[res];
|
||||
return !!strstr(name, "si");
|
||||
}
|
||||
static inline void reg_cast_up(int *cls, int *res) {
|
||||
const char *name = REG_CLASSES[*cls].rsN[*res];
|
||||
if(strstr(name, "a")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 1;
|
||||
} else if(strstr(name, "b")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 3;
|
||||
} else if(strstr(name, "c")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 5;
|
||||
} else if(strstr(name, "dl") || strstr(name, "dh") || strstr(name, "dx")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 7;
|
||||
} else if(strstr(name, "di")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 9;
|
||||
} else if(strstr(name, "si")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 11;
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
I086 = 0,
|
||||
I186 = 1,
|
||||
@ -116,10 +151,6 @@ static inline bool x86_imul_supported() {
|
||||
return x86_target() >= I186;
|
||||
}
|
||||
|
||||
static inline bool x86_is_marked_ptr(ScopeItem *si) {
|
||||
return si->data.var.preclassed && si->data.var.registerClass == REG_CLASS_IA16_PTRS;
|
||||
}
|
||||
|
||||
// Can expression be expressed as a single x86 operand?
|
||||
#define XOP_NOT_XOP 0
|
||||
#define XOP_NOT_MEM 1
|
||||
@ -145,10 +176,11 @@ static inline int is_xop(AST *e) {
|
||||
}
|
||||
|
||||
if(c->nodeKind == AST_EXPR_VAR && c->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||
if(x86_ia16() && !x86_is_marked_ptr(c->exprVar.thing)) {
|
||||
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
|
||||
if(x86_ia16()) { // In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
|
||||
if(!c->exprVar.thing->data.var.preclassed || c->exprVar.thing->data.var.registerClass != REG_CLASS_IA16_PTRS) {
|
||||
return XOP_NOT_XOP;
|
||||
}
|
||||
}
|
||||
|
||||
return XOP_MEM;
|
||||
} else if(
|
||||
@ -158,18 +190,8 @@ static inline int is_xop(AST *e) {
|
||||
if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||
return XOP_MEM;
|
||||
} else if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) {
|
||||
if(x86_ia16() && !x86_is_marked_ptr(c->exprBinOp.operands[1]->exprVar.thing)) {
|
||||
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
|
||||
return XOP_NOT_XOP;
|
||||
}
|
||||
|
||||
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) {
|
||||
if(x86_ia16() && !x86_is_marked_ptr(c->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing)) {
|
||||
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
|
||||
return XOP_NOT_XOP;
|
||||
}
|
||||
|
||||
int scale = c->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val;
|
||||
|
||||
if(scale == 1 || scale == 2 || scale == 4 || scale == 8) {
|
||||
@ -285,52 +307,3 @@ static inline void arch_add_hidden_variables(Scope *scope) {
|
||||
static inline bool x86_is_lea(AST *s) {
|
||||
return !x86_ia16() && s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && is_xop(s->stmtAssign.what) == XOP_NOT_MEM && is_xop(s->stmtAssign.to->exprBinOp.operands[0]) == XOP_NOT_MEM && is_xop(s->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_MEM;
|
||||
}
|
||||
|
||||
static inline int arch_sp_size() {
|
||||
return x86_max_gpr_size();
|
||||
}
|
||||
|
||||
static inline int arch_gpr_size() {
|
||||
return x86_max_gpr_size();
|
||||
}
|
||||
|
||||
// lol
|
||||
static inline bool is_reg_b(int cls, int res) {
|
||||
const char *name = REG_CLASSES[cls].rsN[res];
|
||||
return !strcmp(name, "bl") || !strcmp(name, "bh") || !!strstr(name, "bx");
|
||||
}
|
||||
static inline bool is_reg_di(int cls, int res) {
|
||||
const char *name = REG_CLASSES[cls].rsN[res];
|
||||
return !!strstr(name, "di");
|
||||
}
|
||||
static inline bool is_reg_si(int cls, int res) {
|
||||
const char *name = REG_CLASSES[cls].rsN[res];
|
||||
return !!strstr(name, "si");
|
||||
}
|
||||
static inline void reg_cast_to_gpr(int *cls, int *res) {
|
||||
const char *name = REG_CLASSES[*cls].rsN[*res];
|
||||
if(strstr(name, "a")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 1;
|
||||
} else if(strstr(name, "b")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 3;
|
||||
} else if(strstr(name, "c")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 5;
|
||||
} else if(strstr(name, "dl") || strstr(name, "dh") || strstr(name, "dx")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 7;
|
||||
} else if(strstr(name, "di")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 9;
|
||||
} else if(strstr(name, "si")) {
|
||||
*cls = REG_CLASS_16_32;
|
||||
*res = 11;
|
||||
}
|
||||
if(x86_ia16()) {
|
||||
(*res)--;
|
||||
}
|
||||
}
|
||||
|
||||
void arch_init();
|
55
src/x86/cg.c
55
src/x86/cg.c
@ -78,8 +78,8 @@ static const char *xv_sz(ScopeItem *v, int sz) {
|
||||
int cls = v->data.var.registerClass, reg = v->data.var.color;
|
||||
|
||||
if(type_size(v->type) != sz) {
|
||||
if(sz == x86_max_gpr_size()) {
|
||||
reg_cast_to_gpr(&cls, ®);
|
||||
if(sz == 4) {
|
||||
reg_cast_up(&cls, ®);
|
||||
} else abort();
|
||||
}
|
||||
|
||||
@ -185,17 +185,13 @@ static const char *xop_sz(AST *tlc, AST *e, int sz) {
|
||||
} else if(p->nodeKind == AST_EXPR_VAR && p->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||
pr = snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(p->exprVar.thing, 0));
|
||||
} 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) {
|
||||
if(x86_ia16()) {
|
||||
pr = snprintf(ret, XOPBUFSZ, "[bp + %li]", p->exprBinOp.operands[1]->exprPrim.val - tlc->chunk.stackReservation);
|
||||
} else {
|
||||
pr = snprintf(ret, XOPBUFSZ, "[esp + %i]", p->exprBinOp.operands[1]->exprPrim.val);
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} else if(e->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||
pr = snprintf(ret, XOPBUFSZ, x86_ia16() ? "sp" : "esp");
|
||||
pr = snprintf(ret, XOPBUFSZ, "esp");
|
||||
} else if(e->nodeKind == AST_EXPR_VAR) {
|
||||
ScopeItem *v = e->exprVar.thing;
|
||||
|
||||
@ -247,14 +243,8 @@ void cg_chunk(CGState *cg, AST *a) {
|
||||
AST *s = a->chunk.statementFirst;
|
||||
|
||||
if(a->chunk.stackReservation) {
|
||||
if(x86_ia16()) {
|
||||
printf("push bp\n");
|
||||
printf("mov bp, sp\n");
|
||||
printf("sub sp, %lu\n", a->chunk.stackReservation);
|
||||
} else {
|
||||
printf("sub esp, %lu\n", a->chunk.stackReservation);
|
||||
}
|
||||
}
|
||||
|
||||
// Potentially complex pattern matching
|
||||
while(s) {
|
||||
@ -362,20 +352,15 @@ void cg_chunk(CGState *cg, AST *a) {
|
||||
|
||||
AST *e = s->stmtAssign.to;
|
||||
|
||||
if(x86_ia16()) {
|
||||
puts("push cx");
|
||||
puts("push dx");
|
||||
} else {
|
||||
puts("push ecx");
|
||||
puts("push edx");
|
||||
}
|
||||
|
||||
int argCount = e->exprCall.what->expression.type->pointer.of->function.argCount;
|
||||
|
||||
size_t argSize = 0;
|
||||
|
||||
for(int i = argCount - 1; i >= 0; i--) {
|
||||
printf("push %s\n", xop_sz(cg->tlc, e->exprCall.args[i], arch_gpr_size()));
|
||||
printf("push %s\n", xop_sz(cg->tlc, e->exprCall.args[i], 4));
|
||||
|
||||
argSize += (type_size(e->exprCall.args[i]->expression.type) + 3) & ~3;
|
||||
}
|
||||
@ -386,21 +371,10 @@ void cg_chunk(CGState *cg, AST *a) {
|
||||
printf("call %s\n", xop(cg->tlc, e->exprCall.what));
|
||||
}
|
||||
|
||||
if(argSize) {
|
||||
if(x86_ia16()) {
|
||||
printf("add sp, %lu\n", argSize);
|
||||
} else {
|
||||
printf("add esp, %lu\n", argSize);
|
||||
}
|
||||
}
|
||||
if(argSize) printf("add esp, %lu\n", argSize);
|
||||
|
||||
if(x86_ia16()) {
|
||||
puts("pop dx");
|
||||
puts("pop cx");
|
||||
} else {
|
||||
puts("pop edx");
|
||||
puts("pop ecx");
|
||||
}
|
||||
|
||||
} else if(s->nodeKind == AST_STMT_ASSIGN) {
|
||||
|
||||
@ -481,8 +455,8 @@ void cg_chunk(CGState *cg, AST *a) {
|
||||
|
||||
} else {
|
||||
|
||||
const char *dest = xop_sz(cg->tlc, s->stmtAssign.what, x86_max_gpr_size());
|
||||
const char *src = xop_sz(cg->tlc, s->stmtAssign.to->exprCast.what, x86_max_gpr_size());
|
||||
const char *dest = xop_sz(cg->tlc, s->stmtAssign.what, 4);
|
||||
const char *src = xop_sz(cg->tlc, s->stmtAssign.to->exprCast.what, 4);
|
||||
|
||||
if(strcmp(dest, src)) {
|
||||
printf("mov %s, %s\n", dest, src);
|
||||
@ -546,21 +520,8 @@ void cg_chunk(CGState *cg, AST *a) {
|
||||
}
|
||||
|
||||
if(a->chunk.stackReservation) {
|
||||
if(x86_ia16()) {
|
||||
printf("add sp, %lu\n", a->chunk.stackReservation);
|
||||
} else {
|
||||
printf("add esp, %lu\n", a->chunk.stackReservation);
|
||||
}
|
||||
}
|
||||
|
||||
if(x86_ia16()) {
|
||||
if(x86_target() >= I186) {
|
||||
printf("leave\n");
|
||||
} else {
|
||||
printf("mov sp, bp\n");
|
||||
printf("pop bp\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("ret\n");
|
||||
|
||||
@ -774,7 +735,7 @@ static void determine_register_classes(AST *tlc) {
|
||||
if(type_size(tlc->chunk.vars[v]->type) == 1) {
|
||||
tlc->chunk.vars[v]->data.var.registerClass = REG_CLASS_8;
|
||||
} else {
|
||||
tlc->chunk.vars[v]->data.var.registerClass = x86_ia16() ? REG_CLASS_IA16_USEABLE : REG_CLASS_NOT_8;
|
||||
tlc->chunk.vars[v]->data.var.registerClass = REG_CLASS_NOT_8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,106 +117,9 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
|
||||
this->effective = 1;
|
||||
}
|
||||
|
||||
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_NOT && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && (e->exprUnOp.operand->exprBinOp.operator == BINOP_LOGICAL_AND || e->exprUnOp.operand->exprBinOp.operator == BINOP_LOGICAL_OR)) {
|
||||
|
||||
AST *binop = e->exprUnOp.operand;
|
||||
|
||||
e->exprUnOp.operand = binop->exprBinOp.operands[0];
|
||||
binop->exprBinOp.operands[0] = e;
|
||||
|
||||
AST *unop2 = calloc(1, sizeof(ASTExprUnaryOp));
|
||||
unop2->nodeKind = AST_EXPR_UNARY_OP;
|
||||
unop2->expression.type = binop->exprBinOp.operands[1]->expression.type;
|
||||
unop2->exprUnOp.operator = UNOP_NOT;
|
||||
unop2->exprUnOp.operand = binop->exprBinOp.operands[1];
|
||||
binop->exprBinOp.operands[1] = unop2;
|
||||
|
||||
binop->exprBinOp.operator = binop->exprBinOp.operator == BINOP_LOGICAL_AND ? BINOP_LOGICAL_OR : BINOP_LOGICAL_AND;
|
||||
|
||||
s->stmtJump.condition = binop;
|
||||
|
||||
ast_denoop(tlc, &s->stmtJump.condition);
|
||||
|
||||
this->effective = 1;
|
||||
|
||||
} else if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_LOGICAL_OR) {
|
||||
|
||||
AST *cond0 = e->exprBinOp.operands[0];
|
||||
AST *cond1 = e->exprBinOp.operands[1];
|
||||
|
||||
s->stmtJump.condition = cond0;
|
||||
|
||||
AST *jump2 = calloc(1, sizeof(ASTStmtJump));
|
||||
jump2->nodeKind = AST_STMT_JUMP;
|
||||
jump2->stmtJump.condition = cond1;
|
||||
jump2->stmtJump.label = strdup(s->stmtJump.label);
|
||||
jump2->statement.next = s->statement.next;
|
||||
s->statement.next = jump2;
|
||||
|
||||
this->effective = 1;
|
||||
|
||||
} else if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_LOGICAL_AND) {
|
||||
|
||||
static size_t idx = 0;
|
||||
|
||||
AST *cond0 = e->exprBinOp.operands[0];
|
||||
AST *cond1 = e->exprBinOp.operands[1];
|
||||
|
||||
AST *lbl = calloc(1, sizeof(ASTStmtLabel));
|
||||
lbl->nodeKind = AST_STMT_LABEL;
|
||||
lbl->stmtLabel.name = malp("$dla%lu", idx++);
|
||||
lbl->statement.next = s->statement.next;
|
||||
|
||||
AST *unop = calloc(1, sizeof(ASTExprUnaryOp));
|
||||
unop->nodeKind = AST_EXPR_UNARY_OP;
|
||||
unop->expression.type = cond0->expression.type;
|
||||
unop->exprUnOp.operator = UNOP_NOT;
|
||||
unop->exprUnOp.operand = cond0;
|
||||
s->stmtJump.condition = unop;
|
||||
|
||||
AST *jump2 = calloc(1, sizeof(ASTStmtJump));
|
||||
jump2->nodeKind = AST_STMT_JUMP;
|
||||
jump2->stmtJump.condition = cond1;
|
||||
jump2->stmtJump.label = strdup(s->stmtJump.label);
|
||||
jump2->statement.next = lbl;
|
||||
|
||||
s->stmtJump.label = strdup(lbl->stmtLabel.name);
|
||||
|
||||
s->statement.next = jump2;
|
||||
|
||||
ast_denoop(tlc, &s->stmtJump.condition);
|
||||
|
||||
this->effective = 1;
|
||||
|
||||
} else {
|
||||
|
||||
AST *v = e;
|
||||
|
||||
bool not = v->nodeKind == AST_EXPR_UNARY_OP && v->exprUnOp.operator == UNOP_NOT;
|
||||
if(not) {
|
||||
v = v->exprUnOp.operand;
|
||||
}
|
||||
|
||||
if(v->nodeKind != AST_EXPR_VAR) {
|
||||
v = varify(tlc, chu, stmtPrev, s, e);
|
||||
}
|
||||
|
||||
AST *zero = calloc(1, sizeof(ASTExprPrimitive));
|
||||
zero->nodeKind = AST_EXPR_PRIMITIVE;
|
||||
zero->expression.type = v->expression.type;
|
||||
zero->exprPrim.val = 0;
|
||||
|
||||
AST *binop = calloc(1, sizeof(ASTExprBinaryOp));
|
||||
binop->nodeKind = AST_EXPR_BINARY_OP;
|
||||
binop->expression.type = v->expression.type;
|
||||
binop->exprBinOp.operator = not ? BINOP_EQUAL : BINOP_NEQUAL;
|
||||
binop->exprBinOp.operands[0] = v;
|
||||
binop->exprBinOp.operands[1] = zero;
|
||||
|
||||
s->stmtJump.condition = binop;
|
||||
|
||||
ast_denoop(tlc, &s->stmtJump.condition);
|
||||
|
||||
s->stmtJump.condition = varify(tlc, chu, stmtPrev, s, e);
|
||||
this->effective = 1;
|
||||
|
||||
}
|
||||
@ -228,7 +131,7 @@ static void dumben_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *
|
||||
// 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 == SCOPEITEM_VAR && (!retval->exprVar.thing->data.var.precolored || !strchr(REG_CLASSES[retval->exprVar.thing->data.var.registerClass].rsN[retval->exprVar.thing->data.var.color], 'a'))))) {
|
||||
if(retval && (!is_xop(retval) || retval->nodeKind == AST_EXPR_PRIMITIVE || (retval->nodeKind == AST_EXPR_VAR && retval->exprVar.thing->kind == SCOPEITEM_VAR && !strchr(REG_CLASSES[retval->exprVar.thing->data.var.registerClass].rsN[retval->exprVar.thing->data.var.color], 'a')))) {
|
||||
|
||||
retval = s->stmtReturn.val = varify(tlc, chu, stmtPrev, s, retval);
|
||||
this->effective = 1;
|
||||
@ -474,8 +377,8 @@ static void pre_dumb_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, A
|
||||
|
||||
ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
|
||||
offset->nodeKind = AST_EXPR_PRIMITIVE;
|
||||
offset->type = type_u(x86_max_gpr_size());
|
||||
offset->val = 4 + i * x86_max_gpr_size();
|
||||
offset->type = primitive_parse("u32");
|
||||
offset->val = 4 + i * 4;
|
||||
offset->stackGrowth = true;
|
||||
|
||||
ASTExprBinaryOp *sum = calloc(1, sizeof(*sum));
|
||||
@ -604,19 +507,13 @@ static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST
|
||||
*nptr = n->exprBinOp.operands[0];
|
||||
|
||||
*success = true;
|
||||
} else if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_NOT && n->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && binop_comp_opposite(n->exprUnOp.operand->exprBinOp.operator) != BINOP_WTF) {
|
||||
} else if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_NOT && n->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP) {
|
||||
// Turn `!(a op b)` to `(a !op b)`
|
||||
|
||||
n->exprUnOp.operand->exprBinOp.operator = binop_comp_opposite(n->exprUnOp.operand->exprBinOp.operator);
|
||||
|
||||
*nptr = n->exprUnOp.operand;
|
||||
|
||||
*success = true;
|
||||
} else if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_NOT && n->exprUnOp.operand->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operand->exprUnOp.operator == UNOP_NOT) {
|
||||
// Turn `!!x` to `x`
|
||||
|
||||
*nptr = n->exprUnOp.operand->exprUnOp.operand;
|
||||
|
||||
*success = true;
|
||||
} else if(n->nodeKind == AST_EXPR_CAST && n->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && n->exprCast.to->type == TYPE_TYPE_POINTER) {
|
||||
// Turn (x as A*) into x, since all pointer types are identical in Nectar's AST
|
||||
@ -640,40 +537,7 @@ static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST
|
||||
*nptr = (AST*) prim;
|
||||
|
||||
*success = true;
|
||||
} else if(n->nodeKind == AST_EXPR_CAST) {
|
||||
// TODO: Move all compile-time constant casting from ast_cast_expr to here.
|
||||
|
||||
AST *what = n->exprCast.what;
|
||||
Type *to = n->exprCast.to;
|
||||
|
||||
if(what->nodeKind == AST_EXPR_PRIMITIVE && (to->type == TYPE_TYPE_PRIMITIVE || to->type == TYPE_TYPE_POINTER)) {
|
||||
ASTExprPrimitive *ret = calloc(1, 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);
|
||||
}
|
||||
|
||||
*nptr = ret;
|
||||
|
||||
*success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ast_denoop(AST *tlc, AST **node) {
|
||||
if(!node) {
|
||||
node = &tlc;
|
||||
}
|
||||
|
||||
struct DenoopState state = {.targetTLC = tlc};
|
||||
do {
|
||||
state.success = false;
|
||||
generic_visitor(node, NULL, NULL, tlc, tlc, &state, denoop_visitor, NULL);
|
||||
} while(state.success);
|
||||
}
|
||||
|
||||
void dumben_pre(AST *tlc) {
|
||||
@ -688,9 +552,25 @@ void dumben_pre(AST *tlc) {
|
||||
}
|
||||
}
|
||||
|
||||
ast_denoop(tlc, NULL);
|
||||
if(ntc_get_int("pdbg")) {
|
||||
char *astdump = ast_dump(tlc);
|
||||
fprintf(stderr, "### BEFORE DENOOP ###\n%s\n", astdump);
|
||||
free(astdump);
|
||||
}
|
||||
|
||||
struct DenoopState state = {.targetTLC = tlc};
|
||||
do {
|
||||
state.success = false;
|
||||
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, denoop_visitor, NULL);
|
||||
} while(state.success);
|
||||
|
||||
ast_commutativity_pass(tlc);
|
||||
|
||||
if(ntc_get_int("pdbg")) {
|
||||
char *astdump = ast_dump(tlc);
|
||||
fprintf(stderr, "### AFTER DENOOP ###\n%s\n", astdump);
|
||||
free(astdump);
|
||||
}
|
||||
}
|
||||
|
||||
void dumben_go(AST* tlc) {
|
||||
|
Loading…
Reference in New Issue
Block a user