public inbox for ~johnnyrichard/olang-devel@lists.sr.ht
 help / color / mirror / code / Atom feed
* [PATCH olang v1 0/2] codegen: x64: implement function call
@ 2024-09-28  4:29 Johnny Richard
  2024-09-28  4:29 ` [PATCH olang v1 1/2] ast: remove anonymous union on ast_node_t Johnny Richard
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Johnny Richard @ 2024-09-28  4:29 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Johnny Richard (2):
  ast: remove anonymous union on ast_node_t
  codegen: x64: implement function call

 src/ast.h                       |  37 +++--
 src/checker.c                   |   6 +-
 src/codegen_linux_x86_64.c      | 268 ++++++++++++++++++++++++++++++--
 tests/olc/0028_function_call.ol |  10 +-
 4 files changed, 285 insertions(+), 36 deletions(-)


base-commit: 76f8cc2aa8df6bd53fadb4afe012798e5f4c60fe
-- 
2.46.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH olang v1 1/2] ast: remove anonymous union on ast_node_t
  2024-09-28  4:29 [PATCH olang v1 0/2] codegen: x64: implement function call Johnny Richard
@ 2024-09-28  4:29 ` Johnny Richard
  2024-09-29  4:21   ` Carlos Maniero
  2024-09-28  4:29 ` [PATCH olang v1 2/2] codegen: x64: implement function call Johnny Richard
  2024-09-29  4:22 ` [PATCH olang v1 0/2] " Carlos Maniero
  2 siblings, 1 reply; 5+ messages in thread
From: Johnny Richard @ 2024-09-28  4:29 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/ast.h | 37 ++++++++++++++++++++++---------------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/src/ast.h b/src/ast.h
index 66c626d..4791d6b 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -25,7 +25,7 @@
 #include "string_view.h"
 #include "type.h"
 
-typedef struct ast_node ast_node_t;
+typedef union ast_node ast_node_t;
 
 typedef enum
 {
@@ -44,11 +44,13 @@ typedef enum
 
 typedef struct ast_block
 {
+    ast_node_kind_t node_kind;
     list_t *nodes;
 } ast_block_t;
 
 typedef struct ast_translation_unit
 {
+    ast_node_kind_t node_kind;
     list_t *decls;
 } ast_translation_unit_t;
 
@@ -60,6 +62,7 @@ typedef struct ast_fn_param
 
 typedef struct ast_fn_definition
 {
+    ast_node_kind_t node_kind;
     string_view_t id;
     list_t *params;
     string_view_t return_type;
@@ -69,6 +72,7 @@ typedef struct ast_fn_definition
 
 typedef struct ast_fn_call
 {
+    ast_node_kind_t node_kind;
     string_view_t id;
     list_t *args;
     scope_t *scope;
@@ -76,6 +80,7 @@ typedef struct ast_fn_call
 
 typedef struct ast_var_definition
 {
+    ast_node_kind_t node_kind;
     string_view_t id;
     string_view_t type;
     ast_node_t *value;
@@ -89,6 +94,7 @@ typedef enum
 
 typedef struct ast_literal
 {
+    ast_node_kind_t node_kind;
     ast_literal_kind_t kind;
     union
     {
@@ -98,6 +104,7 @@ typedef struct ast_literal
 
 typedef struct ast_ref
 {
+    ast_node_kind_t node_kind;
     string_view_t id;
     scope_t *scope;
 } ast_ref_t;
@@ -126,6 +133,7 @@ typedef enum ast_binary_op_kind
 
 typedef struct ast_binary_op
 {
+    ast_node_kind_t node_kind;
     ast_binary_op_kind_t kind;
     ast_node_t *lhs;
     ast_node_t *rhs;
@@ -133,32 +141,31 @@ typedef struct ast_binary_op
 
 typedef struct ast_return_stmt
 {
+    ast_node_kind_t node_kind;
     ast_node_t *expr;
 } ast_return_stmt_t;
 
 typedef struct ast_if_stmt
 {
+    ast_node_kind_t node_kind;
     ast_node_t *cond;
     ast_node_t *then;
     ast_node_t *_else;
 } ast_if_stmt_t;
 
-typedef struct ast_node
+typedef union ast_node
 {
     ast_node_kind_t kind;
-    union
-    {
-        ast_translation_unit_t as_translation_unit;
-        ast_fn_definition_t as_fn_def;
-        ast_fn_call_t as_fn_call;
-        ast_var_definition_t as_var_def;
-        ast_binary_op_t as_bin_op;
-        ast_literal_t as_literal;
-        ast_ref_t as_ref;
-        ast_block_t as_block;
-        ast_return_stmt_t as_return_stmt;
-        ast_if_stmt_t as_if_stmt;
-    };
+    ast_translation_unit_t as_translation_unit;
+    ast_fn_definition_t as_fn_def;
+    ast_fn_call_t as_fn_call;
+    ast_var_definition_t as_var_def;
+    ast_binary_op_t as_bin_op;
+    ast_literal_t as_literal;
+    ast_ref_t as_ref;
+    ast_block_t as_block;
+    ast_return_stmt_t as_return_stmt;
+    ast_if_stmt_t as_if_stmt;
 } ast_node_t;
 
 ast_node_t *
-- 
2.46.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH olang v1 2/2] codegen: x64: implement function call
  2024-09-28  4:29 [PATCH olang v1 0/2] codegen: x64: implement function call Johnny Richard
  2024-09-28  4:29 ` [PATCH olang v1 1/2] ast: remove anonymous union on ast_node_t Johnny Richard
@ 2024-09-28  4:29 ` Johnny Richard
  2024-09-29  4:22 ` [PATCH olang v1 0/2] " Carlos Maniero
  2 siblings, 0 replies; 5+ messages in thread
From: Johnny Richard @ 2024-09-28  4:29 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

The current function call is limited to parameters with 64bits only and
6 arguments max (we have to allow pass arguments through stack after 6th
argument).

The previous function-definition implementation was using the global
scope to register parameters symbols, after this change function
parameters must have their own scope.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/checker.c                   |   6 +-
 src/codegen_linux_x86_64.c      | 268 ++++++++++++++++++++++++++++++--
 tests/olc/0028_function_call.ol |  10 +-
 3 files changed, 263 insertions(+), 21 deletions(-)

diff --git a/src/checker.c b/src/checker.c
index 7c3767f..b7cbe20 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -65,7 +65,7 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
 
         case AST_NODE_FN_DEF: {
             ast_fn_definition_t *fn_def = &ast->as_fn_def;
-            fn_def->scope = scope;
+            fn_def->scope = scope_push(scope);
 
             list_item_t *item = list_head(fn_def->params);
 
@@ -73,12 +73,12 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
                 ast_fn_param_t *param = (ast_fn_param_t *)item->value;
 
                 symbol_t *symbol = symbol_new(checker->arena, param->id, type_from_id(param->type_id));
-                scope_insert(scope, symbol);
+                scope_insert(fn_def->scope, symbol);
 
                 item = list_next(item);
             }
 
-            populate_scope(checker, scope, ast->as_fn_def.block);
+            populate_scope(checker, fn_def->scope, ast->as_fn_def.block);
             return;
         }
 
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 0173443..e04de0e 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -29,6 +29,34 @@
 // The call instruction pushes EIP into stack so the first 8 bytes from stack
 // must be preserved else the ret instruction will jump to nowere.
 #define X86_CALL_EIP_STACK_OFFSET (8)
+#define X86_CALL_ARG_SIZE 6
+
+typedef enum x86_64_register_type
+{
+    REG_ACCUMULATOR,
+    REG_BASE,
+    REG_COUNTER,
+    REG_DATA,
+    REG_SRC_IDX,
+    REG_DEST_IDX,
+    REG_STACK_PTR,
+    REG_BASE_PTR,
+    REG_R8,
+    REG_R9,
+    REG_R10,
+    REG_R11,
+    REG_R12,
+    REG_R13,
+    REG_R14,
+    REG_R15
+} x86_64_register_type_t;
+
+/**
+ * Arch/ABI      arg1  arg2  arg3  arg4  arg5  arg6  arg7  Notes
+ * ──────────────────────────────────────────────────────────────
+ * x86-64        rdi   rsi   rdx   r10   r8    r9    -
+ */
+static int x86_call_args[X86_CALL_ARG_SIZE] = { REG_DEST_IDX, REG_SRC_IDX, REG_DATA, REG_R10, REG_R8, REG_R9 };
 
 static void
 codegen_linux_x86_64_emit_start_entrypoint(codegen_x86_64_t *codegen);
@@ -40,7 +68,7 @@ static size_t
 type_to_bytes(type_t *type);
 
 static char *
-get_accumulator_reg_for(size_t bytes);
+get_reg_for(x86_64_register_type_t type, size_t bytes);
 
 void
 codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out)
@@ -131,7 +159,31 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             fprintf(codegen->out,
                     "    mov -%ld(%%rbp), %s\n",
                     *offset,
-                    get_accumulator_reg_for(type_to_bytes(&symbol->type)));
+                    get_reg_for(REG_ACCUMULATOR, type_to_bytes(&symbol->type)));
+            return;
+        }
+        case AST_NODE_FN_CALL: {
+            ast_fn_call_t fn_call = expr_node->as_fn_call;
+
+            size_t i = 0;
+            for (list_item_t *item = list_head(fn_call.args); item != NULL; item = list_next(item)) {
+                // FIXME: add support for more args than X86_CALL_ARG_SIZE
+                assert(i < X86_CALL_ARG_SIZE);
+
+                ast_node_t *arg_node = (ast_node_t *)item->value;
+
+                codegen_linux_x86_64_emit_expression(codegen, arg_node);
+
+                // FIXME: should get the correct size according to the ast node
+                fprintf(codegen->out, "    push %s\n", get_reg_for(REG_ACCUMULATOR, 8));
+                ++i;
+            }
+
+            for (; i > 0; --i) {
+                fprintf(codegen->out, "    pop %s\n", get_reg_for(x86_call_args[i - 1], 8));
+            }
+
+            fprintf(codegen->out, "    call " SV_FMT "\n", SV_ARG(fn_call.id));
             return;
         }
         case AST_NODE_BINARY_OP: {
@@ -373,6 +425,7 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 codegen_linux_x86_64_emit_expression(codegen, expr);
 
                 fprintf(codegen->out, "    mov %%rbp, %%rsp\n");
+                fprintf(codegen->out, "    pop %%rbp\n");
                 fprintf(codegen->out, "    ret\n");
 
                 break;
@@ -401,7 +454,7 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
 
                 fprintf(codegen->out,
                         "    mov %s, -%ld(%%rbp)\n",
-                        get_accumulator_reg_for(type_size),
+                        get_reg_for(REG_ACCUMULATOR, type_size),
                         codegen->base_offset);
                 codegen->base_offset += type_size;
 
@@ -497,16 +550,45 @@ calculate_fn_local_size(scope_t *scope)
 }
 
 static void
-codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_t *fn)
+codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_t *fn_def)
 {
     codegen->base_offset = X86_CALL_EIP_STACK_OFFSET;
 
-    ast_node_t *block_node = fn->block;
-    fprintf(codegen->out, "" SV_FMT ":\n", SV_ARG(fn->id));
+    ast_node_t *block_node = fn_def->block;
+    fprintf(codegen->out, "" SV_FMT ":\n", SV_ARG(fn_def->id));
 
+    fprintf(codegen->out, "    push %%rbp\n");
     fprintf(codegen->out, "    mov %%rsp, %%rbp\n");
 
-    size_t local_size = calculate_fn_local_size(fn->scope);
+    size_t i = 0;
+    for (list_item_t *item = list_head(fn_def->params); item != NULL; item = list_next(item)) {
+        assert(i < X86_CALL_ARG_SIZE);
+
+        ast_fn_param_t *param = item->value;
+
+        size_t *offset = arena_alloc(codegen->arena, sizeof(size_t));
+        *offset = codegen->base_offset;
+
+        symbol_t *symbol = scope_lookup(fn_def->scope, param->id);
+        assert(symbol);
+
+        char symbol_ptr[PTR_HEX_CSTR_SIZE];
+        sprintf(symbol_ptr, "%p", (void *)symbol);
+
+        map_put(codegen->symbols_stack_offset, symbol_ptr, offset);
+
+        fprintf(codegen->out,
+                "    mov %s, -%ld(%s)\n",
+                get_reg_for(x86_call_args[i], 8),
+                *offset,
+                get_reg_for(REG_BASE_PTR, 8));
+
+        // FIXME: add offset according to the param size
+        codegen->base_offset += 8;
+        ++i;
+    }
+
+    size_t local_size = calculate_fn_local_size(fn_def->scope);
 
     if (local_size != 0) {
         fprintf(codegen->out, "    sub $%ld, %%rsp\n", local_size);
@@ -519,14 +601,170 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 }
 
 static char *
-get_accumulator_reg_for(size_t bytes)
+get_reg_for(x86_64_register_type_t type, size_t bytes)
 {
-    if (bytes <= 1) {
-        return "%ah";
-    } else if (bytes <= 2) {
-        return "%ax";
-    } else if (bytes <= 4) {
-        return "%eax";
+    switch (type) {
+        case REG_ACCUMULATOR: {
+            if (bytes <= 1) {
+                return "%ah";
+            } else if (bytes <= 2) {
+                return "%ax";
+            } else if (bytes <= 4) {
+                return "%eax";
+            }
+            return "%rax";
+        }
+        case REG_BASE: {
+            if (bytes <= 1) {
+                return "%bh";
+            } else if (bytes <= 2) {
+                return "%bx";
+            } else if (bytes <= 4) {
+                return "%ebx";
+            }
+            return "%rbx";
+        }
+        case REG_COUNTER: {
+            if (bytes <= 1) {
+                return "%ch";
+            } else if (bytes <= 2) {
+                return "%cx";
+            } else if (bytes <= 4) {
+                return "%ecx";
+            }
+            return "%rcx";
+        }
+        case REG_DATA: {
+            if (bytes <= 1) {
+                return "%dh";
+            } else if (bytes <= 2) {
+                return "%dx";
+            } else if (bytes <= 4) {
+                return "%edx";
+            }
+            return "%rdx";
+        }
+        case REG_SRC_IDX: {
+            if (bytes <= 1) {
+                return "%sil";
+            } else if (bytes <= 2) {
+                return "%si";
+            } else if (bytes <= 4) {
+                return "%esi";
+            }
+            return "%rsi";
+        }
+        case REG_DEST_IDX: {
+            if (bytes <= 1) {
+                return "%sil";
+            } else if (bytes <= 2) {
+                return "%di";
+            } else if (bytes <= 4) {
+                return "%edi";
+            }
+            return "%rdi";
+        }
+        case REG_STACK_PTR: {
+            if (bytes <= 1) {
+                return "%spl";
+            } else if (bytes <= 2) {
+                return "%sp";
+            } else if (bytes <= 4) {
+                return "%esp";
+            }
+            return "%rsp";
+        }
+        case REG_BASE_PTR: {
+            if (bytes <= 1) {
+                return "%bpl";
+            } else if (bytes <= 2) {
+                return "%bp";
+            } else if (bytes <= 4) {
+                return "%ebp";
+            }
+            return "%rbp";
+        }
+        case REG_R8: {
+            if (bytes <= 1) {
+                return "%r8b";
+            } else if (bytes <= 2) {
+                return "%r8w";
+            } else if (bytes <= 4) {
+                return "%r8d";
+            }
+            return "%r8";
+        }
+        case REG_R9: {
+            if (bytes <= 1) {
+                return "%r9b";
+            } else if (bytes <= 2) {
+                return "%r9w";
+            } else if (bytes <= 4) {
+                return "%r9d";
+            }
+            return "%r9";
+        }
+        case REG_R10: {
+            if (bytes <= 1) {
+                return "%r10b";
+            } else if (bytes <= 2) {
+                return "%r10w";
+            } else if (bytes <= 4) {
+                return "%r10d";
+            }
+            return "%r10";
+        }
+        case REG_R11: {
+            if (bytes <= 1) {
+                return "%r11b";
+            } else if (bytes <= 2) {
+                return "%r11w";
+            } else if (bytes <= 4) {
+                return "%r11d";
+            }
+            return "%r11";
+        }
+        case REG_R12: {
+            if (bytes <= 1) {
+                return "%r12b";
+            } else if (bytes <= 2) {
+                return "%r12w";
+            } else if (bytes <= 4) {
+                return "%r12d";
+            }
+            return "%r12";
+        }
+        case REG_R13: {
+            if (bytes <= 1) {
+                return "%r13b";
+            } else if (bytes <= 2) {
+                return "%r13w";
+            } else if (bytes <= 4) {
+                return "%r13d";
+            }
+            return "%r13";
+        }
+        case REG_R14: {
+            if (bytes <= 1) {
+                return "%r14b";
+            } else if (bytes <= 2) {
+                return "%r14w";
+            } else if (bytes <= 4) {
+                return "%r14d";
+            }
+            return "%r14";
+        }
+        case REG_R15: {
+            if (bytes <= 1) {
+                return "%r15b";
+            } else if (bytes <= 2) {
+                return "%r15w";
+            } else if (bytes <= 4) {
+                return "%r15d";
+            }
+            return "%r15";
+        }
     }
-    return "%rax";
+    assert(0 && "invalid register");
+    return NULL;
 }
diff --git a/tests/olc/0028_function_call.ol b/tests/olc/0028_function_call.ol
index cfaa969..cfd06de 100644
--- a/tests/olc/0028_function_call.ol
+++ b/tests/olc/0028_function_call.ol
@@ -17,10 +17,14 @@ fn main(): u8 {
   return add(40, 2) 
 }
 
-fn add(a: u32, b: u32): u8 {
+fn add(a: u64, b: u64): u8 {
   return a + b
 }
 
+# TEST test_compile(exit_code=0)
+
+# TEST test_run_binary(exit_code=42)
+
 # TEST test_ast WITH
 # Translation_Unit
 # |-Function_Definition <name:main> <return:u8>
@@ -30,8 +34,8 @@ fn add(a: u32, b: u32): u8 {
 # |       |-Literal <kind:u32> <value:40>
 # |       `-Literal <kind:u32> <value:2>
 # `-Function_Definition <name:add> <return:u8>
-#   |-Param_Definition <name:a> <type:u32>
-#   |-Param_Definition <name:b> <type:u32>
+#   |-Param_Definition <name:a> <type:u64>
+#   |-Param_Definition <name:b> <type:u64>
 #   `-Block
 #     `-Return_Statement
 #       `-Binary_Operation (+)
-- 
2.46.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH olang v1 1/2] ast: remove anonymous union on ast_node_t
  2024-09-28  4:29 ` [PATCH olang v1 1/2] ast: remove anonymous union on ast_node_t Johnny Richard
@ 2024-09-29  4:21   ` Carlos Maniero
  0 siblings, 0 replies; 5+ messages in thread
From: Carlos Maniero @ 2024-09-29  4:21 UTC (permalink / raw)
  To: Johnny Richard, ~johnnyrichard/olang-devel

That's is a very smart trick to handle unions! I'm glad to work with you
on this project. I'm always learning and developing my developer skills.

Thanks for the patch and for the lesson.

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH olang v1 0/2] codegen: x64: implement function call
  2024-09-28  4:29 [PATCH olang v1 0/2] codegen: x64: implement function call Johnny Richard
  2024-09-28  4:29 ` [PATCH olang v1 1/2] ast: remove anonymous union on ast_node_t Johnny Richard
  2024-09-28  4:29 ` [PATCH olang v1 2/2] codegen: x64: implement function call Johnny Richard
@ 2024-09-29  4:22 ` Carlos Maniero
  2 siblings, 0 replies; 5+ messages in thread
From: Carlos Maniero @ 2024-09-29  4:22 UTC (permalink / raw)
  To: Johnny Richard, ~johnnyrichard/olang-devel

Applied! Thank you so much.

To git.sr.ht:~johnnyrichard/olang
   76f8cc2..9a9b1e5  main -> main

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2024-09-29  4:22 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-09-28  4:29 [PATCH olang v1 0/2] codegen: x64: implement function call Johnny Richard
2024-09-28  4:29 ` [PATCH olang v1 1/2] ast: remove anonymous union on ast_node_t Johnny Richard
2024-09-29  4:21   ` Carlos Maniero
2024-09-28  4:29 ` [PATCH olang v1 2/2] codegen: x64: implement function call Johnny Richard
2024-09-29  4:22 ` [PATCH olang v1 0/2] " Carlos Maniero

Code repositories for project(s) associated with this public inbox

	https://git.johnnyrichard.com/olang.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox