From 1dfa2971999b4adf36a33866b0b07af07188da08 Mon Sep 17 00:00:00 2001 From: Carson Fleming Date: Tue, 31 Mar 2026 16:30:29 -1000 Subject: math n shi --- ast.c | 19 +++++++ ast.h | 23 +++++++- codegen.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++--------- parser.c | 87 +++++++++++++++++++++++++---- test/manycalls.c | 24 ++++++++ test/math.c | 3 + 6 files changed, 284 insertions(+), 39 deletions(-) create mode 100644 test/manycalls.c create mode 100644 test/math.c diff --git a/ast.c b/ast.c index 7837cf8..7f97948 100644 --- a/ast.c +++ b/ast.c @@ -75,6 +75,19 @@ static void call_destroy(struct call_node* node) { } } +static void unary_destroy(struct unary_node* node) { + expr_destroy(node->expr); + free(node->expr); +} + +static void binary_destroy(struct binary_node* node) { + expr_destroy(node->lhs); + free(node->lhs); + + expr_destroy(node->rhs); + free(node->rhs); +} + static void expr_destroy(struct expr_node* node) { switch (node->type) { case EXPR_INT_LIT: @@ -93,6 +106,12 @@ static void expr_destroy(struct expr_node* node) { case EXPR_CALL: call_destroy(&node->as._call); break; + case EXPR_UNARY: + unary_destroy(&node->as._unary); + break; + case EXPR_BINARY: + binary_destroy(&node->as._binary); + break; } } diff --git a/ast.h b/ast.h index 2eabb02..4837c2b 100644 --- a/ast.h +++ b/ast.h @@ -52,7 +52,6 @@ struct lval_node { } as; }; -/* TODO: add to expression, parse */ struct assign_node { struct lval_node lval; struct expr_node* rval; @@ -64,6 +63,24 @@ struct call_node { struct expr_node* args_head; }; +struct unary_node { + enum { + UNARY_NEG, + } op; + struct expr_node* expr; +}; + +struct binary_node { + enum { + BINARY_ADD, + BINARY_SUB, + BINARY_MUL, + BINARY_DIV, + } op; + struct expr_node* lhs; + struct expr_node* rhs; +}; + struct expr_node { enum { EXPR_INT_LIT, @@ -73,6 +90,8 @@ struct expr_node { EXPR_VAR_REF, EXPR_ASSIGN, EXPR_CALL, + EXPR_UNARY, + EXPR_BINARY, } type; union { struct int_lit_node _int_lit; @@ -82,6 +101,8 @@ struct expr_node { struct var_ref_node _var_ref; struct assign_node _assign; struct call_node _call; + struct unary_node _unary; + struct binary_node _binary; } as; struct expr_node* next; diff --git a/codegen.c b/codegen.c index 216d41a..662097e 100644 --- a/codegen.c +++ b/codegen.c @@ -23,6 +23,11 @@ static const struct storage_location RV_LOC = { .type = STO_REG, .reg = &RAX, }; +static const struct storage_location MULDIV_LOC = RV_LOC; +static const struct storage_location MULDIV_OVERFLOW_LOC = { + .type = STO_REG, + .reg = &RDX, +}; #define RETURN_LABEL_FMT "%s@coda" #define FULL_REG_SZ 8 @@ -30,6 +35,42 @@ static const struct storage_location RV_LOC = { static struct scope* scope; static const struct fn_decl_node* active_fn; +static struct lval_def allocate_register(unsigned long long sz) { + return (struct lval_def) { + .loc = { + .type = STO_REG, + .reg = &RAX, /* TODO: no real register coloring happening LOL */ + }, + .sz = sz, + }; +} + +static struct lval_def allocate_stack(FILE* outfile, unsigned long long sz) { + fprintf(outfile, "\tsub rsp, %llu\n", sz); + scope->bp_offset += sz; + return (struct lval_def) { + .loc = { + .type = STO_STACK, + .bp_offset = scope->bp_offset, + }, + .sz = sz, + }; +} + +static struct lval_def allocate_temporary( + FILE* outfile, + unsigned long long sz +) { + return allocate_stack(outfile, sz); +} + +static void deallocate_temporary(FILE* outfile, const struct lval_def* tmp) { + if (tmp->loc.type == STO_STACK) { + fprintf(outfile, "\tadd rsp, %llu\n", tmp->sz); + scope->bp_offset -= tmp->sz; + } +} + static void emit_storage_loc( FILE* outfile, const struct storage_location* loc, @@ -83,6 +124,13 @@ static bool locs_equal( CGEN_PANIC("unhandled storage type case"); } +static void emit_size_const(FILE* outfile, unsigned long long sz) { + if (sz > 4) fprintf(outfile, "qword "); + else if (sz > 2) fprintf(outfile, "dword "); + else if (sz > 1) fprintf(outfile, "word "); + else fprintf(outfile, "byte "); +} + static void emit_mov( FILE* outfile, const struct lval_def* dst, @@ -107,25 +155,14 @@ static void emit_mov( case STO_STACK: if (src->type == STO_STACK) { /* `mov mem, mem` is illegal in x86_64 */ - emit_mov( - outfile, - &(struct lval_def) { - .loc = RV_LOC, - .sz = dst->sz, - }, - src); - emit_mov(outfile, dst, &RV_LOC); + struct lval_def tmp = allocate_register(dst->sz); + emit_mov(outfile, &tmp, src); + emit_mov(outfile, dst, &tmp.loc); return; } fprintf(outfile, "\tmov "); - if (src->type == STO_IMM) { - /* must specify size to move immediates into memory*/ - if (dst->sz > 4) fprintf(outfile, "qword "); - else if (dst->sz > 2) fprintf(outfile, "dword "); - else if (dst->sz > 1) fprintf(outfile, "word "); - else fprintf(outfile, "byte "); - } + if (src->type == STO_IMM) emit_size_const(outfile, dst->sz); emit_storage_loc(outfile, &dst->loc, dst->sz); fprintf(outfile, ", "); @@ -148,20 +185,11 @@ static unsigned long long get_type_size(const struct type_node* type) { return type->def.sz; } -static struct lval_def make_stack_lval( +static inline struct lval_def make_stack_lval( FILE* outfile, const struct type_node* type ) { - unsigned long long type_sz = get_type_size(type); - fprintf(outfile, "\tsub rsp, %llu\n", type_sz); - scope->bp_offset += type_sz; - return (struct lval_def) { - .loc = { - .type = STO_STACK, - .bp_offset = scope->bp_offset, - }, - .sz = type_sz, - }; + return allocate_stack(outfile, get_type_size(type)); } static void emit_expr( @@ -336,7 +364,7 @@ static void emit_call( emit_mov(outfile, dst, &RV_LOC); } - /* pop our argument temporaries off the stack */ + /* mass-pop our argument temporaries off the stack */ scope->bp_offset = orig_bp_offset; if (orig_bp_offset > 0) fprintf(outfile, "\tlea rsp, [rbp - %llu]\n", orig_bp_offset); @@ -344,6 +372,85 @@ static void emit_call( fprintf(outfile, "\tmov rsp, rbp\n"); } +static void emit_unary( + FILE* outfile, + const struct unary_node* node, + const struct lval_def* dst +) { + emit_expr(outfile, node->expr, dst); + if (dst == NULL) return; + + switch (node->op) { + case UNARY_NEG: + fprintf(outfile, "\tneg "); + if (dst->loc.type == STO_STACK) emit_size_const(outfile, dst->sz); + emit_storage_loc(outfile, &dst->loc, dst->sz); + fprintf(outfile, "\n"); + break; + } +} + +/* TODO: tighten up/enforce with regard to up-casting smaller operands */ +static void emit_binary( + FILE* outfile, + const struct binary_node* node, + const struct lval_def* dst +) { + if (dst == NULL) { + emit_expr(outfile, node->lhs, NULL); + emit_expr(outfile, node->rhs, NULL); + return; + } + + /* LHS goes in RAX explicitly because imul and idiv are weird */ + struct lval_def rhs_dst = allocate_temporary(outfile, dst->sz); + emit_expr(outfile, node->rhs, &rhs_dst); + struct lval_def lhs_dst = (struct lval_def) { + .loc = MULDIV_LOC, + .sz = dst->sz, + }; + emit_expr(outfile, node->lhs, &lhs_dst); + + switch (node->op) { + case BINARY_ADD: + fprintf(outfile, "\tadd "); + emit_storage_loc(outfile, &lhs_dst.loc, dst->sz); + fprintf(outfile, ", "); + break; + case BINARY_SUB: + fprintf(outfile, "\tsub "); + emit_storage_loc(outfile, &lhs_dst.loc, dst->sz); + fprintf(outfile, ", "); + break; + case BINARY_MUL: + fprintf(outfile, "\timul "); + if (rhs_dst.loc.type == STO_STACK) + emit_size_const(outfile, dst->sz); + break; + case BINARY_DIV: + /* nothing in the top half reg */ + fprintf(outfile, "\txor "); + emit_storage_loc(outfile, &MULDIV_OVERFLOW_LOC, FULL_REG_SZ); + fprintf(outfile, ", "); + emit_storage_loc(outfile, &MULDIV_OVERFLOW_LOC, FULL_REG_SZ); + fprintf(outfile, "\n"); + + fprintf(outfile, "\tidiv "); + if (rhs_dst.loc.type == STO_STACK) + emit_size_const(outfile, dst->sz); + break; + } + + emit_storage_loc(outfile, &rhs_dst.loc, dst->sz); + fprintf(outfile, "\n"); + + /* TODO: deal with RDX overflow shit for imul and idiv */ + emit_mov(outfile, dst, &lhs_dst.loc); + + deallocate_temporary(outfile, &lhs_dst); + deallocate_temporary(outfile, &rhs_dst); +} + static void emit_expr( FILE* outfile, const struct expr_node* node, @@ -371,6 +478,12 @@ static void emit_expr( case EXPR_CALL: emit_call(outfile, &node->as._call, dst); break; + case EXPR_UNARY: + emit_unary(outfile, &node->as._unary, dst); + break; + case EXPR_BINARY: + emit_binary(outfile, &node->as._binary, dst); + break; } } diff --git a/parser.c b/parser.c index f9c7047..6a8ad31 100644 --- a/parser.c +++ b/parser.c @@ -107,21 +107,18 @@ static void parse_var_ref(struct var_ref_node* p_node) { p_node->ident = tok.data.ident; } -static void expr_to_lval(struct lval_node* l_node, struct expr_node* e_node) { - switch (e_node->type) { +static void parse_expr_assign(struct expr_node* p_node) { + switch (p_node->type) { case EXPR_VAR_REF: - *l_node = (struct lval_node) { + p_node->as._assign.lval = (struct lval_node) { .type = LVAL_VAR_REF, - .as._var_ref = e_node->as._var_ref, + .as._var_ref = p_node->as._var_ref, }; return; default: PARSER_PANIC("expression is not assignable"); } -} -static void parse_expr_assign(struct expr_node* p_node) { - expr_to_lval(&p_node->as._assign.lval, p_node); p_node->type = EXPR_ASSIGN; p_node->as._assign.rval = protected_alloc(sizeof(struct expr_node)); @@ -167,6 +164,59 @@ static void parse_expr_call(struct expr_node* p_node) { expect(TK_RPAREN); } +static void parse_unary(struct unary_node* p_node) { + peek_or_panic(); + switch (tok.type) { + case TK_NEG: + expect(TK_NEG); + p_node->op = UNARY_NEG; + break; + default: + PARSER_PANIC("expected unary operator"); + } + + p_node->expr = protected_alloc(sizeof(struct expr_node)); + parse_expr(p_node->expr); +} + +static void parse_expr_binary(struct expr_node* p_node) { + struct expr_node* lhs = protected_alloc(sizeof(struct expr_node)); + struct expr_node* rhs = protected_alloc(sizeof(struct expr_node)); + + *lhs = *p_node; + *p_node = (struct expr_node) { + .type = EXPR_BINARY, + .as._binary = { + .lhs = lhs, + .rhs = rhs, + }, + }; + + peek_or_panic(); + switch (tok.type) { + case TK_PLUS: + expect(TK_PLUS); + p_node->as._binary.op = BINARY_ADD; + break; + case TK_NEG: + expect(TK_NEG); + p_node->as._binary.op = BINARY_SUB; + break; + case TK_STAR: + expect(TK_STAR); + p_node->as._binary.op = BINARY_MUL; + break; + case TK_DIV: + expect(TK_DIV); + p_node->as._binary.op = BINARY_DIV; + break; + default: + PARSER_PANIC("expected binary operator"); + } + + parse_expr(rhs); +} + static void parse_expr(struct expr_node* p_node) { peek_or_panic(); switch (tok.type) { @@ -175,6 +225,10 @@ static void parse_expr(struct expr_node* p_node) { parse_expr(p_node); expect(TK_RPAREN); break; + case TK_NEG: + p_node->type = EXPR_UNARY; + parse_unary(&p_node->as._unary); + break; case TK_INT_LIT: case TK_CHAR_LIT: case TK_FLOAT_LIT: @@ -190,10 +244,21 @@ static void parse_expr(struct expr_node* p_node) { } peek_or_panic(); - if (tok.type == TK_ASSIGN) - parse_expr_assign(p_node); - else if (tok.type == TK_LPAREN) - parse_expr_call(p_node); + switch (tok.type) { + case TK_ASSIGN: + parse_expr_assign(p_node); + break; + case TK_LPAREN: + parse_expr_call(p_node); + break; + case TK_PLUS: + case TK_NEG: + case TK_STAR: + case TK_DIV: + parse_expr_binary(p_node); + break; + default: + } } static void parse_var_decl(struct var_decl_node* p_node) { diff --git a/test/manycalls.c b/test/manycalls.c new file mode 100644 index 0000000..bfcc775 --- /dev/null +++ b/test/manycalls.c @@ -0,0 +1,24 @@ +int one() { + return 1; +} + +int two() { + return 2; +} + +int three() { + return 3; +} + +int four() { + return 4; +} + +int main() { + int d; + int a = one(); + int b = two(); + int c = three(); + d = four(); + return d; +} diff --git a/test/math.c b/test/math.c new file mode 100644 index 0000000..2131546 --- /dev/null +++ b/test/math.c @@ -0,0 +1,3 @@ +int main(int argc, char** argv) { + return argc * -5; +} -- cgit v1.2.3