Fully compiling to WASM again
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 24 Jun 2020 02:10:40 +0000 (21:10 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 24 Jun 2020 02:10:40 +0000 (21:10 -0500)
docs/plan
include/onyxwasm.h
onyx
progs/game.onyx
progs/minimal.onyx
progs/new_minimal.onyx
src/onyx.c
src/onyxsempass.c
src/onyxtypecheck.c
src/onyxwasm.c

index fa9396ab7173b95485bf59e025d2697e305328d2..ac906133951ed9c8266054db870dcdbd18ee4678 100644 (file)
--- a/docs/plan
+++ b/docs/plan
@@ -62,10 +62,10 @@ HOW:
                [ ] Simple while loop is functioning as expected
                [ ] break and continue semantics
                [X] Function calling works for the builtin types
-               [ ] Function return values are type checked
+               [X] Function return values are type checked
 
        Stage 2:
-               [ ] Order of symbol declaration is irrelevant
+               [X] Order of symbol declaration is irrelevant
                        Either:
                        make a graph of symbol dependencies and produce a schedule on the graph
                        that would allow for all symbols to be resolved
@@ -76,7 +76,7 @@ HOW:
                        This could be slow but it would be easier than creating a graph
                        scheduling algorithm.
 
-        [ ] Consequence of the above, recursion works
+        [X] Consequence of the above, recursion works
 
                [ ] Devise and implement a simple set of implicit type casting rules.
                        - Numeric literals always type cast to whatever type is needed (very flexible).
index 3ae0f67ebdc55c762bbd85d22b41268158f17bb4..1fcd8d36a3b708c648f294630e16abc4dbebf6ae 100644 (file)
@@ -292,7 +292,8 @@ typedef struct OnyxWasmModule {
        i32 export_count;
 } OnyxWasmModule;
 
-OnyxWasmModule onyx_wasm_generate_module(bh_allocator alloc, OnyxAstNode* program);
+OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc);
+void onyx_wasm_module_compile(OnyxWasmModule* module, OnyxAstNode* program);
 void onyx_wasm_module_free(OnyxWasmModule* module);
 void onyx_wasm_module_write_to_file(OnyxWasmModule* module, bh_file file);
 
diff --git a/onyx b/onyx
index 8c6c53a73be7048de096302eae3d6e624f3730f8..1c06a808a2322bcea86d37ecdc07a2903a7c9be0 100755 (executable)
Binary files a/onyx and b/onyx differ
index 5f90fd7261028db0170893a6790b811048a518f9..a37d4c59d241456af527ad17a66c356539857b06 100644 (file)
@@ -2,6 +2,8 @@
 gfx_draw_rect :: foreign "gfx" "draw_rect" proc (x i32, y i32, w i32, h i32) ---
 
 export main :: proc {
+    update();
+    draw();
 }
 
 update :: proc {}
index 38b1d12e52f7eb5432d6228080d2a51156ea8897..76197666ae3c3a823f91036a6f90c1647d83a452 100644 (file)
@@ -4,20 +4,21 @@ print_float :: foreign "host" "print" proc (value f32) ---
 print_if    :: foreign "host" "print" proc (i i32, f f32) ---
 
 export main :: proc {
-    output := do_stuff();
+    output := do_stuff() - 1;
 
     if output == -66 {
         new_output := abs(output) * 2;
         print(new_output);
 
     } elseif output == -67 {
-        new_output := 10;
-        print(1234);
+        print(factorial(6));
 
     } else {
         print(-1);
     }
 
+    print(fib(5));
+
     print(output);
     print_float(float_test());
 
@@ -48,6 +49,11 @@ abs :: proc (val i32) -> i32 {
     return val;
 }
 
+fib :: proc (n i32) -> i32 {
+    if n <= 1 { return 1; }
+    return fib(n - 1) + fib(n - 2);
+}
+
 diff_square :: proc (a i32, b i32) -> i32 {
        // Typechecked
        c := a - b; // Mutable
index 56c8f0b6c2257ea5aafacfe5a242f808bc8f33c2..d0e7432387b3e9f14b640ed4cea006ac4518675c 100644 (file)
@@ -2,7 +2,7 @@ print :: foreign "host" "print" proc (value i32) ---
 
 simple_test :: proc {
     a: i32 = 5;
-    b: i64 = 6 as i64;
+    b: i32 = 6;
 
     if a > b {
         foo := 123;
@@ -13,6 +13,12 @@ simple_test :: proc {
     print(c);
 }
 
+fib :: proc (n i32) -> i32 {
+    if n <= 1 { return 1; }
+
+    return fib(n - 1) + fib(n - 2);
+}
+
 foo :: proc -> i32 {
     return 10;
 }
index 4699a7e69abcac7116cef22b08f85b51f32209b9..6561b53c96afb0f034738e607fa8702d131e6584 100644 (file)
@@ -69,14 +69,15 @@ int main(int argc, char *argv[]) {
 
        // NOTE: 4th: Generate a WASM module from the parse tree and
        // write it to a file.
-//     OnyxWasmModule wasm_mod = onyx_wasm_generate_module(alloc, program);
-//
-//     bh_file out_file;
-//     bh_file_create(&out_file, "out.wasm");
-//     onyx_wasm_module_write_to_file(&wasm_mod, out_file);
-//     bh_file_close(&out_file);
-//
-//     onyx_wasm_module_free(&wasm_mod);
+    OnyxWasmModule wasm_mod = onyx_wasm_module_create(alloc);
+    onyx_wasm_module_compile(&wasm_mod, program);
+
+    bh_file out_file;
+    bh_file_create(&out_file, "out.wasm");
+    onyx_wasm_module_write_to_file(&wasm_mod, out_file);
+    bh_file_close(&out_file);
+
+    onyx_wasm_module_free(&wasm_mod);
 main_exit: // NOTE: Cleanup, since C doesn't have defer
        bh_arena_free(&sp_arena);
        bh_arena_free(&msg_arena);
index 0426789b44b01509403f69e72c2777eaf457b484..587786a4641daa650dd2cde88a5cf7839c208d49 100644 (file)
@@ -1,5 +1,6 @@
 #define BH_DEBUG
 #include "onyxsempass.h"
+#include "onyxutils.h"
 
 OnyxSemPassState onyx_sempass_create(bh_allocator alloc, bh_allocator node_alloc, OnyxMessages* msgs) {
     OnyxSemPassState state = {
@@ -17,12 +18,71 @@ OnyxSemPassState onyx_sempass_create(bh_allocator alloc, bh_allocator node_alloc
     return state;
 }
 
-static void collapse_scopes(OnyxAstNode* root_node, OnyxAstNodeScope* prev_scope) {
 
+// NOTE: If the compiler is expanded to support more targets than just
+// WASM, this function may not be needed. It brings all of the locals
+// defined in sub-scopes up to the function-block level. This is a
+// requirement of WASM, but not of other targets.
+static void collapse_scopes(OnyxAstNode* root_node) {
+    bh_arr(OnyxAstNodeBlock*) traversal_queue = NULL;
+    bh_arr_new(global_scratch_allocator, traversal_queue, 4);
+    bh_arr_set_length(traversal_queue, 0);
+
+    OnyxAstNode* walker = root_node;
+    while (walker) {
+        if (walker->kind == ONYX_AST_NODE_KIND_FUNCDEF) {
+            OnyxAstNodeScope* top_scope = walker->as_funcdef.body->scope;
+
+            bh_arr_push(traversal_queue, walker->as_funcdef.body);
+            while (!bh_arr_is_empty(traversal_queue)) {
+                OnyxAstNodeBlock* block = traversal_queue[0];
+
+                if (block->kind == ONYX_AST_NODE_KIND_IF) {
+                    OnyxAstNodeIf* if_node = (OnyxAstNodeIf *) block;
+                    if (if_node->true_block)
+                        bh_arr_push(traversal_queue, (OnyxAstNodeBlock *) if_node->true_block);
+
+                    if (if_node->false_block)
+                        bh_arr_push(traversal_queue, (OnyxAstNodeBlock *) if_node->false_block);
+
+                } else {
+
+                    if (block->scope != top_scope && block->scope->last_local != NULL) {
+                        OnyxAstNodeLocal* last_local = block->scope->last_local;
+                        while (last_local && last_local->prev_local != NULL) last_local = last_local->prev_local;
+
+                        last_local->prev_local = top_scope->last_local;
+                        top_scope->last_local = block->scope->last_local;
+                        block->scope->last_local = NULL;
+                    }
+
+                    OnyxAstNode* walker = block->body;
+                    while (walker) {
+                        if (walker->kind == ONYX_AST_NODE_KIND_BLOCK) {
+                            bh_arr_push(traversal_queue, (OnyxAstNodeBlock *) walker);
+
+                        } else if (walker->kind == ONYX_AST_NODE_KIND_IF) {
+                            if (walker->as_if.true_block)
+                                bh_arr_push(traversal_queue, (OnyxAstNodeBlock *) walker->as_if.true_block);
+
+                            if (walker->as_if.false_block)
+                                bh_arr_push(traversal_queue, (OnyxAstNodeBlock *) walker->as_if.false_block);
+                        }
+
+                        walker = walker->next;
+                    }
+                }
+
+                bh_arr_deleten(traversal_queue, 0, 1);
+            }
+        }
+
+        walker = walker->next;
+    }
 }
 
 void onyx_sempass(OnyxSemPassState* state, OnyxAstNode* root_node) {
     onyx_resolve_symbols(state, root_node);
     onyx_type_check(state, root_node);
-    collapse_scopes(root_node, NULL);
+    collapse_scopes(root_node);
 }
index 9a620468fcf423a485e1907f68d67f0503de4fc4..3f0cb9e738157b836882d692f9a8a9ad14c31f52 100644 (file)
@@ -72,7 +72,15 @@ static void typecheck_return(OnyxSemPassState* state, OnyxAstNode* retnode) {
 }
 
 static void typecheck_if(OnyxSemPassState* state, OnyxAstNodeIf* ifnode) {
-    // NOTE: Add check for boolean type on condition
+    typecheck_expression(state, ifnode->cond);
+    if (ifnode->cond->type != &builtin_types[ONYX_TYPE_INFO_KIND_INT32]) {
+        onyx_message_add(state->msgs,
+                ONYX_MESSAGE_TYPE_LITERAL,
+                ifnode->cond->token->pos,
+                "expected integer type for condition");
+        return;
+    }
+
     if (ifnode->true_block) typecheck_statement(state, ifnode->true_block);
     if (ifnode->false_block) typecheck_statement(state, ifnode->false_block);
 }
@@ -277,6 +285,14 @@ static void typecheck_function_defintion(OnyxSemPassState* state, OnyxAstNodeFun
                     "function parameter types must be known");
             return;
         }
+
+        if (param->type->size == 0) {
+            onyx_message_add(state->msgs,
+                    ONYX_MESSAGE_TYPE_LITERAL,
+                    param->token->pos,
+                    "function parameters must have non-void types");
+            return;
+        }
     }
 
     state->expected_return_type = func->return_type;
index 771f913d98ce846c289a1b562bf8b787b8e22f7b..4d4a424072e6812a6009046b00105e4e4ac1487f 100644 (file)
@@ -208,48 +208,48 @@ static WasmType onyx_type_to_wasm_type(OnyxTypeInfo* type) {
        return WASM_TYPE_VOID;
 }
 
-static void process_function_body(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeFuncDef* fd);
-static void process_block(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeBlock* block);
-static void process_statement(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* stmt);
-static void process_assign_lval(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* lval);
-static void process_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* assign);
-static void process_if(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeIf* if_node);
-static void process_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* expr);
-static void process_cast(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* cast);
-static void process_return(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* ret);
-
-static void process_function_body(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeFuncDef* fd) {
+static void compile_function_body(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeFuncDef* fd);
+static void compile_block(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeBlock* block);
+static void compile_statement(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* stmt);
+static void compile_assign_lval(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* lval);
+static void compile_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* assign);
+static void compile_if(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeIf* if_node);
+static void compile_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* expr);
+static void compile_cast(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* cast);
+static void compile_return(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* ret);
+
+static void compile_function_body(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeFuncDef* fd) {
        if (fd->body == NULL) return;
 
        forll (OnyxAstNode, stmt, fd->body->body, next) {
-               process_statement(mod, func, stmt);
+               compile_statement(mod, func, stmt);
        }
 
        bh_arr_push(func->code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
 }
 
-static void process_block(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeBlock* block) {
+static void compile_block(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeBlock* block) {
        bh_arr_push(func->code, ((WasmInstruction){ WI_BLOCK_START, 0x40 }));
 
        forll (OnyxAstNode, stmt, block->body, next) {
-               process_statement(mod, func, stmt);
+               compile_statement(mod, func, stmt);
        }
 
        bh_arr_push(func->code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
 }
 
-static void process_statement(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* stmt) {
+static void compile_statement(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* stmt) {
        switch (stmt->kind) {
                case ONYX_AST_NODE_KIND_SCOPE: break;
-               case ONYX_AST_NODE_KIND_RETURN: process_return(mod, func, stmt); break;
-               case ONYX_AST_NODE_KIND_ASSIGNMENT: process_assignment(mod, func, stmt); break;
-        case ONYX_AST_NODE_KIND_IF: process_if(mod, func, (OnyxAstNodeIf *) stmt); break;
-        case ONYX_AST_NODE_KIND_CALL: process_expression(mod, func, stmt); break;
+               case ONYX_AST_NODE_KIND_RETURN: compile_return(mod, func, stmt); break;
+               case ONYX_AST_NODE_KIND_ASSIGNMENT: compile_assignment(mod, func, stmt); break;
+        case ONYX_AST_NODE_KIND_IF: compile_if(mod, func, (OnyxAstNodeIf *) stmt); break;
+        case ONYX_AST_NODE_KIND_CALL: compile_expression(mod, func, stmt); break;
                default: break;
        }
 }
 
-static void process_assign_lval(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* lval) {
+static void compile_assign_lval(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* lval) {
        if (lval->kind == ONYX_AST_NODE_KIND_LOCAL || lval->kind == ONYX_AST_NODE_KIND_PARAM) {
                i32 localidx = (i32) bh_imap_get(&mod->local_map, (u64) lval);
 
@@ -259,31 +259,44 @@ static void process_assign_lval(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode
        }
 }
 
-static void process_if(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeIf* if_node) {
-    process_expression(mod, func, if_node->cond);
+static void compile_if(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNodeIf* if_node) {
+    compile_expression(mod, func, if_node->cond);
     bh_arr_push(func->code, ((WasmInstruction){ WI_IF_START, 0x40 }));
 
     if (if_node->true_block) {
         // NOTE: This is kind of gross, but making a function for this doesn't feel right
-        forll (OnyxAstNode, stmt, if_node->true_block, next) {
-            process_statement(mod, func, stmt);
+
+        if (if_node->true_block->kind == ONYX_AST_NODE_KIND_IF) {
+            forll (OnyxAstNode, stmt, if_node->true_block, next) {
+                compile_statement(mod, func, stmt);
+            }
+        } else if (if_node->true_block->kind == ONYX_AST_NODE_KIND_BLOCK) {
+            forll (OnyxAstNode, stmt, if_node->true_block->as_block.body, next) {
+                compile_statement(mod, func, stmt);
+            }
         }
     }
 
     if (if_node->false_block) {
         bh_arr_push(func->code, ((WasmInstruction){ WI_ELSE, 0x00 }));
 
-        forll (OnyxAstNode, stmt, if_node->false_block, next) {
-            process_statement(mod, func, stmt);
+        if (if_node->false_block->kind == ONYX_AST_NODE_KIND_IF) {
+            forll (OnyxAstNode, stmt, if_node->false_block, next) {
+                compile_statement(mod, func, stmt);
+            }
+        } else if (if_node->false_block->kind == ONYX_AST_NODE_KIND_BLOCK) {
+            forll (OnyxAstNode, stmt, if_node->false_block->as_block.body, next) {
+                compile_statement(mod, func, stmt);
+            }
         }
     }
 
     bh_arr_push(func->code, ((WasmInstruction){ WI_IF_END, 0x00 }));
 }
 
-static void process_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* assign) {
-       process_expression(mod, func, assign->right);
-       process_assign_lval(mod, func, assign->left);
+static void compile_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* assign) {
+       compile_expression(mod, func, assign->right);
+       compile_assign_lval(mod, func, assign->left);
 }
 
 #define BIN_OP_PROCESS(ast_binop, wasm_binop) \
@@ -300,8 +313,8 @@ static void process_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode*
                                default: assert(("Invalid type", 0)); \
                        } \
  \
-                       process_expression(mod, func, expr->left); \
-                       process_expression(mod, func, expr->right); \
+                       compile_expression(mod, func, expr->left); \
+                       compile_expression(mod, func, expr->right); \
                        bh_arr_push(func->code, ((WasmInstruction){ instr_type, 0x00 })); \
                        break; \
                }
@@ -326,13 +339,13 @@ static void process_assignment(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode*
                                        default: assert(("Invalid type", 0)); \
                                } \
  \
-                               process_expression(mod, func, expr->left); \
-                               process_expression(mod, func, expr->right); \
+                               compile_expression(mod, func, expr->left); \
+                               compile_expression(mod, func, expr->right); \
                                bh_arr_push(func->code, ((WasmInstruction){ instr_type, 0x00 })); \
                                break; \
                        }
 
-static void process_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* expr) {
+static void compile_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* expr) {
        switch (expr->kind) {
                BIN_OP_PROCESS(ADD, ADD);
                BIN_OP_PROCESS(MINUS, SUB);
@@ -361,8 +374,8 @@ static void process_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode*
                                        default: assert(("Invalid type", 0));
                                }
 
-                               process_expression(mod, func, expr->left);
-                               process_expression(mod, func, expr->right);
+                               compile_expression(mod, func, expr->left);
+                               compile_expression(mod, func, expr->right);
                                bh_arr_push(func->code, ((WasmInstruction){ instr_type, 0x00 }));
                                break;
                        }
@@ -373,16 +386,16 @@ static void process_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode*
 
                 if (type_kind == ONYX_TYPE_INFO_KIND_INT32) {
                     bh_arr_push(func->code, ((WasmInstruction){ WI_I32_CONST, 0x00 }));
-                    process_expression(mod, func, expr->left);
+                    compile_expression(mod, func, expr->left);
                     bh_arr_push(func->code, ((WasmInstruction){ WI_I32_SUB, 0x00 }));
 
                 } else if (type_kind == ONYX_TYPE_INFO_KIND_INT64) {
                     bh_arr_push(func->code, ((WasmInstruction){ WI_I64_CONST, 0x00 }));
-                    process_expression(mod, func, expr->left);
+                    compile_expression(mod, func, expr->left);
                     bh_arr_push(func->code, ((WasmInstruction){ WI_I64_SUB, 0x00 }));
 
                 } else {
-                    process_expression(mod, func, expr->left);
+                    compile_expression(mod, func, expr->left);
 
                     if (type_kind == ONYX_TYPE_INFO_KIND_FLOAT32)
                         bh_arr_push(func->code, ((WasmInstruction){ WI_F32_NEG, 0x00 }));
@@ -402,7 +415,7 @@ static void process_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode*
                                break;
                        }
 
-               case ONYX_AST_NODE_KIND_CAST: process_cast(mod, func, expr); break;
+               case ONYX_AST_NODE_KIND_CAST: compile_cast(mod, func, expr); break;
                case ONYX_AST_NODE_KIND_LITERAL:
                        {
                 OnyxAstNodeNumLit* lit = &expr->as_numlit;
@@ -427,13 +440,13 @@ static void process_expression(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode*
                                break;
                        }
 
-               case ONYX_AST_NODE_KIND_BLOCK: process_block(mod, func, (OnyxAstNodeBlock *) expr); break;
+               case ONYX_AST_NODE_KIND_BLOCK: compile_block(mod, func, (OnyxAstNodeBlock *) expr); break;
 
                case ONYX_AST_NODE_KIND_CALL:
                        {
                                OnyxAstNodeCall* call = &expr->as_call;
                                forll (OnyxAstNode, arg, call->arguments, next) {
-                                       process_expression(mod, func, arg->left);
+                                       compile_expression(mod, func, arg->left);
                                }
 
                 i32 func_idx = (i32) bh_imap_get(&mod->func_map, (u64) call->callee);
@@ -459,8 +472,8 @@ static const WasmInstructionType cast_map[][6] = {
        /* F64 */ { WI_I32_FROM_F64_S,  WI_I32_FROM_F64_U,      WI_I64_FROM_F64_S,      WI_I64_FROM_F64_U,      WI_F32_FROM_F64,        WI_NOP,                   },
 };
 
-static void process_cast(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* cast) {
-       process_expression(mod, func, cast->left);
+static void compile_cast(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* cast) {
+       compile_expression(mod, func, cast->left);
 
        OnyxTypeInfo* from = cast->left->type;
        OnyxTypeInfo* to = cast->type;
@@ -492,9 +505,9 @@ static void process_cast(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* cast)
        }
 }
 
-static void process_return(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* ret) {
+static void compile_return(OnyxWasmModule* mod, WasmFunc* func, OnyxAstNode* ret) {
        if (ret->left) {
-               process_expression(mod, func, ret->left);
+               compile_expression(mod, func, ret->left);
        }
 
        bh_arr_push(func->code, ((WasmInstruction){ WI_RETURN, 0x00 }));
@@ -542,7 +555,7 @@ static i32 generate_type_idx(OnyxWasmModule* mod, OnyxAstNodeFuncDef* fd) {
     return type_idx;
 }
 
-static void process_function_definition(OnyxWasmModule* mod, OnyxAstNodeFuncDef* fd) {
+static void compile_function_definition(OnyxWasmModule* mod, OnyxAstNodeFuncDef* fd) {
     i32 type_idx = generate_type_idx(mod, fd);
 
        WasmFunc wasm_func = {
@@ -555,12 +568,12 @@ static void process_function_definition(OnyxWasmModule* mod, OnyxAstNodeFuncDef*
                },
                .code = NULL,
        };
-       i32 func_idx = mod->next_func_idx++;
-    bh_imap_put(&mod->func_map, (u64) fd, func_idx);
 
        if (fd->flags & ONYX_AST_FLAG_EXPORTED) {
                onyx_token_null_toggle(*fd->token);
 
+        i32 func_idx = (i32) bh_imap_get(&mod->func_map, (u64) fd);
+
                WasmExport wasm_export = {
                        .kind = WASM_FOREIGN_FUNCTION,
                        .idx = func_idx,
@@ -597,7 +610,7 @@ static void process_function_definition(OnyxWasmModule* mod, OnyxAstNodeFuncDef*
         }
 
         // Generate code
-        process_function_body(mod, &wasm_func, fd);
+        compile_function_body(mod, &wasm_func, fd);
     } else {
         // NOTE: Empty bodies still need a block end instruction
         bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
@@ -609,12 +622,10 @@ static void process_function_definition(OnyxWasmModule* mod, OnyxAstNodeFuncDef*
        bh_imap_clear(&mod->local_map);
 }
 
-static void process_foreign(OnyxWasmModule* module, OnyxAstNodeForeign* foreign) {
+static void compile_foreign(OnyxWasmModule* module, OnyxAstNodeForeign* foreign) {
     if (foreign->import->kind == ONYX_AST_NODE_KIND_FUNCDEF) {
         i32 type_idx = generate_type_idx(module, &foreign->import->as_funcdef);
 
-        bh_imap_put(&module->func_map, (u64) &foreign->import->as_funcdef, module->next_import_func_idx++);
-
         WasmImport import = {
             .kind = WASM_FOREIGN_FUNCTION,
             .idx = type_idx,
@@ -631,7 +642,7 @@ static void process_foreign(OnyxWasmModule* module, OnyxAstNodeForeign* foreign)
     }
 }
 
-OnyxWasmModule onyx_wasm_generate_module(bh_allocator alloc, OnyxAstNode* program) {
+OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc) {
        OnyxWasmModule module = {
                .allocator = alloc,
 
@@ -659,12 +670,34 @@ OnyxWasmModule onyx_wasm_generate_module(bh_allocator alloc, OnyxAstNode* progra
        bh_imap_init(&module.local_map, bh_heap_allocator());
     bh_imap_init(&module.func_map, bh_heap_allocator());
 
-    // NOTE: Count number of import functions
+    return module;
+}
+
+void onyx_wasm_module_compile(OnyxWasmModule* module, OnyxAstNode* program) {
        OnyxAstNode* walker = program;
     while (walker) {
         if (walker->kind == ONYX_AST_NODE_KIND_FOREIGN
             && walker->as_foreign.import->kind == ONYX_AST_NODE_KIND_FUNCDEF) {
-            module.next_func_idx++;
+            module->next_func_idx++;
+        }
+
+        walker = walker->next;
+    }
+
+    walker = program;
+    while (walker) {
+        if (walker->kind == ONYX_AST_NODE_KIND_FUNCDEF) {
+            i32 func_idx = module->next_func_idx++;
+            bh_imap_put(&module->func_map, (u64) walker, func_idx);
+        }
+
+        if (walker->kind == ONYX_AST_NODE_KIND_FOREIGN) {
+            OnyxAstNodeForeign* foreign = &walker->as_foreign;
+
+            if (foreign->import->kind == ONYX_AST_NODE_KIND_FUNCDEF) {
+                i32 func_idx = module->next_import_func_idx++;
+                bh_imap_put(&module->func_map, (u64) foreign->import, func_idx);
+            }
         }
 
         walker = walker->next;
@@ -674,18 +707,16 @@ OnyxWasmModule onyx_wasm_generate_module(bh_allocator alloc, OnyxAstNode* progra
        while (walker) {
                switch (walker->kind) {
                        case ONYX_AST_NODE_KIND_FUNCDEF:
-                               process_function_definition(&module, &walker->as_funcdef);
+                               compile_function_definition(module, &walker->as_funcdef);
                                break;
             case ONYX_AST_NODE_KIND_FOREIGN:
-                process_foreign(&module, &walker->as_foreign);
+                compile_foreign(module, &walker->as_foreign);
                 break;
                        default: break;
                }
 
                walker = walker->next;
        }
-
-       return module;
 }
 
 void onyx_wasm_module_free(OnyxWasmModule* module) {