Commit 0d8c9fcb18

Andrew Kelley <andrew@ziglang.org>
2019-08-05 06:41:49
support async functions with inferred error sets
1 parent f27e5d4
src/all_types.hpp
@@ -3705,7 +3705,7 @@ static const size_t err_union_payload_index = 1;
 static const size_t coro_fn_ptr_index = 0;
 static const size_t coro_resume_index = 1;
 static const size_t coro_awaiter_index = 2;
-static const size_t coro_arg_start = 3;
+static const size_t coro_ret_start = 3;
 
 // TODO call graph analysis to find out what this number needs to be for every function
 // MUST BE A POWER OF TWO.
src/analyze.cpp
@@ -7,6 +7,7 @@
 
 #include "analyze.hpp"
 #include "ast_render.hpp"
+#include "codegen.hpp"
 #include "config.h"
 #include "error.hpp"
 #include "ir.hpp"
@@ -5212,23 +5213,34 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
     ZigList<ZigType *> field_types = {};
     ZigList<const char *> field_names = {};
 
-    field_names.append("fn_ptr");
+    field_names.append("@fn_ptr");
     field_types.append(fn_type);
 
-    field_names.append("resume_index");
+    field_names.append("@resume_index");
     field_types.append(g->builtin_types.entry_usize);
 
-    field_names.append("awaiter");
+    field_names.append("@awaiter");
     field_types.append(g->builtin_types.entry_usize);
 
     FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
     ZigType *ptr_return_type = get_pointer_to_type(g, fn_type_id->return_type, false);
-    field_names.append("result_ptr");
+    field_names.append("@ptr_result");
     field_types.append(ptr_return_type);
 
-    field_names.append("result");
+    field_names.append("@result");
     field_types.append(fn_type_id->return_type);
 
+    if (codegen_fn_has_err_ret_tracing(g, fn_type_id->return_type)) {
+        field_names.append("@ptr_stack_trace");
+        field_types.append(get_ptr_to_stack_trace_type(g));
+
+        field_names.append("@stack_trace");
+        field_types.append(g->stack_trace_type);
+
+        field_names.append("@instruction_addresses");
+        field_types.append(get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count));
+    }
+
     for (size_t arg_i = 0; arg_i < fn_type_id->param_count; arg_i += 1) {
         FnTypeParamInfo *param_info = &fn_type_id->param_info[arg_i];
         AstNode *param_decl_node = get_param_decl_node(fn, arg_i);
@@ -5237,7 +5249,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
         if (param_decl_node && !is_var_args) {
             param_name = param_decl_node->data.param_decl.name;
         } else {
-            param_name = buf_sprintf("arg%" ZIG_PRI_usize "", arg_i);
+            param_name = buf_sprintf("@arg%" ZIG_PRI_usize, arg_i);
         }
         ZigType *param_type = param_info->type;
         field_names.append(buf_ptr(param_name));
@@ -5260,7 +5272,13 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
                 continue;
             }
         }
-        field_names.append(instruction->name_hint);
+        const char *name;
+        if (*instruction->name_hint == 0) {
+            name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));
+        } else {
+            name = instruction->name_hint;
+        }
+        field_names.append(name);
         field_types.append(child_type);
     }
 
@@ -7369,7 +7387,7 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) {
     }
     fn_type->data.fn.gen_return_type = gen_return_type;
 
-    if (prefix_arg_error_return_trace) {
+    if (prefix_arg_error_return_trace && !is_async) {
         ZigType *gen_type = get_ptr_to_stack_trace_type(g);
         gen_param_types.append(get_llvm_type(g, gen_type));
         param_di_types.append(get_llvm_di_type(g, gen_type));
@@ -7527,110 +7545,112 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re
     ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
 
     ZigType *result_type = any_frame_type->data.any_frame.result_type;
-    if (result_type == nullptr || !type_has_bits(result_type)) {
-        LLVMTypeRef ptr_result_type = LLVMPointerType(fn_type, 0);
-        if (result_type == nullptr) {
-            g->anyframe_fn_type = ptr_result_type;
-        }
-        // label (grep this): [coro_frame_struct_layout]
-        LLVMTypeRef field_types[] = {
-            ptr_result_type, // fn_ptr
-            usize_type_ref, // resume_index
-            usize_type_ref, // awaiter
-        };
-        LLVMStructSetBody(frame_header_type, field_types, 3, false);
+    ZigType *ptr_result_type = (result_type == nullptr) ? nullptr : get_pointer_to_type(g, result_type, false);
+    LLVMTypeRef ptr_fn_llvm_type = LLVMPointerType(fn_type, 0);
+    if (result_type == nullptr) {
+        g->anyframe_fn_type = ptr_fn_llvm_type;
+    }
 
-        ZigLLVMDIType *di_element_types[] = {
-            ZigLLVMCreateDebugMemberType(g->dbuilder,
-                ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "fn_ptr",
-                di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, field_types[0]),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types[0]),
-                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, 0),
-                ZigLLVM_DIFlags_Zero, usize_di_type),
-            ZigLLVMCreateDebugMemberType(g->dbuilder,
-                ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "resume_index",
-                di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, field_types[1]),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types[1]),
-                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, 1),
-                ZigLLVM_DIFlags_Zero, usize_di_type),
-            ZigLLVMCreateDebugMemberType(g->dbuilder,
-                ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "awaiter",
-                di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, field_types[2]),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types[2]),
-                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, 2),
-                ZigLLVM_DIFlags_Zero, usize_di_type),
-        };
-        ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
-                compile_unit_scope, buf_ptr(name),
-                di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, frame_header_type),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, frame_header_type),
-                ZigLLVM_DIFlags_Zero,
-                nullptr, di_element_types, 3, 0, nullptr, "");
+    ZigList<LLVMTypeRef> field_types = {};
+    ZigList<ZigLLVMDIType *> di_element_types = {};
 
-        ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type);
-    } else {
-        ZigType *ptr_result_type = get_pointer_to_type(g, result_type, false);
-        // label (grep this): [coro_frame_struct_layout]
-        LLVMTypeRef field_types[] = {
-            LLVMPointerType(fn_type, 0), // fn_ptr
-            usize_type_ref, // resume_index
-            usize_type_ref, // awaiter
-            get_llvm_type(g, ptr_result_type), // result_ptr
-            get_llvm_type(g, result_type), // result
-        };
-        LLVMStructSetBody(frame_header_type, field_types, 5, false);
+    // label (grep this): [coro_frame_struct_layout]
+    field_types.append(ptr_fn_llvm_type); // fn_ptr
+    field_types.append(usize_type_ref); // resume_index
+    field_types.append(usize_type_ref); // awaiter
 
-        ZigLLVMDIType *di_element_types[] = {
-            ZigLLVMCreateDebugMemberType(g->dbuilder,
-                ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "fn_ptr",
-                di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, field_types[0]),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types[0]),
-                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, 0),
-                ZigLLVM_DIFlags_Zero, usize_di_type),
-            ZigLLVMCreateDebugMemberType(g->dbuilder,
-                ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "awaiter",
-                di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, field_types[1]),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types[1]),
-                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, 1),
-                ZigLLVM_DIFlags_Zero, usize_di_type),
-            ZigLLVMCreateDebugMemberType(g->dbuilder,
-                ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "awaiter",
-                di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, field_types[2]),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types[2]),
-                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, 2),
-                ZigLLVM_DIFlags_Zero, usize_di_type),
+    bool have_result_type = result_type != nullptr && type_has_bits(result_type);
+    if (have_result_type) {
+        field_types.append(get_llvm_type(g, ptr_result_type)); // ptr_result
+        field_types.append(get_llvm_type(g, result_type)); // result
+        if (codegen_fn_has_err_ret_tracing(g, result_type)) {
+            field_types.append(get_llvm_type(g, get_ptr_to_stack_trace_type(g))); // ptr_stack_trace
+            field_types.append(get_llvm_type(g, g->stack_trace_type)); // stack_trace
+            field_types.append(get_llvm_type(g, get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count))); // instruction_addresses
+        }
+    }
+    LLVMStructSetBody(frame_header_type, field_types.items, field_types.length, false);
+
+    di_element_types.append(
+        ZigLLVMCreateDebugMemberType(g->dbuilder,
+            ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "fn_ptr",
+            di_file, line,
+            8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+            8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+            8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
+            ZigLLVM_DIFlags_Zero, usize_di_type));
+    di_element_types.append(
+        ZigLLVMCreateDebugMemberType(g->dbuilder,
+            ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "resume_index",
+            di_file, line,
+            8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+            8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+            8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
+            ZigLLVM_DIFlags_Zero, usize_di_type));
+    di_element_types.append(
+        ZigLLVMCreateDebugMemberType(g->dbuilder,
+            ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "awaiter",
+            di_file, line,
+            8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+            8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+            8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
+            ZigLLVM_DIFlags_Zero, usize_di_type));
+
+    if (have_result_type) {
+        di_element_types.append(
             ZigLLVMCreateDebugMemberType(g->dbuilder,
-                ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result_ptr",
+                ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "ptr_result",
                 di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, field_types[3]),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types[3]),
-                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, 3),
-                ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_result_type)),
+                8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
+                ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, ptr_result_type)));
+        di_element_types.append(
             ZigLLVMCreateDebugMemberType(g->dbuilder,
                 ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result",
                 di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, field_types[4]),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types[4]),
-                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, 4),
-                ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, result_type)),
-        };
-        ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
-                compile_unit_scope, buf_ptr(name),
-                di_file, line,
-                8*LLVMABISizeOfType(g->target_data_ref, frame_header_type),
-                8*LLVMABIAlignmentOfType(g->target_data_ref, frame_header_type),
-                ZigLLVM_DIFlags_Zero,
-                nullptr, di_element_types, 5, 0, nullptr, "");
+                8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
+                ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, result_type)));
+
+        if (codegen_fn_has_err_ret_tracing(g, result_type)) {
+            di_element_types.append(
+                ZigLLVMCreateDebugMemberType(g->dbuilder,
+                    ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "ptr_stack_trace",
+                    di_file, line,
+                    8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                    8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                    8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
+                    ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, get_ptr_to_stack_trace_type(g))));
+            di_element_types.append(
+                ZigLLVMCreateDebugMemberType(g->dbuilder,
+                    ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "stack_trace",
+                    di_file, line,
+                    8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                    8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                    8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
+                    ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, g->stack_trace_type)));
+            di_element_types.append(
+                ZigLLVMCreateDebugMemberType(g->dbuilder,
+                    ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "instruction_addresses",
+                    di_file, line,
+                    8*LLVMABISizeOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                    8*LLVMABIAlignmentOfType(g->target_data_ref, field_types.at(di_element_types.length)),
+                    8*LLVMOffsetOfElement(g->target_data_ref, frame_header_type, di_element_types.length),
+                    ZigLLVM_DIFlags_Zero, get_llvm_di_type(g, get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count))));
+        }
+    };
 
-        ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type);
-    }
+    ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
+            compile_unit_scope, buf_ptr(name),
+            di_file, line,
+            8*LLVMABISizeOfType(g->target_data_ref, frame_header_type),
+            8*LLVMABIAlignmentOfType(g->target_data_ref, frame_header_type),
+            ZigLLVM_DIFlags_Zero,
+            nullptr, di_element_types.items, di_element_types.length, 0, nullptr, "");
+
+    ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type);
 }
 
 static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
src/codegen.cpp
@@ -297,12 +297,30 @@ static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
     zig_unreachable();
 }
 
+// label (grep this): [coro_frame_struct_layout]
+static uint32_t frame_index_trace(CodeGen *g, FnTypeId *fn_type_id) {
+    // [0] *ReturnType
+    // [1] ReturnType
+    uint32_t return_field_count = type_has_bits(fn_type_id->return_type) ? 2 : 0;
+    return coro_ret_start + return_field_count;
+}
+
+// label (grep this): [coro_frame_struct_layout]
+static uint32_t frame_index_arg(CodeGen *g, FnTypeId *fn_type_id) {
+    bool have_stack_trace = g->have_err_ret_tracing && codegen_fn_has_err_ret_tracing(g, fn_type_id->return_type);
+    // [0] *StackTrace
+    // [1] StackTrace
+    // [2] [stack_trace_ptr_count]usize
+    uint32_t trace_field_count = have_stack_trace ? 3 : 0;
+    return frame_index_trace(g, fn_type_id) + trace_field_count;
+}
+
 static uint32_t get_err_ret_trace_arg_index(CodeGen *g, ZigFn *fn_table_entry) {
     if (!g->have_err_ret_tracing) {
         return UINT32_MAX;
     }
-    if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) {
-        return 0;
+    if (fn_is_async(fn_table_entry)) {
+        return UINT32_MAX;
     }
     ZigType *fn_type = fn_table_entry->type_entry;
     if (!fn_type_can_fail(&fn_type->data.fn.fn_type_id)) {
@@ -438,10 +456,6 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
     } else {
         LLVMSetFunctionCallConv(llvm_fn, get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc));
     }
-    if (cc == CallingConventionAsync) {
-        addLLVMFnAttr(llvm_fn, "optnone");
-        addLLVMFnAttr(llvm_fn, "noinline");
-    }
 
     bool want_cold = fn->is_cold || cc == CallingConventionCold;
     if (want_cold) {
@@ -1273,8 +1287,8 @@ static LLVMValueRef get_cur_err_ret_trace_val(CodeGen *g, Scope *scope) {
     if (!g->have_err_ret_tracing) {
         return nullptr;
     }
-    if (g->cur_fn->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) {
-        return g->cur_err_ret_trace_val_stack;
+    if (fn_is_async(g->cur_fn)) {
+        return LLVMBuildLoad(g->builder, g->cur_err_ret_trace_val_arg, "");
     }
     if (g->cur_err_ret_trace_val_stack != nullptr) {
         return g->cur_err_ret_trace_val_stack;
@@ -2006,7 +2020,6 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable,
 {
     if (fn_is_async(g->cur_fn)) {
         LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
-        LLVMValueRef locals_ptr = g->cur_ret_ptr;
         bool ret_type_has_bits = return_instruction->value != nullptr &&
             type_has_bits(return_instruction->value->value.type);
         ZigType *ret_type = ret_type_has_bits ? return_instruction->value->value.type : nullptr;
@@ -2018,7 +2031,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable,
 
         LLVMValueRef result_ptr_as_usize;
         if (ret_type_has_bits) {
-            LLVMValueRef result_ptr_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_arg_start, "");
+            LLVMValueRef result_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_ret_start, "");
             LLVMValueRef result_ptr = LLVMBuildLoad(g->builder, result_ptr_ptr, "");
             if (!handle_is_ptr(ret_type)) {
                 // It's a scalar, so it didn't get written to the result ptr. Do that now.
@@ -3256,7 +3269,7 @@ static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutable *executable,
         return nullptr;
     src_assert(g->cur_ret_ptr != nullptr, instruction->base.source_node);
     if (fn_is_async(g->cur_fn)) {
-        LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_arg_start, "");
+        LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_ret_start, "");
         return LLVMBuildLoad(g->builder, ptr_ptr, "");
     }
     return g->cur_ret_ptr;
@@ -3356,12 +3369,6 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI
     }
 }
 
-static bool get_prefix_arg_err_ret_stack(CodeGen *g, FnTypeId *fn_type_id) {
-    return g->have_err_ret_tracing &&
-        (fn_type_id->return_type->id == ZigTypeIdErrorUnion ||
-         fn_type_id->return_type->id == ZigTypeIdErrorSet);
-}
-
 static LLVMValueRef get_new_stack_addr(CodeGen *g, LLVMValueRef new_stack) {
     LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_ptr_index, "");
     LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_len_index, "");
@@ -3402,7 +3409,7 @@ static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) {
 static void render_async_spills(CodeGen *g) {
     ZigType *fn_type = g->cur_fn->type_entry;
     ZigType *import = get_scope_import(&g->cur_fn->fndef_scope->base);
-    size_t async_var_index = coro_arg_start + (type_has_bits(fn_type->data.fn.fn_type_id.return_type) ? 2 : 0);
+    uint32_t async_var_index = frame_index_arg(g, &fn_type->data.fn.fn_type_id);
     for (size_t var_i = 0; var_i < g->cur_fn->variable_list.length; var_i += 1) {
         ZigVar *var = g->cur_fn->variable_list.at(var_i);
 
@@ -3518,11 +3525,11 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
     CallingConvention cc = fn_type->data.fn.fn_type_id.cc;
 
     bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id);
-    bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id);
+    bool prefix_arg_err_ret_stack = codegen_fn_has_err_ret_tracing(g, fn_type_id->return_type);
     bool is_var_args = fn_type_id->is_var_args;
     ZigList<LLVMValueRef> gen_param_values = {};
     LLVMValueRef result_loc = instruction->result_loc ? ir_llvm_value(g, instruction->result_loc) : nullptr;
-    LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->llvm_type);
+    LLVMValueRef zero = LLVMConstNull(usize_type_ref);
     LLVMValueRef frame_result_loc;
     LLVMValueRef awaiter_init_val;
     LLVMValueRef ret_ptr;
@@ -3534,7 +3541,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
 
             if (ret_has_bits) {
                 // Use the result location which is inside the frame if this is an async call.
-                ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, "");
+                ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_ret_start + 1, "");
             }
         } else {
             LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack);
@@ -3564,14 +3571,49 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
                 ret_ptr = result_loc;
             }
         }
+
+        if (prefix_arg_err_ret_stack) {
+            uint32_t trace_field_index = frame_index_trace(g, fn_type_id);
+            LLVMValueRef trace_field_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc,
+                    trace_field_index, "");
+            LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc,
+                    trace_field_index + 1, "");
+            LLVMValueRef trace_field_addrs = LLVMBuildStructGEP(g->builder, frame_result_loc,
+                    trace_field_index + 2, "");
+            LLVMBuildStore(g->builder, trace_field_ptr, trace_field_ptr_ptr);
+
+            LLVMValueRef index_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 0, "");
+            LLVMBuildStore(g->builder, zero, index_ptr);
+
+            LLVMValueRef addrs_slice_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 1, "");
+            LLVMValueRef addrs_ptr_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_ptr_index, "");
+            LLVMValueRef indices[] = { LLVMConstNull(usize_type_ref), LLVMConstNull(usize_type_ref) };
+            LLVMValueRef trace_field_addrs_as_ptr = LLVMBuildInBoundsGEP(g->builder, trace_field_addrs, indices, 2, "");
+            LLVMBuildStore(g->builder, trace_field_addrs_as_ptr, addrs_ptr_ptr);
+
+            LLVMValueRef addrs_len_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_len_index, "");
+            LLVMBuildStore(g->builder, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), addrs_len_ptr);
+        }
     } else if (callee_is_async) {
         frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
-        awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr,
-                g->builtin_types.entry_usize->llvm_type, ""); // caller's own frame pointer
+        awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr, usize_type_ref, ""); // caller's own frame pointer
         if (ret_has_bits) {
-            // Use the call instruction's result location.
-            ret_ptr = result_loc;
+            if (result_loc != nullptr) {
+                // Use the call instruction's result location.
+                ret_ptr = result_loc;
+            } else {
+                // return type is a scalar, but we still need a pointer to it. Use the async fn frame.
+                ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_ret_start + 1, "");
+            }
+        }
+
+        if (prefix_arg_err_ret_stack) {
+            uint32_t trace_field_index = frame_index_trace(g, fn_type_id);
+            LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, trace_field_index, "");
+            LLVMValueRef err_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);
+            LLVMBuildStore(g->builder, err_trace_val, trace_field_ptr);
         }
+
     }
     if (instruction->is_async || callee_is_async) {
         assert(frame_result_loc != nullptr);
@@ -3584,19 +3626,14 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
         LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_resume_index, "");
         LLVMBuildStore(g->builder, zero, resume_index_ptr);
 
-        if (prefix_arg_err_ret_stack) {
-            zig_panic("TODO");
-        }
-
         LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_awaiter_index, "");
         LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr);
 
         if (ret_has_bits) {
-            LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
+            LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_ret_start, "");
             LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);
         }
-    }
-    if (!instruction->is_async && !callee_is_async) {
+    } else {
         if (first_arg_ret) {
             gen_param_values.append(result_loc);
         }
@@ -3628,16 +3665,15 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
     LLVMValueRef result;
 
     if (instruction->is_async || callee_is_async) {
-        size_t ret_2_or_0 = type_has_bits(fn_type->data.fn.fn_type_id.return_type) ? 2 : 0;
-        size_t arg_start_i = coro_arg_start + ret_2_or_0;
+        uint32_t arg_start_i = frame_index_arg(g, &fn_type->data.fn.fn_type_id);
 
         LLVMValueRef casted_frame;
         if (instruction->new_stack != nullptr) {
             // We need the frame type to be a pointer to a struct that includes the args
-            // label (grep this): [coro_frame_struct_layout]
             size_t field_count = arg_start_i + gen_param_values.length;
             LLVMTypeRef *field_types = allocate_nonzero<LLVMTypeRef>(field_count);
             LLVMGetStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc)), field_types);
+            assert(LLVMCountStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc))) == arg_start_i);
             for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) {
                 field_types[arg_start_i + arg_i] = LLVMTypeOf(gen_param_values.at(arg_i));
             }
@@ -5198,7 +5234,7 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
     LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, coro_awaiter_index, "");
     LLVMValueRef result_ptr_as_usize;
     if (type_has_bits(result_type)) {
-        LLVMValueRef result_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, coro_arg_start, "");
+        LLVMValueRef result_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, coro_ret_start, "");
         LLVMValueRef result_ptr = LLVMBuildLoad(g->builder, result_ptr_ptr, "");
         result_ptr_as_usize = LLVMBuildPtrToInt(g->builder, result_ptr, usize_type_ref, "");
     } else {
@@ -5259,23 +5295,6 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
     }
 }
 
-static LLVMTypeRef anyframe_fn_type(CodeGen *g) {
-    if (g->anyframe_fn_type != nullptr)
-        return g->anyframe_fn_type;
-
-    LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
-    ZigType *anyframe_type = get_any_frame_type(g, nullptr);
-    LLVMTypeRef return_type = LLVMVoidType();
-    LLVMTypeRef param_types[] = {
-        get_llvm_type(g, anyframe_type),
-        usize_type_ref,
-    };
-    LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, 2, false);
-    g->anyframe_fn_type = LLVMPointerType(fn_type, 0);
-
-    return g->anyframe_fn_type;
-}
-
 static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable,
         IrInstructionCoroResume *instruction)
 {
@@ -5285,7 +5304,7 @@ static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable,
     assert(frame_type->id == ZigTypeIdAnyFrame);
     LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, frame, coro_fn_ptr_index, "");
     LLVMValueRef uncasted_fn_val = LLVMBuildLoad(g->builder, fn_ptr_ptr, "");
-    LLVMValueRef fn_val = LLVMBuildIntToPtr(g->builder, uncasted_fn_val, anyframe_fn_type(g), "");
+    LLVMValueRef fn_val = LLVMBuildIntToPtr(g->builder, uncasted_fn_val, g->anyframe_fn_type, "");
     LLVMValueRef arg_val = ir_want_runtime_safety(g, &instruction->base) ?
         LLVMConstAllOnes(usize_type_ref) : LLVMGetUndef(usize_type_ref);
     LLVMValueRef args[] = {frame, arg_val};
@@ -6636,7 +6655,8 @@ static void do_code_gen(CodeGen *g) {
         }
 
         // error return tracing setup
-        bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && !is_async && !have_err_ret_trace_arg;
+        bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn &&
+            !is_async && !have_err_ret_trace_arg;
         LLVMValueRef err_ret_array_val = nullptr;
         if (have_err_ret_trace_stack) {
             ZigType *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count);
@@ -6780,6 +6800,11 @@ static void do_code_gen(CodeGen *g) {
             g->cur_async_awaiter_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_awaiter_index, "");
             LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_resume_index, "");
             g->cur_async_resume_index_ptr = resume_index_ptr;
+            if (codegen_fn_has_err_ret_tracing(g, fn_type_id->return_type)) {
+                uint32_t field_index = frame_index_trace(g, fn_type_id);
+                g->cur_err_ret_trace_val_arg = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, field_index, "");
+            }
+
             LLVMValueRef resume_index = LLVMBuildLoad(g->builder, resume_index_ptr, "");
             LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, resume_index, bad_resume_block, 4);
             g->cur_async_switch_instr = switch_instr;
@@ -9691,3 +9716,9 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget
 
     return g;
 }
+
+bool codegen_fn_has_err_ret_tracing(CodeGen *g, ZigType *return_type) {
+    return g->have_err_ret_tracing &&
+        (return_type->id == ZigTypeIdErrorUnion ||
+         return_type->id == ZigTypeIdErrorSet);
+}
src/codegen.hpp
@@ -61,5 +61,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g);
 TargetSubsystem detect_subsystem(CodeGen *g);
 
 void codegen_release_caches(CodeGen *codegen);
+bool codegen_fn_has_err_ret_tracing(CodeGen *g, ZigType *return_type);
 
 #endif
src/ir.cpp
@@ -14859,11 +14859,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc
         ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,
         IrInstruction *casted_new_stack)
 {
-    if (fn_entry == nullptr) {
-        if (call_instruction->new_stack == nullptr) {
-            ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
-            return ira->codegen->invalid_instruction;
-        }
+    if (casted_new_stack != nullptr) {
         // this is an @asyncCall
 
         if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) {
@@ -14881,6 +14877,9 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc
         IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, nullptr, fn_ref,
                 arg_count, casted_args, FnInlineAuto, true, casted_new_stack, ret_ptr, anyframe_type);
         return &call_gen->base;
+    } else if (fn_entry == nullptr) {
+        ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
+        return ira->codegen->invalid_instruction;
     }
 
     ZigType *frame_type = get_coro_frame_type(ira->codegen, fn_entry);
test/stage1/behavior/coroutines.zig
@@ -161,13 +161,13 @@ fn seq(c: u8) void {
 
 test "coroutine suspend with block" {
     const p = async testSuspendBlock();
-    expect(!result);
+    expect(!global_result);
     resume a_promise;
-    expect(result);
+    expect(global_result);
 }
 
 var a_promise: anyframe = undefined;
-var result = false;
+var global_result = false;
 async fn testSuspendBlock() void {
     suspend {
         comptime expect(@typeOf(@frame()) == *@Frame(testSuspendBlock));
@@ -178,7 +178,7 @@ async fn testSuspendBlock() void {
     // var our_handle: anyframe = @frame();
     expect(a_promise == anyframe(@frame()));
 
-    result = true;
+    global_result = true;
 }
 
 var await_a_promise: anyframe = undefined;
@@ -283,29 +283,56 @@ test "@asyncCall with return type" {
     const Foo = struct {
         bar: async fn () i32,
 
-        async fn afunc() i32 {
+        var global_frame: anyframe = undefined;
+
+        async fn middle() i32 {
+            return afunc();
+        }
+
+        fn afunc() i32 {
+            global_frame = @frame();
             suspend;
             return 1234;
         }
     };
-    var foo = Foo{ .bar = Foo.afunc };
-    var bytes: [64]u8 = undefined;
+    var foo = Foo{ .bar = Foo.middle };
+    var bytes: [100]u8 = undefined;
     var aresult: i32 = 0;
-    const frame = @asyncCall(&bytes, &aresult, foo.bar);
+    _ = @asyncCall(&bytes, &aresult, foo.bar);
     expect(aresult == 0);
-    resume frame;
+    resume Foo.global_frame;
     expect(aresult == 1234);
 }
 
-//test "async fn with inferred error set" {
-//    const p = async failing();
-//    resume p;
-//}
-//
-//async fn failing() !void {
-//    suspend;
-//    return error.Fail;
-//}
+test "async fn with inferred error set" {
+    const S = struct {
+        var global_frame: anyframe = undefined;
+
+        fn doTheTest() void {
+            var frame: [1]@Frame(middle) = undefined;
+            var result: anyerror!void = undefined;
+            _ = @asyncCall(@sliceToBytes(frame[0..]), &result, middle);
+            resume global_frame;
+            std.testing.expectError(error.Fail, result);
+        }
+
+        async fn middle() !void {
+            var f = async middle2();
+            return await f;
+        }
+
+        fn middle2() !void {
+            return failing();
+        }
+
+        fn failing() !void {
+            global_frame = @frame();
+            suspend;
+            return error.Fail;
+        }
+    };
+    S.doTheTest();
+}
 
 //test "error return trace across suspend points - early return" {
 //    const p = nonFailing();
@@ -422,24 +449,24 @@ test "async function call return value" {
 
 test "suspension points inside branching control flow" {
     const S = struct {
-        var global_result: i32 = 10;
+        var result: i32 = 10;
 
         fn doTheTest() void {
-            expect(10 == global_result);
+            expect(10 == result);
             var frame = async func(true);
-            expect(10 == global_result);
+            expect(10 == result);
             resume frame;
-            expect(11 == global_result);
+            expect(11 == result);
             resume frame;
-            expect(12 == global_result);
+            expect(12 == result);
             resume frame;
-            expect(13 == global_result);
+            expect(13 == result);
         }
 
         fn func(b: bool) void {
             while (b) {
                 suspend;
-                global_result += 1;
+                result += 1;
             }
         }
     };
BRANCH_TODO
@@ -1,3 +1,4 @@
+ * delete IrInstructionMarkErrRetTracePtr
  * go over the commented out tests
  * error return tracing
  * compile error for error: expected anyframe->T, found 'anyframe'
@@ -32,3 +33,4 @@
    - resume
    - anyframe, anyframe->T
  * safety for double await
+ * call graph analysis to have fewer stack trace frames