aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-08 11:26:42 +0200
committerhachem <im@hachem.wtf>2026-09-08 11:26:42 +0200
commit21be45a904a0afc767214298721bd2bbce19a054 (patch)
tree4e97ae690457f82cbedd30bb8f7cf67898e578a5 /src
parent8d73e3f2cdba9ce3591bab5c02e5d90ce62a8de3 (diff)
feat: add signed memory loads
Diffstat (limited to 'src')
-rw-r--r--src/codegen/nasm.c64
-rw-r--r--src/lexer/lexer.c2
-rw-r--r--src/lexer/lexer.h1
-rw-r--r--src/parser/ast.c3
-rw-r--r--src/parser/ast.h9
-rw-r--r--src/sema/sema.c18
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;