diff --git a/BRANCH_TODO b/BRANCH_TODO index f76252d935..fcd98f0f71 100644 --- a/BRANCH_TODO +++ b/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 diff --git a/src/all_types.hpp b/src/all_types.hpp index 7c903677a8..1ea4954dec 100644 --- a/src/all_types.hpp +++ b/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. diff --git a/src/analyze.cpp b/src/analyze.cpp index e7480c579b..e1b386d9af 100644 --- a/src/analyze.cpp +++ b/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 field_types = {}; ZigList 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; + 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; + } + + ZigList field_types = {}; + ZigList di_element_types = {}; + + // 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 + + 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 } - // 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); + } + LLVMStructSetBody(frame_header_type, field_types.items, field_types.length, false); - 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, ""); + 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)); - 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); - - ZigLLVMDIType *di_element_types[] = { + if (have_result_type) { + di_element_types.append( ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "fn_ptr", + ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "ptr_result", 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), - ZigLLVMCreateDebugMemberType(g->dbuilder, - ZigLLVMTypeToScope(any_frame_type->llvm_di_type), "result_ptr", - 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))); - ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_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)))); + } + }; + + 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) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 4ecdfd3bdd..59289523a7 100644 --- a/src/codegen.cpp +++ b/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 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(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); +} diff --git a/src/codegen.hpp b/src/codegen.hpp index cdff61a26f..0ee4ce837e 100644 --- a/src/codegen.hpp +++ b/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 diff --git a/src/ir.cpp b/src/ir.cpp index 45a48d6f50..e6212fa079 100644 --- a/src/ir.cpp +++ b/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); diff --git a/test/stage1/behavior/coroutines.zig b/test/stage1/behavior/coroutines.zig index ccf9485b51..c4f4cd3c99 100644 --- a/test/stage1/behavior/coroutines.zig +++ b/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; } } };