authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-03 16:14:24-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-03 16:17:42-04:00
log87710a1cc2c4d0e7ecc309e430f7d33baadc5f02
tree1a5d38b2a65f0e63e463c5beb94d81c7af8a9320
parentc87920966133d3285b60ccd022282e3f53789e0c
signature Commit is signed but in an unrecognized format.

implement `@asyncCall` which supports async function pointers


8 files changed, 247 insertions(+), 61 deletions(-)

BRANCH_TODO+14-2
...@@ -1,9 +1,8 @@...@@ -1,9 +1,8 @@
1 * @asyncCall with an async function pointer
1 * compile error for error: expected anyframe->T, found 'anyframe'2 * compile error for error: expected anyframe->T, found 'anyframe'
2 * compile error for error: expected anyframe->T, found 'i32'3 * compile error for error: expected anyframe->T, found 'i32'
3 * await of a non async function4 * await of a non async function
4 * await in single-threaded mode
5 * async call on a non async function5 * async call on a non async function
6 * @asyncCall with an async function pointer
7 * cancel6 * cancel
8 * defer and errdefer7 * defer and errdefer
9 * safety for double await8 * safety for double await
...@@ -21,3 +20,16 @@...@@ -21,3 +20,16 @@
21 * compile error for copying a frame20 * compile error for copying a frame
22 * compile error for resuming a const frame pointer21 * compile error for resuming a const frame pointer
23 * runtime safety enabling/disabling scope has to be coordinated across resume/await/calls/return22 * runtime safety enabling/disabling scope has to be coordinated across resume/await/calls/return
23 * await in single-threaded mode
24 * calling a generic function which is async
25 * make sure `await @asyncCall` and `await async` are handled correctly.
26 * allow @asyncCall with a real @Frame(func) (the point of this is result pointer)
27 * documentation
28 - @asyncCall
29 - @frame
30 - @Frame
31 - @frameSize
32 - coroutines section
33 - suspend
34 - resume
35 - anyframe, anyframe->T
src/all_types.hpp+3
...@@ -1503,6 +1503,7 @@ enum BuiltinFnId {...@@ -1503,6 +1503,7 @@ enum BuiltinFnId {
1503 BuiltinFnIdInlineCall,1503 BuiltinFnIdInlineCall,
1504 BuiltinFnIdNoInlineCall,1504 BuiltinFnIdNoInlineCall,
1505 BuiltinFnIdNewStackCall,1505 BuiltinFnIdNewStackCall,
1506 BuiltinFnIdAsyncCall,
1506 BuiltinFnIdTypeId,1507 BuiltinFnIdTypeId,
1507 BuiltinFnIdShlExact,1508 BuiltinFnIdShlExact,
1508 BuiltinFnIdShrExact,1509 BuiltinFnIdShrExact,
...@@ -1553,6 +1554,7 @@ enum PanicMsgId {...@@ -1553,6 +1554,7 @@ enum PanicMsgId {
1553 PanicMsgIdBadAwait,1554 PanicMsgIdBadAwait,
1554 PanicMsgIdBadReturn,1555 PanicMsgIdBadReturn,
1555 PanicMsgIdResumedAnAwaitingFn,1556 PanicMsgIdResumedAnAwaitingFn,
1557 PanicMsgIdFrameTooSmall,
15561558
1557 PanicMsgIdCount,1559 PanicMsgIdCount,
1558};1560};
...@@ -3699,6 +3701,7 @@ static const size_t maybe_null_index = 1;...@@ -3699,6 +3701,7 @@ static const size_t maybe_null_index = 1;
3699static const size_t err_union_err_index = 0;3701static const size_t err_union_err_index = 0;
3700static const size_t err_union_payload_index = 1;3702static const size_t err_union_payload_index = 1;
37013703
3704// label (grep this): [coro_frame_struct_layout]
3702static const size_t coro_fn_ptr_index = 0;3705static const size_t coro_fn_ptr_index = 0;
3703static const size_t coro_awaiter_index = 1;3706static const size_t coro_awaiter_index = 1;
3704static const size_t coro_arg_start = 2;3707static const size_t coro_arg_start = 2;
src/analyze.cpp+3
...@@ -5205,6 +5205,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {...@@ -5205,6 +5205,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
5205 call->frame_result_loc = &alloca_gen->base;5205 call->frame_result_loc = &alloca_gen->base;
5206 }5206 }
52075207
5208 // label (grep this): [coro_frame_struct_layout]
5208 ZigList<ZigType *> field_types = {};5209 ZigList<ZigType *> field_types = {};
5209 ZigList<const char *> field_names = {};5210 ZigList<const char *> field_names = {};
52105211
...@@ -7525,6 +7526,7 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re...@@ -7525,6 +7526,7 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re
7525 if (result_type == nullptr) {7526 if (result_type == nullptr) {
7526 g->anyframe_fn_type = ptr_result_type;7527 g->anyframe_fn_type = ptr_result_type;
7527 }7528 }
7529 // label (grep this): [coro_frame_struct_layout]
7528 LLVMTypeRef field_types[] = {7530 LLVMTypeRef field_types[] = {
7529 ptr_result_type, // fn_ptr7531 ptr_result_type, // fn_ptr
7530 usize_type_ref, // awaiter7532 usize_type_ref, // awaiter
...@@ -7558,6 +7560,7 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re...@@ -7558,6 +7560,7 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re
7558 ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type);7560 ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type);
7559 } else {7561 } else {
7560 ZigType *ptr_result_type = get_pointer_to_type(g, result_type, false);7562 ZigType *ptr_result_type = get_pointer_to_type(g, result_type, false);
7563 // label (grep this): [coro_frame_struct_layout]
7561 LLVMTypeRef field_types[] = {7564 LLVMTypeRef field_types[] = {
7562 LLVMPointerType(fn_type, 0), // fn_ptr7565 LLVMPointerType(fn_type, 0), // fn_ptr
7563 usize_type_ref, // awaiter7566 usize_type_ref, // awaiter
src/codegen.cpp+79-26
...@@ -879,6 +879,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {...@@ -879,6 +879,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
879 return buf_create_from_str("async function returned twice");879 return buf_create_from_str("async function returned twice");
880 case PanicMsgIdResumedAnAwaitingFn:880 case PanicMsgIdResumedAnAwaitingFn:
881 return buf_create_from_str("awaiting function resumed");881 return buf_create_from_str("awaiting function resumed");
882 case PanicMsgIdFrameTooSmall:
883 return buf_create_from_str("frame too small");
882 }884 }
883 zig_unreachable();885 zig_unreachable();
884}886}
...@@ -3479,7 +3481,18 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) {...@@ -3479,7 +3481,18 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) {
3479 }3481 }
3480}3482}
34813483
3484static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) {
3485 LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type;
3486 LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0);
3487 LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, "");
3488 LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true);
3489 LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, "");
3490 return LLVMBuildLoad(g->builder, prefix_ptr, "");
3491}
3492
3482static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCallGen *instruction) {3493static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCallGen *instruction) {
3494 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
3495
3483 LLVMValueRef fn_val;3496 LLVMValueRef fn_val;
3484 ZigType *fn_type;3497 ZigType *fn_type;
3485 bool callee_is_async;3498 bool callee_is_async;
...@@ -3511,34 +3524,54 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3511,34 +3524,54 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3511 LLVMValueRef awaiter_init_val;3524 LLVMValueRef awaiter_init_val;
3512 LLVMValueRef ret_ptr;3525 LLVMValueRef ret_ptr;
3513 if (instruction->is_async) {3526 if (instruction->is_async) {
3514 frame_result_loc = result_loc;
3515 awaiter_init_val = zero;3527 awaiter_init_val = zero;
3516 if (ret_has_bits) {
3517 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, "");
3518 }
35193528
3520 // Use the result location which is inside the frame if this is an async call.3529 if (instruction->new_stack == nullptr) {
3521 if (ret_has_bits) {3530 frame_result_loc = result_loc;
3522 LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");3531
3523 LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);3532 if (ret_has_bits) {
3533 // Use the result location which is inside the frame if this is an async call.
3534 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, "");
3535 }
3536 } else {
3537 LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack);
3538 if (ir_want_runtime_safety(g, &instruction->base)) {
3539 LLVMValueRef given_len_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_len_index, "");
3540 LLVMValueRef given_frame_len = LLVMBuildLoad(g->builder, given_len_ptr, "");
3541 LLVMValueRef actual_frame_len = gen_frame_size(g, fn_val);
3542
3543 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckFail");
3544 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckOk");
3545
3546 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntUGE, given_frame_len, actual_frame_len, "");
3547 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
3548
3549 LLVMPositionBuilderAtEnd(g->builder, fail_block);
3550 gen_safety_crash(g, PanicMsgIdFrameTooSmall);
3551
3552 LLVMPositionBuilderAtEnd(g->builder, ok_block);
3553 }
3554 LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, "");
3555 LLVMValueRef frame_ptr = LLVMBuildLoad(g->builder, frame_ptr_ptr, "");
3556 frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr,
3557 get_llvm_type(g, instruction->base.value.type), "");
3558
3559 if (ret_has_bits) {
3560 // Use the result location provided to the @asyncCall builtin
3561 ret_ptr = result_loc;
3562 }
3524 }3563 }
3525 } else if (callee_is_async) {3564 } else if (callee_is_async) {
3526 frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);3565 frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
3527 awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr,3566 awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr,
3528 g->builtin_types.entry_usize->llvm_type, ""); // caller's own frame pointer3567 g->builtin_types.entry_usize->llvm_type, ""); // caller's own frame pointer
3529 if (ret_has_bits) {3568 if (ret_has_bits) {
3569 // Use the call instruction's result location.
3530 ret_ptr = result_loc;3570 ret_ptr = result_loc;
3531 }3571 }
3532
3533 // Use the call instruction's result location.
3534 if (ret_has_bits) {
3535 LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
3536 LLVMBuildStore(g->builder, result_loc, ret_ptr_ptr);
3537 }
3538 }3572 }
3539 if (instruction->is_async || callee_is_async) {3573 if (instruction->is_async || callee_is_async) {
3540 assert(frame_result_loc != nullptr);3574 assert(frame_result_loc != nullptr);
3541 assert(instruction->fn_entry != nullptr);
35423575
3543 if (prefix_arg_err_ret_stack) {3576 if (prefix_arg_err_ret_stack) {
3544 zig_panic("TODO");3577 zig_panic("TODO");
...@@ -3547,6 +3580,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3547,6 +3580,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3547 LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_awaiter_index, "");3580 LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_awaiter_index, "");
3548 LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr);3581 LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr);
35493582
3583 if (ret_has_bits) {
3584 LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
3585 LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);
3586 }
3550 }3587 }
3551 if (!instruction->is_async && !callee_is_async) {3588 if (!instruction->is_async && !callee_is_async) {
3552 if (first_arg_ret) {3589 if (first_arg_ret) {
...@@ -3581,16 +3618,37 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3581,16 +3618,37 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
35813618
3582 if (instruction->is_async || callee_is_async) {3619 if (instruction->is_async || callee_is_async) {
3583 size_t ret_2_or_0 = type_has_bits(fn_type->data.fn.fn_type_id.return_type) ? 2 : 0;3620 size_t ret_2_or_0 = type_has_bits(fn_type->data.fn.fn_type_id.return_type) ? 2 : 0;
3621 size_t arg_start_i = coro_arg_start + ret_2_or_0;
3622
3623 LLVMValueRef casted_frame;
3624 if (instruction->new_stack != nullptr) {
3625 // We need the frame type to be a pointer to a struct that includes the args
3626 // label (grep this): [coro_frame_struct_layout]
3627 size_t field_count = arg_start_i + gen_param_values.length;
3628 LLVMTypeRef *field_types = allocate_nonzero<LLVMTypeRef>(field_count);
3629 LLVMGetStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc)), field_types);
3630 for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) {
3631 field_types[arg_start_i + arg_i] = LLVMTypeOf(gen_param_values.at(arg_i));
3632 }
3633 LLVMTypeRef frame_with_args_type = LLVMStructType(field_types, field_count, false);
3634 LLVMTypeRef ptr_frame_with_args_type = LLVMPointerType(frame_with_args_type, 0);
3635
3636 casted_frame = LLVMBuildBitCast(g->builder, frame_result_loc, ptr_frame_with_args_type, "");
3637 } else {
3638 casted_frame = frame_result_loc;
3639 }
3640
3584 for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) {3641 for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) {
3585 LLVMValueRef arg_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc,3642 LLVMValueRef arg_ptr = LLVMBuildStructGEP(g->builder, casted_frame, arg_start_i + arg_i, "");
3586 coro_arg_start + ret_2_or_0 + arg_i, "");
3587 LLVMBuildStore(g->builder, gen_param_values.at(arg_i), arg_ptr);3643 LLVMBuildStore(g->builder, gen_param_values.at(arg_i), arg_ptr);
3588 }3644 }
3589 }3645 }
3590 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
3591 if (instruction->is_async) {3646 if (instruction->is_async) {
3592 LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(usize_type_ref)};3647 LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(usize_type_ref)};
3593 ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, "");3648 ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, "");
3649 if (instruction->new_stack != nullptr) {
3650 return frame_result_loc;
3651 }
3594 return nullptr;3652 return nullptr;
3595 } else if (callee_is_async) {3653 } else if (callee_is_async) {
3596 ZigType *ptr_result_type = get_pointer_to_type(g, src_return_type, true);3654 ZigType *ptr_result_type = get_pointer_to_type(g, src_return_type, true);
...@@ -5223,13 +5281,8 @@ static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable,...@@ -5223,13 +5281,8 @@ static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable,
5223static LLVMValueRef ir_render_frame_size(CodeGen *g, IrExecutable *executable,5281static LLVMValueRef ir_render_frame_size(CodeGen *g, IrExecutable *executable,
5224 IrInstructionFrameSizeGen *instruction)5282 IrInstructionFrameSizeGen *instruction)
5225{5283{
5226 LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type;
5227 LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0);
5228 LLVMValueRef fn_val = ir_llvm_value(g, instruction->fn);5284 LLVMValueRef fn_val = ir_llvm_value(g, instruction->fn);
5229 LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, "");5285 return gen_frame_size(g, fn_val);
5230 LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true);
5231 LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, "");
5232 return LLVMBuildLoad(g->builder, prefix_ptr, "");
5233}5286}
52345287
5235static void set_debug_location(CodeGen *g, IrInstruction *instruction) {5288static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
...@@ -7097,13 +7150,13 @@ static void define_builtin_fns(CodeGen *g) {...@@ -7097,13 +7150,13 @@ static void define_builtin_fns(CodeGen *g) {
7097 create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2);7150 create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2);
7098 create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2);7151 create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2);
7099 create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2);7152 create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2);
7100 //Needs library support on Windows7153 create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2);
7101 //create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2);
7102 create_builtin_fn(g, BuiltinFnIdRound, "round", 2);7154 create_builtin_fn(g, BuiltinFnIdRound, "round", 2);
7103 create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4);7155 create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4);
7104 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);7156 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
7105 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);7157 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
7106 create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX);7158 create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX);
7159 create_builtin_fn(g, BuiltinFnIdAsyncCall, "asyncCall", SIZE_MAX);
7107 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);7160 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
7108 create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);7161 create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
7109 create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);7162 create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
src/ir.cpp+85-17
...@@ -1402,6 +1402,10 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s...@@ -1402,6 +1402,10 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s
1402 if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block);1402 if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block);
1403 for (size_t i = 0; i < arg_count; i += 1)1403 for (size_t i = 0; i < arg_count; i += 1)
1404 ir_ref_instruction(args[i], irb->current_basic_block);1404 ir_ref_instruction(args[i], irb->current_basic_block);
1405 if (is_async && new_stack != nullptr) {
1406 // in this case the arg at the end is the return pointer
1407 ir_ref_instruction(args[arg_count], irb->current_basic_block);
1408 }
1405 if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);1409 if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);
14061410
1407 return &call_instruction->base;1411 return &call_instruction->base;
...@@ -5203,8 +5207,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -5203,8 +5207,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
5203 }5207 }
5204 case BuiltinFnIdNewStackCall:5208 case BuiltinFnIdNewStackCall:
5205 {5209 {
5206 if (node->data.fn_call_expr.params.length == 0) {5210 if (node->data.fn_call_expr.params.length < 2) {
5207 add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));5211 add_node_error(irb->codegen, node,
5212 buf_sprintf("expected at least 2 arguments, found %" ZIG_PRI_usize,
5213 node->data.fn_call_expr.params.length));
5208 return irb->codegen->invalid_instruction;5214 return irb->codegen->invalid_instruction;
5209 }5215 }
52105216
...@@ -5232,6 +5238,50 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -5232,6 +5238,50 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
5232 FnInlineAuto, false, new_stack, result_loc);5238 FnInlineAuto, false, new_stack, result_loc);
5233 return ir_lval_wrap(irb, scope, call, lval, result_loc);5239 return ir_lval_wrap(irb, scope, call, lval, result_loc);
5234 }5240 }
5241 case BuiltinFnIdAsyncCall:
5242 {
5243 size_t arg_offset = 3;
5244 if (node->data.fn_call_expr.params.length < arg_offset) {
5245 add_node_error(irb->codegen, node,
5246 buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
5247 arg_offset, node->data.fn_call_expr.params.length));
5248 return irb->codegen->invalid_instruction;
5249 }
5250
5251 AstNode *bytes_node = node->data.fn_call_expr.params.at(0);
5252 IrInstruction *bytes = ir_gen_node(irb, bytes_node, scope);
5253 if (bytes == irb->codegen->invalid_instruction)
5254 return bytes;
5255
5256 AstNode *ret_ptr_node = node->data.fn_call_expr.params.at(1);
5257 IrInstruction *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope);
5258 if (ret_ptr == irb->codegen->invalid_instruction)
5259 return ret_ptr;
5260
5261 AstNode *fn_ref_node = node->data.fn_call_expr.params.at(2);
5262 IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
5263 if (fn_ref == irb->codegen->invalid_instruction)
5264 return fn_ref;
5265
5266 size_t arg_count = node->data.fn_call_expr.params.length - arg_offset;
5267
5268 // last "arg" is return pointer
5269 IrInstruction **args = allocate<IrInstruction*>(arg_count + 1);
5270
5271 for (size_t i = 0; i < arg_count; i += 1) {
5272 AstNode *arg_node = node->data.fn_call_expr.params.at(i + arg_offset);
5273 IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
5274 if (arg == irb->codegen->invalid_instruction)
5275 return arg;
5276 args[i] = arg;
5277 }
5278
5279 args[arg_count] = ret_ptr;
5280
5281 IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
5282 FnInlineAuto, true, bytes, result_loc);
5283 return ir_lval_wrap(irb, scope, call, lval, result_loc);
5284 }
5235 case BuiltinFnIdTypeId:5285 case BuiltinFnIdTypeId:
5236 {5286 {
5237 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);5287 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -14817,11 +14867,31 @@ static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInst...@@ -14817,11 +14867,31 @@ static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInst
14817}14867}
1481814868
14819static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry,14869static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry,
14820 ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count)14870 ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,
14871 IrInstruction *casted_new_stack)
14821{14872{
14822 if (fn_entry == nullptr) {14873 if (fn_entry == nullptr) {
14823 ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));14874 if (call_instruction->new_stack == nullptr) {
14824 return ira->codegen->invalid_instruction;14875 ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
14876 return ira->codegen->invalid_instruction;
14877 }
14878 // this is an @asyncCall
14879
14880 if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) {
14881 ir_add_error(ira, fn_ref,
14882 buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name)));
14883 return ira->codegen->invalid_instruction;
14884 }
14885
14886 IrInstruction *ret_ptr = call_instruction->args[call_instruction->arg_count]->child;
14887 if (type_is_invalid(ret_ptr->value.type))
14888 return ira->codegen->invalid_instruction;
14889
14890 ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_type->data.fn.fn_type_id.return_type);
14891
14892 IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, nullptr, fn_ref,
14893 arg_count, casted_args, FnInlineAuto, true, casted_new_stack, ret_ptr, anyframe_type);
14894 return &call_gen->base;
14825 }14895 }
1482614896
14827 ZigType *frame_type = get_coro_frame_type(ira->codegen, fn_entry);14897 ZigType *frame_type = get_coro_frame_type(ira->codegen, fn_entry);
...@@ -15559,13 +15629,13 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c...@@ -15559,13 +15629,13 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1555915629
15560 size_t impl_param_count = impl_fn_type_id->param_count;15630 size_t impl_param_count = impl_fn_type_id->param_count;
15561 if (call_instruction->is_async) {15631 if (call_instruction->is_async) {
15562 zig_panic("TODO async call");15632 IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry,
15633 nullptr, casted_args, call_param_count, casted_new_stack);
15634 return ir_finish_anal(ira, result);
15563 }15635 }
1556415636
15565 if (!call_instruction->is_async) {15637 if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {
15566 if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {15638 parent_fn_entry->inferred_async_node = fn_ref->source_node;
15567 parent_fn_entry->inferred_async_node = fn_ref->source_node;
15568 }
15569 }15639 }
1557015640
15571 IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base,15641 IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base,
...@@ -15645,18 +15715,16 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c...@@ -15645,18 +15715,16 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
15645 return ira->codegen->invalid_instruction;15715 return ira->codegen->invalid_instruction;
15646 }15716 }
1564715717
15648 if (!call_instruction->is_async) {
15649 if (fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {
15650 parent_fn_entry->inferred_async_node = fn_ref->source_node;
15651 }
15652 }
15653
15654 if (call_instruction->is_async) {15718 if (call_instruction->is_async) {
15655 IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref,15719 IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref,
15656 casted_args, call_param_count);15720 casted_args, call_param_count, casted_new_stack);
15657 return ir_finish_anal(ira, result);15721 return ir_finish_anal(ira, result);
15658 }15722 }
1565915723
15724 if (fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {
15725 parent_fn_entry->inferred_async_node = fn_ref->source_node;
15726 }
15727
15660 IrInstruction *result_loc;15728 IrInstruction *result_loc;
15661 if (handle_is_ptr(return_type)) {15729 if (handle_is_ptr(return_type)) {
15662 result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,15730 result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
test/compile_errors.zig+12
...@@ -2,6 +2,18 @@ const tests = @import("tests.zig");...@@ -2,6 +2,18 @@ const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.add(
6 "non async function pointer passed to @asyncCall",
7 \\export fn entry() void {
8 \\ var ptr = afunc;
9 \\ var bytes: [100]u8 = undefined;
10 \\ _ = @asyncCall(&bytes, {}, ptr);
11 \\}
12 \\fn afunc() void { }
13 ,
14 "tmp.zig:4:32: error: expected async function, found 'fn() void'",
15 );
16
5 cases.add(17 cases.add(
6 "runtime-known async function called",18 "runtime-known async function called",
7 \\export fn entry() void {19 \\export fn entry() void {
test/runtime_safety.zig+15
...@@ -1,6 +1,20 @@...@@ -1,6 +1,20 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompareOutputContext) void {3pub fn addCases(cases: *tests.CompareOutputContext) void {
4 cases.addRuntimeSafety("@asyncCall with too small a frame",
5 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
6 \\ @import("std").os.exit(126);
7 \\}
8 \\pub fn main() void {
9 \\ var bytes: [1]u8 = undefined;
10 \\ var ptr = other;
11 \\ var frame = @asyncCall(&bytes, {}, ptr);
12 \\}
13 \\async fn other() void {
14 \\ suspend;
15 \\}
16 );
17
4 cases.addRuntimeSafety("resuming a function which is awaiting a frame",18 cases.addRuntimeSafety("resuming a function which is awaiting a frame",
5 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {19 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
6 \\ @import("std").os.exit(126);20 \\ @import("std").os.exit(126);
...@@ -17,6 +31,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -17,6 +31,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
17 \\ suspend;31 \\ suspend;
18 \\}32 \\}
19 );33 );
34
20 cases.addRuntimeSafety("resuming a function which is awaiting a call",35 cases.addRuntimeSafety("resuming a function which is awaiting a call",
21 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {36 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
22 \\ @import("std").os.exit(126);37 \\ @import("std").os.exit(126);
test/stage1/behavior/coroutines.zig+36-16
...@@ -260,22 +260,42 @@ test "async function with dot syntax" {...@@ -260,22 +260,42 @@ test "async function with dot syntax" {
260 expect(S.y == 2);260 expect(S.y == 2);
261}261}
262262
263//test "async fn pointer in a struct field" {263test "async fn pointer in a struct field" {
264// var data: i32 = 1;264 var data: i32 = 1;
265// const Foo = struct {265 const Foo = struct {
266// bar: async fn (*i32) void,266 bar: async fn (*i32) void,
267// };267 };
268// var foo = Foo{ .bar = simpleAsyncFn2 };268 var foo = Foo{ .bar = simpleAsyncFn2 };
269// const p = async foo.bar(&data);269 var bytes: [64]u8 = undefined;
270// expect(data == 2);270 const p = @asyncCall(&bytes, {}, foo.bar, &data);
271// resume p;271 comptime expect(@typeOf(p) == anyframe->void);
272// expect(data == 4);272 expect(data == 2);
273//}273 resume p;
274//async fn simpleAsyncFn2(y: *i32) void {274 expect(data == 4);
275// defer y.* += 2;275}
276// y.* += 1;276async fn simpleAsyncFn2(y: *i32) void {
277// suspend;277 defer y.* += 2;
278//}278 y.* += 1;
279 suspend;
280}
281
282test "@asyncCall with return type" {
283 const Foo = struct {
284 bar: async fn () i32,
285
286 async fn afunc() i32 {
287 suspend;
288 return 1234;
289 }
290 };
291 var foo = Foo{ .bar = Foo.afunc };
292 var bytes: [64]u8 = undefined;
293 var aresult: i32 = 0;
294 const frame = @asyncCall(&bytes, &aresult, foo.bar);
295 expect(aresult == 0);
296 resume frame;
297 expect(aresult == 1234);
298}
279299
280//test "async fn with inferred error set" {300//test "async fn with inferred error set" {
281// const p = async failing();301// const p = async failing();