* [PATCH olang v1 2/2] codegen: x86_64: implement binary operations
2024-04-27 12:14 [PATCH olang v1 0/2] codegen: x86_64: implement binary operations Johnny Richard
2024-04-27 12:14 ` [PATCH olang v1 1/2] codegen: x86_64: extract function to emit expression Johnny Richard
@ 2024-04-27 12:14 ` Johnny Richard
2024-04-27 11:21 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-05-03 2:04 ` [PATCH olang v1 2/2] codegen: x86_64: implement binary operations Carlos Maniero
2024-08-25 13:52 ` [PATCH olang v1 0/2] " Johnny Richard
2 siblings, 2 replies; 6+ messages in thread
From: Johnny Richard @ 2024-04-27 12:14 UTC (permalink / raw)
To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard
In order to simplify most of the binary operation expressions we are
using stack to store previous execution. It makes the implementation
easy but inefficient.
We can optimize this codegen in a near future as soon as re get IR in
place.
This change also include a small fix on binary operation precedence for
TOKEN_CMP_EQ.
Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
examples/sum.ol | 3 +
src/codegen_linux_x86_64.c | 232 ++++++++++++++++++++++++++++++++++++-
src/parser.c | 2 +-
3 files changed, 232 insertions(+), 5 deletions(-)
create mode 100644 examples/sum.ol
diff --git a/examples/sum.ol b/examples/sum.ol
new file mode 100644
index 0000000..b724088
--- /dev/null
+++ b/examples/sum.ol
@@ -0,0 +1,3 @@
+fn main(): u32 {
+ return 1 + 4 + 60 + 4
+}
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 72db1c6..0accef4 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -23,6 +23,8 @@
#define SYS_exit (60)
+size_t label_index;
+
static void
codegen_linux_x86_64_emit_start_entrypoint(FILE *out);
@@ -32,6 +34,7 @@ codegen_linux_x86_64_emit_function(FILE *out, ast_fn_definition_t *fn);
void
codegen_linux_x86_64_emit_program(FILE *out, ast_node_t *node)
{
+ label_index = 0;
codegen_linux_x86_64_emit_start_entrypoint(out);
assert(node->kind == AST_NODE_PROGRAM);
@@ -56,6 +59,12 @@ codegen_linux_x86_64_emit_start_entrypoint(FILE *out)
fprintf(out, " syscall\n");
}
+static size_t
+codegen_linux_x86_64_get_next_label(void)
+{
+ return ++label_index;
+}
+
static void
codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
{
@@ -63,14 +72,229 @@ codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
case AST_NODE_LITERAL: {
ast_literal_t literal_u32 = expr_node->data.as_literal;
assert(literal_u32.kind == AST_LITERAL_U32);
- uint32_t exit_code = literal_u32.value.as_u32;
+ uint32_t n = literal_u32.value.as_u32;
- fprintf(out, " mov $%d, %%eax\n", exit_code);
+ fprintf(out, " mov $%d, %%rax\n", n);
return;
}
- case AST_NODE_BINARY_OP:
+ case AST_NODE_BINARY_OP: {
+ ast_binary_op_t bin_op = expr_node->data.as_bin_op;
+ switch (bin_op.kind) {
+ case AST_BINOP_ADDITION: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " add %%rcx, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_MULTIPLICATION: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " mul %%rcx\n");
+
+ return;
+ }
+ case AST_BINOP_DIVISION: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " div %%rcx\n");
+
+ return;
+ }
+ case AST_BINOP_REMINDER: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+ fprintf(out, " xor %%edx, %%edx\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " div %%rcx\n");
+ fprintf(out, " mov %%edx, %%eax\n");
+
+ return;
+ }
+ case AST_BINOP_SUBTRACTION: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " sub %%rcx, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_CMP_EQ: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " cmp %%rcx, %%rax\n");
+ fprintf(out, " sete %%al\n");
+ fprintf(out, " movzb %%al, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_CMP_LT: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " cmp %%rcx, %%rax\n");
+ fprintf(out, " setl %%al\n");
+ fprintf(out, " movzb %%al, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_CMP_GT: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " cmp %%rcx, %%rax\n");
+ fprintf(out, " setg %%al\n");
+ fprintf(out, " movzb %%al, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_CMP_NEQ: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " cmp %%rcx, %%rax\n");
+ fprintf(out, " setne %%al\n");
+ fprintf(out, " movzb %%al, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_CMP_LEQ: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " cmp %%rcx, %%rax\n");
+ fprintf(out, " setle %%al\n");
+ fprintf(out, " movzb %%al, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_CMP_GEQ: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " cmp %%rcx, %%rax\n");
+ fprintf(out, " setge %%al\n");
+ fprintf(out, " movzb %%al, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_BITWISE_LSHIFT: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " shl %%cl, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_BITWISE_RSHIFT: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " shr %%cl, %%rax\n");
+
+ return;
+ }
+ case AST_BINOP_BITWISE_XOR: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " xor %%ecx, %%eax\n");
+
+ return;
+ }
+ case AST_BINOP_BITWISE_AND: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " and %%ecx, %%eax\n");
+
+ return;
+ }
+ case AST_BINOP_BITWISE_OR: {
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " push %%rax\n");
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " pop %%rcx\n");
+ fprintf(out, " or %%ecx, %%eax\n");
+
+ return;
+ }
+ case AST_BINOP_LOGICAL_AND: {
+ size_t label_exit = codegen_linux_x86_64_get_next_label();
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " cmp $0, %%rax\n");
+ fprintf(out, " je .L%ld\n", label_exit);
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " cmp $0, %%rax\n");
+ fprintf(out, " je .L%ld\n", label_exit);
+ fprintf(out, " mov $1, %%rax\n");
+ fprintf(out, ".L%ld:\n", label_exit);
+
+ return;
+ }
+ case AST_BINOP_LOGICAL_OR: {
+ size_t label_t = codegen_linux_x86_64_get_next_label();
+ size_t label_f = codegen_linux_x86_64_get_next_label();
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+ fprintf(out, " cmp $0, %%rax\n");
+ fprintf(out, " jne .L%ld\n", label_t);
+
+ codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+ fprintf(out, " cmp $0, %%rax\n");
+ fprintf(out, " je .L%ld\n", label_f);
+
+ fprintf(out, ".L%ld:\n", label_t);
+ fprintf(out, " mov $1, %%rax\n");
+ fprintf(out, ".L%ld:\n", label_f);
+
+ return;
+ }
+ default: {
+ assert(0 && "unsupported binary operation");
+ return;
+ }
+ }
+ }
default:
- assert(0 && "NOT IMPLEMENTED");
+ assert(0 && "unsupported expression");
}
}
diff --git a/src/parser.c b/src/parser.c
index 5ba5a3f..5dd4ef1 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -152,7 +152,7 @@ get_binary_op_precedence(token_kind_t kind)
case TOKEN_CMP_LEQ:
case TOKEN_CMP_GEQ:
return BINOP_CMP_RELATIONAL_PREC;
- case TOKEN_EQ:
+ case TOKEN_CMP_EQ:
case TOKEN_CMP_NEQ:
return BINOP_CMP_EQUALITY_PREC;
case TOKEN_AND:
--
2.44.0
^ permalink raw reply [flat|nested] 6+ messages in thread