authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-02 01:05:34-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-02 01:06:00-04:00
log056c4e2c988c0a2ff6f1be8fe18a0a056d848271
tree86d830fbc801536ec06d7f5d7e609ff01e32288a
parent0f879d02a4c4b1de0e28c2863c1e5f3760eb5b19
signaturelock-open Commit is signed but in an unrecognized format.

implement async await and return


9 files changed, 384 insertions(+), 146 deletions(-)

BRANCH_TODO+5-1
......@@ -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'
23 * await of a non async function
34 * await in single-threaded mode
45 * async call on a non async function
......@@ -13,3 +14,6 @@
1314 * @typeInfo for @Frame(func)
1415 * peer type resolution of *@Frame(func) and anyframe
1516 * 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 {
15501550 PanicMsgIdFloatToInt,
15511551 PanicMsgIdPtrCastNull,
15521552 PanicMsgIdBadResume,
1553 PanicMsgIdBadAwait,
1554 PanicMsgIdBadReturn,
15531555
15541556 PanicMsgIdCount,
15551557};
......@@ -1795,7 +1797,6 @@ struct CodeGen {
17951797 ZigType *entry_arg_tuple;
17961798 ZigType *entry_enum_literal;
17971799 ZigType *entry_any_frame;
1798 ZigType *entry_async_fn;
17991800 } builtin_types;
18001801
18011802 ZigType *align_amt_type;
......@@ -2348,6 +2349,7 @@ enum IrInstructionId {
23482349 IrInstructionIdUnionInitNamedField,
23492350 IrInstructionIdSuspendBegin,
23502351 IrInstructionIdSuspendBr,
2352 IrInstructionIdAwait,
23512353 IrInstructionIdCoroResume,
23522354};
23532355
......@@ -3600,6 +3602,12 @@ struct IrInstructionSuspendBr {
36003602 IrBasicBlock *resume_block;
36013603};
36023604
3605struct IrInstructionAwait {
3606 IrInstruction base;
3607
3608 IrInstruction *frame;
3609};
3610
36033611struct IrInstructionCoroResume {
36043612 IrInstruction base;
36053613
src/analyze.cpp+21-4
......@@ -3807,6 +3807,9 @@ static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) {
38073807 } else if (fn->inferred_async_node->type == NodeTypeSuspend) {
38083808 add_error_note(g, msg, fn->inferred_async_node,
38093809 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"));
38103813 } else {
38113814 zig_unreachable();
38123815 }
......@@ -7361,7 +7364,7 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) {
73617364 param_di_types.append(get_llvm_di_type(g, gen_type));
73627365 }
73637366 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);
73657368
73667369 ZigType *frame_type = get_any_frame_type(g, fn_type_id->return_type);
73677370 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) {
73707373 fn_type->data.fn.gen_param_info[0].src_index = 0;
73717374 fn_type->data.fn.gen_param_info[0].gen_index = 0;
73727375 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;
73737383 } else {
73747384 fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count);
73757385 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) {
74347444
74357445 ZigType *gen_return_type = g->builtin_types.entry_void;
74367446 ZigList<ZigLLVMDIType *> param_di_types = {};
7447 ZigList<LLVMTypeRef> gen_param_types = {};
74377448 // first "parameter" is return value
74387449 param_di_types.append(get_llvm_di_type(g, gen_return_type));
74397450
74407451 ZigType *frame_type = get_coro_frame_type(g, fn);
74417452 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));
74437454 param_di_types.append(get_llvm_di_type(g, ptr_type));
74447455
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);
74467462 fn->raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);
74477463}
74487464
......@@ -7493,7 +7509,8 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re
74937509 8*g->pointer_size_bytes, 8*g->builtin_types.entry_usize->abi_align, buf_ptr(&any_frame_type->name));
74947510
74957511 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);
74977514 LLVMTypeRef usize_type_ref = get_llvm_type(g, g->builtin_types.entry_usize);
74987515 ZigLLVMDIType *usize_di_type = get_llvm_di_type(g, g->builtin_types.entry_usize);
74997516 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) {
11491149 }
11501150 case NodeTypeSuspend:
11511151 {
1152 fprintf(ar->f, "suspend");
11531152 if (node->data.suspend.block != nullptr) {
1153 fprintf(ar->f, "suspend ");
11541154 render_node_grouped(ar, node->data.suspend.block);
1155 } else {
1156 fprintf(ar->f, "suspend\n");
11551157 }
11561158 break;
11571159 }
src/codegen.cpp+154-8
......@@ -873,6 +873,10 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
873873 return buf_create_from_str("cast causes pointer to be null");
874874 case PanicMsgIdBadResume:
875875 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");
876880 }
877881 zig_unreachable();
878882}
......@@ -1991,14 +1995,66 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut
19911995
19921996static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {
19931997 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
19942004 if (ir_want_runtime_safety(g, &return_instruction->base)) {
1995 LLVMValueRef locals_ptr = g->cur_ret_ptr;
19962005 LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_fn_ptr_index, "");
19972006 LLVMValueRef new_resume_fn = g->cur_fn->resume_blocks.last()->split_llvm_fn;
19982007 LLVMBuildStore(g->builder, new_resume_fn, resume_index_ptr);
19992008 }
20002009
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);
20012043 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
20022058 return nullptr;
20032059 }
20042060 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
35143570 }
35153571 }
35163572 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, "");
35183575 return nullptr;
35193576 } else if (callee_is_async) {
3577 ZigType *ptr_result_type = get_pointer_to_type(g, src_return_type, true);
35203578 LLVMValueRef split_llvm_fn = make_fn_llvm_value(g, g->cur_fn);
35213579 LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_fn_ptr_index, "");
35223580 LLVMBuildStore(g->builder, split_llvm_fn, fn_ptr_ptr);
35233581
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, "");
35253584 ZigLLVMSetTailCall(call_inst);
35263585 LLVMBuildRetVoid(g->builder);
35273586
......@@ -3530,7 +3589,15 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
35303589 LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "CallResume");
35313590 LLVMPositionBuilderAtEnd(g->builder, call_bb);
35323591 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 }
35343601 }
35353602
35363603 if (instruction->new_stack == nullptr) {
......@@ -4829,7 +4896,7 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
48294896 LLVMValueRef operand = ir_llvm_value(g, instruction->operand);
48304897
48314898 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);
48334900 }
48344901
48354902 // 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,
49905057 return nullptr;
49915058}
49925059
5060static 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
49935131static LLVMTypeRef anyframe_fn_type(CodeGen *g) {
49945132 if (g->anyframe_fn_type != nullptr)
49955133 return g->anyframe_fn_type;
49965134
5135 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
49975136 ZigType *anyframe_type = get_any_frame_type(g, nullptr);
4998 LLVMTypeRef param_type = get_llvm_type(g, anyframe_type);
49995137 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);
50015143 g->anyframe_fn_type = LLVMPointerType(fn_type, 0);
50025144
50035145 return g->anyframe_fn_type;
......@@ -5006,13 +5148,15 @@ static LLVMTypeRef anyframe_fn_type(CodeGen *g) {
50065148static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable,
50075149 IrInstructionCoroResume *instruction)
50085150{
5151 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
50095152 LLVMValueRef frame = ir_llvm_value(g, instruction->frame);
50105153 ZigType *frame_type = instruction->frame->value.type;
50115154 assert(frame_type->id == ZigTypeIdAnyFrame);
50125155 LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, frame, coro_fn_ptr_index, "");
50135156 LLVMValueRef uncasted_fn_val = LLVMBuildLoad(g->builder, fn_ptr_ptr, "");
50145157 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, "");
50165160 return nullptr;
50175161}
50185162
......@@ -5279,6 +5423,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
52795423 return ir_render_coro_resume(g, executable, (IrInstructionCoroResume *)instruction);
52805424 case IrInstructionIdFrameSizeGen:
52815425 return ir_render_frame_size(g, executable, (IrInstructionFrameSizeGen *)instruction);
5426 case IrInstructionIdAwait:
5427 return ir_render_await(g, executable, (IrInstructionAwait *)instruction);
52825428 }
52835429 zig_unreachable();
52845430}
src/ir.cpp+82-15
......@@ -1052,6 +1052,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendBr *) {
10521052 return IrInstructionIdSuspendBr;
10531053}
10541054
1055static constexpr IrInstructionId ir_instruction_id(IrInstructionAwait *) {
1056 return IrInstructionIdAwait;
1057}
1058
10551059static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) {
10561060 return IrInstructionIdCoroResume;
10571061}
......@@ -3274,6 +3278,17 @@ static IrInstruction *ir_build_suspend_br(IrBuilder *irb, Scope *scope, AstNode
32743278 return &instruction->base;
32753279}
32763280
3281static 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
32773292static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode *source_node,
32783293 IrInstruction *frame)
32793294{
......@@ -7774,11 +7789,26 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node)
77747789static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
77757790 assert(node->type == NodeTypeAwaitExpr);
77767791
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);
77787808 if (target_inst == irb->codegen->invalid_instruction)
77797809 return irb->codegen->invalid_instruction;
77807810
7781 zig_panic("TODO ir_gen_await_expr");
7811 return ir_build_await(irb, scope, node, target_inst);
77827812}
77837813
77847814static 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
77897819 add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition"));
77907820 return irb->codegen->invalid_instruction;
77917821 }
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 }
78017822 ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope);
78027823 if (existing_suspend_scope) {
78037824 if (!existing_suspend_scope->reported_err) {
......@@ -7808,7 +7829,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
78087829 return irb->codegen->invalid_instruction;
78097830 }
78107831
7811 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "Resume");
7832 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
78127833
78137834 ir_build_suspend_begin(irb, parent_scope, node, resume_block);
78147835 if (node->data.suspend.block != nullptr) {
......@@ -24372,19 +24393,62 @@ static IrInstruction *ir_analyze_instruction_suspend_br(IrAnalyze *ira, IrInstru
2437224393 return ir_finish_anal(ira, result);
2437324394}
2437424395
24375static IrInstruction *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) {
24396static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstructionAwait *instruction) {
2437624397 IrInstruction *frame_ptr = instruction->frame->child;
2437724398 if (type_is_invalid(frame_ptr->value.type))
2437824399 return ira->codegen->invalid_instruction;
2437924400
24401 ZigType *result_type;
2438024402 IrInstruction *frame;
2438124403 if (frame_ptr->value.type->id == ZigTypeIdPointer &&
2438224404 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)
2438424406 {
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;
2438624409 } 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
24439static 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 {
2438724449 frame = frame_ptr;
24450 } else {
24451 frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr);
2438824452 }
2438924453
2439024454 ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr);
......@@ -24691,6 +24755,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
2469124755 return ir_analyze_instruction_suspend_br(ira, (IrInstructionSuspendBr *)instruction);
2469224756 case IrInstructionIdCoroResume:
2469324757 return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction);
24758 case IrInstructionIdAwait:
24759 return ir_analyze_instruction_await(ira, (IrInstructionAwait *)instruction);
2469424760 }
2469524761 zig_unreachable();
2469624762}
......@@ -24826,6 +24892,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2482624892 case IrInstructionIdSuspendBegin:
2482724893 case IrInstructionIdSuspendBr:
2482824894 case IrInstructionIdCoroResume:
24895 case IrInstructionIdAwait:
2482924896 return true;
2483024897
2483124898 case IrInstructionIdPhi:
src/ir_print.cpp+9
......@@ -1546,6 +1546,12 @@ static void ir_print_coro_resume(IrPrint *irp, IrInstructionCoroResume *instruct
15461546 fprintf(irp->f, ")");
15471547}
15481548
1549static 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
15491555static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
15501556 ir_print_prefix(irp, instruction);
15511557 switch (instruction->id) {
......@@ -2025,6 +2031,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
20252031 case IrInstructionIdCoroResume:
20262032 ir_print_coro_resume(irp, (IrInstructionCoroResume *)instruction);
20272033 break;
2034 case IrInstructionIdAwait:
2035 ir_print_await(irp, (IrInstructionAwait *)instruction);
2036 break;
20282037 }
20292038 fprintf(irp->f, "\n");
20302039}
test/stage1/behavior/coroutine_await_struct.zig+4-4
......@@ -6,12 +6,12 @@ const Foo = struct {
66 x: i32,
77};
88
9var await_a_promise: promise = undefined;
9var await_a_promise: anyframe = undefined;
1010var await_final_result = Foo{ .x = 0 };
1111
1212test "coroutine await struct" {
1313 await_seq('a');
14 const p = async<std.heap.direct_allocator> await_amain() catch unreachable;
14 const p = async await_amain();
1515 await_seq('f');
1616 resume await_a_promise;
1717 await_seq('i');
......@@ -20,7 +20,7 @@ test "coroutine await struct" {
2020}
2121async fn await_amain() void {
2222 await_seq('b');
23 const p = async await_another() catch unreachable;
23 const p = async await_another();
2424 await_seq('e');
2525 await_final_result = await p;
2626 await_seq('h');
......@@ -29,7 +29,7 @@ async fn await_another() Foo {
2929 await_seq('c');
3030 suspend {
3131 await_seq('d');
32 await_a_promise = @handle();
32 await_a_promise = @frame();
3333 }
3434 await_seq('g');
3535 return Foo{ .x = 1234 };
test/stage1/behavior/coroutines.zig+97-112
......@@ -180,97 +180,85 @@ async fn testSuspendBlock() void {
180180 result = true;
181181}
182182
183//var await_a_promise: anyframe = undefined;
184//var await_final_result: i32 = 0;
185//
186//test "coroutine await" {
187// await_seq('a');
188// const p = async<allocator> await_amain() catch unreachable;
189// await_seq('f');
190// resume await_a_promise;
191// await_seq('i');
192// expect(await_final_result == 1234);
193// expect(std.mem.eql(u8, await_points, "abcdefghi"));
194//}
195//async fn await_amain() void {
196// await_seq('b');
197// const p = async await_another() catch unreachable;
198// await_seq('e');
199// await_final_result = await p;
200// await_seq('h');
201//}
202//async fn await_another() i32 {
203// await_seq('c');
204// suspend {
205// await_seq('d');
206// await_a_promise = @frame();
207// }
208// await_seq('g');
209// return 1234;
210//}
211//
212//var await_points = [_]u8{0} ** "abcdefghi".len;
213//var await_seq_index: usize = 0;
214//
215//fn await_seq(c: u8) void {
216// await_points[await_seq_index] = c;
217// await_seq_index += 1;
218//}
219//
220//var early_final_result: i32 = 0;
221//
222//test "coroutine await early return" {
223// early_seq('a');
224// const p = async<allocator> early_amain() catch @panic("out of memory");
225// early_seq('f');
226// expect(early_final_result == 1234);
227// expect(std.mem.eql(u8, early_points, "abcdef"));
228//}
229//async fn early_amain() void {
230// early_seq('b');
231// const p = async early_another() catch @panic("out of memory");
232// early_seq('d');
233// early_final_result = await p;
234// early_seq('e');
235//}
236//async fn early_another() i32 {
237// early_seq('c');
238// return 1234;
239//}
240//
241//var early_points = [_]u8{0} ** "abcdef".len;
242//var early_seq_index: usize = 0;
243//
244//fn early_seq(c: u8) void {
245// early_points[early_seq_index] = c;
246// early_seq_index += 1;
247//}
248//
249//test "coro allocation failure" {
250// var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0);
251// if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) {
252// @panic("expected allocation failure");
253// } else |err| switch (err) {
254// error.OutOfMemory => {},
255// }
256//}
257//async fn asyncFuncThatNeverGetsRun() void {
258// @panic("coro frame allocation should fail");
259//}
260//
261//test "async function with dot syntax" {
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//
183var await_a_promise: anyframe = undefined;
184var await_final_result: i32 = 0;
185
186test "coroutine await" {
187 await_seq('a');
188 const p = async await_amain();
189 await_seq('f');
190 resume await_a_promise;
191 await_seq('i');
192 expect(await_final_result == 1234);
193 expect(std.mem.eql(u8, await_points, "abcdefghi"));
194}
195async fn await_amain() void {
196 await_seq('b');
197 const p = async await_another();
198 await_seq('e');
199 await_final_result = await p;
200 await_seq('h');
201}
202async fn await_another() i32 {
203 await_seq('c');
204 suspend {
205 await_seq('d');
206 await_a_promise = @frame();
207 }
208 await_seq('g');
209 return 1234;
210}
211
212var await_points = [_]u8{0} ** "abcdefghi".len;
213var await_seq_index: usize = 0;
214
215fn await_seq(c: u8) void {
216 await_points[await_seq_index] = c;
217 await_seq_index += 1;
218}
219
220var early_final_result: i32 = 0;
221
222test "coroutine await early return" {
223 early_seq('a');
224 const p = async early_amain();
225 early_seq('f');
226 expect(early_final_result == 1234);
227 expect(std.mem.eql(u8, early_points, "abcdef"));
228}
229async fn early_amain() void {
230 early_seq('b');
231 const p = async early_another();
232 early_seq('d');
233 early_final_result = await p;
234 early_seq('e');
235}
236async fn early_another() i32 {
237 early_seq('c');
238 return 1234;
239}
240
241var early_points = [_]u8{0} ** "abcdef".len;
242var early_seq_index: usize = 0;
243
244fn early_seq(c: u8) void {
245 early_points[early_seq_index] = c;
246 early_seq_index += 1;
247}
248
249test "async function with dot syntax" {
250 const S = struct {
251 var y: i32 = 1;
252 async fn foo() void {
253 y += 1;
254 suspend;
255 }
256 };
257 const p = async S.foo();
258 // can't cancel in tests because they are non-async functions
259 expect(S.y == 2);
260}
261
274262//test "async fn pointer in a struct field" {
275263// var data: i32 = 1;
276264// const Foo = struct {
......@@ -287,18 +275,17 @@ async fn testSuspendBlock() void {
287275// y.* += 1;
288276// suspend;
289277//}
290//
278
291279//test "async fn with inferred error set" {
292// const p = (async<allocator> failing()) catch unreachable;
280// const p = async failing();
293281// resume p;
294// cancel p;
295282//}
296283//
297284//async fn failing() !void {
298285// suspend;
299286// return error.Fail;
300287//}
301//
288
302289//test "error return trace across suspend points - early return" {
303290// const p = nonFailing();
304291// resume p;
......@@ -331,20 +318,18 @@ async fn testSuspendBlock() void {
331318// }
332319// };
333320//}
334//
335//test "break from suspend" {
336// var buf: [500]u8 = undefined;
337// var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
338// var my_result: i32 = 1;
339// const p = try async<a> testBreakFromSuspend(&my_result);
340// cancel p;
341// std.testing.expect(my_result == 2);
342//}
343//async fn testBreakFromSuspend(my_result: *i32) void {
344// suspend {
345// resume @frame();
346// }
347// my_result.* += 1;
348// suspend;
349// my_result.* += 1;
350//}
321
322test "break from suspend" {
323 var my_result: i32 = 1;
324 const p = async testBreakFromSuspend(&my_result);
325 // can't cancel here
326 std.testing.expect(my_result == 2);
327}
328async fn testBreakFromSuspend(my_result: *i32) void {
329 suspend {
330 resume @frame();
331 }
332 my_result.* += 1;
333 suspend;
334 my_result.* += 1;
335}