added returning structs; much easier than anticipated
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 18 Aug 2020 01:23:49 +0000 (20:23 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 18 Aug 2020 01:23:49 +0000 (20:23 -0500)
core/alloc.onyx
docs/plan
include/onyxastnodes.h
include/onyxwasm.h
onyx
progs/stack_based.onyx
src/onyxwasm.c

index e70f900d4d6e308060ef7e04437c2f451f9ae531..377b758960dd0ac6aa8fc4c2a2dbf0e9cb22af70 100644 (file)
@@ -206,15 +206,6 @@ scratch_alloc_init :: proc (a: ^Allocator, ss: ^ScratchState) {
 }
 
 
-#private return_scratch_size  :: 256
-#private return_scratch_buff  :  [return_scratch_size] u8
-#private return_scratch_state :  ScratchState;
-
-return_scratch_alloc : Allocator;
-
 memory_init :: proc {
     heap_init();
-
-    scratch_state_init(^return_scratch_state, return_scratch_buff, return_scratch_size);
-    scratch_alloc_init(^return_scratch_alloc, ^return_scratch_state);
 }
\ No newline at end of file
index 614d56fefeff9d2e38500e2ca6b14448a76328ed..2f58f7e809694569671d6f47d2a3480d7fc8d997 100644 (file)
--- a/docs/plan
+++ b/docs/plan
@@ -181,6 +181,12 @@ HOW:
 
         [X] #size on structs
 
+        [X] multiple return value
+            - Returning on the stack
+
+        [X] returning structs
+            - This will put forward a lot of the work that will be done for multiple return values
+
         [ ] 'use' enums and packages at an arbitrary scope
 
         [ ] 'when' statements
@@ -215,16 +221,6 @@ HOW:
             - Dynamic resizing?
             - They are just very hard to use at the moment
 
-        [ ] multiple return values
-            - THIS IS NOT GOING TO BE FUN. 
-            - Wasm multi-value proposal is not finalized and nothing has implemented it yet
-            - This means other methods of returning multiple things would be needed:    
-                - globals
-                - on the stack
-
-        [ ] returning structs
-            - This will put forward a lot of the work that will be done for multiple return values
-
         [X] Start work on evaluating compile time known values.
             - An expression marked COMPTIME will be reduced to its value in the parse tree.
 
index ca9e7499090c1bd5b114fd6cc8107e5a13aa6262..06225a023b6dddee1b8ba2477f7c3c5f476bedbd 100644 (file)
@@ -240,6 +240,12 @@ typedef enum OnyxIntrinsic {
     ONYX_INTRINSIC_F64_COPYSIGN,
 } OnyxIntrinsic;
 
+typedef enum CallingConvention {
+    CC_Undefined,
+    CC_Return_Wasm,
+    CC_Return_Stack
+} CallingConvention;
+
 
 // Base Nodes
 #define AstNode_members {     \
@@ -543,4 +549,11 @@ static inline b32 node_is_type(AstNode* node) {
     return (node->kind > Ast_Kind_Type_Start) && (node->kind < Ast_Kind_Type_End);
 }
 
+static inline CallingConvention type_function_get_cc(Type* type) {
+    if (type == NULL) return CC_Undefined;
+    if (type->kind != Type_Kind_Function) return CC_Undefined;
+    if (type->Function.return_type->kind == Type_Kind_Struct) return CC_Return_Stack;
+    return CC_Return_Wasm;
+}
+
 #endif // #ifndef ONYXASTNODES_H
index b99798ad806c4fb1739a4a3d15d53f1f501946de..c448a30330fb5ed4abe30acd909c94a565bd51e4 100644 (file)
@@ -340,6 +340,7 @@ typedef struct OnyxWasmModule {
 
     i32 *stack_top_ptr;
     u64 stack_base_idx;
+    CallingConvention curr_cc;
 
     b32 has_stack_locals : 1;
 } OnyxWasmModule;
diff --git a/onyx b/onyx
index 3e28fe9aca207135d442f747dd302c725ed494b0..7e7a4a81acf136b808249de5dbd4dc6b857bbe9b 100755 (executable)
Binary files a/onyx and b/onyx differ
index 5301b59af5891a6abba03ce709bb9cf5cb309449..8842eaff26312e6e609e8bb11c431553a2b7079c 100644 (file)
@@ -60,12 +60,6 @@ mag_squared :: proc (use v: Vec3) -> i32 {
     return x * x + y * y + z * z;
 }
 
-vec_add :: proc (v: Vec3, u: Vec3, use out: ^Vec3) {
-    x = v.x + u.x;
-    y = v.y + u.y;
-    z = v.z + u.z;
-}
-
 clamp :: proc (v: i32, lo: i32, hi: i32) -> i32 {
     if v < lo do return lo;
     if v > hi do return hi;
@@ -154,7 +148,7 @@ start :: proc #export {
     mag_squared(varr[2]) |> print();
 
     v1 := Vec3.{};
-    v2 := *vadd(v1, Vec3.{ 0, 0, 0 });
+    v2 := vmul(vadd(v1, Vec3.{ 1, 2, 3 }), 3);
   
     print(v2.x);
     print(v2.y);
@@ -167,15 +161,16 @@ start :: proc #export {
     print_hex(cast(u64) un.i);
 }
 
-vadd :: proc (v1: Vec3, v2: Vec3) -> ^Vec3 {
-    out := cast(^Vec3) alloc(return_scratch_alloc, sizeof Vec3);
-    *out = Vec3.{
+vadd :: proc (v1: Vec3, v2: Vec3) -> Vec3 {
+    return Vec3.{
         x = v1.x + v2.x,
         y = v1.y + v2.y,
         z = v1.z + v2.z,
     };
+}
 
-    return out;
+vmul :: proc (use v: Vec3, s: i32) -> Vec3 {
+    return Vec3.{ x = x * s, y = y * s, z = z * s };
 }
 
 UnionTest :: struct #union {
index fe1de9d3ebe1340f358a8c4e27cd03e9e05b7b95..f526e6b907944f74db43371ea6bb6d5b08e6a05a 100644 (file)
@@ -363,7 +363,8 @@ COMPILE_FUNC(field_access_location,         AstFieldAccess* field, u64* offset_r
 COMPILE_FUNC(local_location,                AstLocal* local, u64* offset_return);
 COMPILE_FUNC(memory_reservation_location,   AstMemRes* memres);
 COMPILE_FUNC(struct_load,                   Type* type, u64 offset);
-COMPILE_FUNC(struct_store,                  AstTyped* lval);
+COMPILE_FUNC(struct_lval,                   AstTyped* lval);
+COMPILE_FUNC(struct_store,                  Type* type, u64 offset);
 COMPILE_FUNC(struct_literal,                AstStructLiteral* sl);
 COMPILE_FUNC(expression,                    AstTyped* expr);
 COMPILE_FUNC(cast,                          AstUnaryOp* cast);
@@ -457,7 +458,7 @@ COMPILE_FUNC(assignment, AstBinaryOp* assign) {
 
     if (assign->right->type->kind == Type_Kind_Struct) {
         compile_expression(mod, &code, assign->right);
-        compile_struct_store(mod, &code, assign->left);
+        compile_struct_lval(mod, &code, assign->left);
 
         *pcode = code;
         return;
@@ -526,9 +527,10 @@ COMPILE_FUNC(assignment, AstBinaryOp* assign) {
 COMPILE_FUNC(store_instruction, Type* type, u32 offset) {
     bh_arr(WasmInstruction) code = *pcode;
 
-    assert(("Should use compile_struct_store instead", type->kind != Type_Kind_Struct));
-
-    u32 alignment = type_get_alignment_log2(type);
+    if (type->kind == Type_Kind_Struct) {
+        compile_struct_store(mod, pcode, type, offset);
+        return;
+    }
 
     if (type->kind == Type_Kind_Enum) {
         type = type->Enum.backing;
@@ -538,6 +540,8 @@ COMPILE_FUNC(store_instruction, Type* type, u32 offset) {
         type = &basic_types[Basic_Kind_U32];
     }
 
+    u32 alignment = type_get_alignment_log2(type);
+
     i32 store_size  = type_size_of(type);
     i32 is_basic    = type->kind == Type_Kind_Basic || type->kind == Type_Kind_Pointer;
     i32 is_pointer  = is_basic && (type->Basic.flags & Basic_Flag_Pointer);
@@ -909,9 +913,24 @@ COMPILE_FUNC(call, AstCall* call) {
         compile_expression(mod, &code, arg->value);
     }
 
+    CallingConvention cc = type_function_get_cc(call->callee->type);
+    assert(cc != CC_Undefined);
+
+    Type* return_type = call->callee->type->Function.return_type;
+    u32 return_size = type_size_of(return_type);
+    u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+
+    if (cc == CC_Return_Stack) {
+        WID(WI_GLOBAL_GET, stack_top_idx);
+        WID(WI_I32_CONST, return_size);
+        WI(WI_I32_ADD);
+        WID(WI_GLOBAL_SET, stack_top_idx);
+    }
+
     if (call->callee->kind == Ast_Kind_Function) {
         i32 func_idx = (i32) bh_imap_get(&mod->index_map, (u64) call->callee);
         bh_arr_push(code, ((WasmInstruction){ WI_CALL, func_idx }));
+
     } else {
         compile_expression(mod, &code, call->callee);
 
@@ -919,6 +938,16 @@ COMPILE_FUNC(call, AstCall* call) {
         WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
     }
 
+    if (cc == CC_Return_Stack) {
+        WID(WI_GLOBAL_GET, stack_top_idx);
+        WID(WI_I32_CONST, return_size);
+        WI(WI_I32_SUB);
+        WID(WI_GLOBAL_SET, stack_top_idx);
+
+        WID(WI_GLOBAL_GET, stack_top_idx);
+        compile_load_instruction(mod, &code, return_type, 0);
+    }
+
     *pcode = code;
 }
 
@@ -1108,7 +1137,7 @@ COMPILE_FUNC(struct_load, Type* type, u64 offset) {
     *pcode = code;
 }
 
-COMPILE_FUNC(struct_store, AstTyped* lval) {
+COMPILE_FUNC(struct_lval, AstTyped* lval) {
     // NOTE: Expects the stack to look like:
     //      mem_1
     //      mem_2
@@ -1131,10 +1160,27 @@ COMPILE_FUNC(struct_store, AstTyped* lval) {
         default: assert(0);
     }
 
+    compile_struct_store(mod, &code, lval->type, offset);
+
+    *pcode = code;
+}
+
+COMPILE_FUNC(struct_store, Type* type, u64 offset) {
+    // NOTE: Expects the stack to look like:
+    //      mem_1
+    //      mem_2
+    //      ...
+    //      mem_n
+    //      loc
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    assert(type->kind == Type_Kind_Struct);
+
     u64 loc_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
     WIL(WI_LOCAL_SET, loc_idx);
 
-    bh_arr_rev_each(StructMember *, smem, lval->type->Struct.memarr) {
+    bh_arr_rev_each(StructMember *, smem, type->Struct.memarr) {
         WasmType wt = onyx_type_to_wasm_type((*smem)->type);
         u64 tmp_idx = local_raw_allocate(mod->local_alloc, wt);
 
@@ -1505,7 +1551,28 @@ COMPILE_FUNC(return, AstReturn* ret) {
     bh_arr(WasmInstruction) code = *pcode;
 
     if (ret->expr) {
-        compile_expression(mod, &code, ret->expr);
+        if (mod->curr_cc == CC_Return_Stack) {
+            if (ret->expr->type->kind == Type_Kind_Struct) {
+                compile_expression(mod, &code, ret->expr);
+
+                WIL(WI_LOCAL_GET, mod->stack_base_idx);
+                WID(WI_I32_CONST, type_size_of(ret->expr->type));
+                WI(WI_I32_SUB);
+
+                compile_store_instruction(mod, &code, ret->expr->type, 0);
+
+            } else {
+                WIL(WI_LOCAL_GET, mod->stack_base_idx);
+                WID(WI_I32_CONST, type_size_of(ret->expr->type));
+                WI(WI_I32_SUB);
+
+                compile_expression(mod, &code, ret->expr);
+                compile_store_instruction(mod, &code, ret->expr->type, 0);
+            }
+
+        } else {
+            compile_expression(mod, &code, ret->expr);
+        }
     }
 
     compile_deferred_stmts(mod, &code, (AstNode *) ret);
@@ -1544,7 +1611,7 @@ COMPILE_FUNC(stack_enter, u64 stacksize) {
 COMPILE_FUNC(stack_leave, u32 unused) {
     bh_arr(WasmInstruction) code = *pcode;
 
-    u32 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+    u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
 
     WIL(WI_LOCAL_GET, mod->stack_base_idx);
     WID(WI_GLOBAL_SET, stack_top_idx);
@@ -1577,6 +1644,7 @@ static i32 generate_type_idx(OnyxWasmModule* mod, Type* ft) {
     }
     *(t++) = ':';
 
+    // HACK: Slightly: the wasm type for structs has to be 0x00
     WasmType return_type = onyx_type_to_wasm_type(ft->Function.return_type);
     *(t++) = (char) return_type;
     *t = '\0';
@@ -1705,7 +1773,10 @@ static void compile_function(OnyxWasmModule* mod, AstFunction* fd) {
         mod->local_alloc = &wasm_func.locals;
         mod->local_alloc->param_count = localidx;
 
-        mod->has_stack_locals = 0;
+        mod->curr_cc = type_function_get_cc(fd->type);
+        assert(mod->curr_cc != CC_Undefined);
+
+        mod->has_stack_locals = (mod->curr_cc == CC_Return_Stack);
         bh_arr_each(AstLocal *, local, fd->locals)
             mod->has_stack_locals |= !local_is_wasm_local(*local);
 
@@ -1716,7 +1787,6 @@ static void compile_function(OnyxWasmModule* mod, AstFunction* fd) {
             mod->stack_base_idx = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
         }
 
-
         // Generate code
         compile_function_body(mod, &wasm_func.code, fd);