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
  2024-09-28  4:29 ` [PATCH olang v1 2/2] codegen: x64: implement function call Johnny Richard
  0 siblings, 2 replies; 3+ 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] 3+ 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-28  4:29 ` [PATCH olang v1 2/2] codegen: x64: implement function call Johnny Richard
  1 sibling, 0 replies; 3+ 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] 3+ 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
  1 sibling, 0 replies; 3+ 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] 3+ messages in thread

end of thread, other threads:[~2024-09-28  2:30 UTC | newest]

Thread overview: 3+ 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-28  4:29 ` [PATCH olang v1 2/2] codegen: x64: implement function call Johnny Richard

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