| author | |
| committer | |
| log | 87710a1cc2c4d0e7ecc309e430f7d33baadc5f02 |
| tree | 1a5d38b2a65f0e63e463c5beb94d81c7af8a9320 |
| parent | c87920966133d3285b60ccd022282e3f53789e0c |
| signature |
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 function | 4 | * await of a non async function |
| 4 | * await in single-threaded mode | ||
| 5 | * async call on a non async function | 5 | * async call on a non async function |
| 6 | * @asyncCall with an async function pointer | ||
| 7 | * cancel | 6 | * cancel |
| 8 | * defer and errdefer | 7 | * defer and errdefer |
| 9 | * safety for double await | 8 | * safety for double await |
| ... | @@ -21,3 +20,16 @@ | ... | @@ -21,3 +20,16 @@ |
| 21 | * compile error for copying a frame | 20 | * compile error for copying a frame |
| 22 | * compile error for resuming a const frame pointer | 21 | * compile error for resuming a const frame pointer |
| 23 | * runtime safety enabling/disabling scope has to be coordinated across resume/await/calls/return | 22 | * 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, | ||
| 1556 | 1558 | ||
| 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; |
| 3699 | static const size_t err_union_err_index = 0; | 3701 | static const size_t err_union_err_index = 0; |
| 3700 | static const size_t err_union_payload_index = 1; | 3702 | static const size_t err_union_payload_index = 1; |
| 3701 | 3703 | ||
| 3704 | // label (grep this): [coro_frame_struct_layout] | ||
| 3702 | static const size_t coro_fn_ptr_index = 0; | 3705 | static const size_t coro_fn_ptr_index = 0; |
| 3703 | static const size_t coro_awaiter_index = 1; | 3706 | static const size_t coro_awaiter_index = 1; |
| 3704 | static const size_t coro_arg_start = 2; | 3707 | static 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 | } |
| 5207 | 5207 | ||
| 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 = {}; |
| 5210 | 5211 | ||
| ... | @@ -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_ptr | 7531 | ptr_result_type, // fn_ptr |
| 7530 | usize_type_ref, // awaiter | 7532 | 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_ptr | 7565 | LLVMPointerType(fn_type, 0), // fn_ptr |
| 7563 | usize_type_ref, // awaiter | 7566 | 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 | } |
| 3481 | 3483 | ||
| 3484 | static 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 | |||
| 3482 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCallGen *instruction) { | 3493 | static 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 | } | ||
| 3519 | 3528 | ||
| 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 pointer | 3567 | 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); | ||
| 3542 | 3575 | ||
| 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); |
| 3549 | 3582 | ||
| 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 |
| 3581 | 3618 | ||
| 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, |
| 5223 | static LLVMValueRef ir_render_frame_size(CodeGen *g, IrExecutable *executable, | 5281 | static 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 | } |
| 5234 | 5287 | ||
| 5235 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { | 5288 | static 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 Windows | 7153 | 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); |
| 1406 | 1410 | ||
| 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 | } |
| 5210 | 5216 | ||
| ... | @@ -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 | } |
| 14818 | 14868 | ||
| 14819 | static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry, | 14869 | static 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 | } |
| 14826 | 14896 | ||
| 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 |
| 15559 | 15629 | ||
| 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 | } |
| 15564 | 15636 | ||
| 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 | } |
| 15570 | 15640 | ||
| 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 | } |
| 15647 | 15717 | ||
| 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 | } |
| 15659 | 15723 | ||
| 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"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub 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 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompareOutputContext) void { | 3 | pub 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 | } |
| 262 | 262 | ||
| 263 | //test "async fn pointer in a struct field" { | 263 | test "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; | 276 | async fn simpleAsyncFn2(y: *i32) void { |
| 277 | // suspend; | 277 | defer y.* += 2; |
| 278 | //} | 278 | y.* += 1; |
| 279 | suspend; | ||
| 280 | } | ||
| 281 | |||
| 282 | test "@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 | } | ||
| 279 | 299 | ||
| 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(); |