diff --git a/CMakeLists.txt b/CMakeLists.txt index d8cf0c507d8d..aa3cfbfeac14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,7 +426,6 @@ set(ZIG_MAIN_SRC "${CMAKE_SOURCE_DIR}/src/main.cpp") set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/userland.cpp") set(ZIG_SOURCES - "${CMAKE_SOURCE_DIR}/src/glibc.cpp" "${CMAKE_SOURCE_DIR}/src/analyze.cpp" "${CMAKE_SOURCE_DIR}/src/ast_render.cpp" "${CMAKE_SOURCE_DIR}/src/bigfloat.cpp" @@ -438,6 +437,7 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/compiler.cpp" "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" "${CMAKE_SOURCE_DIR}/src/error.cpp" + "${CMAKE_SOURCE_DIR}/src/glibc.cpp" "${CMAKE_SOURCE_DIR}/src/ir.cpp" "${CMAKE_SOURCE_DIR}/src/ir_print.cpp" "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp" diff --git a/src/all_types.hpp b/src/all_types.hpp index 7fe035ad1c33..3f61e77f6678 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1251,6 +1251,7 @@ enum ZigTypeId { ZigTypeIdBoundFn, ZigTypeIdArgTuple, ZigTypeIdOpaque, + ZigTypeIdCoroFrame, ZigTypeIdVector, ZigTypeIdEnumLiteral, }; @@ -1265,6 +1266,11 @@ struct ZigTypeOpaque { Buf *bare_name; }; +struct ZigTypeCoroFrame { + ZigFn *fn; + ZigType *locals_struct; +}; + struct ZigType { ZigTypeId id; Buf name; @@ -1290,6 +1296,7 @@ struct ZigType { ZigTypeBoundFn bound_fn; ZigTypeVector vector; ZigTypeOpaque opaque; + ZigTypeCoroFrame frame; } data; // use these fields to make sure we don't duplicate type table entries for the same type @@ -1340,6 +1347,7 @@ struct ZigFn { ScopeBlock *def_scope; // parent is child_scope Buf symbol_name; ZigType *type_entry; // function type + ZigType *frame_type; // coro frame type // in the case of normal functions this is the implicit return type // in the case of async functions this is the implicit return type according to the // zig source code, not according to zig ir @@ -1356,6 +1364,7 @@ struct ZigFn { ZigList alloca_gen_list; ZigList variable_list; + ZigList resume_blocks; Buf *section_name; AstNode *set_alignstack_node; @@ -1365,6 +1374,7 @@ struct ZigFn { ZigList export_list; LLVMValueRef valgrind_client_request_array; + LLVMBasicBlockRef preamble_llvm_block; FnInline fn_inline; FnAnalState anal_state; @@ -1512,6 +1522,7 @@ enum PanicMsgId { PanicMsgIdBadEnumValue, PanicMsgIdFloatToInt, PanicMsgIdPtrCastNull, + PanicMsgIdBadResume, PanicMsgIdCount, }; @@ -1755,6 +1766,7 @@ struct CodeGen { ZigType *entry_global_error_set; ZigType *entry_arg_tuple; ZigType *entry_enum_literal; + ZigType *entry_frame_header; } builtin_types; ZigType *align_amt_type; ZigType *stack_trace_type; @@ -2119,6 +2131,8 @@ struct IrBasicBlock { size_t ref_count; // index into the basic block list size_t index; + // for coroutines, the resume_index which corresponds to this block + size_t resume_index; LLVMBasicBlockRef llvm_block; LLVMBasicBlockRef llvm_exit_block; // The instruction that referenced this basic block and caused us to @@ -2297,6 +2311,8 @@ enum IrInstructionId { IrInstructionIdEndExpr, IrInstructionIdPtrOfArrayToSlice, IrInstructionIdUnionInitNamedField, + IrInstructionIdSuspendBegin, + IrInstructionIdSuspendBr, }; struct IrInstruction { @@ -3511,6 +3527,18 @@ struct IrInstructionPtrOfArrayToSlice { IrInstruction *result_loc; }; +struct IrInstructionSuspendBegin { + IrInstruction base; + + IrBasicBlock *resume_block; +}; + +struct IrInstructionSuspendBr { + IrInstruction base; + + IrBasicBlock *resume_block; +}; + enum ResultLocId { ResultLocIdInvalid, ResultLocIdNone, @@ -3593,6 +3621,8 @@ static const size_t maybe_null_index = 1; static const size_t err_union_err_index = 0; static const size_t err_union_payload_index = 1; +static const size_t coro_resume_index_index = 0; + // TODO call graph analysis to find out what this number needs to be for every function // MUST BE A POWER OF TWO. static const size_t stack_trace_ptr_count = 32; diff --git a/src/analyze.cpp b/src/analyze.cpp index 15e12caa8d40..2b93c390e0d5 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -228,6 +228,8 @@ AstNode *type_decl_node(ZigType *type_entry) { return type_entry->data.enumeration.decl_node; case ZigTypeIdUnion: return type_entry->data.unionation.decl_node; + case ZigTypeIdCoroFrame: + return type_entry->data.frame.fn->proto_node; case ZigTypeIdOpaque: case ZigTypeIdMetaType: case ZigTypeIdVoid: @@ -262,6 +264,20 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) { return type_entry->data.structure.resolve_status >= status; case ZigTypeIdUnion: return type_entry->data.unionation.resolve_status >= status; + case ZigTypeIdCoroFrame: + switch (status) { + case ResolveStatusInvalid: + zig_unreachable(); + case ResolveStatusUnstarted: + case ResolveStatusZeroBitsKnown: + return true; + case ResolveStatusAlignmentKnown: + case ResolveStatusSizeKnown: + return type_entry->data.frame.locals_struct != nullptr; + case ResolveStatusLLVMFwdDecl: + case ResolveStatusLLVMFull: + return type_entry->llvm_type != nullptr; + } case ZigTypeIdEnum: switch (status) { case ResolveStatusUnstarted: @@ -345,6 +361,25 @@ static const char *ptr_len_to_star_str(PtrLen ptr_len) { zig_unreachable(); } +ZigType *get_coro_frame_type(CodeGen *g, ZigFn *fn) { + if (fn->frame_type != nullptr) { + return fn->frame_type; + } + + ZigType *entry = new_type_table_entry(ZigTypeIdCoroFrame); + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "@Frame(%s)", buf_ptr(&fn->symbol_name)); + + entry->data.frame.fn = fn; + + // Coroutine frames are always non-zero bits because they always have a resume index. + entry->abi_size = SIZE_MAX; + entry->size_in_bits = SIZE_MAX; + + fn->frame_type = entry; + return entry; +} + ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero) @@ -1039,6 +1074,7 @@ static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: + case ZigTypeIdCoroFrame: add_node_error(g, source_node, buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation", buf_ptr(&type_entry->name))); @@ -1127,6 +1163,7 @@ bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) { case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdVoid: + case ZigTypeIdCoroFrame: return false; case ZigTypeIdOpaque: case ZigTypeIdUnreachable: @@ -1297,6 +1334,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: switch (type_requires_comptime(g, type_entry)) { case ReqCompTimeNo: break; @@ -1392,6 +1430,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: switch (type_requires_comptime(g, fn_type_id.return_type)) { case ReqCompTimeInvalid: return g->builtin_types.entry_invalid; @@ -1825,6 +1864,39 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { return ErrorNone; } +static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) { + assert(frame_type->data.frame.locals_struct == nullptr); + + ZigFn *fn = frame_type->data.frame.fn; + switch (fn->anal_state) { + case FnAnalStateInvalid: + return ErrorSemanticAnalyzeFail; + case FnAnalStateComplete: + break; + case FnAnalStateReady: + analyze_fn_body(g, fn); + if (fn->anal_state == FnAnalStateInvalid) + return ErrorSemanticAnalyzeFail; + break; + case FnAnalStateProbing: + add_node_error(g, fn->proto_node, + buf_sprintf("cannot resolve '%s': function not fully analyzed yet", + buf_ptr(&frame_type->name))); + return ErrorSemanticAnalyzeFail; + } + // TODO iterate over fn->alloca_gen_list + ZigList field_types = {}; + ZigList field_names = {}; + + field_names.append("resume_index"); + field_types.append(g->builtin_types.entry_usize); + + assert(field_names.length == field_types.length); + frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name), + field_names.items, field_types.items, field_names.length); + return ErrorNone; +} + static bool type_is_valid_extern_enum_tag(CodeGen *g, ZigType *ty) { // Only integer types are allowed by the C ABI if(ty->id != ZigTypeIdInt) @@ -2997,6 +3069,7 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: return type_entry; } zig_unreachable(); @@ -3496,6 +3569,7 @@ bool is_container(ZigType *type_entry) { case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: return false; } zig_unreachable(); @@ -3552,6 +3626,7 @@ Error resolve_container_type(CodeGen *g, ZigType *type_entry) { case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: zig_unreachable(); } zig_unreachable(); @@ -4002,6 +4077,7 @@ bool handle_is_ptr(ZigType *type_entry) { return false; case ZigTypeIdArray: case ZigTypeIdStruct: + case ZigTypeIdCoroFrame: return type_has_bits(type_entry); case ZigTypeIdErrorUnion: return type_has_bits(type_entry->data.error_union.payload_type); @@ -4246,6 +4322,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case ZigTypeIdVector: // TODO better hashing algorithm return 3647867726; + case ZigTypeIdCoroFrame: + // TODO better hashing algorithm + return 675741936; case ZigTypeIdBoundFn: case ZigTypeIdInvalid: case ZigTypeIdUnreachable: @@ -4310,6 +4389,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case ZigTypeIdOpaque: case ZigTypeIdErrorSet: case ZigTypeIdEnum: + case ZigTypeIdCoroFrame: return false; case ZigTypeIdPointer: @@ -4381,6 +4461,7 @@ static bool return_type_is_cacheable(ZigType *return_type) { case ZigTypeIdEnum: case ZigTypeIdPointer: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: return true; case ZigTypeIdArray: @@ -4512,6 +4593,7 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { case ZigTypeIdBool: case ZigTypeIdFloat: case ZigTypeIdErrorUnion: + case ZigTypeIdCoroFrame: return OnePossibleValueNo; case ZigTypeIdUndefined: case ZigTypeIdNull: @@ -4599,6 +4681,7 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) { case ZigTypeIdFloat: case ZigTypeIdVoid: case ZigTypeIdUnreachable: + case ZigTypeIdCoroFrame: return ReqCompTimeNo; } zig_unreachable(); @@ -4941,6 +5024,8 @@ Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) { return resolve_enum_zero_bits(g, ty); } else if (ty->id == ZigTypeIdUnion) { return resolve_union_alignment(g, ty); + } else if (ty->id == ZigTypeIdCoroFrame) { + return resolve_coro_frame(g, ty); } return ErrorNone; case ResolveStatusSizeKnown: @@ -4950,6 +5035,8 @@ Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) { return resolve_enum_zero_bits(g, ty); } else if (ty->id == ZigTypeIdUnion) { return resolve_union_type(g, ty); + } else if (ty->id == ZigTypeIdCoroFrame) { + return resolve_coro_frame(g, ty); } return ErrorNone; case ResolveStatusLLVMFwdDecl: @@ -5144,6 +5231,8 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { return false; } return true; + case ZigTypeIdCoroFrame: + zig_panic("TODO"); case ZigTypeIdUndefined: zig_panic("TODO"); case ZigTypeIdNull: @@ -5496,6 +5585,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "(args value)"); return; } + case ZigTypeIdCoroFrame: + buf_appendf(buf, "(TODO: coroutine frame value)"); + return; + } zig_unreachable(); } @@ -5542,6 +5635,7 @@ uint32_t type_id_hash(TypeId x) { case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: + case ZigTypeIdCoroFrame: zig_unreachable(); case ZigTypeIdErrorUnion: return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type); @@ -5590,6 +5684,7 @@ bool type_id_eql(TypeId a, TypeId b) { case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: + case ZigTypeIdCoroFrame: zig_unreachable(); case ZigTypeIdErrorUnion: return a.data.error_union.err_set_type == b.data.error_union.err_set_type && @@ -5818,10 +5913,12 @@ size_t type_id_index(ZigType *entry) { return 20; case ZigTypeIdOpaque: return 21; - case ZigTypeIdVector: + case ZigTypeIdCoroFrame: return 22; - case ZigTypeIdEnumLiteral: + case ZigTypeIdVector: return 23; + case ZigTypeIdEnumLiteral: + return 24; } zig_unreachable(); } @@ -5878,6 +5975,8 @@ const char *type_id_name(ZigTypeId id) { return "Opaque"; case ZigTypeIdVector: return "Vector"; + case ZigTypeIdCoroFrame: + return "Frame"; } zig_unreachable(); } @@ -5947,7 +6046,7 @@ bool type_can_fail(ZigType *type_entry) { } bool fn_type_can_fail(FnTypeId *fn_type_id) { - return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync; + return type_can_fail(fn_type_id->return_type); } // ErrorNone - result pointer has the type @@ -6935,12 +7034,12 @@ static void resolve_llvm_types_array(CodeGen *g, ZigType *type) { debug_align_in_bits, get_llvm_di_type(g, elem_type), (int)type->data.array.len); } -static void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type) { +void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type, ZigFn *fn) { if (fn_type->llvm_di_type != nullptr) return; FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; bool first_arg_return = want_first_arg_sret(g, fn_type_id); - bool is_async = fn_type_id->cc == CallingConventionAsync; + bool is_async = fn_type_id->cc == CallingConventionAsync || (fn != nullptr && fn->resume_blocks.length != 0); bool is_c_abi = fn_type_id->cc == CallingConventionC; bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id); // +1 for maybe making the first argument the return value @@ -6955,7 +7054,7 @@ static void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type) { param_di_types.append(get_llvm_di_type(g, fn_type_id->return_type)); ZigType *gen_return_type; if (is_async) { - gen_return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); + gen_return_type = g->builtin_types.entry_usize; } else if (!type_has_bits(fn_type_id->return_type)) { gen_return_type = g->builtin_types.entry_void; } else if (first_arg_return) { @@ -6974,13 +7073,10 @@ static void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type) { param_di_types.append(get_llvm_di_type(g, gen_type)); } if (is_async) { - // coroutine frame pointer - // TODO if we can make this typed a little more it will be better for - // debug symbols. - // TODO do we need to make this aligned more? - ZigType *void_star = get_pointer_to_type(g, g->builtin_types.entry_c_void, false); - gen_param_types.append(get_llvm_type(g, void_star)); - param_di_types.append(get_llvm_di_type(g, void_star)); + ZigType *frame_type = (fn == nullptr) ? g->builtin_types.entry_frame_header : get_coro_frame_type(g, fn); + ZigType *ptr_type = get_pointer_to_type(g, frame_type, false); + gen_param_types.append(get_llvm_type(g, ptr_type)); + param_di_types.append(get_llvm_di_type(g, ptr_type)); } fn_type->data.fn.gen_param_info = allocate(fn_type_id->param_count); @@ -7055,6 +7151,17 @@ static void resolve_llvm_types_anyerror(CodeGen *g) { get_llvm_di_type(g, g->err_tag_type), ""); } +static void resolve_llvm_types_coro_frame(CodeGen *g, ZigType *frame_type, ResolveStatus wanted_resolve_status) { + if (frame_type->llvm_di_type != nullptr) return; + + resolve_llvm_types_struct(g, frame_type->data.frame.locals_struct, wanted_resolve_status); + frame_type->llvm_type = frame_type->data.frame.locals_struct->llvm_type; + frame_type->llvm_di_type = frame_type->data.frame.locals_struct->llvm_di_type; + frame_type->abi_size = frame_type->data.frame.locals_struct->abi_size; + frame_type->abi_align = frame_type->data.frame.locals_struct->abi_align; + frame_type->size_in_bits = frame_type->data.frame.locals_struct->size_in_bits; +} + static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) { assert(type->id == ZigTypeIdOpaque || type_is_resolved(type, ResolveStatusSizeKnown)); assert(wanted_resolve_status > ResolveStatusSizeKnown); @@ -7096,7 +7203,7 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r case ZigTypeIdArray: return resolve_llvm_types_array(g, type); case ZigTypeIdFn: - return resolve_llvm_types_fn(g, type); + return resolve_llvm_types_fn(g, type, nullptr); case ZigTypeIdErrorSet: { if (type->llvm_di_type != nullptr) return; @@ -7115,6 +7222,8 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r type->abi_align, get_llvm_di_type(g, type->data.vector.elem_type), type->data.vector.len); return; } + case ZigTypeIdCoroFrame: + return resolve_llvm_types_coro_frame(g, type, wanted_resolve_status); } zig_unreachable(); } diff --git a/src/analyze.hpp b/src/analyze.hpp index fbbdece8ba25..286ff5e043fa 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -16,6 +16,7 @@ ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg); ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg); void emit_error_notes_for_ref_stack(CodeGen *g, ErrorMsg *msg); ZigType *new_type_table_entry(ZigTypeId id); +ZigType *get_coro_frame_type(CodeGen *g, ZigFn *fn); ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const); ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, @@ -247,4 +248,6 @@ void src_assert(bool ok, AstNode *source_node); bool is_container(ZigType *type_entry); ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name); +void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type, ZigFn *fn); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 4cc99b39a876..85784e5ac535 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -498,7 +498,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { ZigType *fn_type = fn_table_entry->type_entry; // Make the raw_type_ref populated - (void)get_llvm_type(g, fn_type); + resolve_llvm_types_fn(g, fn_type, fn_table_entry); LLVMTypeRef fn_llvm_type = fn_type->data.fn.raw_type_ref; if (fn_table_entry->body_node == nullptr) { LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, buf_ptr(symbol_name)); @@ -921,9 +921,8 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) { return false; } -static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) { +static bool ir_want_runtime_safety_scope(CodeGen *g, Scope *scope) { // TODO memoize - Scope *scope = instruction->scope; while (scope) { if (scope->id == ScopeIdBlock) { ScopeBlock *block_scope = (ScopeBlock *)scope; @@ -941,6 +940,10 @@ static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) { g->build_mode != BuildModeSmallRelease); } +static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) { + return ir_want_runtime_safety_scope(g, instruction->scope); +} + static Buf *panic_msg_buf(PanicMsgId msg_id) { switch (msg_id) { case PanicMsgIdCount: @@ -981,6 +984,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("integer part of floating point value out of bounds"); case PanicMsgIdPtrCastNull: return buf_create_from_str("cast causes pointer to be null"); + case PanicMsgIdBadResume: + return buf_create_from_str("invalid resume of async function"); } zig_unreachable(); } @@ -1027,14 +1032,18 @@ static void gen_safety_crash(CodeGen *g, PanicMsgId msg_id) { gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr); } -static void gen_assertion(CodeGen *g, PanicMsgId msg_id, IrInstruction *source_instruction) { - if (ir_want_runtime_safety(g, source_instruction)) { +static void gen_assertion_scope(CodeGen *g, PanicMsgId msg_id, Scope *source_scope) { + if (ir_want_runtime_safety_scope(g, source_scope)) { gen_safety_crash(g, msg_id); } else { LLVMBuildUnreachable(g->builder); } } +static void gen_assertion(CodeGen *g, PanicMsgId msg_id, IrInstruction *source_instruction) { + return gen_assertion_scope(g, msg_id, source_instruction->scope); +} + static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { if (g->stacksave_fn_val) return g->stacksave_fn_val; @@ -2092,6 +2101,10 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut } static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { + if (g->cur_fn->resume_blocks.length != 0) { + LLVMBuildRet(g->builder, LLVMGetUndef(g->builtin_types.entry_usize->llvm_type)); + return nullptr; + } if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { if (return_instruction->value == nullptr) { LLVMBuildRetVoid(g->builder); @@ -3375,8 +3388,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI static bool get_prefix_arg_err_ret_stack(CodeGen *g, FnTypeId *fn_type_id) { return g->have_err_ret_tracing && (fn_type_id->return_type->id == ZigTypeIdErrorUnion || - fn_type_id->return_type->id == ZigTypeIdErrorSet || - fn_type_id->cc == CallingConventionAsync); + fn_type_id->return_type->id == ZigTypeIdErrorSet); } static LLVMValueRef get_new_stack_addr(CodeGen *g, LLVMValueRef new_stack) { @@ -3440,15 +3452,23 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr bool is_var_args = fn_type_id->is_var_args; ZigList gen_param_values = {}; LLVMValueRef result_loc = instruction->result_loc ? ir_llvm_value(g, instruction->result_loc) : nullptr; - if (first_arg_ret) { + if (instruction->is_async) { + assert(result_loc != nullptr); + assert(instruction->fn_entry != nullptr); + LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, result_loc, coro_resume_index_index, ""); + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->llvm_type); + LLVMBuildStore(g->builder, zero, resume_index_ptr); + + if (prefix_arg_err_ret_stack) { + zig_panic("TODO"); + } + gen_param_values.append(result_loc); - } - if (prefix_arg_err_ret_stack) { + } else if (first_arg_ret) { + gen_param_values.append(result_loc); + } else if (prefix_arg_err_ret_stack) { gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope)); } - if (instruction->is_async) { - zig_panic("TODO codegen async call"); - } FnWalk fn_walk = {}; fn_walk.id = FnWalkIdCall; fn_walk.data.call.inst = instruction; @@ -3489,9 +3509,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->is_async) { - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_payload_index, ""); - LLVMBuildStore(g->builder, result, payload_ptr); - return result_loc; + return nullptr; } if (src_return_type->id == ZigTypeIdUnreachable) { @@ -4921,6 +4939,24 @@ static LLVMValueRef ir_render_assert_non_null(CodeGen *g, IrExecutable *executab return nullptr; } +static LLVMValueRef ir_render_suspend_begin(CodeGen *g, IrExecutable *executable, + IrInstructionSuspendBegin *instruction) +{ + LLVMValueRef locals_ptr = g->cur_ret_ptr; + LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_resume_index_index, ""); + LLVMValueRef new_resume_index = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, + instruction->resume_block->resume_index, false); + LLVMBuildStore(g->builder, new_resume_index, resume_index_ptr); + return nullptr; +} + +static LLVMValueRef ir_render_suspend_br(CodeGen *g, IrExecutable *executable, + IrInstructionSuspendBr *instruction) +{ + LLVMBuildRet(g->builder, LLVMGetUndef(g->builtin_types.entry_usize->llvm_type)); + return nullptr; +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5161,6 +5197,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_resize_slice(g, executable, (IrInstructionResizeSlice *)instruction); case IrInstructionIdPtrOfArrayToSlice: return ir_render_ptr_of_array_to_slice(g, executable, (IrInstructionPtrOfArrayToSlice *)instruction); + case IrInstructionIdSuspendBegin: + return ir_render_suspend_begin(g, executable, (IrInstructionSuspendBegin *)instruction); + case IrInstructionIdSuspendBr: + return ir_render_suspend_br(g, executable, (IrInstructionSuspendBr *)instruction); } zig_unreachable(); } @@ -5422,7 +5462,8 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con } return val; } - + case ZigTypeIdCoroFrame: + zig_panic("TODO bit pack a coroutine frame"); } zig_unreachable(); } @@ -5943,7 +5984,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case ZigTypeIdArgTuple: case ZigTypeIdOpaque: zig_unreachable(); - + case ZigTypeIdCoroFrame: + zig_panic("TODO"); } zig_unreachable(); } @@ -6027,12 +6069,20 @@ static void generate_error_name_table(CodeGen *g) { static void build_all_basic_blocks(CodeGen *g, ZigFn *fn) { IrExecutable *executable = &fn->analyzed_executable; assert(executable->basic_block_list.length > 0); + LLVMValueRef fn_val = fn_llvm_value(g, fn); + LLVMBasicBlockRef first_bb = nullptr; + if (fn->resume_blocks.length != 0) { + first_bb = LLVMAppendBasicBlock(fn_val, "AsyncSwitch"); + fn->preamble_llvm_block = first_bb; + } for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) { IrBasicBlock *bb = executable->basic_block_list.at(block_i); - bb->llvm_block = LLVMAppendBasicBlock(fn_llvm_value(g, fn), bb->name_hint); + bb->llvm_block = LLVMAppendBasicBlock(fn_val, bb->name_hint); + } + if (first_bb == nullptr) { + first_bb = executable->basic_block_list.at(0)->llvm_block; } - IrBasicBlock *entry_bb = executable->basic_block_list.at(0); - LLVMPositionBuilderAtEnd(g->builder, entry_bb->llvm_block); + LLVMPositionBuilderAtEnd(g->builder, first_bb); } static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val, @@ -6209,7 +6259,7 @@ static void do_code_gen(CodeGen *g) { build_all_basic_blocks(g, fn_table_entry); clear_debug_source_node(g); - if (want_sret) { + if (want_sret || fn_table_entry->resume_blocks.length != 0) { g->cur_ret_ptr = LLVMGetParam(fn, 0); } else if (handle_is_ptr(fn_type_id->return_type)) { g->cur_ret_ptr = build_alloca(g, fn_type_id->return_type, "result", 0); @@ -6357,6 +6407,41 @@ static void do_code_gen(CodeGen *g) { fn_walk_init.data.inits.gen_i = gen_i_init; walk_function_params(g, fn_table_entry->type_entry, &fn_walk_init); + if (fn_table_entry->resume_blocks.length != 0) { + if (!g->strip_debug_symbols) { + AstNode *source_node = fn_table_entry->proto_node; + ZigLLVMSetCurrentDebugLocation(g->builder, (int)source_node->line + 1, + (int)source_node->column + 1, get_di_scope(g, fn_table_entry->child_scope)); + } + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + LLVMBasicBlockRef bad_resume_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadResume"); + LLVMPositionBuilderAtEnd(g->builder, bad_resume_block); + gen_assertion_scope(g, PanicMsgIdBadResume, fn_table_entry->child_scope); + + LLVMBasicBlockRef get_size_block = LLVMAppendBasicBlock(g->cur_fn_val, "GetSize"); + LLVMPositionBuilderAtEnd(g->builder, get_size_block); + assert(fn_table_entry->frame_type->abi_size != 0); + assert(fn_table_entry->frame_type->abi_size != SIZE_MAX); + LLVMValueRef size_val = LLVMConstInt(usize_type_ref, fn_table_entry->frame_type->abi_size, false); + LLVMBuildRet(g->builder, size_val); + + LLVMPositionBuilderAtEnd(g->builder, fn_table_entry->preamble_llvm_block); + LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, + coro_resume_index_index, ""); + LLVMValueRef resume_index = LLVMBuildLoad(g->builder, resume_index_ptr, ""); + // The +1 is because index 0 is reserved for getting the size. + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, resume_index, bad_resume_block, + fn_table_entry->resume_blocks.length + 1); + + LLVMValueRef zero = LLVMConstNull(usize_type_ref); + LLVMAddCase(switch_instr, zero, get_size_block); + + for (size_t resume_i = 0; resume_i < fn_table_entry->resume_blocks.length; resume_i += 1) { + LLVMValueRef case_value = LLVMConstInt(usize_type_ref, resume_i + 1, false); + LLVMAddCase(switch_instr, case_value, fn_table_entry->resume_blocks.at(resume_i)->llvm_block); + } + } + ir_render(g, fn_table_entry); } @@ -6644,9 +6729,13 @@ static void define_builtin_types(CodeGen *g) { g->primitive_type_table.put(&entry->name, entry); } + { + const char *field_names[] = {"resume_index"}; + ZigType *field_types[] = {g->builtin_types.entry_usize}; + g->builtin_types.entry_frame_header = get_struct_type(g, "(frame header)", field_names, field_types, 1); + } } - static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name, size_t count) { BuiltinFnEntry *builtin_fn = allocate(1); buf_init_from_str(&builtin_fn->name, name); @@ -7072,6 +7161,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " BoundFn: Fn,\n" " ArgTuple: void,\n" " Opaque: void,\n" + " Frame: void,\n" " Vector: Vector,\n" " EnumLiteral: void,\n" "\n\n" @@ -8335,6 +8425,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e case ZigTypeIdArgTuple: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: + case ZigTypeIdCoroFrame: zig_unreachable(); case ZigTypeIdVoid: case ZigTypeIdUnreachable: @@ -8518,6 +8609,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu case ZigTypeIdUndefined: case ZigTypeIdNull: case ZigTypeIdArgTuple: + case ZigTypeIdCoroFrame: zig_unreachable(); } } @@ -8685,6 +8777,7 @@ static void gen_h_file(CodeGen *g) { case ZigTypeIdOptional: case ZigTypeIdFn: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: zig_unreachable(); case ZigTypeIdEnum: if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { diff --git a/src/ir.cpp b/src/ir.cpp index f23fe1b7d08a..2332e28c8478 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -318,6 +318,7 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { case ZigTypeIdFn: case ZigTypeIdArgTuple: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: return false; } zig_unreachable(); @@ -1026,6 +1027,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInitNamedFi return IrInstructionIdUnionInitNamedField; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendBegin *) { + return IrInstructionIdSuspendBegin; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendBr *) { + return IrInstructionIdSuspendBr; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -3183,6 +3192,30 @@ static IrInstruction *ir_build_end_expr(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } +static IrInstruction *ir_build_suspend_begin(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrBasicBlock *resume_block) +{ + IrInstructionSuspendBegin *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base.value.type = irb->codegen->builtin_types.entry_void; + instruction->resume_block = resume_block; + + ir_ref_bb(resume_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_suspend_br(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrBasicBlock *resume_block) +{ + IrInstructionSuspendBr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; + instruction->resume_block = resume_block; + + ir_ref_bb(resume_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; @@ -3286,6 +3319,18 @@ static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock * ir_set_cursor_at_end(irb, basic_block); } +static ScopeSuspend *get_scope_suspend(Scope *scope) { + while (scope) { + if (scope->id == ScopeIdSuspend) + return (ScopeSuspend *)scope; + if (scope->id == ScopeIdFnDef) + return nullptr; + + scope = scope->parent; + } + return nullptr; +} + static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { while (scope) { if (scope->id == ScopeIdDeferExpr) @@ -3308,14 +3353,9 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode { ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value)); - bool is_async = exec_is_async(irb->exec); - if (!is_async) { - IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value); - return_inst->is_gen = is_generated_code; - return return_inst; - } - - zig_panic("TODO async return"); + IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value); + return_inst->is_gen = is_generated_code; + return return_inst; } static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { @@ -5393,12 +5433,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } bool is_async = node->data.fn_call_expr.is_async; - if (is_async) { - zig_panic("TODO async fn call"); - } - - IrInstruction *fn_call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, - is_async, nullptr, result_loc); + IrInstruction *fn_call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, + FnInlineAuto, is_async, nullptr, result_loc); return ir_lval_wrap(irb, scope, fn_call, lval, result_loc); } @@ -7655,7 +7691,45 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeSuspend); - zig_panic("TODO ir_gen_suspend"); + ZigFn *fn_entry = exec_fn_entry(irb->exec); + if (!fn_entry) { + add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition")); + return irb->codegen->invalid_instruction; + } + ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); + if (scope_defer_expr) { + if (!scope_defer_expr->reported_err) { + ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression")); + add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here")); + scope_defer_expr->reported_err = true; + } + return irb->codegen->invalid_instruction; + } + ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); + if (existing_suspend_scope) { + if (!existing_suspend_scope->reported_err) { + ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block")); + add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here")); + existing_suspend_scope->reported_err = true; + } + return irb->codegen->invalid_instruction; + } + + IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "Resume"); + + ir_build_suspend_begin(irb, parent_scope, node, resume_block); + if (node->data.suspend.block != nullptr) { + Scope *child_scope; + ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); + suspend_scope->resume_block = resume_block; + child_scope = &suspend_scope->base; + IrInstruction *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); + } + + IrInstruction *result = ir_build_suspend_br(irb, parent_scope, node, resume_block); + ir_set_cursor_at_end_and_append_block(irb, resume_block); + return result; } static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, @@ -7854,13 +7928,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // Entry block gets a reference because we enter it to begin. ir_ref_bb(irb->current_basic_block); - ZigFn *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) { - zig_panic("ir_gen async fn"); - } - IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr); assert(result); if (irb->exec->invalid) @@ -12659,6 +12726,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * case ZigTypeIdNull: case ZigTypeIdErrorUnion: case ZigTypeIdUnion: + case ZigTypeIdCoroFrame: operator_allowed = false; break; case ZigTypeIdOptional: @@ -14023,6 +14091,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: + case ZigTypeIdCoroFrame: ir_add_error(ira, target, buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name))); break; @@ -14047,6 +14116,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdEnumLiteral: + case ZigTypeIdCoroFrame: ir_add_error(ira, target, buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value.type->name))); break; @@ -14553,6 +14623,20 @@ static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInst return ir_const_void(ira, &instruction->base); } +static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry, + ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count) +{ + ir_assert(fn_entry != nullptr, &call_instruction->base); + + ZigType *frame_type = get_coro_frame_type(ira->codegen, fn_entry); + IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, + frame_type, nullptr, true, true); + if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) { + return result_loc; + } + return ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count, + casted_args, FnInlineAuto, true, nullptr, result_loc, frame_type); +} static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i) { @@ -15366,16 +15450,18 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c if (type_is_invalid(return_type)) return ira->codegen->invalid_instruction; - if (call_instruction->is_async) { - zig_panic("TODO async call"); - } - if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && fn_inline == FnInlineNever) { ir_add_error(ira, &call_instruction->base, buf_sprintf("no-inline call of inline function")); return ira->codegen->invalid_instruction; } + if (call_instruction->is_async) { + IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref, + casted_args, call_param_count); + return ir_finish_anal(ira, result); + } + IrInstruction *result_loc; if (handle_is_ptr(return_type)) { result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, @@ -15535,7 +15621,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source zig_unreachable(); } -static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { +static IrInstruction *ir_analyze_optional_type(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { Error err; IrInstruction *value = un_op_instruction->value->child; ZigType *type_entry = ir_resolve_type(ira, value); @@ -15569,6 +15655,7 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_ case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: + case ZigTypeIdCoroFrame: return ir_const_type(ira, &un_op_instruction->base, get_optional_type(ira->codegen, type_entry)); case ZigTypeIdUnreachable: case ZigTypeIdOpaque: @@ -15733,7 +15820,7 @@ static IrInstruction *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstruction return result; } case IrUnOpOptional: - return ir_analyze_maybe(ira, instruction); + return ir_analyze_optional_type(ira, instruction); } zig_unreachable(); } @@ -17340,6 +17427,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: { ResolveStatus needed_status = (align_bytes == 0) ? ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown; @@ -17454,6 +17542,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: { if ((err = ensure_complete_type(ira->codegen, child_type))) return ira->codegen->invalid_instruction; @@ -17504,6 +17593,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: { uint64_t size_in_bytes = type_size(ira->codegen, type_entry); return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes); @@ -18067,6 +18157,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: ir_add_error(ira, &switch_target_instruction->base, buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_instruction; @@ -19906,6 +19997,8 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr break; } + case ZigTypeIdCoroFrame: + zig_panic("TODO @typeInfo for coro frames"); } assert(result != nullptr); @@ -21660,6 +21753,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdVector: + case ZigTypeIdCoroFrame: { uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry); return ir_const_unsigned(ira, &instruction->base, align_in_bytes); @@ -22815,6 +22909,8 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_write_value_bytes fn type"); case ZigTypeIdUnion: zig_panic("TODO buf_write_value_bytes union type"); + case ZigTypeIdCoroFrame: + zig_panic("TODO buf_write_value_bytes coro frame type"); } zig_unreachable(); } @@ -22994,6 +23090,8 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou zig_panic("TODO buf_read_value_bytes fn type"); case ZigTypeIdUnion: zig_panic("TODO buf_read_value_bytes union type"); + case ZigTypeIdCoroFrame: + zig_panic("TODO buf_read_value_bytes coro frame type"); } zig_unreachable(); } @@ -24021,6 +24119,33 @@ static IrInstruction *ir_analyze_instruction_union_init_named_field(IrAnalyze *i union_type, field_name, field_result_loc, result_loc); } +static IrInstruction *ir_analyze_instruction_suspend_begin(IrAnalyze *ira, IrInstructionSuspendBegin *instruction) { + IrBasicBlock *new_bb = ir_get_new_bb_runtime(ira, instruction->resume_block, &instruction->base); + if (new_bb == nullptr) + return ir_unreach_error(ira); + return ir_build_suspend_begin(&ira->new_irb, instruction->base.scope, instruction->base.source_node, new_bb); +} + +static IrInstruction *ir_analyze_instruction_suspend_br(IrAnalyze *ira, IrInstructionSuspendBr *instruction) { + IrBasicBlock *old_dest_block = instruction->resume_block; + + IrBasicBlock *new_bb = ir_get_new_bb_runtime(ira, old_dest_block, &instruction->base); + if (new_bb == nullptr) + return ir_unreach_error(ira); + + ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); + ir_assert(fn_entry != nullptr, &instruction->base); + fn_entry->resume_blocks.append(new_bb); + // This is done after appending the block because resume_index 0 is reserved for querying the size. + new_bb->resume_index = fn_entry->resume_blocks.length; + + ir_push_resume_block(ira, old_dest_block); + + IrInstruction *result = ir_build_suspend_br(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, new_bb); + return ir_finish_anal(ira, result); +} + static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -24304,6 +24429,10 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction return ir_analyze_instruction_bit_cast_src(ira, (IrInstructionBitCastSrc *)instruction); case IrInstructionIdUnionInitNamedField: return ir_analyze_instruction_union_init_named_field(ira, (IrInstructionUnionInitNamedField *)instruction); + case IrInstructionIdSuspendBegin: + return ir_analyze_instruction_suspend_begin(ira, (IrInstructionSuspendBegin *)instruction); + case IrInstructionIdSuspendBr: + return ir_analyze_instruction_suspend_br(ira, (IrInstructionSuspendBr *)instruction); } zig_unreachable(); } @@ -24436,6 +24565,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdOptionalWrap: case IrInstructionIdVectorToArray: case IrInstructionIdResetResult: + case IrInstructionIdSuspendBegin: + case IrInstructionIdSuspendBr: return true; case IrInstructionIdPhi: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 9ea70ba7ab01..3a77e92bc70e 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1503,6 +1503,16 @@ static void ir_print_union_init_named_field(IrPrint *irp, IrInstructionUnionInit fprintf(irp->f, ")"); } +static void ir_print_suspend_begin(IrPrint *irp, IrInstructionSuspendBegin *instruction) { + fprintf(irp->f, "@suspendBegin()"); +} + +static void ir_print_suspend_br(IrPrint *irp, IrInstructionSuspendBr *instruction) { + fprintf(irp->f, "@suspendBr("); + ir_print_other_block(irp, instruction->resume_block); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1961,6 +1971,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdUnionInitNamedField: ir_print_union_init_named_field(irp, (IrInstructionUnionInitNamedField *)instruction); break; + case IrInstructionIdSuspendBegin: + ir_print_suspend_begin(irp, (IrInstructionSuspendBegin *)instruction); + break; + case IrInstructionIdSuspendBr: + ir_print_suspend_br(irp, (IrInstructionSuspendBr *)instruction); + break; } fprintf(irp->f, "\n"); }