Commit 99985ad6fc

Andrew Kelley <superjoe30@gmail.com>
2018-02-23 09:03:06
implement Zig IR for async functions
See #727
1 parent ca1b77b
src/all_types.hpp
@@ -56,7 +56,13 @@ struct IrExecutable {
     IrAnalyze *analysis;
     Scope *begin_scope;
     ZigList<Tld *> 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;
 
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;
 }
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);
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();
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<typename T>
 static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(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<IrInstructionConst>(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<IrInstructionConst>(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<ConstExprValue>(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<IrInstructionCoroId>(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<IrInstructionCoroSuspend>(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<IrInstructionCoroEnd>(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<IrInstructionCoroFree>(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<IrInstructionCoroResume>(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<IrInstruction *>(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<IrBasicBlock *>(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<IrInstructionSwitchBrCase>(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<IrBasicBlock *>(2);
+        IrInstruction **incoming_values = allocate<IrInstruction *>(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<IrInstruction *>(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<IrAnalyze>(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:
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");
 }