authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-30 13:42:26-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-07-30 13:42:26-04:00
log5d4a02c350a18a70cf1f92f6638b5d26689c16b4
tree30c9da2e8dc9a0aaef2ac59c9694291ec0f5c9ab
parent608ff52dc3ea356b23fb6ae92fbca9fbb18c7892
parentcfe03c764de0d2edfbad74a71d7c18f0fd68b506
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #1307 from ziglang/cancel-semantics

improved coroutine cancel semantics

10 files changed, 486 insertions(+), 142 deletions(-)

doc/langref.html.in+7-7
......@@ -4665,24 +4665,24 @@ async fn testSuspendBlock() void {
46654665 block, while the old thread continued executing the suspend block.
46664666 </p>
46674667 <p>
4668 However, if you use labeled <code>break</code> on the suspend block, the coroutine
4668 However, the coroutine can be directly resumed from the suspend block, in which case it
46694669 never returns to its resumer and continues executing.
46704670 </p>
46714671 {#code_begin|test#}
46724672const std = @import("std");
46734673const assert = std.debug.assert;
46744674
4675test "break from suspend" {
4675test "resume from suspend" {
46764676 var buf: [500]u8 = undefined;
46774677 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
46784678 var my_result: i32 = 1;
4679 const p = try async<a> testBreakFromSuspend(&my_result);
4679 const p = try async<a> testResumeFromSuspend(&my_result);
46804680 cancel p;
46814681 std.debug.assert(my_result == 2);
46824682}
4683async fn testBreakFromSuspend(my_result: *i32) void {
4684 s: suspend |p| {
4685 break :s;
4683async fn testResumeFromSuspend(my_result: *i32) void {
4684 suspend |p| {
4685 resume p;
46864686 }
46874687 my_result.* += 1;
46884688 suspend;
......@@ -7336,7 +7336,7 @@ Defer(body) = ("defer" | "deferror") body
73367336
73377337IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
73387338
7339SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
7339SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
73407340
73417341IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
73427342
src/all_types.hpp+2-4
......@@ -60,7 +60,7 @@ struct IrExecutable {
6060 ZigList<Tld *> tld_list;
6161
6262 IrInstruction *coro_handle;
63 IrInstruction *coro_awaiter_field_ptr; // this one is shared and in the promise
63 IrInstruction *atomic_state_field_ptr; // this one is shared and in the promise
6464 IrInstruction *coro_result_ptr_field_ptr;
6565 IrInstruction *coro_result_field_ptr;
6666 IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise
......@@ -898,7 +898,6 @@ struct AstNodeAwaitExpr {
898898};
899899
900900struct AstNodeSuspend {
901 Buf *name;
902901 AstNode *block;
903902 AstNode *promise_symbol;
904903};
......@@ -1929,7 +1928,6 @@ struct ScopeLoop {
19291928struct ScopeSuspend {
19301929 Scope base;
19311930
1932 Buf *name;
19331931 IrBasicBlock *resume_block;
19341932 bool reported_err;
19351933};
......@@ -3245,7 +3243,7 @@ static const size_t stack_trace_ptr_count = 30;
32453243#define RESULT_FIELD_NAME "result"
32463244#define ASYNC_ALLOC_FIELD_NAME "allocFn"
32473245#define ASYNC_FREE_FIELD_NAME "freeFn"
3248#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle"
3246#define ATOMIC_STATE_FIELD_NAME "atomic_state"
32493247// these point to data belonging to the awaiter
32503248#define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr"
32513249#define RESULT_PTR_FIELD_NAME "result_ptr"
src/analyze.cpp+9-5
......@@ -161,7 +161,6 @@ ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {
161161 assert(node->type == NodeTypeSuspend);
162162 ScopeSuspend *scope = allocate<ScopeSuspend>(1);
163163 init_scope(&scope->base, ScopeIdSuspend, node, parent);
164 scope->name = node->data.suspend.name;
165164 return scope;
166165}
167166
......@@ -519,11 +518,11 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
519518 return return_type->promise_frame_parent;
520519 }
521520
522 TypeTableEntry *awaiter_handle_type = get_optional_type(g, g->builtin_types.entry_promise);
521 TypeTableEntry *atomic_state_type = g->builtin_types.entry_usize;
523522 TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);
524523
525524 ZigList<const char *> field_names = {};
526 field_names.append(AWAITER_HANDLE_FIELD_NAME);
525 field_names.append(ATOMIC_STATE_FIELD_NAME);
527526 field_names.append(RESULT_FIELD_NAME);
528527 field_names.append(RESULT_PTR_FIELD_NAME);
529528 if (g->have_err_ret_tracing) {
......@@ -533,7 +532,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
533532 }
534533
535534 ZigList<TypeTableEntry *> field_types = {};
536 field_types.append(awaiter_handle_type);
535 field_types.append(atomic_state_type);
537536 field_types.append(return_type);
538537 field_types.append(result_ptr_type);
539538 if (g->have_err_ret_tracing) {
......@@ -6228,7 +6227,12 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
62286227 } else if (type_entry->id == TypeTableEntryIdOpaque) {
62296228 return 1;
62306229 } else {
6231 return LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref);
6230 uint32_t llvm_alignment = LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref);
6231 // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
6232 if (type_entry->id == TypeTableEntryIdPromise && llvm_alignment < 8) {
6233 return 8;
6234 }
6235 return llvm_alignment;
62326236 }
62336237}
62346238
src/ir.cpp+357-97
......@@ -3097,20 +3097,47 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
30973097 return return_inst;
30983098 }
30993099
3100 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
3101 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node,
3102 get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
3103 // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig
3104 IrInstruction *replacement_value = irb->exec->coro_handle;
3105 IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node,
3106 promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr,
3107 AtomicRmwOp_xchg, AtomicOrderSeqCst);
3108 ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle);
3109 IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle);
3100 IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended");
3101 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended");
3102 IrBasicBlock *store_awaiter_block = ir_create_basic_block(irb, scope, "StoreAwaiter");
3103 IrBasicBlock *check_canceled_block = ir_create_basic_block(irb, scope, "CheckCanceled");
3104
3105 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
3106 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
3107 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
3108 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
3109 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
31103110 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
3111 return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final,
3112 is_comptime);
3113 // the above blocks are rendered by ir_gen after the rest of codegen
3111 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
3112
3113 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
3114 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
3115 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
3116 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, ptr_mask, nullptr,
3117 AtomicRmwOp_or, AtomicOrderSeqCst);
3118
3119 IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
3120 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
3121 ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
3122
3123 ir_set_cursor_at_end_and_append_block(irb, suspended_block);
3124 ir_build_unreachable(irb, scope, node);
3125
3126 ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
3127 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
3128 // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here
3129 IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
3130 ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime);
3131
3132 ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block);
3133 IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr);
3134 ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle);
3135 ir_build_br(irb, scope, node, irb->exec->coro_normal_final, is_comptime);
3136
3137 ir_set_cursor_at_end_and_append_block(irb, check_canceled_block);
3138 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
3139 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
3140 return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime);
31143141}
31153142
31163143static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
......@@ -6159,15 +6186,6 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop
61596186 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
61606187}
61616188
6162static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) {
6163 IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false);
6164
6165 IrBasicBlock *dest_block = suspend_scope->resume_block;
6166 ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
6167
6168 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
6169}
6170
61716189static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
61726190 assert(node->type == NodeTypeBreak);
61736191
......@@ -6208,12 +6226,8 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
62086226 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
62096227 }
62106228 } else if (search_scope->id == ScopeIdSuspend) {
6211 ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope;
6212 if (node->data.break_expr.name != nullptr &&
6213 (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name)))
6214 {
6215 return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope);
6216 }
6229 add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block"));
6230 return irb->codegen->invalid_instruction;
62176231 }
62186232 search_scope = search_scope->parent;
62196233 }
......@@ -6649,30 +6663,150 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
66496663 async_allocator_type_value, is_var_args);
66506664}
66516665
6652static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
6666static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node,
6667 IrInstruction *target_inst, bool cancel_non_suspended, bool cancel_awaited)
6668{
6669 IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone");
6670 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
6671 IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn");
6672 IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn");
6673 IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
6674
6675 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6676 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
6677 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
6678 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
6679 IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
6680 get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
6681 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
6682 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
6683 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
6684 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
6685
6686 // TODO relies on Zig not re-ordering fields
6687 IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst);
6688 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
6689 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
6690 IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6691 atomic_state_field_name);
6692
6693 // set the is_canceled bit
6694 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6695 usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr,
6696 AtomicRmwOp_or, AtomicOrderSeqCst);
6697
6698 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
6699 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
6700 ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
6701
6702 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
6703 IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
6704 IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false);
6705 ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime);
6706
6707 ir_set_cursor_at_end_and_append_block(irb, post_return_block);
6708 if (cancel_awaited) {
6709 ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
6710 } else {
6711 IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
6712 IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
6713 ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime);
6714 }
6715
6716 ir_set_cursor_at_end_and_append_block(irb, pre_return_block);
6717 if (cancel_awaited) {
6718 if (cancel_non_suspended) {
6719 ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
6720 } else {
6721 IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
6722 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
6723 ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime);
6724 }
6725 } else {
6726 ir_build_br(irb, scope, node, done_block, is_comptime);
6727 }
6728
6729 ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
6730 ir_build_cancel(irb, scope, node, target_inst);
6731 ir_build_br(irb, scope, node, done_block, is_comptime);
6732
6733 ir_set_cursor_at_end_and_append_block(irb, done_block);
6734 return ir_build_const_void(irb, scope, node);
6735}
6736
6737static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) {
66536738 assert(node->type == NodeTypeCancel);
66546739
6655 IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, parent_scope);
6740 IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope);
66566741 if (target_inst == irb->codegen->invalid_instruction)
66576742 return irb->codegen->invalid_instruction;
66586743
6659 return ir_build_cancel(irb, parent_scope, node, target_inst);
6744 return ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
6745}
6746
6747static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode *node,
6748 IrInstruction *target_inst)
6749{
6750 IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone");
6751 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
6752 IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended");
6753 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "IsNotSuspended");
6754
6755 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6756 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
6757 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
6758 IrInstruction *and_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, is_suspended_mask);
6759 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
6760 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
6761 IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
6762 get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
6763
6764 // TODO relies on Zig not re-ordering fields
6765 IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst);
6766 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
6767 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
6768 IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6769 atomic_state_field_name);
6770
6771 // clear the is_suspended bit
6772 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6773 usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr,
6774 AtomicRmwOp_and, AtomicOrderSeqCst);
6775
6776 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
6777 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
6778 ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
6779
6780 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
6781 IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
6782 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
6783 ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
6784
6785 ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
6786 ir_build_unreachable(irb, scope, node);
6787
6788 ir_set_cursor_at_end_and_append_block(irb, suspended_block);
6789 ir_build_coro_resume(irb, scope, node, target_inst);
6790 ir_build_br(irb, scope, node, done_block, is_comptime);
6791
6792 ir_set_cursor_at_end_and_append_block(irb, done_block);
6793 return ir_build_const_void(irb, scope, node);
66606794}
66616795
6662static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
6796static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) {
66636797 assert(node->type == NodeTypeResume);
66646798
6665 IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, parent_scope);
6799 IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope);
66666800 if (target_inst == irb->codegen->invalid_instruction)
66676801 return irb->codegen->invalid_instruction;
66686802
6669 return ir_build_coro_resume(irb, parent_scope, node, target_inst);
6803 return ir_gen_resume_target(irb, scope, node, target_inst);
66706804}
66716805
6672static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
6806static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
66736807 assert(node->type == NodeTypeAwaitExpr);
66746808
6675 IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, parent_scope);
6809 IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope);
66766810 if (target_inst == irb->codegen->invalid_instruction)
66776811 return irb->codegen->invalid_instruction;
66786812
......@@ -6686,7 +6820,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
66866820 return irb->codegen->invalid_instruction;
66876821 }
66886822
6689 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);
6823 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope);
66906824 if (scope_defer_expr) {
66916825 if (!scope_defer_expr->reported_err) {
66926826 add_node_error(irb->codegen, node, buf_sprintf("cannot await inside defer expression"));
......@@ -6697,81 +6831,157 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
66976831
66986832 Scope *outer_scope = irb->exec->begin_scope;
66996833
6700 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, parent_scope, node, target_inst);
6834 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst);
67016835 Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
6702 IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name);
6836 IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
67036837
67046838 if (irb->codegen->have_err_ret_tracing) {
6705 IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);
6839 IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
67066840 Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
6707 IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
6708 ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
6709 }
6710
6711 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);
6712 IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr,
6713 awaiter_handle_field_name);
6714
6715 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
6716 VariableTableEntry *result_var = ir_create_var(irb, node, parent_scope, nullptr,
6841 IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
6842 ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
6843 }
6844
6845 IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited");
6846 IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited");
6847 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
6848 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend");
6849 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend");
6850 IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend");
6851 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup");
6852 IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume");
6853 IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget");
6854 IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
6855 IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers");
6856 IrBasicBlock *destroy_block = ir_create_basic_block(irb, scope, "DestroyBlock");
6857 IrBasicBlock *my_suspended_block = ir_create_basic_block(irb, scope, "AlreadySuspended");
6858 IrBasicBlock *my_not_suspended_block = ir_create_basic_block(irb, scope, "NotAlreadySuspended");
6859 IrBasicBlock *do_suspend_block = ir_create_basic_block(irb, scope, "DoSuspend");
6860
6861 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
6862 IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6863 atomic_state_field_name);
6864
6865 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
6866 IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false);
6867 IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node);
6868 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
6869 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6870 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
6871 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
6872 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
6873 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
6874 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
6875
6876 VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr,
67176877 false, false, true, const_bool_false);
6718 IrInstruction *undefined_value = ir_build_const_undefined(irb, parent_scope, node);
6719 IrInstruction *target_promise_type = ir_build_typeof(irb, parent_scope, node, target_inst);
6720 IrInstruction *promise_result_type = ir_build_promise_result_type(irb, parent_scope, node, target_promise_type);
6721 ir_build_await_bookkeeping(irb, parent_scope, node, promise_result_type);
6722 ir_build_var_decl(irb, parent_scope, node, result_var, promise_result_type, nullptr, undefined_value);
6723 IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var);
6724 ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr);
6725 IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle);
6726 IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node,
6727 get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
6728 IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, parent_scope, node,
6729 promise_type_val, awaiter_field_ptr, nullptr, irb->exec->coro_handle, nullptr,
6730 AtomicRmwOp_xchg, AtomicOrderSeqCst);
6731 IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle);
6732 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend");
6733 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend");
6734 IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend");
6735 ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);
6878 IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst);
6879 IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type);
6880 ir_build_await_bookkeeping(irb, scope, node, promise_result_type);
6881 ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value);
6882 IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var);
6883 ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr);
6884 IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle);
6885
6886 IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle);
6887 IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false);
6888 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6889 usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr,
6890 AtomicRmwOp_or, AtomicOrderSeqCst);
6891
6892 IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
6893 IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
6894 ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false);
6895
6896 ir_set_cursor_at_end_and_append_block(irb, already_awaited_block);
6897 ir_build_unreachable(irb, scope, node);
6898
6899 ir_set_cursor_at_end_and_append_block(irb, not_awaited_block);
6900 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
6901 IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
6902 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
6903 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
6904 ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false);
6905
6906 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
6907 ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);
6908
6909 ir_set_cursor_at_end_and_append_block(irb, cancel_target_block);
6910 ir_build_cancel(irb, scope, node, target_inst);
6911 ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
67366912
67376913 ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);
67386914 if (irb->codegen->have_err_ret_tracing) {
67396915 Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
6740 IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_field_name);
6741 IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);
6742 ir_build_merge_err_ret_traces(irb, parent_scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
6916 IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
6917 IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
6918 ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
67436919 }
67446920 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
6745 IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name);
6921 IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
67466922 // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to,
67476923 // because we're about to destroy the memory. So we store it into our result variable.
6748 IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr);
6749 ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result);
6750 ir_build_cancel(irb, parent_scope, node, target_inst);
6751 ir_build_br(irb, parent_scope, node, merge_block, const_bool_false);
6924 IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr);
6925 ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result);
6926 ir_build_cancel(irb, scope, node, target_inst);
6927 ir_build_br(irb, scope, node, merge_block, const_bool_false);
6928
67526929
67536930 ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block);
6754 IrInstruction *suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false);
6755 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6756 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
6931 IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6932 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
6933 AtomicRmwOp_or, AtomicOrderSeqCst);
6934 IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false);
6935 IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false);
6936 ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false);
6937
6938 ir_set_cursor_at_end_and_append_block(irb, my_suspended_block);
6939 ir_build_unreachable(irb, scope, node);
6940
6941 ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block);
6942 IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false);
6943 IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false);
6944 ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false);
6945
6946 ir_set_cursor_at_end_and_append_block(irb, do_suspend_block);
6947 IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false);
67576948
67586949 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
6759 cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0);
6950 cases[0].value = ir_build_const_u8(irb, scope, node, 0);
67606951 cases[0].block = resume_block;
6761 cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1);
6762 cases[1].block = cleanup_block;
6763 ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
6952 cases[1].value = ir_build_const_u8(irb, scope, node, 1);
6953 cases[1].block = destroy_block;
6954 ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block,
67646955 2, cases, const_bool_false, nullptr);
67656956
6957 ir_set_cursor_at_end_and_append_block(irb, destroy_block);
6958 ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
6959 ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
6960
67666961 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6767 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
6768 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
6962 IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false);
6963 IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6964 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr,
6965 AtomicRmwOp_or, AtomicOrderSeqCst);
6966 IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false);
6967 IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false);
6968 IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false);
6969 ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false);
6970
6971 ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
6972 IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr);
6973 ir_gen_cancel_target(irb, scope, node, my_await_handle, true, false);
6974 ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false));
6975
6976 ir_set_cursor_at_end_and_append_block(irb, do_defers_block);
6977 ir_gen_defers_for_block(irb, scope, outer_scope, true);
6978 ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false));
67696979
67706980 ir_set_cursor_at_end_and_append_block(irb, resume_block);
6771 ir_build_br(irb, parent_scope, node, merge_block, const_bool_false);
6981 ir_build_br(irb, scope, node, merge_block, const_bool_false);
67726982
67736983 ir_set_cursor_at_end_and_append_block(irb, merge_block);
6774 return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr);
6984 return ir_build_load_ptr(irb, scope, node, my_result_var_ptr);
67756985}
67766986
67776987static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
......@@ -6810,9 +7020,52 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
68107020
68117021 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
68127022 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
6813
6814 IrInstruction *suspend_code;
7023 IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended");
7024 IrBasicBlock *canceled_block = ir_create_basic_block(irb, parent_scope, "IsCanceled");
7025 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled");
7026 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended");
7027 IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter");
7028
7029 IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise);
7030 IrInstruction *const_bool_true = ir_build_const_bool(irb, parent_scope, node, true);
68157031 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
7032 IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize);
7033 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001
7034 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010
7035 IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0);
7036 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111
7037 IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
7038
7039 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node,
7040 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
7041 AtomicRmwOp_or, AtomicOrderSeqCst);
7042
7043 IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
7044 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
7045 ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false);
7046
7047 ir_set_cursor_at_end_and_append_block(irb, canceled_block);
7048 IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
7049 IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
7050 IrBasicBlock *post_canceled_block = irb->current_basic_block;
7051 ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false);
7052
7053 ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block);
7054 IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr);
7055 ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false);
7056 IrBasicBlock *post_cancel_awaiter_block = irb->current_basic_block;
7057 ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false);
7058
7059 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
7060 IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
7061 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
7062 ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false);
7063
7064 ir_set_cursor_at_end_and_append_block(irb, suspended_block);
7065 ir_build_unreachable(irb, parent_scope, node);
7066
7067 ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
7068 IrInstruction *suspend_code;
68167069 if (node->data.suspend.block == nullptr) {
68177070 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
68187071 } else {
......@@ -6840,13 +7093,20 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
68407093 cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));
68417094 cases[0].block = resume_block;
68427095 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));
6843 cases[1].block = cleanup_block;
7096 cases[1].block = canceled_block;
68447097 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
68457098 2, cases, const_bool_false, nullptr));
68467099
68477100 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
7101 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
7102 IrInstruction **incoming_values = allocate<IrInstruction *>(2);
7103 incoming_blocks[0] = post_canceled_block;
7104 incoming_values[0] = const_bool_true;
7105 incoming_blocks[1] = post_cancel_awaiter_block;
7106 incoming_values[1] = const_bool_false;
7107 IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
68487108 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
6849 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
7109 ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false));
68507110
68517111 ir_set_cursor_at_end_and_append_block(irb, resume_block);
68527112 return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
......@@ -7087,10 +7347,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
70877347 IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);
70887348 irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
70897349
7090 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);
7091 irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
7092 awaiter_handle_field_name);
7093 ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, null_value);
7350 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
7351 irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
7352 atomic_state_field_name);
7353 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
7354 ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero);
70947355 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
70957356 irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
70967357 result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
......@@ -7108,7 +7369,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
71087369 // coordinate with builtin.zig
71097370 Buf *index_name = buf_create_from_str("index");
71107371 IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name);
7111 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
71127372 ir_build_store_ptr(irb, scope, node, index_ptr, zero);
71137373
71147374 Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses");
......@@ -7231,7 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
72317491 ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);
72327492
72337493 ir_set_cursor_at_end_and_append_block(irb, resume_block);
7234 ir_build_coro_resume(irb, scope, node, awaiter_handle);
7494 ir_gen_resume_target(irb, scope, node, awaiter_handle);
72357495 ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
72367496 }
72377497
src/parser.cpp+2-23
......@@ -648,30 +648,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
648648}
649649
650650/*
651SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
651SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
652652*/
653653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
654654 size_t orig_token_index = *token_index;
655655
656 Token *name_token = nullptr;
657 Token *token = &pc->tokens->at(*token_index);
658 if (token->id == TokenIdSymbol) {
659 *token_index += 1;
660 Token *colon_token = &pc->tokens->at(*token_index);
661 if (colon_token->id == TokenIdColon) {
662 *token_index += 1;
663 name_token = token;
664 token = &pc->tokens->at(*token_index);
665 } else if (mandatory) {
666 ast_expect_token(pc, colon_token, TokenIdColon);
667 zig_unreachable();
668 } else {
669 *token_index = orig_token_index;
670 return nullptr;
671 }
672 }
673
674 Token *suspend_token = token;
656 Token *suspend_token = &pc->tokens->at(*token_index);
675657 if (suspend_token->id == TokenIdKeywordSuspend) {
676658 *token_index += 1;
677659 } else if (mandatory) {
......@@ -693,9 +675,6 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
693675 }
694676
695677 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
696 if (name_token != nullptr) {
697 node->data.suspend.name = token_buf(name_token);
698 }
699678 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
700679 ast_eat_token(pc, token_index, TokenIdBinOr);
701680 node->data.suspend.block = ast_parse_block(pc, token_index, true);
std/debug/index.zig+12-2
......@@ -27,7 +27,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void {
2727 const stderr = getStderrStream() catch return;
2828 stderr.print(fmt, args) catch return;
2929}
30fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
30pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
3131 if (stderr_stream) |st| {
3232 return st;
3333 } else {
......@@ -172,6 +172,16 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var,
172172 }
173173}
174174
175pub inline fn getReturnAddress(frame_count: usize) usize {
176 var fp = @ptrToInt(@frameAddress());
177 var i: usize = 0;
178 while (fp != 0 and i < frame_count) {
179 fp = @intToPtr(*const usize, fp).*;
180 i += 1;
181 }
182 return @intToPtr(*const usize, fp + @sizeOf(usize)).*;
183}
184
175185pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void {
176186 const AddressState = union(enum) {
177187 NotLookingForStartAddress,
......@@ -205,7 +215,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
205215 }
206216}
207217
208fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void {
218pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void {
209219 switch (builtin.os) {
210220 builtin.Os.windows => return error.UnsupportedDebugInfo,
211221 builtin.Os.macosx => {
std/event/loop.zig+2-2
......@@ -55,7 +55,7 @@ pub const Loop = struct {
5555 /// After initialization, call run().
5656 /// TODO copy elision / named return values so that the threads referencing *Loop
5757 /// have the correct pointer value.
58 fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {
58 pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {
5959 return self.initInternal(allocator, 1);
6060 }
6161
......@@ -64,7 +64,7 @@ pub const Loop = struct {
6464 /// After initialization, call run().
6565 /// TODO copy elision / named return values so that the threads referencing *Loop
6666 /// have the correct pointer value.
67 fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
67 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
6868 const core_count = try std.os.cpuCount(allocator);
6969 return self.initInternal(allocator, core_count);
7070 }
test/behavior.zig+1
......@@ -16,6 +16,7 @@ comptime {
1616 _ = @import("cases/bugs/828.zig");
1717 _ = @import("cases/bugs/920.zig");
1818 _ = @import("cases/byval_arg_var.zig");
19 _ = @import("cases/cancel.zig");
1920 _ = @import("cases/cast.zig");
2021 _ = @import("cases/const_slice_child.zig");
2122 _ = @import("cases/coroutine_await_struct.zig");
test/cases/cancel.zig created+92
......@@ -0,0 +1,92 @@
1const std = @import("std");
2
3var defer_f1: bool = false;
4var defer_f2: bool = false;
5var defer_f3: bool = false;
6
7test "cancel forwards" {
8 var da = std.heap.DirectAllocator.init();
9 defer da.deinit();
10
11 const p = async<&da.allocator> f1() catch unreachable;
12 cancel p;
13 std.debug.assert(defer_f1);
14 std.debug.assert(defer_f2);
15 std.debug.assert(defer_f3);
16}
17
18async fn f1() void {
19 defer {
20 defer_f1 = true;
21 }
22 await (async f2() catch unreachable);
23}
24
25async fn f2() void {
26 defer {
27 defer_f2 = true;
28 }
29 await (async f3() catch unreachable);
30}
31
32async fn f3() void {
33 defer {
34 defer_f3 = true;
35 }
36 suspend;
37}
38
39var defer_b1: bool = false;
40var defer_b2: bool = false;
41var defer_b3: bool = false;
42var defer_b4: bool = false;
43
44test "cancel backwards" {
45 var da = std.heap.DirectAllocator.init();
46 defer da.deinit();
47
48 const p = async<&da.allocator> b1() catch unreachable;
49 cancel p;
50 std.debug.assert(defer_b1);
51 std.debug.assert(defer_b2);
52 std.debug.assert(defer_b3);
53 std.debug.assert(defer_b4);
54}
55
56async fn b1() void {
57 defer {
58 defer_b1 = true;
59 }
60 await (async b2() catch unreachable);
61}
62
63var b4_handle: promise = undefined;
64
65async fn b2() void {
66 const b3_handle = async b3() catch unreachable;
67 resume b4_handle;
68 cancel b4_handle;
69 defer {
70 defer_b2 = true;
71 }
72 const value = await b3_handle;
73 @panic("unreachable");
74}
75
76async fn b3() i32 {
77 defer {
78 defer_b3 = true;
79 }
80 await (async b4() catch unreachable);
81 return 1234;
82}
83
84async fn b4() void {
85 defer {
86 defer_b4 = true;
87 }
88 suspend |p| {
89 b4_handle = p;
90 }
91 suspend;
92}
test/cases/coroutines.zig+2-2
......@@ -244,8 +244,8 @@ test "break from suspend" {
244244 std.debug.assert(my_result == 2);
245245}
246246async fn testBreakFromSuspend(my_result: *i32) void {
247 s: suspend |p| {
248 break :s;
247 suspend |p| {
248 resume p;
249249 }
250250 my_result.* += 1;
251251 suspend;