From 0d8c9fcb18b399bd2afedbcbcc7736326ef92297 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Aug 2019 00:41:49 -0400 Subject: support async functions with inferred error sets --- src/codegen.cpp | 141 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 55 deletions(-) (limited to 'src/codegen.cpp') 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); +} -- cgit v1.2.3