| author | |
| committer | |
| log | 056c4e2c988c0a2ff6f1be8fe18a0a056d848271 |
| tree | 86d830fbc801536ec06d7f5d7e609ff01e32288a |
| parent | 0f879d02a4c4b1de0e28c2863c1e5f3760eb5b19 |
| signature | Commit is signed but in an unrecognized format. |
9 files changed, 384 insertions(+), 146 deletions(-)
BRANCH_TODO+5-1| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | * await | 1 | * compile error for error: expected anyframe->T, found 'anyframe' |
| 2 | * compile error for error: expected anyframe->T, found 'i32' | ||
| 2 | * await of a non async function | 3 | * await of a non async function |
| 3 | * await in single-threaded mode | 4 | * await in single-threaded mode |
| 4 | * async call on a non async function | 5 | * async call on a non async function |
| ... | @@ -13,3 +14,6 @@ | ... | @@ -13,3 +14,6 @@ |
| 13 | * @typeInfo for @Frame(func) | 14 | * @typeInfo for @Frame(func) |
| 14 | * peer type resolution of *@Frame(func) and anyframe | 15 | * peer type resolution of *@Frame(func) and anyframe |
| 15 | * peer type resolution of *@Frame(func) and anyframe->T when the return type matches | 16 | * peer type resolution of *@Frame(func) and anyframe->T when the return type matches |
| 17 | * returning a value from within a suspend block | ||
| 18 | * struct types as the return type of an async function. make sure it works with return result locations. | ||
| 19 | * make resuming inside a suspend block, with nothing after it, a must-tail call. |
src/all_types.hpp+9-1| ... | @@ -1550,6 +1550,8 @@ enum PanicMsgId { | ... | @@ -1550,6 +1550,8 @@ enum PanicMsgId { |
| 1550 | PanicMsgIdFloatToInt, | 1550 | PanicMsgIdFloatToInt, |
| 1551 | PanicMsgIdPtrCastNull, | 1551 | PanicMsgIdPtrCastNull, |
| 1552 | PanicMsgIdBadResume, | 1552 | PanicMsgIdBadResume, |
| 1553 | PanicMsgIdBadAwait, | ||
| 1554 | PanicMsgIdBadReturn, | ||
| 1553 | 1555 | ||
| 1554 | PanicMsgIdCount, | 1556 | PanicMsgIdCount, |
| 1555 | }; | 1557 | }; |
| ... | @@ -1795,7 +1797,6 @@ struct CodeGen { | ... | @@ -1795,7 +1797,6 @@ struct CodeGen { |
| 1795 | ZigType *entry_arg_tuple; | 1797 | ZigType *entry_arg_tuple; |
| 1796 | ZigType *entry_enum_literal; | 1798 | ZigType *entry_enum_literal; |
| 1797 | ZigType *entry_any_frame; | 1799 | ZigType *entry_any_frame; |
| 1798 | ZigType *entry_async_fn; | ||
| 1799 | } builtin_types; | 1800 | } builtin_types; |
| 1800 | 1801 | ||
| 1801 | ZigType *align_amt_type; | 1802 | ZigType *align_amt_type; |
| ... | @@ -2348,6 +2349,7 @@ enum IrInstructionId { | ... | @@ -2348,6 +2349,7 @@ enum IrInstructionId { |
| 2348 | IrInstructionIdUnionInitNamedField, | 2349 | IrInstructionIdUnionInitNamedField, |
| 2349 | IrInstructionIdSuspendBegin, | 2350 | IrInstructionIdSuspendBegin, |
| 2350 | IrInstructionIdSuspendBr, | 2351 | IrInstructionIdSuspendBr, |
| 2352 | IrInstructionIdAwait, | ||
| 2351 | IrInstructionIdCoroResume, | 2353 | IrInstructionIdCoroResume, |
| 2352 | }; | 2354 | }; |
| 2353 | 2355 | ||
| ... | @@ -3600,6 +3602,12 @@ struct IrInstructionSuspendBr { | ... | @@ -3600,6 +3602,12 @@ struct IrInstructionSuspendBr { |
| 3600 | IrBasicBlock *resume_block; | 3602 | IrBasicBlock *resume_block; |
| 3601 | }; | 3603 | }; |
| 3602 | 3604 | ||
| 3605 | struct IrInstructionAwait { | ||
| 3606 | IrInstruction base; | ||
| 3607 | |||
| 3608 | IrInstruction *frame; | ||
| 3609 | }; | ||
| 3610 | |||
| 3603 | struct IrInstructionCoroResume { | 3611 | struct IrInstructionCoroResume { |
| 3604 | IrInstruction base; | 3612 | IrInstruction base; |
| 3605 | 3613 |
src/analyze.cpp+21-4| ... | @@ -3807,6 +3807,9 @@ static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) { | ... | @@ -3807,6 +3807,9 @@ static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) { |
| 3807 | } else if (fn->inferred_async_node->type == NodeTypeSuspend) { | 3807 | } else if (fn->inferred_async_node->type == NodeTypeSuspend) { |
| 3808 | add_error_note(g, msg, fn->inferred_async_node, | 3808 | add_error_note(g, msg, fn->inferred_async_node, |
| 3809 | buf_sprintf("suspends here")); | 3809 | buf_sprintf("suspends here")); |
| 3810 | } else if (fn->inferred_async_node->type == NodeTypeAwaitExpr) { | ||
| 3811 | add_error_note(g, msg, fn->inferred_async_node, | ||
| 3812 | buf_sprintf("await is a suspend point")); | ||
| 3810 | } else { | 3813 | } else { |
| 3811 | zig_unreachable(); | 3814 | zig_unreachable(); |
| 3812 | } | 3815 | } |
| ... | @@ -7361,7 +7364,7 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) { | ... | @@ -7361,7 +7364,7 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) { |
| 7361 | param_di_types.append(get_llvm_di_type(g, gen_type)); | 7364 | param_di_types.append(get_llvm_di_type(g, gen_type)); |
| 7362 | } | 7365 | } |
| 7363 | if (is_async) { | 7366 | if (is_async) { |
| 7364 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(1); | 7367 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(2); |
| 7365 | 7368 | ||
| 7366 | ZigType *frame_type = get_any_frame_type(g, fn_type_id->return_type); | 7369 | ZigType *frame_type = get_any_frame_type(g, fn_type_id->return_type); |
| 7367 | gen_param_types.append(get_llvm_type(g, frame_type)); | 7370 | gen_param_types.append(get_llvm_type(g, frame_type)); |
| ... | @@ -7370,6 +7373,13 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) { | ... | @@ -7370,6 +7373,13 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) { |
| 7370 | fn_type->data.fn.gen_param_info[0].src_index = 0; | 7373 | fn_type->data.fn.gen_param_info[0].src_index = 0; |
| 7371 | fn_type->data.fn.gen_param_info[0].gen_index = 0; | 7374 | fn_type->data.fn.gen_param_info[0].gen_index = 0; |
| 7372 | fn_type->data.fn.gen_param_info[0].type = frame_type; | 7375 | fn_type->data.fn.gen_param_info[0].type = frame_type; |
| 7376 | |||
| 7377 | gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize)); | ||
| 7378 | param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize)); | ||
| 7379 | |||
| 7380 | fn_type->data.fn.gen_param_info[1].src_index = 1; | ||
| 7381 | fn_type->data.fn.gen_param_info[1].gen_index = 1; | ||
| 7382 | fn_type->data.fn.gen_param_info[1].type = g->builtin_types.entry_usize; | ||
| 7373 | } else { | 7383 | } else { |
| 7374 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count); | 7384 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count); |
| 7375 | for (size_t i = 0; i < fn_type_id->param_count; i += 1) { | 7385 | for (size_t i = 0; i < fn_type_id->param_count; i += 1) { |
| ... | @@ -7434,15 +7444,21 @@ void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) { | ... | @@ -7434,15 +7444,21 @@ void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) { |
| 7434 | 7444 | ||
| 7435 | ZigType *gen_return_type = g->builtin_types.entry_void; | 7445 | ZigType *gen_return_type = g->builtin_types.entry_void; |
| 7436 | ZigList<ZigLLVMDIType *> param_di_types = {}; | 7446 | ZigList<ZigLLVMDIType *> param_di_types = {}; |
| 7447 | ZigList<LLVMTypeRef> gen_param_types = {}; | ||
| 7437 | // first "parameter" is return value | 7448 | // first "parameter" is return value |
| 7438 | param_di_types.append(get_llvm_di_type(g, gen_return_type)); | 7449 | param_di_types.append(get_llvm_di_type(g, gen_return_type)); |
| 7439 | 7450 | ||
| 7440 | ZigType *frame_type = get_coro_frame_type(g, fn); | 7451 | ZigType *frame_type = get_coro_frame_type(g, fn); |
| 7441 | ZigType *ptr_type = get_pointer_to_type(g, frame_type, false); | 7452 | ZigType *ptr_type = get_pointer_to_type(g, frame_type, false); |
| 7442 | LLVMTypeRef gen_param_type = get_llvm_type(g, ptr_type); | 7453 | gen_param_types.append(get_llvm_type(g, ptr_type)); |
| 7443 | param_di_types.append(get_llvm_di_type(g, ptr_type)); | 7454 | param_di_types.append(get_llvm_di_type(g, ptr_type)); |
| 7444 | 7455 | ||
| 7445 | fn->raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type), &gen_param_type, 1, false); | 7456 | // this parameter is used to pass the result pointer when await completes |
| 7457 | gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize)); | ||
| 7458 | param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize)); | ||
| 7459 | |||
| 7460 | fn->raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type), | ||
| 7461 | gen_param_types.items, gen_param_types.length, false); | ||
| 7446 | fn->raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); | 7462 | fn->raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); |
| 7447 | } | 7463 | } |
| 7448 | 7464 | ||
| ... | @@ -7493,7 +7509,8 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re | ... | @@ -7493,7 +7509,8 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re |
| 7493 | 8*g->pointer_size_bytes, 8*g->builtin_types.entry_usize->abi_align, buf_ptr(&any_frame_type->name)); | 7509 | 8*g->pointer_size_bytes, 8*g->builtin_types.entry_usize->abi_align, buf_ptr(&any_frame_type->name)); |
| 7494 | 7510 | ||
| 7495 | LLVMTypeRef llvm_void = LLVMVoidType(); | 7511 | LLVMTypeRef llvm_void = LLVMVoidType(); |
| 7496 | LLVMTypeRef fn_type = LLVMFunctionType(llvm_void, &any_frame_type->llvm_type, 1, false); | 7512 | LLVMTypeRef arg_types[] = {any_frame_type->llvm_type, g->builtin_types.entry_usize->llvm_type}; |
| 7513 | LLVMTypeRef fn_type = LLVMFunctionType(llvm_void, arg_types, 2, false); | ||
| 7497 | LLVMTypeRef usize_type_ref = get_llvm_type(g, g->builtin_types.entry_usize); | 7514 | LLVMTypeRef usize_type_ref = get_llvm_type(g, g->builtin_types.entry_usize); |
| 7498 | ZigLLVMDIType *usize_di_type = get_llvm_di_type(g, g->builtin_types.entry_usize); | 7515 | ZigLLVMDIType *usize_di_type = get_llvm_di_type(g, g->builtin_types.entry_usize); |
| 7499 | ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); | 7516 | ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); |
src/ast_render.cpp+3-1| ... | @@ -1149,9 +1149,11 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { | ... | @@ -1149,9 +1149,11 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 1149 | } | 1149 | } |
| 1150 | case NodeTypeSuspend: | 1150 | case NodeTypeSuspend: |
| 1151 | { | 1151 | { |
| 1152 | fprintf(ar->f, "suspend"); | ||
| 1153 | if (node->data.suspend.block != nullptr) { | 1152 | if (node->data.suspend.block != nullptr) { |
| 1153 | fprintf(ar->f, "suspend "); | ||
| 1154 | render_node_grouped(ar, node->data.suspend.block); | 1154 | render_node_grouped(ar, node->data.suspend.block); |
| 1155 | } else { | ||
| 1156 | fprintf(ar->f, "suspend\n"); | ||
| 1155 | } | 1157 | } |
| 1156 | break; | 1158 | break; |
| 1157 | } | 1159 | } |
src/codegen.cpp+154-8| ... | @@ -873,6 +873,10 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { | ... | @@ -873,6 +873,10 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 873 | return buf_create_from_str("cast causes pointer to be null"); | 873 | return buf_create_from_str("cast causes pointer to be null"); |
| 874 | case PanicMsgIdBadResume: | 874 | case PanicMsgIdBadResume: |
| 875 | return buf_create_from_str("invalid resume of async function"); | 875 | return buf_create_from_str("invalid resume of async function"); |
| 876 | case PanicMsgIdBadAwait: | ||
| 877 | return buf_create_from_str("async function awaited twice"); | ||
| 878 | case PanicMsgIdBadReturn: | ||
| 879 | return buf_create_from_str("async function returned twice"); | ||
| 876 | } | 880 | } |
| 877 | zig_unreachable(); | 881 | zig_unreachable(); |
| 878 | } | 882 | } |
| ... | @@ -1991,14 +1995,66 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut | ... | @@ -1991,14 +1995,66 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut |
| 1991 | 1995 | ||
| 1992 | static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { | 1996 | static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { |
| 1993 | if (fn_is_async(g->cur_fn)) { | 1997 | if (fn_is_async(g->cur_fn)) { |
| 1998 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | ||
| 1999 | LLVMValueRef locals_ptr = g->cur_ret_ptr; | ||
| 2000 | bool ret_type_has_bits = return_instruction->value != nullptr && | ||
| 2001 | type_has_bits(return_instruction->value->value.type); | ||
| 2002 | ZigType *ret_type = ret_type_has_bits ? return_instruction->value->value.type : nullptr; | ||
| 2003 | |||
| 1994 | if (ir_want_runtime_safety(g, &return_instruction->base)) { | 2004 | if (ir_want_runtime_safety(g, &return_instruction->base)) { |
| 1995 | LLVMValueRef locals_ptr = g->cur_ret_ptr; | ||
| 1996 | LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_fn_ptr_index, ""); | 2005 | LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_fn_ptr_index, ""); |
| 1997 | LLVMValueRef new_resume_fn = g->cur_fn->resume_blocks.last()->split_llvm_fn; | 2006 | LLVMValueRef new_resume_fn = g->cur_fn->resume_blocks.last()->split_llvm_fn; |
| 1998 | LLVMBuildStore(g->builder, new_resume_fn, resume_index_ptr); | 2007 | LLVMBuildStore(g->builder, new_resume_fn, resume_index_ptr); |
| 1999 | } | 2008 | } |
| 2000 | 2009 | ||
| 2010 | LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_awaiter_index, ""); | ||
| 2011 | LLVMValueRef result_ptr_as_usize; | ||
| 2012 | if (ret_type_has_bits) { | ||
| 2013 | LLVMValueRef result_ptr_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_arg_start, ""); | ||
| 2014 | LLVMValueRef result_ptr = LLVMBuildLoad(g->builder, result_ptr_ptr, ""); | ||
| 2015 | if (!handle_is_ptr(ret_type)) { | ||
| 2016 | // It's a scalar, so it didn't get written to the result ptr. Do that now. | ||
| 2017 | LLVMBuildStore(g->builder, ir_llvm_value(g, return_instruction->value), result_ptr); | ||
| 2018 | } | ||
| 2019 | result_ptr_as_usize = LLVMBuildPtrToInt(g->builder, result_ptr, usize_type_ref, ""); | ||
| 2020 | } else { | ||
| 2021 | result_ptr_as_usize = LLVMGetUndef(usize_type_ref); | ||
| 2022 | } | ||
| 2023 | LLVMValueRef zero = LLVMConstNull(usize_type_ref); | ||
| 2024 | LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); | ||
| 2025 | LLVMValueRef prev_val = LLVMBuildAtomicRMW(g->builder, LLVMAtomicRMWBinOpXchg, awaiter_ptr, | ||
| 2026 | all_ones, LLVMAtomicOrderingSequentiallyConsistent, g->is_single_threaded); | ||
| 2027 | |||
| 2028 | LLVMBasicBlockRef bad_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadReturn"); | ||
| 2029 | LLVMBasicBlockRef early_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "EarlyReturn"); | ||
| 2030 | LLVMBasicBlockRef resume_them_block = LLVMAppendBasicBlock(g->cur_fn_val, "ResumeThem"); | ||
| 2031 | |||
| 2032 | LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, prev_val, resume_them_block, 2); | ||
| 2033 | |||
| 2034 | LLVMAddCase(switch_instr, zero, early_return_block); | ||
| 2035 | LLVMAddCase(switch_instr, all_ones, bad_return_block); | ||
| 2036 | |||
| 2037 | // Something has gone horribly wrong, and this is an invalid second return. | ||
| 2038 | LLVMPositionBuilderAtEnd(g->builder, bad_return_block); | ||
| 2039 | gen_assertion(g, PanicMsgIdBadReturn, &return_instruction->base); | ||
| 2040 | |||
| 2041 | // The caller will deal with fetching the result - we're done. | ||
| 2042 | LLVMPositionBuilderAtEnd(g->builder, early_return_block); | ||
| 2001 | LLVMBuildRetVoid(g->builder); | 2043 | LLVMBuildRetVoid(g->builder); |
| 2044 | |||
| 2045 | // We need to resume the caller by tail calling them. | ||
| 2046 | LLVMPositionBuilderAtEnd(g->builder, resume_them_block); | ||
| 2047 | ZigType *any_frame_type = get_any_frame_type(g, ret_type); | ||
| 2048 | LLVMValueRef their_frame_ptr = LLVMBuildIntToPtr(g->builder, prev_val, | ||
| 2049 | get_llvm_type(g, any_frame_type), ""); | ||
| 2050 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, their_frame_ptr, coro_fn_ptr_index, ""); | ||
| 2051 | LLVMValueRef awaiter_fn = LLVMBuildLoad(g->builder, fn_ptr_ptr, ""); | ||
| 2052 | LLVMValueRef args[] = {their_frame_ptr, result_ptr_as_usize}; | ||
| 2053 | LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, awaiter_fn, args, 2, LLVMFastCallConv, | ||
| 2054 | ZigLLVM_FnInlineAuto, ""); | ||
| 2055 | ZigLLVMSetTailCall(call_inst); | ||
| 2056 | LLVMBuildRetVoid(g->builder); | ||
| 2057 | |||
| 2002 | return nullptr; | 2058 | return nullptr; |
| 2003 | } | 2059 | } |
| 2004 | if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { | 2060 | if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { |
| ... | @@ -3514,14 +3570,17 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr | ... | @@ -3514,14 +3570,17 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3514 | } | 3570 | } |
| 3515 | } | 3571 | } |
| 3516 | if (instruction->is_async) { | 3572 | if (instruction->is_async) { |
| 3517 | ZigLLVMBuildCall(g->builder, fn_val, &frame_result_loc, 1, llvm_cc, fn_inline, ""); | 3573 | LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(g->builtin_types.entry_usize->llvm_type)}; |
| 3574 | ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, ""); | ||
| 3518 | return nullptr; | 3575 | return nullptr; |
| 3519 | } else if (callee_is_async) { | 3576 | } else if (callee_is_async) { |
| 3577 | ZigType *ptr_result_type = get_pointer_to_type(g, src_return_type, true); | ||
| 3520 | LLVMValueRef split_llvm_fn = make_fn_llvm_value(g, g->cur_fn); | 3578 | LLVMValueRef split_llvm_fn = make_fn_llvm_value(g, g->cur_fn); |
| 3521 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_fn_ptr_index, ""); | 3579 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_fn_ptr_index, ""); |
| 3522 | LLVMBuildStore(g->builder, split_llvm_fn, fn_ptr_ptr); | 3580 | LLVMBuildStore(g->builder, split_llvm_fn, fn_ptr_ptr); |
| 3523 | 3581 | ||
| 3524 | LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, fn_val, &frame_result_loc, 1, llvm_cc, fn_inline, ""); | 3582 | LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(g->builtin_types.entry_usize->llvm_type)}; |
| 3583 | LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, ""); | ||
| 3525 | ZigLLVMSetTailCall(call_inst); | 3584 | ZigLLVMSetTailCall(call_inst); |
| 3526 | LLVMBuildRetVoid(g->builder); | 3585 | LLVMBuildRetVoid(g->builder); |
| 3527 | 3586 | ||
| ... | @@ -3530,7 +3589,15 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr | ... | @@ -3530,7 +3589,15 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3530 | LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "CallResume"); | 3589 | LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "CallResume"); |
| 3531 | LLVMPositionBuilderAtEnd(g->builder, call_bb); | 3590 | LLVMPositionBuilderAtEnd(g->builder, call_bb); |
| 3532 | render_async_var_decls(g, instruction->base.scope); | 3591 | render_async_var_decls(g, instruction->base.scope); |
| 3533 | return nullptr; | 3592 | |
| 3593 | if (type_has_bits(src_return_type)) { | ||
| 3594 | LLVMValueRef spilled_result_ptr = LLVMGetParam(g->cur_fn_val, 1); | ||
| 3595 | LLVMValueRef casted_spilled_result_ptr = LLVMBuildIntToPtr(g->builder, spilled_result_ptr, | ||
| 3596 | get_llvm_type(g, ptr_result_type), ""); | ||
| 3597 | return get_handle_value(g, casted_spilled_result_ptr, src_return_type, ptr_result_type); | ||
| 3598 | } else { | ||
| 3599 | return nullptr; | ||
| 3600 | } | ||
| 3534 | } | 3601 | } |
| 3535 | 3602 | ||
| 3536 | if (instruction->new_stack == nullptr) { | 3603 | if (instruction->new_stack == nullptr) { |
| ... | @@ -4829,7 +4896,7 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, | ... | @@ -4829,7 +4896,7 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, |
| 4829 | LLVMValueRef operand = ir_llvm_value(g, instruction->operand); | 4896 | LLVMValueRef operand = ir_llvm_value(g, instruction->operand); |
| 4830 | 4897 | ||
| 4831 | if (get_codegen_ptr_type(operand_type) == nullptr) { | 4898 | if (get_codegen_ptr_type(operand_type) == nullptr) { |
| 4832 | return LLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, false); | 4899 | return LLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); |
| 4833 | } | 4900 | } |
| 4834 | 4901 | ||
| 4835 | // it's a pointer but we need to treat it as an int | 4902 | // it's a pointer but we need to treat it as an int |
| ... | @@ -4990,14 +5057,89 @@ static LLVMValueRef ir_render_suspend_br(CodeGen *g, IrExecutable *executable, | ... | @@ -4990,14 +5057,89 @@ static LLVMValueRef ir_render_suspend_br(CodeGen *g, IrExecutable *executable, |
| 4990 | return nullptr; | 5057 | return nullptr; |
| 4991 | } | 5058 | } |
| 4992 | 5059 | ||
| 5060 | static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInstructionAwait *instruction) { | ||
| 5061 | LLVMValueRef target_frame_ptr = ir_llvm_value(g, instruction->frame); | ||
| 5062 | ZigType *result_type = instruction->base.value.type; | ||
| 5063 | ZigType *ptr_result_type = get_pointer_to_type(g, result_type, true); | ||
| 5064 | |||
| 5065 | // Prepare to be suspended | ||
| 5066 | LLVMValueRef split_llvm_fn = make_fn_llvm_value(g, g->cur_fn); | ||
| 5067 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_fn_ptr_index, ""); | ||
| 5068 | LLVMBuildStore(g->builder, split_llvm_fn, fn_ptr_ptr); | ||
| 5069 | |||
| 5070 | // At this point resuming the function will do the correct thing. | ||
| 5071 | // This code is as if it is running inside the suspend block. | ||
| 5072 | |||
| 5073 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | ||
| 5074 | // caller's own frame pointer | ||
| 5075 | LLVMValueRef awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr, usize_type_ref, ""); | ||
| 5076 | LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, coro_awaiter_index, ""); | ||
| 5077 | LLVMValueRef result_ptr_as_usize; | ||
| 5078 | if (type_has_bits(result_type)) { | ||
| 5079 | LLVMValueRef result_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, coro_arg_start, ""); | ||
| 5080 | LLVMValueRef result_ptr = LLVMBuildLoad(g->builder, result_ptr_ptr, ""); | ||
| 5081 | result_ptr_as_usize = LLVMBuildPtrToInt(g->builder, result_ptr, usize_type_ref, ""); | ||
| 5082 | } else { | ||
| 5083 | result_ptr_as_usize = LLVMGetUndef(usize_type_ref); | ||
| 5084 | } | ||
| 5085 | LLVMValueRef prev_val = LLVMBuildAtomicRMW(g->builder, LLVMAtomicRMWBinOpXchg, awaiter_ptr, awaiter_init_val, | ||
| 5086 | LLVMAtomicOrderingSequentiallyConsistent, g->is_single_threaded); | ||
| 5087 | |||
| 5088 | LLVMBasicBlockRef bad_await_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadAwait"); | ||
| 5089 | LLVMBasicBlockRef complete_suspend_block = LLVMAppendBasicBlock(g->cur_fn_val, "CompleteSuspend"); | ||
| 5090 | LLVMBasicBlockRef early_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "EarlyReturn"); | ||
| 5091 | |||
| 5092 | LLVMValueRef zero = LLVMConstNull(usize_type_ref); | ||
| 5093 | LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); | ||
| 5094 | LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, prev_val, bad_await_block, 2); | ||
| 5095 | |||
| 5096 | LLVMAddCase(switch_instr, zero, complete_suspend_block); | ||
| 5097 | LLVMAddCase(switch_instr, all_ones, early_return_block); | ||
| 5098 | |||
| 5099 | // We discovered that another awaiter was already here. | ||
| 5100 | LLVMPositionBuilderAtEnd(g->builder, bad_await_block); | ||
| 5101 | gen_assertion(g, PanicMsgIdBadAwait, &instruction->base); | ||
| 5102 | |||
| 5103 | // Rely on the target to resume us from suspension. | ||
| 5104 | LLVMPositionBuilderAtEnd(g->builder, complete_suspend_block); | ||
| 5105 | LLVMBuildRetVoid(g->builder); | ||
| 5106 | |||
| 5107 | // The async function has already completed. So we use a tail call to resume ourselves. | ||
| 5108 | LLVMPositionBuilderAtEnd(g->builder, early_return_block); | ||
| 5109 | LLVMValueRef args[] = {g->cur_ret_ptr, result_ptr_as_usize}; | ||
| 5110 | LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, split_llvm_fn, args, 2, LLVMFastCallConv, | ||
| 5111 | ZigLLVM_FnInlineAuto, ""); | ||
| 5112 | ZigLLVMSetTailCall(call_inst); | ||
| 5113 | LLVMBuildRetVoid(g->builder); | ||
| 5114 | |||
| 5115 | g->cur_fn_val = split_llvm_fn; | ||
| 5116 | g->cur_ret_ptr = LLVMGetParam(split_llvm_fn, 0); | ||
| 5117 | LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "AwaitResume"); | ||
| 5118 | LLVMPositionBuilderAtEnd(g->builder, call_bb); | ||
| 5119 | render_async_var_decls(g, instruction->base.scope); | ||
| 5120 | |||
| 5121 | if (type_has_bits(result_type)) { | ||
| 5122 | LLVMValueRef spilled_result_ptr = LLVMGetParam(g->cur_fn_val, 1); | ||
| 5123 | LLVMValueRef casted_spilled_result_ptr = LLVMBuildIntToPtr(g->builder, spilled_result_ptr, | ||
| 5124 | get_llvm_type(g, ptr_result_type), ""); | ||
| 5125 | return get_handle_value(g, casted_spilled_result_ptr, result_type, ptr_result_type); | ||
| 5126 | } else { | ||
| 5127 | return nullptr; | ||
| 5128 | } | ||
| 5129 | } | ||
| 5130 | |||
| 4993 | static LLVMTypeRef anyframe_fn_type(CodeGen *g) { | 5131 | static LLVMTypeRef anyframe_fn_type(CodeGen *g) { |
| 4994 | if (g->anyframe_fn_type != nullptr) | 5132 | if (g->anyframe_fn_type != nullptr) |
| 4995 | return g->anyframe_fn_type; | 5133 | return g->anyframe_fn_type; |
| 4996 | 5134 | ||
| 5135 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | ||
| 4997 | ZigType *anyframe_type = get_any_frame_type(g, nullptr); | 5136 | ZigType *anyframe_type = get_any_frame_type(g, nullptr); |
| 4998 | LLVMTypeRef param_type = get_llvm_type(g, anyframe_type); | ||
| 4999 | LLVMTypeRef return_type = LLVMVoidType(); | 5137 | LLVMTypeRef return_type = LLVMVoidType(); |
| 5000 | LLVMTypeRef fn_type = LLVMFunctionType(return_type, &param_type, 1, false); | 5138 | LLVMTypeRef param_types[] = { |
| 5139 | get_llvm_type(g, anyframe_type), | ||
| 5140 | usize_type_ref, | ||
| 5141 | }; | ||
| 5142 | LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, 2, false); | ||
| 5001 | g->anyframe_fn_type = LLVMPointerType(fn_type, 0); | 5143 | g->anyframe_fn_type = LLVMPointerType(fn_type, 0); |
| 5002 | 5144 | ||
| 5003 | return g->anyframe_fn_type; | 5145 | return g->anyframe_fn_type; |
| ... | @@ -5006,13 +5148,15 @@ static LLVMTypeRef anyframe_fn_type(CodeGen *g) { | ... | @@ -5006,13 +5148,15 @@ static LLVMTypeRef anyframe_fn_type(CodeGen *g) { |
| 5006 | static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable, | 5148 | static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable, |
| 5007 | IrInstructionCoroResume *instruction) | 5149 | IrInstructionCoroResume *instruction) |
| 5008 | { | 5150 | { |
| 5151 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | ||
| 5009 | LLVMValueRef frame = ir_llvm_value(g, instruction->frame); | 5152 | LLVMValueRef frame = ir_llvm_value(g, instruction->frame); |
| 5010 | ZigType *frame_type = instruction->frame->value.type; | 5153 | ZigType *frame_type = instruction->frame->value.type; |
| 5011 | assert(frame_type->id == ZigTypeIdAnyFrame); | 5154 | assert(frame_type->id == ZigTypeIdAnyFrame); |
| 5012 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, frame, coro_fn_ptr_index, ""); | 5155 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, frame, coro_fn_ptr_index, ""); |
| 5013 | LLVMValueRef uncasted_fn_val = LLVMBuildLoad(g->builder, fn_ptr_ptr, ""); | 5156 | LLVMValueRef uncasted_fn_val = LLVMBuildLoad(g->builder, fn_ptr_ptr, ""); |
| 5014 | LLVMValueRef fn_val = LLVMBuildIntToPtr(g->builder, uncasted_fn_val, anyframe_fn_type(g), ""); | 5157 | LLVMValueRef fn_val = LLVMBuildIntToPtr(g->builder, uncasted_fn_val, anyframe_fn_type(g), ""); |
| 5015 | ZigLLVMBuildCall(g->builder, fn_val, &frame, 1, LLVMFastCallConv, ZigLLVM_FnInlineAuto, ""); | 5158 | LLVMValueRef args[] = {frame, LLVMGetUndef(usize_type_ref)}; |
| 5159 | ZigLLVMBuildCall(g->builder, fn_val, args, 2, LLVMFastCallConv, ZigLLVM_FnInlineAuto, ""); | ||
| 5016 | return nullptr; | 5160 | return nullptr; |
| 5017 | } | 5161 | } |
| 5018 | 5162 | ||
| ... | @@ -5279,6 +5423,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -5279,6 +5423,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5279 | return ir_render_coro_resume(g, executable, (IrInstructionCoroResume *)instruction); | 5423 | return ir_render_coro_resume(g, executable, (IrInstructionCoroResume *)instruction); |
| 5280 | case IrInstructionIdFrameSizeGen: | 5424 | case IrInstructionIdFrameSizeGen: |
| 5281 | return ir_render_frame_size(g, executable, (IrInstructionFrameSizeGen *)instruction); | 5425 | return ir_render_frame_size(g, executable, (IrInstructionFrameSizeGen *)instruction); |
| 5426 | case IrInstructionIdAwait: | ||
| 5427 | return ir_render_await(g, executable, (IrInstructionAwait *)instruction); | ||
| 5282 | } | 5428 | } |
| 5283 | zig_unreachable(); | 5429 | zig_unreachable(); |
| 5284 | } | 5430 | } |
src/ir.cpp+82-15| ... | @@ -1052,6 +1052,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendBr *) { | ... | @@ -1052,6 +1052,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendBr *) { |
| 1052 | return IrInstructionIdSuspendBr; | 1052 | return IrInstructionIdSuspendBr; |
| 1053 | } | 1053 | } |
| 1054 | 1054 | ||
| 1055 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAwait *) { | ||
| 1056 | return IrInstructionIdAwait; | ||
| 1057 | } | ||
| 1058 | |||
| 1055 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) { | 1059 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) { |
| 1056 | return IrInstructionIdCoroResume; | 1060 | return IrInstructionIdCoroResume; |
| 1057 | } | 1061 | } |
| ... | @@ -3274,6 +3278,17 @@ static IrInstruction *ir_build_suspend_br(IrBuilder *irb, Scope *scope, AstNode | ... | @@ -3274,6 +3278,17 @@ static IrInstruction *ir_build_suspend_br(IrBuilder *irb, Scope *scope, AstNode |
| 3274 | return &instruction->base; | 3278 | return &instruction->base; |
| 3275 | } | 3279 | } |
| 3276 | 3280 | ||
| 3281 | static IrInstruction *ir_build_await(IrBuilder *irb, Scope *scope, AstNode *source_node, | ||
| 3282 | IrInstruction *frame) | ||
| 3283 | { | ||
| 3284 | IrInstructionAwait *instruction = ir_build_instruction<IrInstructionAwait>(irb, scope, source_node); | ||
| 3285 | instruction->frame = frame; | ||
| 3286 | |||
| 3287 | ir_ref_instruction(frame, irb->current_basic_block); | ||
| 3288 | |||
| 3289 | return &instruction->base; | ||
| 3290 | } | ||
| 3291 | |||
| 3277 | static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode *source_node, | 3292 | static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 3278 | IrInstruction *frame) | 3293 | IrInstruction *frame) |
| 3279 | { | 3294 | { |
| ... | @@ -7774,11 +7789,26 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) | ... | @@ -7774,11 +7789,26 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) |
| 7774 | static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { | 7789 | static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { |
| 7775 | assert(node->type == NodeTypeAwaitExpr); | 7790 | assert(node->type == NodeTypeAwaitExpr); |
| 7776 | 7791 | ||
| 7777 | IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope); | 7792 | ZigFn *fn_entry = exec_fn_entry(irb->exec); |
| 7793 | if (!fn_entry) { | ||
| 7794 | add_node_error(irb->codegen, node, buf_sprintf("await outside function definition")); | ||
| 7795 | return irb->codegen->invalid_instruction; | ||
| 7796 | } | ||
| 7797 | ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope); | ||
| 7798 | if (existing_suspend_scope) { | ||
| 7799 | if (!existing_suspend_scope->reported_err) { | ||
| 7800 | ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block")); | ||
| 7801 | add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here")); | ||
| 7802 | existing_suspend_scope->reported_err = true; | ||
| 7803 | } | ||
| 7804 | return irb->codegen->invalid_instruction; | ||
| 7805 | } | ||
| 7806 | |||
| 7807 | IrInstruction *target_inst = ir_gen_node_extra(irb, node->data.await_expr.expr, scope, LValPtr, nullptr); | ||
| 7778 | if (target_inst == irb->codegen->invalid_instruction) | 7808 | if (target_inst == irb->codegen->invalid_instruction) |
| 7779 | return irb->codegen->invalid_instruction; | 7809 | return irb->codegen->invalid_instruction; |
| 7780 | 7810 | ||
| 7781 | zig_panic("TODO ir_gen_await_expr"); | 7811 | return ir_build_await(irb, scope, node, target_inst); |
| 7782 | } | 7812 | } |
| 7783 | 7813 | ||
| 7784 | static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { | 7814 | static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { |
| ... | @@ -7789,15 +7819,6 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod | ... | @@ -7789,15 +7819,6 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod |
| 7789 | add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition")); | 7819 | add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition")); |
| 7790 | return irb->codegen->invalid_instruction; | 7820 | return irb->codegen->invalid_instruction; |
| 7791 | } | 7821 | } |
| 7792 | ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); | ||
| 7793 | if (scope_defer_expr) { | ||
| 7794 | if (!scope_defer_expr->reported_err) { | ||
| 7795 | ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression")); | ||
| 7796 | add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here")); | ||
| 7797 | scope_defer_expr->reported_err = true; | ||
| 7798 | } | ||
| 7799 | return irb->codegen->invalid_instruction; | ||
| 7800 | } | ||
| 7801 | ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); | 7822 | ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); |
| 7802 | if (existing_suspend_scope) { | 7823 | if (existing_suspend_scope) { |
| 7803 | if (!existing_suspend_scope->reported_err) { | 7824 | if (!existing_suspend_scope->reported_err) { |
| ... | @@ -7808,7 +7829,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod | ... | @@ -7808,7 +7829,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod |
| 7808 | return irb->codegen->invalid_instruction; | 7829 | return irb->codegen->invalid_instruction; |
| 7809 | } | 7830 | } |
| 7810 | 7831 | ||
| 7811 | IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "Resume"); | 7832 | IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); |
| 7812 | 7833 | ||
| 7813 | ir_build_suspend_begin(irb, parent_scope, node, resume_block); | 7834 | ir_build_suspend_begin(irb, parent_scope, node, resume_block); |
| 7814 | if (node->data.suspend.block != nullptr) { | 7835 | if (node->data.suspend.block != nullptr) { |
| ... | @@ -24372,19 +24393,62 @@ static IrInstruction *ir_analyze_instruction_suspend_br(IrAnalyze *ira, IrInstru | ... | @@ -24372,19 +24393,62 @@ static IrInstruction *ir_analyze_instruction_suspend_br(IrAnalyze *ira, IrInstru |
| 24372 | return ir_finish_anal(ira, result); | 24393 | return ir_finish_anal(ira, result); |
| 24373 | } | 24394 | } |
| 24374 | 24395 | ||
| 24375 | static IrInstruction *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) { | 24396 | static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstructionAwait *instruction) { |
| 24376 | IrInstruction *frame_ptr = instruction->frame->child; | 24397 | IrInstruction *frame_ptr = instruction->frame->child; |
| 24377 | if (type_is_invalid(frame_ptr->value.type)) | 24398 | if (type_is_invalid(frame_ptr->value.type)) |
| 24378 | return ira->codegen->invalid_instruction; | 24399 | return ira->codegen->invalid_instruction; |
| 24379 | 24400 | ||
| 24401 | ZigType *result_type; | ||
| 24380 | IrInstruction *frame; | 24402 | IrInstruction *frame; |
| 24381 | if (frame_ptr->value.type->id == ZigTypeIdPointer && | 24403 | if (frame_ptr->value.type->id == ZigTypeIdPointer && |
| 24382 | frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle && | 24404 | frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle && |
| 24383 | frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdAnyFrame) | 24405 | frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdCoroFrame) |
| 24384 | { | 24406 | { |
| 24385 | frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr); | 24407 | result_type = frame_ptr->value.type->data.pointer.child_type->data.frame.fn->type_entry->data.fn.fn_type_id.return_type; |
| 24408 | frame = frame_ptr; | ||
| 24386 | } else { | 24409 | } else { |
| 24410 | frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr); | ||
| 24411 | if (frame->value.type->id != ZigTypeIdAnyFrame || | ||
| 24412 | frame->value.type->data.any_frame.result_type == nullptr) | ||
| 24413 | { | ||
| 24414 | ir_add_error(ira, &instruction->base, | ||
| 24415 | buf_sprintf("expected anyframe->T, found '%s'", buf_ptr(&frame->value.type->name))); | ||
| 24416 | return ira->codegen->invalid_instruction; | ||
| 24417 | } | ||
| 24418 | result_type = frame->value.type->data.any_frame.result_type; | ||
| 24419 | } | ||
| 24420 | |||
| 24421 | ZigType *any_frame_type = get_any_frame_type(ira->codegen, result_type); | ||
| 24422 | IrInstruction *casted_frame = ir_implicit_cast(ira, frame, any_frame_type); | ||
| 24423 | if (type_is_invalid(casted_frame->value.type)) | ||
| 24424 | return ira->codegen->invalid_instruction; | ||
| 24425 | |||
| 24426 | ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); | ||
| 24427 | ir_assert(fn_entry != nullptr, &instruction->base); | ||
| 24428 | |||
| 24429 | if (fn_entry->inferred_async_node == nullptr) { | ||
| 24430 | fn_entry->inferred_async_node = instruction->base.source_node; | ||
| 24431 | } | ||
| 24432 | |||
| 24433 | IrInstruction *result = ir_build_await(&ira->new_irb, | ||
| 24434 | instruction->base.scope, instruction->base.source_node, frame); | ||
| 24435 | result->value.type = result_type; | ||
| 24436 | return ir_finish_anal(ira, result); | ||
| 24437 | } | ||
| 24438 | |||
| 24439 | static IrInstruction *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) { | ||
| 24440 | IrInstruction *frame_ptr = instruction->frame->child; | ||
| 24441 | if (type_is_invalid(frame_ptr->value.type)) | ||
| 24442 | return ira->codegen->invalid_instruction; | ||
| 24443 | |||
| 24444 | IrInstruction *frame; | ||
| 24445 | if (frame_ptr->value.type->id == ZigTypeIdPointer && | ||
| 24446 | frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle && | ||
| 24447 | frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdCoroFrame) | ||
| 24448 | { | ||
| 24387 | frame = frame_ptr; | 24449 | frame = frame_ptr; |
| 24450 | } else { | ||
| 24451 | frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr); | ||
| 24388 | } | 24452 | } |
| 24389 | 24453 | ||
| 24390 | ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr); | 24454 | ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr); |
| ... | @@ -24691,6 +24755,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction | ... | @@ -24691,6 +24755,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction |
| 24691 | return ir_analyze_instruction_suspend_br(ira, (IrInstructionSuspendBr *)instruction); | 24755 | return ir_analyze_instruction_suspend_br(ira, (IrInstructionSuspendBr *)instruction); |
| 24692 | case IrInstructionIdCoroResume: | 24756 | case IrInstructionIdCoroResume: |
| 24693 | return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction); | 24757 | return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction); |
| 24758 | case IrInstructionIdAwait: | ||
| 24759 | return ir_analyze_instruction_await(ira, (IrInstructionAwait *)instruction); | ||
| 24694 | } | 24760 | } |
| 24695 | zig_unreachable(); | 24761 | zig_unreachable(); |
| 24696 | } | 24762 | } |
| ... | @@ -24826,6 +24892,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -24826,6 +24892,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 24826 | case IrInstructionIdSuspendBegin: | 24892 | case IrInstructionIdSuspendBegin: |
| 24827 | case IrInstructionIdSuspendBr: | 24893 | case IrInstructionIdSuspendBr: |
| 24828 | case IrInstructionIdCoroResume: | 24894 | case IrInstructionIdCoroResume: |
| 24895 | case IrInstructionIdAwait: | ||
| 24829 | return true; | 24896 | return true; |
| 24830 | 24897 | ||
| 24831 | case IrInstructionIdPhi: | 24898 | case IrInstructionIdPhi: |
src/ir_print.cpp+9| ... | @@ -1546,6 +1546,12 @@ static void ir_print_coro_resume(IrPrint *irp, IrInstructionCoroResume *instruct | ... | @@ -1546,6 +1546,12 @@ static void ir_print_coro_resume(IrPrint *irp, IrInstructionCoroResume *instruct |
| 1546 | fprintf(irp->f, ")"); | 1546 | fprintf(irp->f, ")"); |
| 1547 | } | 1547 | } |
| 1548 | 1548 | ||
| 1549 | static void ir_print_await(IrPrint *irp, IrInstructionAwait *instruction) { | ||
| 1550 | fprintf(irp->f, "@await("); | ||
| 1551 | ir_print_other_instruction(irp, instruction->frame); | ||
| 1552 | fprintf(irp->f, ")"); | ||
| 1553 | } | ||
| 1554 | |||
| 1549 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | 1555 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1550 | ir_print_prefix(irp, instruction); | 1556 | ir_print_prefix(irp, instruction); |
| 1551 | switch (instruction->id) { | 1557 | switch (instruction->id) { |
| ... | @@ -2025,6 +2031,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -2025,6 +2031,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 2025 | case IrInstructionIdCoroResume: | 2031 | case IrInstructionIdCoroResume: |
| 2026 | ir_print_coro_resume(irp, (IrInstructionCoroResume *)instruction); | 2032 | ir_print_coro_resume(irp, (IrInstructionCoroResume *)instruction); |
| 2027 | break; | 2033 | break; |
| 2034 | case IrInstructionIdAwait: | ||
| 2035 | ir_print_await(irp, (IrInstructionAwait *)instruction); | ||
| 2036 | break; | ||
| 2028 | } | 2037 | } |
| 2029 | fprintf(irp->f, "\n"); | 2038 | fprintf(irp->f, "\n"); |
| 2030 | } | 2039 | } |
test/stage1/behavior/coroutine_await_struct.zig+4-4| ... | @@ -6,12 +6,12 @@ const Foo = struct { | ... | @@ -6,12 +6,12 @@ const Foo = struct { |
| 6 | x: i32, | 6 | x: i32, |
| 7 | }; | 7 | }; |
| 8 | 8 | ||
| 9 | var await_a_promise: promise = undefined; | 9 | var await_a_promise: anyframe = undefined; |
| 10 | var await_final_result = Foo{ .x = 0 }; | 10 | var await_final_result = Foo{ .x = 0 }; |
| 11 | 11 | ||
| 12 | test "coroutine await struct" { | 12 | test "coroutine await struct" { |
| 13 | await_seq('a'); | 13 | await_seq('a'); |
| 14 | const p = async<std.heap.direct_allocator> await_amain() catch unreachable; | 14 | const p = async await_amain(); |
| 15 | await_seq('f'); | 15 | await_seq('f'); |
| 16 | resume await_a_promise; | 16 | resume await_a_promise; |
| 17 | await_seq('i'); | 17 | await_seq('i'); |
| ... | @@ -20,7 +20,7 @@ test "coroutine await struct" { | ... | @@ -20,7 +20,7 @@ test "coroutine await struct" { |
| 20 | } | 20 | } |
| 21 | async fn await_amain() void { | 21 | async fn await_amain() void { |
| 22 | await_seq('b'); | 22 | await_seq('b'); |
| 23 | const p = async await_another() catch unreachable; | 23 | const p = async await_another(); |
| 24 | await_seq('e'); | 24 | await_seq('e'); |
| 25 | await_final_result = await p; | 25 | await_final_result = await p; |
| 26 | await_seq('h'); | 26 | await_seq('h'); |
| ... | @@ -29,7 +29,7 @@ async fn await_another() Foo { | ... | @@ -29,7 +29,7 @@ async fn await_another() Foo { |
| 29 | await_seq('c'); | 29 | await_seq('c'); |
| 30 | suspend { | 30 | suspend { |
| 31 | await_seq('d'); | 31 | await_seq('d'); |
| 32 | await_a_promise = @handle(); | 32 | await_a_promise = @frame(); |
| 33 | } | 33 | } |
| 34 | await_seq('g'); | 34 | await_seq('g'); |
| 35 | return Foo{ .x = 1234 }; | 35 | return Foo{ .x = 1234 }; |
test/stage1/behavior/coroutines.zig+97-112| ... | @@ -180,97 +180,85 @@ async fn testSuspendBlock() void { | ... | @@ -180,97 +180,85 @@ async fn testSuspendBlock() void { |
| 180 | result = true; | 180 | result = true; |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | //var await_a_promise: anyframe = undefined; | 183 | var await_a_promise: anyframe = undefined; |
| 184 | //var await_final_result: i32 = 0; | 184 | var await_final_result: i32 = 0; |
| 185 | // | 185 | |
| 186 | //test "coroutine await" { | 186 | test "coroutine await" { |
| 187 | // await_seq('a'); | 187 | await_seq('a'); |
| 188 | // const p = async<allocator> await_amain() catch unreachable; | 188 | const p = async await_amain(); |
| 189 | // await_seq('f'); | 189 | await_seq('f'); |
| 190 | // resume await_a_promise; | 190 | resume await_a_promise; |
| 191 | // await_seq('i'); | 191 | await_seq('i'); |
| 192 | // expect(await_final_result == 1234); | 192 | expect(await_final_result == 1234); |
| 193 | // expect(std.mem.eql(u8, await_points, "abcdefghi")); | 193 | expect(std.mem.eql(u8, await_points, "abcdefghi")); |
| 194 | //} | 194 | } |
| 195 | //async fn await_amain() void { | 195 | async fn await_amain() void { |
| 196 | // await_seq('b'); | 196 | await_seq('b'); |
| 197 | // const p = async await_another() catch unreachable; | 197 | const p = async await_another(); |
| 198 | // await_seq('e'); | 198 | await_seq('e'); |
| 199 | // await_final_result = await p; | 199 | await_final_result = await p; |
| 200 | // await_seq('h'); | 200 | await_seq('h'); |
| 201 | //} | 201 | } |
| 202 | //async fn await_another() i32 { | 202 | async fn await_another() i32 { |
| 203 | // await_seq('c'); | 203 | await_seq('c'); |
| 204 | // suspend { | 204 | suspend { |
| 205 | // await_seq('d'); | 205 | await_seq('d'); |
| 206 | // await_a_promise = @frame(); | 206 | await_a_promise = @frame(); |
| 207 | // } | 207 | } |
| 208 | // await_seq('g'); | 208 | await_seq('g'); |
| 209 | // return 1234; | 209 | return 1234; |
| 210 | //} | 210 | } |
| 211 | // | 211 | |
| 212 | //var await_points = [_]u8{0} ** "abcdefghi".len; | 212 | var await_points = [_]u8{0} ** "abcdefghi".len; |
| 213 | //var await_seq_index: usize = 0; | 213 | var await_seq_index: usize = 0; |
| 214 | // | 214 | |
| 215 | //fn await_seq(c: u8) void { | 215 | fn await_seq(c: u8) void { |
| 216 | // await_points[await_seq_index] = c; | 216 | await_points[await_seq_index] = c; |
| 217 | // await_seq_index += 1; | 217 | await_seq_index += 1; |
| 218 | //} | 218 | } |
| 219 | // | 219 | |
| 220 | //var early_final_result: i32 = 0; | 220 | var early_final_result: i32 = 0; |
| 221 | // | 221 | |
| 222 | //test "coroutine await early return" { | 222 | test "coroutine await early return" { |
| 223 | // early_seq('a'); | 223 | early_seq('a'); |
| 224 | // const p = async<allocator> early_amain() catch @panic("out of memory"); | 224 | const p = async early_amain(); |
| 225 | // early_seq('f'); | 225 | early_seq('f'); |
| 226 | // expect(early_final_result == 1234); | 226 | expect(early_final_result == 1234); |
| 227 | // expect(std.mem.eql(u8, early_points, "abcdef")); | 227 | expect(std.mem.eql(u8, early_points, "abcdef")); |
| 228 | //} | 228 | } |
| 229 | //async fn early_amain() void { | 229 | async fn early_amain() void { |
| 230 | // early_seq('b'); | 230 | early_seq('b'); |
| 231 | // const p = async early_another() catch @panic("out of memory"); | 231 | const p = async early_another(); |
| 232 | // early_seq('d'); | 232 | early_seq('d'); |
| 233 | // early_final_result = await p; | 233 | early_final_result = await p; |
| 234 | // early_seq('e'); | 234 | early_seq('e'); |
| 235 | //} | 235 | } |
| 236 | //async fn early_another() i32 { | 236 | async fn early_another() i32 { |
| 237 | // early_seq('c'); | 237 | early_seq('c'); |
| 238 | // return 1234; | 238 | return 1234; |
| 239 | //} | 239 | } |
| 240 | // | 240 | |
| 241 | //var early_points = [_]u8{0} ** "abcdef".len; | 241 | var early_points = [_]u8{0} ** "abcdef".len; |
| 242 | //var early_seq_index: usize = 0; | 242 | var early_seq_index: usize = 0; |
| 243 | // | 243 | |
| 244 | //fn early_seq(c: u8) void { | 244 | fn early_seq(c: u8) void { |
| 245 | // early_points[early_seq_index] = c; | 245 | early_points[early_seq_index] = c; |
| 246 | // early_seq_index += 1; | 246 | early_seq_index += 1; |
| 247 | //} | 247 | } |
| 248 | // | 248 | |
| 249 | //test "coro allocation failure" { | 249 | test "async function with dot syntax" { |
| 250 | // var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0); | 250 | const S = struct { |
| 251 | // if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) { | 251 | var y: i32 = 1; |
| 252 | // @panic("expected allocation failure"); | 252 | async fn foo() void { |
| 253 | // } else |err| switch (err) { | 253 | y += 1; |
| 254 | // error.OutOfMemory => {}, | 254 | suspend; |
| 255 | // } | 255 | } |
| 256 | //} | 256 | }; |
| 257 | //async fn asyncFuncThatNeverGetsRun() void { | 257 | const p = async S.foo(); |
| 258 | // @panic("coro frame allocation should fail"); | 258 | // can't cancel in tests because they are non-async functions |
| 259 | //} | 259 | expect(S.y == 2); |
| 260 | // | 260 | } |
| 261 | //test "async function with dot syntax" { | 261 | |
| 262 | // const S = struct { | ||
| 263 | // var y: i32 = 1; | ||
| 264 | // async fn foo() void { | ||
| 265 | // y += 1; | ||
| 266 | // suspend; | ||
| 267 | // } | ||
| 268 | // }; | ||
| 269 | // const p = try async<allocator> S.foo(); | ||
| 270 | // cancel p; | ||
| 271 | // expect(S.y == 2); | ||
| 272 | //} | ||
| 273 | // | ||
| 274 | //test "async fn pointer in a struct field" { | 262 | //test "async fn pointer in a struct field" { |
| 275 | // var data: i32 = 1; | 263 | // var data: i32 = 1; |
| 276 | // const Foo = struct { | 264 | // const Foo = struct { |
| ... | @@ -287,18 +275,17 @@ async fn testSuspendBlock() void { | ... | @@ -287,18 +275,17 @@ async fn testSuspendBlock() void { |
| 287 | // y.* += 1; | 275 | // y.* += 1; |
| 288 | // suspend; | 276 | // suspend; |
| 289 | //} | 277 | //} |
| 290 | // | 278 | |
| 291 | //test "async fn with inferred error set" { | 279 | //test "async fn with inferred error set" { |
| 292 | // const p = (async<allocator> failing()) catch unreachable; | 280 | // const p = async failing(); |
| 293 | // resume p; | 281 | // resume p; |
| 294 | // cancel p; | ||
| 295 | //} | 282 | //} |
| 296 | // | 283 | // |
| 297 | //async fn failing() !void { | 284 | //async fn failing() !void { |
| 298 | // suspend; | 285 | // suspend; |
| 299 | // return error.Fail; | 286 | // return error.Fail; |
| 300 | //} | 287 | //} |
| 301 | // | 288 | |
| 302 | //test "error return trace across suspend points - early return" { | 289 | //test "error return trace across suspend points - early return" { |
| 303 | // const p = nonFailing(); | 290 | // const p = nonFailing(); |
| 304 | // resume p; | 291 | // resume p; |
| ... | @@ -331,20 +318,18 @@ async fn testSuspendBlock() void { | ... | @@ -331,20 +318,18 @@ async fn testSuspendBlock() void { |
| 331 | // } | 318 | // } |
| 332 | // }; | 319 | // }; |
| 333 | //} | 320 | //} |
| 334 | // | 321 | |
| 335 | //test "break from suspend" { | 322 | test "break from suspend" { |
| 336 | // var buf: [500]u8 = undefined; | 323 | var my_result: i32 = 1; |
| 337 | // var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; | 324 | const p = async testBreakFromSuspend(&my_result); |
| 338 | // var my_result: i32 = 1; | 325 | // can't cancel here |
| 339 | // const p = try async<a> testBreakFromSuspend(&my_result); | 326 | std.testing.expect(my_result == 2); |
| 340 | // cancel p; | 327 | } |
| 341 | // std.testing.expect(my_result == 2); | 328 | async fn testBreakFromSuspend(my_result: *i32) void { |
| 342 | //} | 329 | suspend { |
| 343 | //async fn testBreakFromSuspend(my_result: *i32) void { | 330 | resume @frame(); |
| 344 | // suspend { | 331 | } |
| 345 | // resume @frame(); | 332 | my_result.* += 1; |
| 346 | // } | 333 | suspend; |
| 347 | // my_result.* += 1; | 334 | my_result.* += 1; |
| 348 | // suspend; | 335 | } |
| 349 | // my_result.* += 1; | ||
| 350 | //} |