public inbox for ~johnnyrichard/olang-devel@lists.sr.ht
 help / color / mirror / code / Atom feed
From: Johnny Richard <johnny@johnnyrichard.com>
To: ~johnnyrichard/olang-devel@lists.sr.ht
Cc: Johnny Richard <johnny@johnnyrichard.com>
Subject: [PATCH olang v1 2/2] codegen: x64: implement function call
Date: Sat, 28 Sep 2024 06:29:28 +0200	[thread overview]
Message-ID: <20240928043016.104228-3-johnny@johnnyrichard.com> (raw)
In-Reply-To: <20240928043016.104228-1-johnny@johnnyrichard.com>

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


      parent reply	other threads:[~2024-09-28  2:30 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-09-28  4:29 [PATCH olang v1 0/2] " 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 [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240928043016.104228-3-johnny@johnnyrichard.com \
    --to=johnny@johnnyrichard.com \
    --cc=~johnnyrichard/olang-devel@lists.sr.ht \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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