Compare commits

...

5 Commits

Author SHA1 Message Date
Mid
cdcc1f6d3d Update DOCUMENTATION.md 2025-09-13 11:21:14 +03:00
Mid
2771609049 Add logical op tests 2025-09-13 11:20:11 +03:00
Mid
d0262c586e So many bug fixes like omg why didn't u use bp for stacc idiot 2025-09-13 11:19:49 +03:00
Mid
13333c971a Add && and || 2025-09-13 11:17:11 +03:00
Mid
b4272a67d1 Add more deepcopies 2025-09-13 11:14:30 +03:00
19 changed files with 389 additions and 136 deletions

View File

@ -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. 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.
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.
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,6 +32,12 @@ 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.
@ -173,7 +179,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 this abstraction assumes 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 we gave up finding any patterns and assume the CPU to be a soup.
(64-bit is not considered)

6
examples/LogicalAnd.nct Normal file
View File

@ -0,0 +1,6 @@
u16 x = 0;
u16 y = 5;
if(x && y) {
u16 z = 9;
}

View File

@ -0,0 +1,7 @@
u16 x = 0;
u16 y = 5;
u16 z = 9;
if(x && y || z) {
u16 w = 15;
}

6
examples/LogicalOr.nct Normal file
View File

@ -0,0 +1,6 @@
u16 x = 0;
u16 y = 5;
if(x || y) {
u16 z = 9;
}

View File

@ -182,6 +182,12 @@ 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();

View File

@ -73,6 +73,9 @@ typedef enum ENUMPAK {
BINOP_LEQUAL = 13,
BINOP_GEQUAL = 14,
BINOP_LOGICAL_AND = 15,
BINOP_LOGICAL_OR = 16,
BINOP_WTF = 999,
} BinaryOp;

View File

@ -129,6 +129,12 @@ 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();
}

View File

@ -5,6 +5,7 @@
#include"scope.h"
#include<assert.h>
#include<stdlib.h>
#include"x86/arch.h"
struct Spill2StackState {
AST *targetTLC;
@ -30,7 +31,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 = primitive_parse("u32");
rsp->type = type_u(arch_gpr_size());
ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
offset->nodeKind = AST_EXPR_PRIMITIVE;

View File

@ -153,6 +153,10 @@ 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++) {

View File

@ -143,9 +143,17 @@ 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;

View File

@ -27,7 +27,9 @@ 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,

View File

@ -80,6 +80,8 @@ 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);

View File

@ -38,9 +38,6 @@ 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;
@ -143,36 +140,6 @@ 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;
}
@ -250,7 +217,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
}
if(lOP == 6) {
if(lOP == 7) {
AST *e = NULL;
if(peek(P, 0).type == TOKEN_NUMBER) {
@ -261,7 +228,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
ASTExprStackPointer *ret = alloc_node(P, sizeof(*ret));
ret->nodeKind = AST_EXPR_STACK_POINTER;
ret->type = primitive_parse("u32");
ret->type = type_u(arch_sp_size());
e = (AST*) ret;
} else if(!strcmp(peek(P, 0).content, "@salloc")) {
@ -288,7 +255,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
ret->ofExpr = nct_parse_expression(P, lOP - 1);
}
ret->type = primitive_parse("u32");
ret->type = type_u(arch_gpr_size());
e = (AST*) ret;
} else {
@ -347,7 +314,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
return e;
} else if(lOP == 5) {
} else if(lOP == 6) {
if(maybe(P, TOKEN_STAR)) {
ASTExprUnaryOp *astop = alloc_node(P, sizeof(*astop));
astop->nodeKind = AST_EXPR_UNARY_OP;
@ -401,7 +368,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 == 5) {
AST *ret = nct_parse_expression(P, lOP + 1);
while(peek(P, 0).type == TOKEN_PAREN_L || peek(P, 0).type == TOKEN_SQUAREN_L) {
@ -494,7 +461,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 = primitive_parse("u32");
scale->type = type_u(arch_gpr_size());
scale->val = typesize;
ASTExprBinaryOp *mul = alloc_node(P, sizeof(*mul));
@ -524,7 +491,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
return ret;
} else if(lOP == 3) {
} else if(lOP == 4) {
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) {
@ -564,7 +531,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
return ret;
} else if(lOP == 2) {
} else if(lOP == 3) {
AST *ret = nct_parse_expression(P, lOP + 1);
if(
@ -603,7 +570,7 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
return ret;
} else if(lOP == 1) {
} else if(lOP == 2) {
AST *ret = nct_parse_expression(P, lOP + 1);
while(maybe(P, TOKEN_AS)) {
@ -611,6 +578,35 @@ 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);
@ -644,8 +640,6 @@ AST *nct_parse_expression(Parser *P, int lOP) {
}
}
//ret = ast_expression_optimize(ret);
return ret;
}
#ifdef DEBUG
@ -1108,11 +1102,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);
@ -1120,8 +1114,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];
@ -1136,7 +1130,7 @@ static void nct_parse_statement(Parser *P) {
P->guardedVarCount = 0;
free(P->guardedVars);
P->guardedVars = NULL;*/
}
//}
return;
} else if(maybe(P, TOKEN_BREAK)) {

View File

@ -19,7 +19,6 @@ 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;

View File

@ -5,6 +5,7 @@
#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
@ -114,4 +115,10 @@ 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

View File

@ -27,9 +27,15 @@ RegisterClass REG_CLASSES[] = {
},
[REG_CLASS_IA16_PTRS] = {
.rMask = HWR_IND | HWR_BX,
.rs = {HWR_DI, HWR_SI, HWR_BX, HWR_BP},
.rsN = {"di", "si", "bx", "bp"},
.rsS = {2, 2, 2, 2},
.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},
},
[REG_CLASS_DATASEGS] = {
.rMask = HWR_SEGREGS,
@ -50,3 +56,7 @@ bool arch_verify_target() {
int arch_ptr_size() {
return x86_ia16() ? 2 : 4;
}
void arch_init() {
}

View File

@ -60,45 +60,10 @@ typedef struct RegisterClass {
#define REG_CLASS_16_32 2
#define REG_CLASS_IND 3
#define REG_CLASS_IA16_PTRS 4
#define REG_CLASS_DATASEGS 5
#define REG_CLASS_IA16_USEABLE 5
#define REG_CLASS_DATASEGS 6
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,
@ -151,6 +116,10 @@ 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
@ -176,11 +145,10 @@ static inline int is_xop(AST *e) {
}
if(c->nodeKind == AST_EXPR_VAR && c->exprVar.thing->kind == SCOPEITEM_VAR) {
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) {
if(x86_ia16() && !x86_is_marked_ptr(c->exprVar.thing)) {
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
return XOP_NOT_XOP;
}
}
return XOP_MEM;
} else if(
@ -190,8 +158,18 @@ 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) {
@ -307,3 +285,52 @@ 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();

View File

@ -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 == 4) {
reg_cast_up(&cls, &reg);
if(sz == x86_max_gpr_size()) {
reg_cast_to_gpr(&cls, &reg);
} else abort();
}
@ -185,13 +185,17 @@ 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, "esp");
pr = snprintf(ret, XOPBUFSZ, x86_ia16() ? "sp" : "esp");
} else if(e->nodeKind == AST_EXPR_VAR) {
ScopeItem *v = e->exprVar.thing;
@ -243,8 +247,14 @@ 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) {
@ -352,15 +362,20 @@ 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], 4));
printf("push %s\n", xop_sz(cg->tlc, e->exprCall.args[i], arch_gpr_size()));
argSize += (type_size(e->exprCall.args[i]->expression.type) + 3) & ~3;
}
@ -371,10 +386,21 @@ void cg_chunk(CGState *cg, AST *a) {
printf("call %s\n", xop(cg->tlc, e->exprCall.what));
}
if(argSize) printf("add esp, %lu\n", argSize);
if(argSize) {
if(x86_ia16()) {
printf("add sp, %lu\n", argSize);
} else {
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) {
@ -455,8 +481,8 @@ void cg_chunk(CGState *cg, AST *a) {
} else {
const char *dest = xop_sz(cg->tlc, s->stmtAssign.what, 4);
const char *src = xop_sz(cg->tlc, s->stmtAssign.to->exprCast.what, 4);
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());
if(strcmp(dest, src)) {
printf("mov %s, %s\n", dest, src);
@ -520,8 +546,21 @@ 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");
@ -735,7 +774,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 = REG_CLASS_NOT_8;
tlc->chunk.vars[v]->data.var.registerClass = x86_ia16() ? REG_CLASS_IA16_USEABLE : REG_CLASS_NOT_8;
}
}
}

View File

@ -117,9 +117,106 @@ 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 {
s->stmtJump.condition = varify(tlc, chu, stmtPrev, s, e);
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);
this->effective = 1;
}
@ -131,7 +228,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 && !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 && (!retval->exprVar.thing->data.var.precolored || !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;
@ -377,8 +474,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 = primitive_parse("u32");
offset->val = 4 + i * 4;
offset->type = type_u(x86_max_gpr_size());
offset->val = 4 + i * x86_max_gpr_size();
offset->stackGrowth = true;
ASTExprBinaryOp *sum = calloc(1, sizeof(*sum));
@ -507,13 +604,19 @@ 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) {
} 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) {
// 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
@ -537,7 +640,40 @@ 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) {
@ -552,25 +688,9 @@ void dumben_pre(AST *tlc) {
}
}
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_denoop(tlc, NULL);
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) {