diff options
| author | hachem <im@hachem.wtf> | 2026-09-08 11:26:42 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-09-08 11:26:42 +0200 |
| commit | 21be45a904a0afc767214298721bd2bbce19a054 (patch) | |
| tree | 4e97ae690457f82cbedd30bb8f7cf67898e578a5 /src | |
| parent | 8d73e3f2cdba9ce3591bab5c02e5d90ce62a8de3 (diff) | |
feat: add signed memory loads
Diffstat (limited to 'src')
| -rw-r--r-- | src/codegen/nasm.c | 64 | ||||
| -rw-r--r-- | src/lexer/lexer.c | 2 | ||||
| -rw-r--r-- | src/lexer/lexer.h | 1 | ||||
| -rw-r--r-- | src/parser/ast.c | 3 | ||||
| -rw-r--r-- | src/parser/ast.h | 9 | ||||
| -rw-r--r-- | src/sema/sema.c | 18 |
6 files changed, 94 insertions, 3 deletions
diff --git a/src/codegen/nasm.c b/src/codegen/nasm.c index 8bcdda3..198cb5c 100644 --- a/src/codegen/nasm.c +++ b/src/codegen/nasm.c @@ -14,6 +14,10 @@ static void emit_const_expr(struct Expr* expr, FILE* out) case EXPR_PRIMARY: fprintf(out, "%.*s", (int)expr->primary.token.length, expr->primary.token.start); break; + case EXPR_UNARY: + fprintf(out, "%.*s", (int)expr->unary.op.length, expr->unary.op.start); + emit_const_expr(expr->unary.operand, out); + break; case EXPR_BINARY: emit_const_expr(expr->binary.left, out); fprintf(out, " %.*s ", (int)expr->binary.op.length, expr->binary.op.start); @@ -370,6 +374,14 @@ static bool fold_const(struct Program* program, struct Expr* expr, uint64_t* out return fold_const(program, program->consts[i].value, out); return false; } + case EXPR_UNARY: + { + uint64_t value; + if (!fold_const(program, expr->unary.operand, &value)) + return false; + *out = 0 - value; + return true; + } case EXPR_BINARY: { uint64_t left; @@ -470,6 +482,14 @@ static bool emit_operand(struct Emitter* emitter, struct Expr* expr) fprintf(emitter->out, ".%.*s", (int)expr->member.member.length, expr->member.member.start); return true; } + case EXPR_UNARY: + { + uint64_t value; + if (!fold_const(emitter->program, expr, &value)) + return false; + fprintf(emitter->out, "%lld", (long long)value); + return true; + } case EXPR_BINARY: case EXPR_DEREF: return false; @@ -536,10 +556,22 @@ static bool expr_supported(struct Emitter* emitter, struct Expr* expr) case EXPR_MEMBER: case EXPR_DEREF: return true; + case EXPR_UNARY: + { + uint64_t value; + return fold_const(emitter->program, expr, &value); + } case EXPR_BINARY: if (expr->binary.right->kind != EXPR_PRIMARY && - expr->binary.right->kind != EXPR_MEMBER) + expr->binary.right->kind != EXPR_MEMBER && + expr->binary.right->kind != EXPR_UNARY) return false; + if (expr->binary.right->kind == EXPR_UNARY) + { + uint64_t value; + if (!fold_const(emitter->program, expr->binary.right, &value)) + return false; + } if (expr->binary.right->kind == EXPR_PRIMARY && is_buffer_name(emitter, expr->binary.right->primary.token)) return false; @@ -549,10 +581,35 @@ static bool expr_supported(struct Emitter* emitter, struct Expr* expr) return false; } -// dst = [address], zero-extending narrower loads into the full register +// dst = [address], extending narrower loads into the full register. Unsigned +// loads zero-extend (movzx / a 32-bit mov); signed loads sign-extend (movsx, +// or movsxd for a dword). static void emit_load(struct Emitter* emitter, const char* dst, struct DerefExpr* deref) { FILE* out = emitter->out; + if (deref->is_signed) + { + switch (deref->size) + { + case STORE_SIZE_BYTE: + fprintf(out, "\tmovsx %s, byte [", dst); + break; + case STORE_SIZE_WORD: + fprintf(out, "\tmovsx %s, word [", dst); + break; + case STORE_SIZE_DWORD: + fprintf(out, "\tmovsxd %s, dword [", dst); + break; + default: + fprintf(out, "\tmov %s, [", dst); + break; + } + + emit_operand(emitter, deref->address); + fprintf(out, "]\n"); + return; + } + switch (deref->size) { case STORE_SIZE_BYTE: @@ -1073,6 +1130,9 @@ static void collect_floats_expr(struct FloatTable* floats, struct Expr* expr) case EXPR_PRIMARY: collect_float(floats, expr->primary.token); break; + case EXPR_UNARY: + collect_floats_expr(floats, expr->unary.operand); + break; case EXPR_BINARY: collect_floats_expr(floats, expr->binary.left); collect_floats_expr(floats, expr->binary.right); diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index e08a458..4d468e6 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -45,6 +45,7 @@ static enum TokenType identifier_type(const char* start, size_t length) { "word", 4, TOKEN_WORD }, { "dword", 5, TOKEN_DWORD }, { "qword", 5, TOKEN_QWORD }, + { "signed", 6, TOKEN_SIGNED }, }; for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i += 1) @@ -273,6 +274,7 @@ const char* token_type_name(enum TokenType type) case TOKEN_WORD: return "word"; case TOKEN_DWORD: return "dword"; case TOKEN_QWORD: return "qword"; + case TOKEN_SIGNED: return "signed"; case TOKEN_EQUAL: return "equal"; case TOKEN_PLUS: return "plus"; case TOKEN_MINUS: return "minus"; diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index 2bc43e0..73de02f 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -26,6 +26,7 @@ enum TokenType TOKEN_WORD, TOKEN_DWORD, TOKEN_QWORD, + TOKEN_SIGNED, TOKEN_EQUAL, TOKEN_PLUS, diff --git a/src/parser/ast.c b/src/parser/ast.c index 9c78d1f..63d8a08 100644 --- a/src/parser/ast.c +++ b/src/parser/ast.c @@ -11,6 +11,9 @@ void free_expr(struct Expr* expr) { case EXPR_PRIMARY: break; + case EXPR_UNARY: + free_expr(expr->unary.operand); + break; case EXPR_BINARY: free_expr(expr->binary.left); free_expr(expr->binary.right); diff --git a/src/parser/ast.h b/src/parser/ast.h index 75d0bdd..288bddf 100644 --- a/src/parser/ast.h +++ b/src/parser/ast.h @@ -58,6 +58,7 @@ struct Param enum ExprKind { EXPR_PRIMARY, + EXPR_UNARY, EXPR_BINARY, EXPR_MEMBER, EXPR_DEREF, @@ -68,6 +69,12 @@ struct PrimaryExpr struct Token token; }; +struct UnaryExpr +{ + struct Token op; + struct Expr* operand; +}; + struct BinaryExpr { struct Expr* left; @@ -83,6 +90,7 @@ struct MemberExpr struct DerefExpr { + bool is_signed; enum StoreSize size; struct Expr* address; }; @@ -93,6 +101,7 @@ struct Expr union { struct PrimaryExpr primary; + struct UnaryExpr unary; struct BinaryExpr binary; struct MemberExpr member; struct DerefExpr deref; diff --git a/src/sema/sema.c b/src/sema/sema.c index 6505077..748ccbe 100644 --- a/src/sema/sema.c +++ b/src/sema/sema.c @@ -103,10 +103,11 @@ static bool is_program_const(const struct Program* program, struct Token name) return false; } -static struct Token first_token(struct Expr* expr) +static struct Token first_token(const struct Expr* expr) { switch (expr->kind) { + case EXPR_UNARY: return expr->unary.op; case EXPR_BINARY: return first_token(expr->binary.left); case EXPR_MEMBER: return first_token(expr->member.object); case EXPR_DEREF: return first_token(expr->deref.address); @@ -116,6 +117,9 @@ static struct Token first_token(struct Expr* expr) static bool check_const_value(struct Source source, struct Program* program, struct Expr* expr) { + if (expr->kind == EXPR_UNARY) + return check_const_value(source, program, expr->unary.operand); + if (expr->kind == EXPR_BINARY) { bool left = check_const_value(source, program, expr->binary.left); @@ -319,9 +323,18 @@ static void check_expr(struct RefCheck* check, const struct Expr* expr) check_expr(check, expr->binary.left); check_expr(check, expr->binary.right); break; + case EXPR_UNARY: + check_expr(check, expr->unary.operand); + break; case EXPR_DEREF: { const struct Expr* address = expr->deref.address; + if (expr->deref.is_signed + && expr->deref.size != STORE_SIZE_BYTE + && expr->deref.size != STORE_SIZE_WORD + && expr->deref.size != STORE_SIZE_DWORD) + ref_error(check, first_token(expr), "signed load requires a byte, word, or dword size"); + if (address->kind == EXPR_PRIMARY && (is_register(check, address->primary.token) || is_param(check, address->primary.token))) break; @@ -406,6 +419,9 @@ static void check_stack_size(struct RefCheck* check, struct Expr* expr) check_stack_size(check, expr->binary.left); check_stack_size(check, expr->binary.right); break; + case EXPR_UNARY: + check_stack_size(check, expr->unary.operand); + break; case EXPR_MEMBER: { const struct Expr* object = expr->member.object; |
