aboutsummaryrefslogtreecommitdiff
path: root/src/nasm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nasm.c')
-rw-r--r--src/nasm.c105
1 files changed, 73 insertions, 32 deletions
diff --git a/src/nasm.c b/src/nasm.c
index 15f5f01..7a7d1e6 100644
--- a/src/nasm.c
+++ b/src/nasm.c
@@ -999,51 +999,64 @@ static void emit_float_operand(struct Emitter* emitter, const struct Expr* expr)
fprintf(emitter->out, "%.*s", (int)token.length, token.start);
}
-static void emit_if(struct Emitter* emitter, struct IfStatement* branch)
+// Emits the comparison for `left cmp right` and a jump to `target` taken when
+// the condition is false, so the code that follows runs when it is true. Both
+// if and while build on this. Returns false (after a TODO note) for a form that
+// isn't supported yet.
+static bool emit_branch_test(struct Emitter* emitter, struct Expr* left,
+ struct Token comparison, struct Expr* right, const char* target)
{
- bool is_float = value_is_float(emitter, branch->left) || value_is_float(emitter, branch->right);
- bool has_else = branch->else_count > 0;
-
- uint32_t id = emitter->label_id;
- emitter->label_id += 1;
-
- const char* target = has_else ? ".if_else_" : ".if_end_";
+ bool is_float = value_is_float(emitter, left) || value_is_float(emitter, right);
if (is_float)
{
- const char* jump = float_jump_if_false(branch->comparison.type);
- bool left_reg = branch->left->kind == EXPR_PRIMARY
- && is_float_register(resolve_register(emitter, branch->left->primary.token));
- if (jump == NULL || !left_reg || !value_is_float(emitter, branch->right))
+ const char* jump = float_jump_if_false(comparison.type);
+ bool left_reg = left->kind == EXPR_PRIMARY
+ && is_float_register(resolve_register(emitter, left->primary.token));
+ if (jump == NULL || !left_reg || !value_is_float(emitter, right))
{
- fprintf(emitter->out, "\t; TODO: unsupported if\n");
- return;
+ fprintf(emitter->out, "\t; TODO: unsupported condition\n");
+ return false;
}
fprintf(emitter->out, "\tucomisd ");
- emit_float_operand(emitter, branch->left);
+ emit_float_operand(emitter, left);
fprintf(emitter->out, ", ");
- emit_float_operand(emitter, branch->right);
- fprintf(emitter->out, "\n\t%s %s%u\n", jump, target, id);
+ emit_float_operand(emitter, right);
+ fprintf(emitter->out, "\n\t%s %s\n", jump, target);
+ return true;
}
- else
- {
- const char* jump = jump_if_false(branch->comparison.type);
- if (jump == NULL
- || branch->left->kind == EXPR_BINARY || branch->left->kind == EXPR_DEREF
- || branch->right->kind == EXPR_BINARY || branch->right->kind == EXPR_DEREF)
- {
- fprintf(emitter->out, "\t; TODO: unsupported if\n");
- return;
- }
- fprintf(emitter->out, "\tcmp ");
- emit_operand(emitter, branch->left);
- fprintf(emitter->out, ", ");
- emit_operand(emitter, branch->right);
- fprintf(emitter->out, "\n\t%s %s%u\n", jump, target, id);
+ const char* jump = jump_if_false(comparison.type);
+ if (jump == NULL
+ || left->kind == EXPR_BINARY || left->kind == EXPR_DEREF
+ || right->kind == EXPR_BINARY || right->kind == EXPR_DEREF)
+ {
+ fprintf(emitter->out, "\t; TODO: unsupported condition\n");
+ return false;
}
+ fprintf(emitter->out, "\tcmp ");
+ emit_operand(emitter, left);
+ fprintf(emitter->out, ", ");
+ emit_operand(emitter, right);
+ fprintf(emitter->out, "\n\t%s %s\n", jump, target);
+ return true;
+}
+
+static void emit_if(struct Emitter* emitter, struct IfStatement* branch)
+{
+ bool has_else = branch->else_count > 0;
+
+ uint32_t id = emitter->label_id;
+ emitter->label_id += 1;
+
+ char target[32];
+ snprintf(target, sizeof(target), ".if_%s_%u", has_else ? "else" : "end", id);
+
+ if (!emit_branch_test(emitter, branch->left, branch->comparison, branch->right, target))
+ return;
+
emit_block(emitter, branch->body, branch->body_count);
if (has_else)
@@ -1056,6 +1069,25 @@ static void emit_if(struct Emitter* emitter, struct IfStatement* branch)
fprintf(emitter->out, ".if_end_%u:\n", id);
}
+static void emit_while(struct Emitter* emitter, struct WhileStatement* loop)
+{
+ uint32_t id = emitter->label_id;
+ emitter->label_id += 1;
+
+ char target[32];
+ snprintf(target, sizeof(target), ".while_end_%u", id);
+
+ fprintf(emitter->out, ".while_%u:\n", id);
+
+ if (!emit_branch_test(emitter, loop->left, loop->comparison, loop->right, target))
+ return;
+
+ emit_block(emitter, loop->body, loop->body_count);
+
+ fprintf(emitter->out, "\tjmp .while_%u\n", id);
+ fprintf(emitter->out, ".while_end_%u:\n", id);
+}
+
static void emit_statement(struct Emitter* emitter, struct Statement* statement)
{
FILE* out = emitter->out;
@@ -1076,6 +1108,9 @@ static void emit_statement(struct Emitter* emitter, struct Statement* statement)
case STATEMENT_IF:
emit_if(emitter, &statement->branch);
break;
+ case STATEMENT_WHILE:
+ emit_while(emitter, &statement->loop);
+ break;
case STATEMENT_CALL:
emit_call(emitter, &statement->call);
break;
@@ -1161,6 +1196,12 @@ static void collect_floats_statement(struct FloatTable* floats, struct Statement
for (size_t i = 0; i < statement->branch.else_count; i += 1)
collect_floats_statement(floats, &statement->branch.else_body[i]);
break;
+ case STATEMENT_WHILE:
+ collect_floats_expr(floats, statement->loop.left);
+ collect_floats_expr(floats, statement->loop.right);
+ for (size_t i = 0; i < statement->loop.body_count; i += 1)
+ collect_floats_statement(floats, &statement->loop.body[i]);
+ break;
case STATEMENT_CALL:
for (size_t i = 0; i < statement->call.arg_count; i += 1)
collect_floats_expr(floats, statement->call.args[i]);