aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/nasm.c
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-31 16:24:28 +0200
committerhachem <im@hachem.wtf>2026-08-31 16:24:28 +0200
commitf8a2d02c75df62f74e5907696d5a99a024d59018 (patch)
tree7f374fb0e408ea2dab15dc628b7bb6b0cee0d9e8 /src/codegen/nasm.c
parentcaf904c9a8a30a44fb3fca6df93e97c5cabdb0c0 (diff)
feat: codegen for * and / in expressions and /= on any register
Diffstat (limited to 'src/codegen/nasm.c')
-rw-r--r--src/codegen/nasm.c49
1 files changed, 34 insertions, 15 deletions
diff --git a/src/codegen/nasm.c b/src/codegen/nasm.c
index 0db60dd..e55c791 100644
--- a/src/codegen/nasm.c
+++ b/src/codegen/nasm.c
@@ -209,25 +209,39 @@ static bool emit_operand(struct Emitter* emitter, struct Expr* expr)
return false;
}
+// idiv divides rdx:rax by its operand and leaves the quotient in rax, so a
+// division computes `dst = dst / divisor` through rax (clobbering rax and rdx).
+static void emit_division(struct Emitter* emitter, const char* dst, struct Expr* divisor)
+{
+ bool dst_is_rax = strcmp(dst, "rax") == 0;
+
+ if (!dst_is_rax)
+ fprintf(emitter->out, "\tmov rax, %s\n", dst);
+ fprintf(emitter->out, "\tcqo\n");
+ fprintf(emitter->out, "\tidiv ");
+ emit_operand(emitter, divisor);
+ fprintf(emitter->out, "\n");
+ if (!dst_is_rax)
+ fprintf(emitter->out, "\tmov %s, rax\n", dst);
+}
+
static void emit_divide(struct Emitter* emitter, struct AssignStatement* assign)
{
- struct Token target = resolve_register(emitter, assign->target);
- bool target_is_rax = target.length == 3 && memcmp(target.start, "rax", 3) == 0;
- if (assign->target_deref || !target_is_rax)
+ if (assign->target_deref)
{
fprintf(emitter->out, "\t; TODO: unsupported division\n");
return;
}
- fprintf(emitter->out, "\tcqo\n");
- fprintf(emitter->out, "\tidiv ");
- emit_operand(emitter, assign->value);
- fprintf(emitter->out, "\n");
+ struct Token target = resolve_register(emitter, assign->target);
+ char dst[32];
+ snprintf(dst, sizeof(dst), "%.*s", (int)target.length, target.start);
+ emit_division(emitter, dst, assign->value);
}
// an expression can be evaluated into a register when it is a single term
-// (primary or member), or a left-associative chain of '+'/'-' whose right
-// operands are plain operands (never a buffer or a nested binary)
+// (primary or member), or a left-associative chain of binary operators whose
+// right operands are plain operands (never a buffer or a nested binary)
static bool expr_supported(struct Emitter* emitter, struct Expr* expr)
{
switch (expr->kind)
@@ -236,13 +250,10 @@ static bool expr_supported(struct Emitter* emitter, struct Expr* expr)
case EXPR_MEMBER:
return true;
case EXPR_BINARY:
- if (expr->binary.op.type != TOKEN_PLUS &&
- expr->binary.op.type != TOKEN_MINUS)
- return false;
- if (expr->binary.right->kind != EXPR_PRIMARY &&
+ if (expr->binary.right->kind != EXPR_PRIMARY &&
expr->binary.right->kind != EXPR_MEMBER)
return false;
- if (expr->binary.right->kind == EXPR_PRIMARY &&
+ if (expr->binary.right->kind == EXPR_PRIMARY &&
is_buffer_name(emitter, expr->binary.right->primary.token))
return false;
return expr_supported(emitter, expr->binary.left);
@@ -257,7 +268,15 @@ static void emit_expr_into(struct Emitter* emitter, const char* dst, struct Expr
{
emit_expr_into(emitter, dst, expr->binary.left);
- const char* mnemonic = expr->binary.op.type == TOKEN_PLUS ? "add" : "sub";
+ if (expr->binary.op.type == TOKEN_SLASH)
+ {
+ emit_division(emitter, dst, expr->binary.right);
+ return;
+ }
+
+ const char* mnemonic =
+ expr->binary.op.type == TOKEN_PLUS ? "add" :
+ expr->binary.op.type == TOKEN_MINUS ? "sub" : "imul";
fprintf(emitter->out, "\t%s %s, ", mnemonic, dst);
emit_operand(emitter, expr->binary.right);
fprintf(emitter->out, "\n");