added initial implementation of typed varargs
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 7 Sep 2020 21:47:57 +0000 (16:47 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 7 Sep 2020 21:47:57 +0000 (16:47 -0500)
12 files changed:
.vimspector.json
core/stdio.onyx
docs/plan
include/onyxastnodes.h
include/onyxtypes.h
onyx
progs/odin_example.onyx [new file with mode: 0644]
progs/poly_test.onyx
src/onyxchecker.c
src/onyxparser.c
src/onyxtypes.c
src/onyxwasm.c

index 2e8fdc8bc6a82a682478cc53793fc7e01e9bbb7e..899b511c32ce13ca2eeb9c41aa3d9b9a230d0e77 100644 (file)
@@ -6,7 +6,7 @@
                 "type": "cppdbg",
                 "request": "launch",
                 "program": "${workspaceFolder}/onyx",
-                "args": ["-verbose", "examples/02_variables.onyx"],
+                "args": ["-verbose", "progs/poly_test.onyx"],
                 "stopAtEntry": true,
                 "cwd": "${workspaceFolder}",
                 "environment": [],
index e2cfac11a033a79ff23dc7055219b6167119593c..2c74f7a36929627e1d96465413280daf582b5545 100644 (file)
@@ -12,8 +12,8 @@ stdio_init :: proc {
 }
 
 print_string  :: proc (s: string) {
-       string_builder_append(^print_buffer, s);
-       if s.data[s.count - 1] == #char "\n" do print_buffer_flush();
+    string_builder_append(^print_buffer, s);
+    if s.data[s.count - 1] == #char "\n" do print_buffer_flush();
 }
 
 print_cstring :: proc (s: cstring) do string_builder_append(^print_buffer, s);
index 19b7f2e5fce05992bd0efc6e9205f7636743a365..604acdcf40eec9d0ba0a35f11aac6faee3f55926 100644 (file)
--- a/docs/plan
+++ b/docs/plan
@@ -245,13 +245,7 @@ HOW:
             - Do multiple passes if needed
             - Some APIs like WebGL have a ton of foreigns, and most of them aren't even used
 
-        [ ] baked parameters
-            - Compile time known parameters
-            - Removes the argument from the list and replaces the function with the
-                baked function
-
-        [ ] Add SIMD intrinsics
-            - This also requires adding the v128 SIMD type
+        [X] Variadic arguments
 
         [ ] Put type info in data section so it is runtime accessible
             - type name
@@ -260,6 +254,14 @@ HOW:
             - struct member names
             - array length
 
+        [ ] baked parameters
+            - Compile time known parameters
+            - Removes the argument from the list and replaces the function with the
+                baked function
+
+        [ ] Add SIMD intrinsics
+            - This also requires adding the v128 SIMD type
+
         [ ] Type parameterized structs
 
         [ ] Array literals
@@ -284,8 +286,6 @@ HOW:
             - But, profiling says we are spending 50% of the program execution time in the lexer
             - That is awful
 
-        [ ] Variadic arguments
-
         [ ] 'when' statements
             - Compile time conditions
             - Only evalutate code blocks that evaluate to be true
index f32097e87909096dc517efd696b2321bbc0a40bf..66da4b2b0c5c2fd0f0faa71a3bea6a501f443687 100644 (file)
@@ -179,6 +179,8 @@ typedef enum AstFlags {
     Ast_Flag_No_Clone          = BH_BIT(18),
 
     Ast_Flag_Cannot_Take_Addr  = BH_BIT(19),
+    
+    Ast_Flag_Arg_Is_VarArg     = BH_BIT(20),
 } AstFlags;
 
 typedef enum UnaryOp {
@@ -468,8 +470,13 @@ struct AstGlobal        {
         };
     };
 };
-struct AstParam         { AstLocal *local; AstTyped *default_value; };
-struct AstFunction      {
+struct AstParam {
+    AstLocal *local;
+    AstTyped *default_value;
+
+    b32 is_vararg : 1;
+};
+struct AstFunction {
     AstTyped_base;
 
     Scope *scope;
index 6eb3686b7ddbd31723f3eea8e17edd59a7b8e595..5dfe489a3d8ee77c8ba92f00cab43c354987d6fe 100644 (file)
@@ -70,7 +70,8 @@ typedef struct StructMember {
     TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
     TYPE_KIND(Slice, struct { Type *ptr_to_data; })               \
     TYPE_KIND(DynArray, struct { Type *ptr_to_data; })            \
-    TYPE_KIND(Enum, struct { char* name; Type* backing; }) 
+    TYPE_KIND(VarArgs, struct { Type *ptr_to_data; })             \
+    TYPE_KIND(Enum, struct { char* name; Type* backing; })
 
 typedef enum TypeKind {
     Type_Kind_Invalid,
@@ -117,6 +118,7 @@ Type* type_build_function_type(bh_allocator alloc, struct AstFunction* func);
 Type* type_make_pointer(bh_allocator alloc, Type* to);
 Type* type_make_slice(bh_allocator alloc, Type* of);
 Type* type_make_dynarray(bh_allocator alloc, Type* of);
+Type* type_make_varargs(bh_allocator alloc, Type* of);
 
 const char* type_get_name(Type* type);
 u32 type_get_alignment_log2(Type* type);
diff --git a/onyx b/onyx
index a9c8ea999d3a663641035153495ea0d2a0a5c435..ada98626987eb83be0ece26aae9627ae53bb3fc0 100755 (executable)
Binary files a/onyx and b/onyx differ
diff --git a/progs/odin_example.onyx b/progs/odin_example.onyx
new file mode 100644 (file)
index 0000000..24c29d1
--- /dev/null
@@ -0,0 +1,25 @@
+#include_file "core/std/wasi"
+
+use package core
+
+main :: proc (args: [] cstring) {
+    program := "+ + * s - /";
+    accumulator := 0;
+
+    for token: program {
+        switch token {
+            case #char "+" do accumulator += 1;
+            case #char "-" do accumulator -= 1;
+            case #char "*" do accumulator *= 2;
+            case #char "/" do accumulator /= 2;
+            case #char "s" do accumulator *= accumulator;
+            case #default ---
+        }
+    }
+
+    print("The program \"");
+    print(program);
+    print("\" calculates the value ");
+    print(accumulator);
+    print("\n");
+}
index cf256a0a72d3a1f709b02fd4d0e102fa19be4a70..212d7a94dcca53e4a1daec816a5ae2e525205e7c 100644 (file)
@@ -100,9 +100,62 @@ switch_demo :: proc {
     }
 }
 
+vararg_test :: proc (prefix: string, nums: ..i32) {
+    print(prefix);
+    for num: nums {
+        print(num);
+        print(" ");
+    }
+}
+
+NumInfo :: struct {
+    min : i32;
+    max : i32;
+    sum : i32;
+}
+
+get_num_info :: proc (nums: ..i32) -> NumInfo {
+    ni : NumInfo;
+
+    ni.min = nums[0];
+    for num: nums do if num < ni.min do ni.min = num;
+
+    ni.max = nums[0];
+    for num: nums do if num > ni.max do ni.max = num;
+
+    ni.sum = 0;
+    for num: nums do ni.sum += num;
+
+    return ni;
+}
+
+print_strings :: proc (ss: ..string) {
+    for s: ss do print_string(s);
+}
+
+// At some point, it would be nice to have the ability to say something like:
+// complex_varargs :: proc (nums: ..i32, names: ..string) {
+//     ...
+// }
+
 main :: proc (args: [] cstring) {
     switch_demo();
 
+    print_strings("This ", "is ", "a ", "test.\n");
+
+    vararg_test("Here are some numbers:\n", 1, 2, 3, 4, 5);
+    print("\n\n");
+
+    ni := get_num_info(1, 2, 3, 4, 5);
+    print("Some information about those numbers:\n");
+    print("Min: ");
+    print(ni.min);
+    print("\nMax: ");
+    print(ni.max);
+    print("\nSum: ");
+    print(ni.sum);
+    print("\n\n");
+
     res := compose(5, proc (x: i32) -> i32 do return x * 3;,
                       proc (x: i32) -> i32 do return x + 5;);
     print(res);
index 5b697a9eefdaeb504a8d39175ebac3389e17ed70..7815d03827c93883ed5cbce193885ad2e0609edb 100644 (file)
@@ -134,6 +134,19 @@ b32 check_for(AstFor* fornode) {
         fornode->loop_type = For_Loop_Slice;
 
     }
+    else if (iter_type->kind == Type_Kind_VarArgs) {
+        if (fornode->by_pointer) {
+            onyx_report_error(fornode->var->token->pos, "Cannot iterate by pointer over '%s'.", type_get_name(iter_type));
+            return 1;
+        }
+
+        can_iterate = 1;
+
+        fornode->var->type = iter_type->VarArgs.ptr_to_data->Pointer.elem;
+
+        // NOTE: Slices are VarArgs are being treated the same here.
+        fornode->loop_type = For_Loop_Slice;
+    }
     else if (iter_type->kind == Type_Kind_DynArray) {
         can_iterate = 1;
 
@@ -219,7 +232,11 @@ static AstTyped* match_overloaded_function(AstCall* call, AstOverloadedFunction*
         while (arg != NULL) {
             fill_in_type((AstTyped *) arg);
 
-            if (!types_are_compatible(*param_type, arg->type)) goto no_match;
+            if ((*param_type)->kind == Type_Kind_VarArgs) {
+                if (!types_are_compatible((*param_type)->VarArgs.ptr_to_data->Pointer.elem, arg->type))
+                    goto no_match;
+            }
+            else if (!types_are_compatible(*param_type, arg->type)) goto no_match;
 
             param_type++;
             arg = (AstArgument *) arg->next;
@@ -376,7 +393,7 @@ b32 check_call(AstCall* call) {
         }
     }
 
-    // NOTE: If we calling an intrinsic function, translate the
+    // NOTE: If we are calling an intrinsic function, translate the
     // call into an intrinsic call node.
     if (callee->flags & Ast_Flag_Intrinsic) {
         call->kind = Ast_Kind_Intrinsic_Call;
@@ -444,14 +461,38 @@ b32 check_call(AstCall* call) {
     Type **formal_params = callee->type->Function.params;
     actual = call->arguments;
 
+    Type* variadic_type = NULL;
+
     i32 arg_pos = 0;
-    while (arg_pos < callee->type->Function.param_count && actual != NULL) {
-        if (!types_are_compatible(formal_params[arg_pos], actual->type)) {
+    while (1) {
+        if (actual == NULL) break;
+        if (arg_pos >= callee->type->Function.param_count && variadic_type == NULL) break;
+
+        if (variadic_type == NULL && formal_params[arg_pos]->kind == Type_Kind_VarArgs) {
+            variadic_type = formal_params[arg_pos]->VarArgs.ptr_to_data->Pointer.elem;
+        }
+
+        if (variadic_type != NULL) {
+            if (!types_are_compatible(variadic_type, actual->type)) {
+                onyx_report_error(actual->token->pos,
+                        "The function '%b' expects a value of type '%s' for the variadic parameter, '%b', got '%s'.",
+                        callee->token->text, callee->token->length,
+                        type_get_name(formal_params[arg_pos]),
+                        callee->params[arg_pos].local->token->text,
+                        callee->params[arg_pos].local->token->length,
+                        type_get_name(actual->type));
+                return 1;
+            }
+
+            actual->flags |= Ast_Flag_Arg_Is_VarArg;
+
+        } else if (!types_are_compatible(formal_params[arg_pos], actual->type)) {
             onyx_report_error(actual->token->pos,
-                    "The function '%b' expects a value of type '%s' for parameter '%d', got '%s'.",
+                    "The function '%b' expects a value of type '%s' for %d%s parameter, got '%s'.",
                     callee->token->text, callee->token->length,
                     type_get_name(formal_params[arg_pos]),
-                    arg_pos,
+                    arg_pos + 1,
+                    bh_num_suffix(arg_pos + 1),
                     type_get_name(actual->type));
             return 1;
         }
@@ -884,7 +925,8 @@ b32 check_array_access(AstArrayAccess* aa) {
     else if (aa->addr->type->kind == Type_Kind_Array)
         aa->type = aa->addr->type->Array.elem;
     else if (aa->addr->type->kind == Type_Kind_Slice
-            || aa->addr->type->kind == Type_Kind_DynArray) {
+            || aa->addr->type->kind == Type_Kind_DynArray
+            || aa->addr->type->kind == Type_Kind_VarArgs) {
         // If we are accessing on a slice or a dynamic array, implicitly add a field access for the data member
 
         StructMember smem;
@@ -1223,6 +1265,7 @@ b32 check_struct(AstStructType* s_node) {
 
 b32 check_function_header(AstFunction* func) {
     b32 expect_default_param = 0;
+    b32 has_had_varargs = 0;
 
     bh_arr_each(AstParam, param, func->params) {
         AstLocal* local = param->local;
@@ -1233,6 +1276,18 @@ b32 check_function_header(AstFunction* func) {
             return 1;
         }
 
+        if (has_had_varargs && param->is_vararg) {
+            onyx_report_error(local->token->pos,
+                    "Can only have one param that is of variable argument type.");
+            return 1;
+        }
+
+        if (has_had_varargs && !param->is_vararg) {
+            onyx_report_error(local->token->pos,
+                    "Variable arguments must be last in parameter list");
+            return 1;
+        }
+
         if (param->default_value != NULL) expect_default_param = 1;
 
         fill_in_type((AstTyped *) local);
@@ -1242,6 +1297,12 @@ b32 check_function_header(AstFunction* func) {
             return 1;
         }
 
+        if (param->is_vararg) {
+            has_had_varargs = 1;
+
+            local->type = type_make_varargs(semstate.node_allocator, local->type);
+        }
+
         if (local->type->kind != Type_Kind_Array
             && type_size_of(local->type) == 0) {
             onyx_report_error(local->token->pos, "Function parameters cannot have zero-width types.");
index 9fa2a706143474bfb0785201ae20d0df87558a81..f31bb666046e16346bfb356219e1df0510e4372e 100644 (file)
@@ -1419,6 +1419,11 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) {
         }
 
         if (parser->curr->type != '=') {
+            if (parser->curr->type == Token_Type_Dot_Dot) {
+                consume_token(parser);
+                curr_param.is_vararg = 1;
+            }
+
             i32 old_len = bh_arr_length(*parser->polymorph_context.poly_params);
             curr_param.local->type_node = parse_type(parser);
 
index 5fcbe809e26eab98e80fef77b1c81a556e29ac42..0132c26fb7c0ff2c6c3b2b3d2dafc5937e51e48d 100644 (file)
@@ -94,6 +94,11 @@ b32 types_are_surface_compatible(Type* t1, Type* t2) {
             return types_are_compatible(t1->Slice.ptr_to_data->Pointer.elem, t2->Slice.ptr_to_data->Pointer.elem);
         }
 
+        case Type_Kind_VarArgs: {
+            if (t2->kind != Type_Kind_VarArgs) return 0;
+            return types_are_compatible(t1->VarArgs.ptr_to_data->Pointer.elem, t2->VarArgs.ptr_to_data->Pointer.elem);
+        }
+
         case Type_Kind_DynArray: {
             if (t2->kind != Type_Kind_DynArray) return 0;
             return types_are_compatible(t1->DynArray.ptr_to_data->Pointer.elem, t2->DynArray.ptr_to_data->Pointer.elem);
@@ -191,6 +196,11 @@ b32 types_are_compatible(Type* t1, Type* t2) {
             return types_are_compatible(t1->Slice.ptr_to_data->Pointer.elem, t2->Slice.ptr_to_data->Pointer.elem);
         }
 
+        case Type_Kind_VarArgs: {
+            if (t2->kind != Type_Kind_VarArgs) return 0;
+            return types_are_compatible(t1->VarArgs.ptr_to_data->Pointer.elem, t2->VarArgs.ptr_to_data->Pointer.elem);
+        }
+
         case Type_Kind_DynArray: {
             if (t2->kind != Type_Kind_DynArray) return 0;
             return types_are_compatible(t1->DynArray.ptr_to_data->Pointer.elem, t2->DynArray.ptr_to_data->Pointer.elem);
@@ -215,6 +225,7 @@ u32 type_size_of(Type* type) {
         case Type_Kind_Struct:   return type->Struct.size;
         case Type_Kind_Enum:     return type_size_of(type->Enum.backing);
         case Type_Kind_Slice:    return 8;
+        case Type_Kind_VarArgs:  return 8;
         case Type_Kind_DynArray: return 12;
         default:                 return 0;
     }
@@ -231,6 +242,7 @@ u32 type_alignment_of(Type* type) {
         case Type_Kind_Struct:   return type->Struct.alignment;
         case Type_Kind_Enum:     return type_alignment_of(type->Enum.backing);
         case Type_Kind_Slice:    return 4;
+        case Type_Kind_VarArgs:  return 4;
         case Type_Kind_DynArray: return 4;
         default: return 1;
     }
@@ -410,7 +422,7 @@ Type* type_build_function_type(bh_allocator alloc, AstFunction* func) {
     if (param_count > 0) {
         i32 i = 0;
         bh_arr_each(AstParam, param, func->params) {
-            if (param->default_value == NULL)
+            if (param->default_value == NULL && !param->is_vararg)
                 func_type->Function.needed_param_count++;
             func_type->Function.params[i++] = param->local->type;
         }
@@ -446,6 +458,14 @@ Type* type_make_dynarray(bh_allocator alloc, Type* of) {
     return dynarr;
 }
 
+Type* type_make_varargs(bh_allocator alloc, Type* of) {
+    Type* va_type = bh_alloc(alloc, sizeof(Type));
+    va_type->kind = Type_Kind_VarArgs;
+    va_type->VarArgs.ptr_to_data = type_make_pointer(alloc, of);
+
+    return va_type;
+}
+
 const char* type_get_name(Type* type) {
     if (type == NULL) return "unknown";
 
@@ -465,6 +485,7 @@ const char* type_get_name(Type* type) {
                 return "<anonymous enum>";
 
         case Type_Kind_Slice: return bh_aprintf(global_scratch_allocator, "[] %s", type_get_name(type->Slice.ptr_to_data->Pointer.elem));
+        case Type_Kind_VarArgs: return bh_aprintf(global_scratch_allocator, "..%s", type_get_name(type->VarArgs.ptr_to_data->Pointer.elem));
         case Type_Kind_DynArray: return bh_aprintf(global_scratch_allocator, "[..] %s", type_get_name(type->DynArray.ptr_to_data->Pointer.elem));
 
         case Type_Kind_Function: {
@@ -509,6 +530,7 @@ b32 type_lookup_member(Type* type, char* member, StructMember* smem) {
             return 1;
         }
 
+        case Type_Kind_VarArgs:
         case Type_Kind_Slice: {
             if (strcmp(member, "data") == 0) {
                 smem->idx = 0;
@@ -563,6 +585,9 @@ b32 type_lookup_member_by_idx(Type* type, i32 idx, StructMember* smem) {
             return 1;
         }
 
+        // HACK: This relies on the fact that the structures for Slice and VarArgs
+        // are identical.                       - brendanfh   2020/09/07
+        case Type_Kind_VarArgs:
         case Type_Kind_Slice: {
             if (idx == 0) {
                 smem->idx = 0;
@@ -681,6 +706,7 @@ b32 type_is_array_accessible(Type* type) {
     if (type_is_pointer(type)) return 1;
     if (type->kind == Type_Kind_Slice) return 1;
     if (type->kind == Type_Kind_DynArray) return 1;
+    if (type->kind == Type_Kind_VarArgs) return 1;
     return 0;
 }
 
@@ -688,6 +714,7 @@ b32 type_is_structlike(Type* type) {
     if (type->kind == Type_Kind_Struct) return 1;
     if (type->kind == Type_Kind_Slice)  return 1;
     if (type->kind == Type_Kind_DynArray) return 1;
+    if (type->kind == Type_Kind_VarArgs) return 1;
     if (type->kind == Type_Kind_Pointer) {
         if (type->Pointer.elem->kind == Type_Kind_Struct) return 1;
         if (type->Pointer.elem->kind == Type_Kind_Slice)  return 1;
@@ -700,6 +727,7 @@ b32 type_is_structlike_strict(Type* type) {
     if (type->kind == Type_Kind_Struct)   return 1;
     if (type->kind == Type_Kind_Slice)    return 1;
     if (type->kind == Type_Kind_DynArray) return 1;
+    if (type->kind == Type_Kind_VarArgs) return 1;
     return 0;
 }
 
@@ -707,6 +735,7 @@ u32 type_structlike_mem_count(Type* type) {
     switch (type->kind) {
         case Type_Kind_Struct:   return type->Struct.mem_count;
         case Type_Kind_Slice:    return 2;
+        case Type_Kind_VarArgs:  return 2;
         case Type_Kind_DynArray: return 3;
         default: return 0;
     }
@@ -716,7 +745,8 @@ u32 type_structlike_is_simple(Type* type) {
     switch (type->kind) {
         case Type_Kind_Struct:   return type_struct_is_simple(type);
         case Type_Kind_Slice:    return 1;
+        case Type_Kind_VarArgs:  return 1;
         case Type_Kind_DynArray: return 1;
         default: return 0;
     }
-}
\ No newline at end of file
+}
index f47dd4aa9e63e31223d18b1d0a34dccd0c807cc6..d98c8a47f90a7694f8c4f6d3b830223c77c441a7 100644 (file)
@@ -883,7 +883,7 @@ EMIT_FUNC(for_array, AstFor* for_node, u64 iter_local) {
         // NOTE: Storing structs requires that the location to store it is,
         // the top most thing on the stack. Everything requires it to be
         // 'under' the other element being stored.  -brendanfh 2020/09/04
-        if (!it_is_local && var->type->kind != Type_Kind_Struct) {
+        if (!it_is_local && !type_is_structlike(var->type)) {
             emit_local_location(mod, &code, var, &offset);
         }
 
@@ -892,7 +892,7 @@ EMIT_FUNC(for_array, AstFor* for_node, u64 iter_local) {
         if (it_is_local) {
             WIL(WI_LOCAL_SET, iter_local);
         } else {
-            if (var->type->kind != Type_Kind_Struct) {
+            if (!type_is_structlike(var->type)) {
                 emit_store_instruction(mod, &code, var->type, offset);
             } else {
                 emit_local_location(mod, &code, var, &offset);
@@ -979,7 +979,7 @@ EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local) {
         // NOTE: Storing structs requires that the location to store it is,
         // the top most thing on the stack. Everything requires it to be
         // 'under' the other element being stored.  -brendanfh 2020/09/04
-        if (!it_is_local && var->type->kind != Type_Kind_Struct) {
+        if (!it_is_local && !type_is_structlike(var->type)) {
             emit_local_location(mod, &code, var, &offset);
         }
 
@@ -988,7 +988,7 @@ EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local) {
         if (it_is_local) {
             WIL(WI_LOCAL_SET, iter_local);
         } else {
-            if (var->type->kind != Type_Kind_Struct) {
+            if (!type_is_structlike(var->type)) {
                 emit_store_instruction(mod, &code, var->type, offset);
             } else {
                 emit_local_location(mod, &code, var, &offset);
@@ -1279,10 +1279,33 @@ EMIT_FUNC(unaryop, AstUnaryOp* unop) {
 EMIT_FUNC(call, AstCall* call) {
     bh_arr(WasmInstruction) code = *pcode;
 
+    u32 stack_grow_amm = 0;
+    u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+
+    u32 vararg_count = 0;
+
     for (AstArgument *arg = call->arguments;
             arg != NULL;
             arg = (AstArgument *) arg->next) {
+        if ((arg->flags & Ast_Flag_Arg_Is_VarArg) && !type_is_structlike(arg->type)) {
+            WID(WI_GLOBAL_GET, stack_top_idx);
+        }
+
         emit_expression(mod, &code, arg->value);
+
+        if (arg->flags & Ast_Flag_Arg_Is_VarArg) {
+            if (type_is_structlike(arg->type)) WID(WI_GLOBAL_GET, stack_top_idx);
+
+            emit_store_instruction(mod, &code, arg->type, stack_grow_amm);
+
+            stack_grow_amm += type_size_of(arg->type);
+            vararg_count += 1;
+        }
+    }
+
+    if (vararg_count > 0) {
+        WID(WI_GLOBAL_GET, stack_top_idx);
+        WID(WI_I32_CONST, vararg_count);
     }
 
     CallingConvention cc = type_function_get_cc(call->callee->type);
@@ -1293,11 +1316,13 @@ EMIT_FUNC(call, AstCall* call) {
     u32 return_align = type_alignment_of(return_type);
     bh_align(return_size, return_align);
 
-    u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+    stack_grow_amm += return_size;
 
-    if (cc == CC_Return_Stack) {
+    b32 needs_stack = (cc == CC_Return_Stack) || (vararg_count > 0);
+
+    if (needs_stack) {
         WID(WI_GLOBAL_GET, stack_top_idx);
-        WID(WI_I32_CONST, return_size);
+        WID(WI_I32_CONST, stack_grow_amm);
         WI(WI_I32_ADD);
         WID(WI_GLOBAL_SET, stack_top_idx);
     }
@@ -1313,14 +1338,16 @@ EMIT_FUNC(call, AstCall* call) {
         WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
     }
 
-    if (cc == CC_Return_Stack) {
+    if (needs_stack) {
         WID(WI_GLOBAL_GET, stack_top_idx);
-        WID(WI_I32_CONST, return_size);
+        WID(WI_I32_CONST, stack_grow_amm);
         WI(WI_I32_SUB);
         WID(WI_GLOBAL_SET, stack_top_idx);
+    }
 
+    if (cc == CC_Return_Stack) {
         WID(WI_GLOBAL_GET, stack_top_idx);
-        emit_load_instruction(mod, &code, return_type, 0);
+        emit_load_instruction(mod, &code, return_type, stack_grow_amm - return_size);
     }
 
     *pcode = code;
@@ -2359,6 +2386,8 @@ static void emit_string_literal(OnyxWasmModule* mod, AstStrLit* strlit) {
             case 'r': *des++ = '\r'; break;
             case 'v': *des++ = '\v'; break;
             case 'e': *des++ = '\e'; break;
+            case '"': *des++ = '"';  break;
+            case '\\': *des++ = '\\'; break;
             case 'x': {
                 // HACK: This whole way of doing this
                 i++;