diff --git a/src/all_types.hpp b/src/all_types.hpp index 9575f6cdad..002a2d4a4c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -56,7 +56,13 @@ struct IrExecutable { IrAnalyze *analysis; Scope *begin_scope; ZigList tld_list; + IrInstruction *coro_handle; + IrInstruction *coro_awaiter_field_ptr; + IrInstruction *coro_result_ptr_field_ptr; + IrInstruction *implicit_allocator_ptr; + IrBasicBlock *coro_early_final; + IrBasicBlock *coro_normal_final; }; enum OutType { @@ -1968,6 +1974,10 @@ enum IrInstructionId { IrInstructionIdCoroSize, IrInstructionIdCoroBegin, IrInstructionIdCoroAllocFail, + IrInstructionIdCoroSuspend, + IrInstructionIdCoroEnd, + IrInstructionIdCoroFree, + IrInstructionIdCoroResume, }; struct IrInstruction { @@ -2819,6 +2829,8 @@ struct IrInstructionGetImplicitAllocator { struct IrInstructionCoroId { IrInstruction base; + + IrInstruction *promise_ptr; }; struct IrInstructionCoroAlloc { @@ -2844,6 +2856,30 @@ struct IrInstructionCoroAllocFail { IrInstruction *err_val; }; +struct IrInstructionCoroSuspend { + IrInstruction base; + + IrInstruction *save_point; + IrInstruction *is_final; +}; + +struct IrInstructionCoroEnd { + IrInstruction base; +}; + +struct IrInstructionCoroFree { + IrInstruction base; + + IrInstruction *coro_id; + IrInstruction *coro_handle; +}; + +struct IrInstructionCoroResume { + IrInstruction base; + + IrInstruction *awaiter_handle; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index d83f195a85..c00014d8ca 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -475,9 +475,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { if (child_type->zero_bits) { entry->type_ref = LLVMInt1Type(); entry->di_type = g->builtin_types.entry_bool->di_type; - } else if (child_type->id == TypeTableEntryIdPointer || - child_type->id == TypeTableEntryIdFn) - { + } else if (type_is_codegen_pointer(child_type)) { // this is an optimization but also is necessary for calling C // functions where all pointers are maybe pointers // function types are technically pointers @@ -1262,7 +1260,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { case TypeTableEntryIdMaybe: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; - return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; + return type_is_codegen_pointer(child_type); } case TypeTableEntryIdEnum: return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr; @@ -1673,6 +1671,8 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f field->src_index = i; field->gen_index = i; + assert(type_has_bits(field->type_entry)); + auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field); assert(prev_entry == nullptr); } @@ -3669,9 +3669,11 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { TypeTableEntry *get_codegen_ptr_type(TypeTableEntry *type) { if (type->id == TypeTableEntryIdPointer) return type; if (type->id == TypeTableEntryIdFn) return type; + if (type->id == TypeTableEntryIdPromise) return type; if (type->id == TypeTableEntryIdMaybe) { if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return type->data.maybe.child_type; + if (type->data.maybe.child_type->id == TypeTableEntryIdPromise) return type->data.maybe.child_type; } return nullptr; } diff --git a/src/analyze.hpp b/src/analyze.hpp index a4036c597c..2fe41f6572 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -51,6 +51,7 @@ VariableTableEntry *find_variable(CodeGen *g, Scope *orig_context, Buf *name); Tld *find_decl(CodeGen *g, Scope *scope, Buf *name); void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node); bool type_is_codegen_pointer(TypeTableEntry *type); + TypeTableEntry *get_codegen_ptr_type(TypeTableEntry *type); uint32_t get_ptr_align(TypeTableEntry *type); TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry); diff --git a/src/codegen.cpp b/src/codegen.cpp index cb3a98bb88..6b1a2513c7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -542,7 +542,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { if (!type_has_bits(return_type)) { // nothing to do - } else if (return_type->id == TypeTableEntryIdPointer || return_type->id == TypeTableEntryIdFn) { + } else if (type_is_codegen_pointer(return_type)) { addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); } else if (handle_is_ptr(return_type) && calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc)) @@ -2789,7 +2789,7 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, TypeTableEntry *maybe_type, LLV if (child_type->zero_bits) { return maybe_handle; } else { - bool maybe_is_ptr = (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn); + bool maybe_is_ptr = type_is_codegen_pointer(child_type); if (maybe_is_ptr) { return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), ""); } else { @@ -2829,7 +2829,7 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, if (child_type->zero_bits) { return nullptr; } else { - bool maybe_is_ptr = (child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn); + bool maybe_is_ptr = type_is_codegen_pointer(child_type); if (maybe_is_ptr) { return maybe_ptr; } else { @@ -3052,6 +3052,10 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I { align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment; ptr_val = target_val; + } else if (target_type->id == TypeTableEntryIdMaybe && + target_type->data.maybe.child_type->id == TypeTableEntryIdPromise) + { + zig_panic("TODO audit this function"); } else if (target_type->id == TypeTableEntryIdStruct && target_type->data.structure.is_slice) { TypeTableEntry *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; align_bytes = slice_ptr_type->data.pointer.alignment; @@ -3522,9 +3526,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I } LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); - if (child_type->id == TypeTableEntryIdPointer || - child_type->id == TypeTableEntryIdFn) - { + if (type_is_codegen_pointer(child_type)) { return payload_val; } @@ -3716,6 +3718,22 @@ static LLVMValueRef ir_render_coro_alloc_fail(CodeGen *g, IrExecutable *executab zig_panic("TODO ir_render_coro_alloc_fail"); } +static LLVMValueRef ir_render_coro_suspend(CodeGen *g, IrExecutable *executable, IrInstructionCoroSuspend *instruction) { + zig_panic("TODO ir_render_coro_suspend"); +} + +static LLVMValueRef ir_render_coro_end(CodeGen *g, IrExecutable *executable, IrInstructionCoroEnd *instruction) { + zig_panic("TODO ir_render_coro_end"); +} + +static LLVMValueRef ir_render_coro_free(CodeGen *g, IrExecutable *executable, IrInstructionCoroFree *instruction) { + zig_panic("TODO ir_render_coro_free"); +} + +static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable, IrInstructionCoroResume *instruction) { + zig_panic("TODO ir_render_coro_resume"); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -3911,6 +3929,14 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_coro_begin(g, executable, (IrInstructionCoroBegin *)instruction); case IrInstructionIdCoroAllocFail: return ir_render_coro_alloc_fail(g, executable, (IrInstructionCoroAllocFail *)instruction); + case IrInstructionIdCoroSuspend: + return ir_render_coro_suspend(g, executable, (IrInstructionCoroSuspend *)instruction); + case IrInstructionIdCoroEnd: + return ir_render_coro_end(g, executable, (IrInstructionCoroEnd *)instruction); + case IrInstructionIdCoroFree: + return ir_render_coro_free(g, executable, (IrInstructionCoroFree *)instruction); + case IrInstructionIdCoroResume: + return ir_render_coro_resume(g, executable, (IrInstructionCoroResume *)instruction); } zig_unreachable(); } @@ -4155,9 +4181,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c TypeTableEntry *child_type = type_entry->data.maybe.child_type; if (child_type->zero_bits) { return LLVMConstInt(LLVMInt1Type(), const_val->data.x_maybe ? 1 : 0, false); - } else if (child_type->id == TypeTableEntryIdPointer || - child_type->id == TypeTableEntryIdFn) - { + } else if (type_is_codegen_pointer(child_type)) { if (const_val->data.x_maybe) { return gen_const_val(g, const_val->data.x_maybe, ""); } else { @@ -6085,9 +6109,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf if (child_type->zero_bits) { buf_init_from_str(out_buf, "bool"); return; - } else if (child_type->id == TypeTableEntryIdPointer || - child_type->id == TypeTableEntryIdFn) - { + } else if (type_is_codegen_pointer(child_type)) { return get_c_type(g, gen_h, child_type, out_buf); } else { zig_unreachable(); diff --git a/src/ir.cpp b/src/ir.cpp index 68d31a7712..183543b0fe 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -46,7 +46,10 @@ static LVal make_lval_addr(bool is_const, bool is_volatile) { } static const char * ASYNC_ALLOC_FIELD_NAME = "allocFn"; -//static const char * ASYNC_FREE_FIELD_NAME = "freeFn"; +static const char * ASYNC_FREE_FIELD_NAME = "freeFn"; +static const char * AWAITER_HANDLE_FIELD_NAME = "awaiter_handle"; +static const char * RESULT_FIELD_NAME = "result"; +static const char * RESULT_PTR_FIELD_NAME = "result_ptr"; enum ConstCastResultId { ConstCastResultIdOk, @@ -672,6 +675,22 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAllocFail *) return IrInstructionIdCoroAllocFail; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSuspend *) { + return IrInstructionIdCoroSuspend; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroEnd *) { + return IrInstructionIdCoroEnd; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroFree *) { + return IrInstructionIdCoroFree; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) { + return IrInstructionIdCoroResume; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -743,14 +762,6 @@ static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *sou return &return_instruction->base; } -static IrInstruction *ir_build_return_from(IrBuilder *irb, IrInstruction *old_instruction, - IrInstruction *return_value) -{ - IrInstruction *new_instruction = ir_build_return(irb, old_instruction->scope, old_instruction->source_node, return_value); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_create_const(IrBuilder *irb, Scope *scope, AstNode *source_node, TypeTableEntry *type_entry) { @@ -822,6 +833,14 @@ static IrInstruction *ir_build_const_u29(IrBuilder *irb, Scope *scope, AstNode * return &const_instruction->base; } +static IrInstruction *ir_build_const_u8(IrBuilder *irb, Scope *scope, AstNode *source_node, uint8_t value) { + IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->base.value.type = irb->codegen->builtin_types.entry_u8; + const_instruction->base.value.special = ConstValSpecialStatic; + bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value); + return &const_instruction->base; +} + static IrInstruction *ir_create_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node, TypeTableEntry *type_entry) { @@ -909,6 +928,33 @@ static IrInstruction *ir_build_const_c_str_lit(IrBuilder *irb, Scope *scope, Ast return &const_instruction->base; } +static IrInstruction *ir_build_const_promise_init(IrBuilder *irb, Scope *scope, AstNode *source_node, + TypeTableEntry *return_type) +{ + TypeTableEntry *awaiter_handle_type = get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise); + TypeTableEntry *result_ptr_type = get_pointer_to_type(irb->codegen, return_type, false); + const char *field_names[] = {AWAITER_HANDLE_FIELD_NAME, RESULT_FIELD_NAME, RESULT_PTR_FIELD_NAME}; + TypeTableEntry *field_types[] = {awaiter_handle_type, return_type, result_ptr_type}; + size_t field_count = type_has_bits(result_ptr_type) ? 3 : 1; + TypeTableEntry *struct_type = get_struct_type(irb->codegen, "AsyncFramePromise", field_names, field_types, + field_count); + + IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->base.value.type = struct_type; + const_instruction->base.value.special = ConstValSpecialStatic; + const_instruction->base.value.data.x_struct.fields = allocate(2); + const_instruction->base.value.data.x_struct.fields[0].type = awaiter_handle_type; + const_instruction->base.value.data.x_struct.fields[0].special = ConstValSpecialStatic; + const_instruction->base.value.data.x_struct.fields[0].data.x_maybe = nullptr; + if (field_count == 3) { + const_instruction->base.value.data.x_struct.fields[1].type = return_type; + const_instruction->base.value.data.x_struct.fields[1].special = ConstValSpecialUndef; + const_instruction->base.value.data.x_struct.fields[2].type = result_ptr_type; + const_instruction->base.value.data.x_struct.fields[2].special = ConstValSpecialUndef; + } + return &const_instruction->base; +} + static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id, IrInstruction *op1, IrInstruction *op2, bool safety_check_on) { @@ -2451,8 +2497,11 @@ static IrInstruction *ir_build_get_implicit_allocator(IrBuilder *irb, Scope *sco return &instruction->base; } -static IrInstruction *ir_build_coro_id(IrBuilder *irb, Scope *scope, AstNode *source_node) { +static IrInstruction *ir_build_coro_id(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *promise_ptr) { IrInstructionCoroId *instruction = ir_build_instruction(irb, scope, source_node); + instruction->promise_ptr = promise_ptr; + + ir_ref_instruction(promise_ptr, irb->current_basic_block); return &instruction->base; } @@ -2494,6 +2543,48 @@ static IrInstruction *ir_build_coro_alloc_fail(IrBuilder *irb, Scope *scope, Ast return &instruction->base; } +static IrInstruction *ir_build_coro_suspend(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *save_point, IrInstruction *is_final) +{ + IrInstructionCoroSuspend *instruction = ir_build_instruction(irb, scope, source_node); + instruction->save_point = save_point; + instruction->is_final = is_final; + + if (save_point != nullptr) ir_ref_instruction(save_point, irb->current_basic_block); + ir_ref_instruction(is_final, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_coro_end(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionCoroEnd *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstruction *ir_build_coro_free(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *coro_id, IrInstruction *coro_handle) +{ + IrInstructionCoroFree *instruction = ir_build_instruction(irb, scope, source_node); + instruction->coro_id = coro_id; + instruction->coro_handle = coro_handle; + + ir_ref_instruction(coro_id, irb->current_basic_block); + ir_ref_instruction(coro_handle, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *awaiter_handle) +{ + IrInstructionCoroResume *instruction = ir_build_instruction(irb, scope, source_node); + instruction->awaiter_handle = awaiter_handle; + + ir_ref_instruction(awaiter_handle, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -2566,6 +2657,29 @@ static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { return nullptr; } +static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value, + bool is_generated_code) +{ + FnTableEntry *fn_entry = exec_fn_entry(irb->exec); + bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; + if (!is_async) { + IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value); + return_inst->is_gen = is_generated_code; + return return_inst; + } + + if (irb->exec->coro_result_ptr_field_ptr) { + IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); + ir_build_store_ptr(irb, scope, node, result_ptr, return_value); + } + IrInstruction *maybe_await_handle = ir_build_load_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr); + IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle); + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); + return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final, + is_comptime); + // the above blocks are rendered by ir_gen after the rest of codegen +} + static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeReturnExpr); @@ -2615,18 +2729,22 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, } ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime)); + IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt"); ir_set_cursor_at_end_and_append_block(irb, err_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); - ir_build_return(irb, scope, node, return_value); + ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ok_block); ir_gen_defers_for_block(irb, scope, outer_scope, false); - return ir_build_return(irb, scope, node, return_value); + ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block); + return ir_gen_async_return(irb, scope, node, return_value, false); } else { // generate unconditional defers ir_gen_defers_for_block(irb, scope, outer_scope, false); - return ir_build_return(irb, scope, node, return_value); + return ir_gen_async_return(irb, scope, node, return_value, false); } } case ReturnKindError: @@ -2646,7 +2764,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_set_cursor_at_end_and_append_block(irb, return_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr); - ir_build_return(irb, scope, node, err_val); + ir_gen_async_return(irb, scope, node, err_val, false); ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false); @@ -5842,6 +5960,7 @@ static void invalidate_exec(IrExecutable *exec) { invalidate_exec(exec->source_exec); } + bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) { assert(node->owner); @@ -5858,48 +5977,81 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec FnTableEntry *fn_entry = exec_fn_entry(irb->exec); bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; + IrInstruction *u8_ptr_type; + IrInstruction *const_bool_false; + IrInstruction *coro_unwrapped_mem_ptr; + IrInstruction *coro_id; + IrInstruction *coro_promise_ptr; + IrInstruction *coro_result_field_ptr; + TypeTableEntry *return_type; + Buf *result_ptr_field_name; if (is_async) { - IrInstruction *is_comptime_false = ir_build_const_bool(irb, scope, node, false); - IrInstruction *coro_id = ir_build_coro_id(irb, scope, node); + // create the coro promise + const_bool_false = ir_build_const_bool(irb, scope, node, false); + VariableTableEntry *promise_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); + //scope = promise_var->child_scope; + + return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + IrInstruction *promise_init = ir_build_const_promise_init(irb, scope, node, return_type); + ir_build_var_decl(irb, scope, node, promise_var, nullptr, nullptr, promise_init); + + coro_promise_ptr = ir_build_var_ptr(irb, scope, node, promise_var, false, false); + Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); + irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + awaiter_handle_field_name); + if (type_has_bits(return_type)) { + Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); + coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); + result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); + irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + result_ptr_field_name); + ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, coro_result_field_ptr); + } + + u8_ptr_type = ir_build_const_type(irb, scope, node, + get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); + IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, coro_promise_ptr); + coro_id = ir_build_coro_id(irb, scope, node, promise_as_u8_ptr); IrInstruction *need_dyn_alloc = ir_build_coro_alloc(irb, scope, node, coro_id); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); - IrInstruction *u8_ptr_type = ir_build_const_type(irb, scope, node, - get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); IrInstruction *null_ptr = ir_build_int_to_ptr(irb, scope, node, u8_ptr_type, zero); IrBasicBlock *dyn_alloc_block = ir_create_basic_block(irb, scope, "DynAlloc"); IrBasicBlock *coro_begin_block = ir_create_basic_block(irb, scope, "CoroBegin"); - ir_build_cond_br(irb, scope, node, need_dyn_alloc, dyn_alloc_block, coro_begin_block, is_comptime_false); + ir_build_cond_br(irb, scope, node, need_dyn_alloc, dyn_alloc_block, coro_begin_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, dyn_alloc_block); IrInstruction *coro_size = ir_build_coro_size(irb, scope, node); - IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node); + irb->exec->implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); - IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, alloc_field_name); + IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, scope, node, irb->exec->implicit_allocator_ptr, + alloc_field_name); IrInstruction *alloc_fn = ir_build_load_ptr(irb, scope, node, alloc_fn_ptr); IrInstruction *alignment = ir_build_const_u29(irb, scope, node, irb->codegen->pointer_size_bytes * 2); size_t arg_count = 3; IrInstruction **args = allocate(arg_count); - args[0] = implicit_allocator_ptr; // self + args[0] = irb->exec->implicit_allocator_ptr; // self args[1] = coro_size; // byte_count args[2] = alignment; // alignment - IrInstruction *alloc_result = ir_build_call(irb, scope, node, nullptr, alloc_fn, arg_count, args, false, FnInlineAuto, false, nullptr); + IrInstruction *alloc_result = ir_build_call(irb, scope, node, nullptr, alloc_fn, arg_count, args, false, + FnInlineAuto, false, nullptr); IrInstruction *alloc_result_ptr = ir_build_ref(irb, scope, node, alloc_result, true, false); IrInstruction *alloc_result_is_err = ir_build_test_err(irb, scope, node, alloc_result); IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, scope, "AllocError"); IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, scope, "AllocOk"); - ir_build_cond_br(irb, scope, node, alloc_result_is_err, alloc_err_block, alloc_ok_block, is_comptime_false); + ir_build_cond_br(irb, scope, node, alloc_result_is_err, alloc_err_block, alloc_ok_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, alloc_err_block); IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, alloc_result_ptr); ir_build_coro_alloc_fail(irb, scope, node, err_val); ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block); - IrInstruction *unwrapped_mem_ptr = ir_build_unwrap_err_payload(irb, scope, node, alloc_result_ptr, false); + coro_unwrapped_mem_ptr = ir_build_unwrap_err_payload(irb, scope, node, alloc_result_ptr, false); Buf *ptr_field_name = buf_create_from_str("ptr"); - IrInstruction *coro_mem_ptr_field = ir_build_field_ptr(irb, scope, node, unwrapped_mem_ptr, ptr_field_name); + IrInstruction *coro_mem_ptr_field = ir_build_field_ptr(irb, scope, node, coro_unwrapped_mem_ptr, + ptr_field_name); IrInstruction *coro_mem_ptr = ir_build_load_ptr(irb, scope, node, coro_mem_ptr_field); - ir_build_br(irb, scope, node, coro_begin_block, is_comptime_false); + ir_build_br(irb, scope, node, coro_begin_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, coro_begin_block); IrBasicBlock **incoming_blocks = allocate(2); @@ -5910,6 +6062,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec incoming_values[1] = coro_mem_ptr; IrInstruction *coro_mem = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); irb->exec->coro_handle = ir_build_coro_begin(irb, scope, node, coro_id, coro_mem); + irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal"); + irb->exec->coro_normal_final = ir_create_basic_block(irb, scope, "CoroNormalFinal"); } IrInstruction *result = ir_gen_node_extra(irb, node, scope, LVAL_NONE); @@ -5918,7 +6072,84 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec return false; if (!instr_is_unreachable(result)) { - ir_mark_gen(ir_build_return(irb, scope, result->source_node, result)); + ir_gen_async_return(irb, scope, result->source_node, result, true); + } + + if (is_async) { + IrBasicBlock *invalid_resume_block = ir_create_basic_block(irb, scope, "InvalidResume"); + IrBasicBlock *final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup"); + IrBasicBlock *suspend_block = ir_create_basic_block(irb, scope, "Suspend"); + IrBasicBlock *check_free_block = ir_create_basic_block(irb, scope, "CheckFree"); + + ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_early_final); + IrInstruction *const_bool_true = ir_build_const_bool(irb, scope, node, true); + IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, nullptr, const_bool_true); + IrInstructionSwitchBrCase *cases = allocate(2); + cases[0].value = ir_build_const_u8(irb, scope, node, 0); + cases[0].block = invalid_resume_block; + cases[1].value = ir_build_const_u8(irb, scope, node, 1); + cases[1].block = final_cleanup_block; + ir_build_switch_br(irb, scope, node, suspend_code, suspend_block, 2, cases, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, suspend_block); + ir_build_coro_end(irb, scope, node); + ir_build_return(irb, scope, node, irb->exec->coro_handle); + + ir_set_cursor_at_end_and_append_block(irb, invalid_resume_block); + ir_build_unreachable(irb, scope, node); + + ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_normal_final); + ir_build_br(irb, scope, node, check_free_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, final_cleanup_block); + if (type_has_bits(return_type)) { + IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); + IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, result_ptr); + IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, + coro_result_field_ptr); + IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node, + fn_entry->type_entry->data.fn.fn_type_id.return_type); + IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst); + ir_build_memcpy(irb, scope, node, result_ptr_as_u8_ptr, return_value_ptr_as_u8_ptr, size_of_ret_val); + } + ir_build_br(irb, scope, node, check_free_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, check_free_block); + IrBasicBlock **incoming_blocks = allocate(2); + IrInstruction **incoming_values = allocate(2); + incoming_blocks[0] = final_cleanup_block; + incoming_values[0] = const_bool_false; + incoming_blocks[1] = irb->exec->coro_normal_final; + incoming_values[1] = const_bool_true; + IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); + IrInstruction *mem_to_free = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); + IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, mem_to_free); + IrBasicBlock *dyn_free_block = ir_create_basic_block(irb, scope, "DynFree"); + IrBasicBlock *end_free_block = ir_create_basic_block(irb, scope, "EndFree"); + ir_build_cond_br(irb, scope, node, is_non_null, dyn_free_block, end_free_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, dyn_free_block); + Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME); + IrInstruction *free_fn_ptr = ir_build_field_ptr(irb, scope, node, irb->exec->implicit_allocator_ptr, + free_field_name); + IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); + size_t arg_count = 2; + IrInstruction **args = allocate(arg_count); + args[0] = irb->exec->implicit_allocator_ptr; // self + args[1] = ir_build_load_ptr(irb, scope, node, coro_unwrapped_mem_ptr); // old_mem + ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr); + ir_build_br(irb, scope, node, end_free_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, end_free_block); + IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); + ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, suspend_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, resume_block); + IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, + irb->exec->coro_awaiter_field_ptr, false); + IrInstruction *awaiter_handle = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); + ir_build_coro_resume(irb, scope, node, awaiter_handle); + ir_build_br(irb, scope, node, suspend_block, const_bool_false); } return true; @@ -9514,8 +9745,11 @@ static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira, ir_add_error(ira, casted_value, buf_sprintf("function returns address of local variable")); return ir_unreach_error(ira); } - ir_build_return_from(&ira->new_irb, &return_instruction->base, casted_value); - return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); + IrInstruction *result = ir_build_return(&ira->new_irb, return_instruction->base.scope, + return_instruction->base.source_node, casted_value); + result->value.type = ira->codegen->builtin_types.entry_unreachable; + ir_link_new_instruction(result, &return_instruction->base); + return ir_finish_anal(ira, result->value.type); } static TypeTableEntry *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *const_instruction) { @@ -16624,11 +16858,8 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; - if (!(target->value.type->id == TypeTableEntryIdPointer || - target->value.type->id == TypeTableEntryIdFn || - (target->value.type->id == TypeTableEntryIdMaybe && - (target->value.type->data.maybe.child_type->id == TypeTableEntryIdPointer || - target->value.type->data.maybe.child_type->id == TypeTableEntryIdFn)))) + if (!(type_is_codegen_pointer(target->value.type) || (target->value.type->id == TypeTableEntryIdMaybe && + type_is_codegen_pointer(target->value.type->data.maybe.child_type)))) { ir_add_error(ira, target, buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value.type->name))); @@ -16829,7 +17060,12 @@ static TypeTableEntry *ir_analyze_instruction_cancel(IrAnalyze *ira, IrInstructi } static TypeTableEntry *ir_analyze_instruction_coro_id(IrAnalyze *ira, IrInstructionCoroId *instruction) { - IrInstruction *result = ir_build_coro_id(&ira->new_irb, instruction->base.scope, instruction->base.source_node); + IrInstruction *promise_ptr = instruction->promise_ptr->other; + if (type_is_invalid(promise_ptr->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_build_coro_id(&ira->new_irb, instruction->base.scope, instruction->base.source_node, + promise_ptr); ir_link_new_instruction(result, &instruction->base); result->value.type = ira->codegen->builtin_types.entry_usize; return result->value.type; @@ -16889,6 +17125,63 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_fail(IrAnalyze *ira, Ir return ir_finish_anal(ira, result->value.type); } +static TypeTableEntry *ir_analyze_instruction_coro_suspend(IrAnalyze *ira, IrInstructionCoroSuspend *instruction) { + IrInstruction *save_point = nullptr; + if (instruction->save_point != nullptr) { + save_point = instruction->save_point->other; + if (type_is_invalid(save_point->value.type)) + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *is_final = instruction->is_final->other; + if (type_is_invalid(is_final->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_build_coro_suspend(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, save_point, is_final); + ir_link_new_instruction(result, &instruction->base); + result->value.type = ira->codegen->builtin_types.entry_u8; + return result->value.type; +} + +static TypeTableEntry *ir_analyze_instruction_coro_end(IrAnalyze *ira, IrInstructionCoroEnd *instruction) { + IrInstruction *result = ir_build_coro_end(&ira->new_irb, instruction->base.scope, + instruction->base.source_node); + ir_link_new_instruction(result, &instruction->base); + result->value.type = ira->codegen->builtin_types.entry_void; + return result->value.type; +} + +static TypeTableEntry *ir_analyze_instruction_coro_free(IrAnalyze *ira, IrInstructionCoroFree *instruction) { + IrInstruction *coro_id = instruction->coro_id->other; + if (type_is_invalid(coro_id->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *coro_handle = instruction->coro_handle->other; + if (type_is_invalid(coro_handle->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_build_coro_free(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, coro_id, coro_handle); + ir_link_new_instruction(result, &instruction->base); + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); + result->value.type = get_maybe_type(ira->codegen, ptr_type); + return result->value.type; +} + +static TypeTableEntry *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) { + IrInstruction *awaiter_handle = instruction->awaiter_handle->other; + if (type_is_invalid(awaiter_handle->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_build_coro_resume(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, awaiter_handle); + ir_link_new_instruction(result, &instruction->base); + result->value.type = ira->codegen->builtin_types.entry_void; + return result->value.type; +} + + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -17105,6 +17398,14 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_get_implicit_allocator(ira, (IrInstructionGetImplicitAllocator *)instruction); case IrInstructionIdCoroAllocFail: return ir_analyze_instruction_coro_alloc_fail(ira, (IrInstructionCoroAllocFail *)instruction); + case IrInstructionIdCoroSuspend: + return ir_analyze_instruction_coro_suspend(ira, (IrInstructionCoroSuspend *)instruction); + case IrInstructionIdCoroEnd: + return ir_analyze_instruction_coro_end(ira, (IrInstructionCoroEnd *)instruction); + case IrInstructionIdCoroFree: + return ir_analyze_instruction_coro_free(ira, (IrInstructionCoroFree *)instruction); + case IrInstructionIdCoroResume: + return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction); } zig_unreachable(); } @@ -17134,7 +17435,10 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl IrAnalyze *ira = allocate(1); old_exec->analysis = ira; ira->codegen = codegen; - ira->explicit_return_type = expected_type; + + FnTableEntry *fn_entry = exec_fn_entry(old_exec); + bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; + ira->explicit_return_type = is_async ? get_promise_type(codegen, expected_type) : expected_type; ira->old_irb.codegen = codegen; ira->old_irb.exec = old_exec; @@ -17222,6 +17526,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroId: case IrInstructionIdCoroBegin: case IrInstructionIdCoroAllocFail: + case IrInstructionIdCoroEnd: + case IrInstructionIdCoroResume: return true; case IrInstructionIdPhi: @@ -17299,6 +17605,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdGetImplicitAllocator: case IrInstructionIdCoroAlloc: case IrInstructionIdCoroSize: + case IrInstructionIdCoroSuspend: + case IrInstructionIdCoroFree: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index bb49273d5c..ca7eb25879 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1031,7 +1031,9 @@ static void ir_print_get_implicit_allocator(IrPrint *irp, IrInstructionGetImplic } static void ir_print_coro_id(IrPrint *irp, IrInstructionCoroId *instruction) { - fprintf(irp->f, "@coroId()"); + fprintf(irp->f, "@coroId("); + ir_print_other_instruction(irp, instruction->promise_ptr); + fprintf(irp->f, ")"); } static void ir_print_coro_alloc(IrPrint *irp, IrInstructionCoroAlloc *instruction) { @@ -1058,6 +1060,36 @@ static void ir_print_coro_alloc_fail(IrPrint *irp, IrInstructionCoroAllocFail *i fprintf(irp->f, ")"); } +static void ir_print_coro_suspend(IrPrint *irp, IrInstructionCoroSuspend *instruction) { + fprintf(irp->f, "@coroSuspend("); + if (instruction->save_point != nullptr) { + ir_print_other_instruction(irp, instruction->save_point); + } else { + fprintf(irp->f, "null"); + } + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->is_final); + fprintf(irp->f, ")"); +} + +static void ir_print_coro_end(IrPrint *irp, IrInstructionCoroEnd *instruction) { + fprintf(irp->f, "@coroEnd()"); +} + +static void ir_print_coro_free(IrPrint *irp, IrInstructionCoroFree *instruction) { + fprintf(irp->f, "@coroFree("); + ir_print_other_instruction(irp, instruction->coro_id); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->coro_handle); + fprintf(irp->f, ")"); +} + +static void ir_print_coro_resume(IrPrint *irp, IrInstructionCoroResume *instruction) { + fprintf(irp->f, "@coroResume("); + ir_print_other_instruction(irp, instruction->awaiter_handle); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1399,6 +1431,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCoroAllocFail: ir_print_coro_alloc_fail(irp, (IrInstructionCoroAllocFail *)instruction); break; + case IrInstructionIdCoroSuspend: + ir_print_coro_suspend(irp, (IrInstructionCoroSuspend *)instruction); + break; + case IrInstructionIdCoroEnd: + ir_print_coro_end(irp, (IrInstructionCoroEnd *)instruction); + break; + case IrInstructionIdCoroFree: + ir_print_coro_free(irp, (IrInstructionCoroFree *)instruction); + break; + case IrInstructionIdCoroResume: + ir_print_coro_resume(irp, (IrInstructionCoroResume *)instruction); + break; } fprintf(irp->f, "\n"); }