| author | |
| committer | |
| log | 59bf9ca58c992e02423fae2ba8773eac098189b9 |
| tree | 742f097934f954997e70bfd13b3911013a80f141 |
| parent | 11bd50f2b2a74ce25d841a15ba67d042d41b71c2 |
| signature | Commit is signed but in an unrecognized format. |
4 files changed, 90 insertions(+), 56 deletions(-)
src/all_types.hpp+1| ... | ... | @@ -3638,6 +3638,7 @@ static const size_t err_union_err_index = 0; |
| 3638 | 3638 | static const size_t err_union_payload_index = 1; |
| 3639 | 3639 | |
| 3640 | 3640 | static const size_t coro_resume_index_index = 0; |
| 3641 | static const size_t coro_arg_start = 1; | |
| 3641 | 3642 | |
| 3642 | 3643 | // TODO call graph analysis to find out what this number needs to be for every function |
| 3643 | 3644 | // MUST BE A POWER OF TWO. |
src/analyze.cpp+48-23| ... | ... | @@ -1891,6 +1891,21 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) { |
| 1891 | 1891 | field_names.append("resume_index"); |
| 1892 | 1892 | field_types.append(g->builtin_types.entry_usize); |
| 1893 | 1893 | |
| 1894 | for (size_t arg_i = 0; arg_i < fn->type_entry->data.fn.fn_type_id.param_count; arg_i += 1) { | |
| 1895 | FnTypeParamInfo *param_info = &fn->type_entry->data.fn.fn_type_id.param_info[arg_i]; | |
| 1896 | AstNode *param_decl_node = get_param_decl_node(fn, arg_i); | |
| 1897 | Buf *param_name; | |
| 1898 | bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args; | |
| 1899 | if (param_decl_node && !is_var_args) { | |
| 1900 | param_name = param_decl_node->data.param_decl.name; | |
| 1901 | } else { | |
| 1902 | param_name = buf_sprintf("arg%" ZIG_PRI_usize "", arg_i); | |
| 1903 | } | |
| 1904 | ZigType *param_type = param_info[arg_i].type; | |
| 1905 | field_names.append(buf_ptr(param_name)); | |
| 1906 | field_types.append(param_type); | |
| 1907 | } | |
| 1908 | ||
| 1894 | 1909 | assert(field_names.length == field_types.length); |
| 1895 | 1910 | frame_type->data.frame.locals_struct = get_struct_type(g, buf_ptr(&frame_type->name), |
| 1896 | 1911 | field_names.items, field_types.items, field_names.length); |
| ... | ... | @@ -7058,19 +7073,22 @@ void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type, ZigFn *fn) { |
| 7058 | 7073 | // +1 for maybe first argument the error return trace |
| 7059 | 7074 | // +2 for maybe arguments async allocator and error code pointer |
| 7060 | 7075 | ZigList<ZigLLVMDIType *> param_di_types = {}; |
| 7061 | param_di_types.append(get_llvm_di_type(g, fn_type_id->return_type)); | |
| 7062 | 7076 | ZigType *gen_return_type; |
| 7063 | 7077 | if (is_async) { |
| 7064 | 7078 | gen_return_type = g->builtin_types.entry_usize; |
| 7079 | param_di_types.append(get_llvm_di_type(g, gen_return_type)); | |
| 7065 | 7080 | } else if (!type_has_bits(fn_type_id->return_type)) { |
| 7066 | 7081 | gen_return_type = g->builtin_types.entry_void; |
| 7082 | param_di_types.append(get_llvm_di_type(g, gen_return_type)); | |
| 7067 | 7083 | } else if (first_arg_return) { |
| 7084 | gen_return_type = g->builtin_types.entry_void; | |
| 7085 | param_di_types.append(get_llvm_di_type(g, gen_return_type)); | |
| 7068 | 7086 | ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false); |
| 7069 | 7087 | gen_param_types.append(get_llvm_type(g, gen_type)); |
| 7070 | 7088 | param_di_types.append(get_llvm_di_type(g, gen_type)); |
| 7071 | gen_return_type = g->builtin_types.entry_void; | |
| 7072 | 7089 | } else { |
| 7073 | 7090 | gen_return_type = fn_type_id->return_type; |
| 7091 | param_di_types.append(get_llvm_di_type(g, gen_return_type)); | |
| 7074 | 7092 | } |
| 7075 | 7093 | fn_type->data.fn.gen_return_type = gen_return_type; |
| 7076 | 7094 | |
| ... | ... | @@ -7080,36 +7098,43 @@ void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type, ZigFn *fn) { |
| 7080 | 7098 | param_di_types.append(get_llvm_di_type(g, gen_type)); |
| 7081 | 7099 | } |
| 7082 | 7100 | if (is_async) { |
| 7101 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(1); | |
| 7102 | ||
| 7083 | 7103 | ZigType *frame_type = (fn == nullptr) ? g->builtin_types.entry_frame_header : get_coro_frame_type(g, fn); |
| 7084 | 7104 | ZigType *ptr_type = get_pointer_to_type(g, frame_type, false); |
| 7085 | 7105 | gen_param_types.append(get_llvm_type(g, ptr_type)); |
| 7086 | 7106 | param_di_types.append(get_llvm_di_type(g, ptr_type)); |
| 7087 | } | |
| 7088 | 7107 | |
| 7089 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count); | |
| 7090 | for (size_t i = 0; i < fn_type_id->param_count; i += 1) { | |
| 7091 | FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i]; | |
| 7092 | ZigType *type_entry = src_param_info->type; | |
| 7093 | FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i]; | |
| 7108 | fn_type->data.fn.gen_param_info[0].src_index = 0; | |
| 7109 | fn_type->data.fn.gen_param_info[0].gen_index = 0; | |
| 7110 | fn_type->data.fn.gen_param_info[0].type = ptr_type; | |
| 7094 | 7111 | |
| 7095 | gen_param_info->src_index = i; | |
| 7096 | gen_param_info->gen_index = SIZE_MAX; | |
| 7112 | } else { | |
| 7113 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count); | |
| 7114 | for (size_t i = 0; i < fn_type_id->param_count; i += 1) { | |
| 7115 | FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i]; | |
| 7116 | ZigType *type_entry = src_param_info->type; | |
| 7117 | FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i]; | |
| 7097 | 7118 | |
| 7098 | if (is_c_abi || !type_has_bits(type_entry)) | |
| 7099 | continue; | |
| 7119 | gen_param_info->src_index = i; | |
| 7120 | gen_param_info->gen_index = SIZE_MAX; | |
| 7100 | 7121 | |
| 7101 | ZigType *gen_type; | |
| 7102 | if (handle_is_ptr(type_entry)) { | |
| 7103 | gen_type = get_pointer_to_type(g, type_entry, true); | |
| 7104 | gen_param_info->is_byval = true; | |
| 7105 | } else { | |
| 7106 | gen_type = type_entry; | |
| 7107 | } | |
| 7108 | gen_param_info->gen_index = gen_param_types.length; | |
| 7109 | gen_param_info->type = gen_type; | |
| 7110 | gen_param_types.append(get_llvm_type(g, gen_type)); | |
| 7122 | if (is_c_abi || !type_has_bits(type_entry)) | |
| 7123 | continue; | |
| 7111 | 7124 | |
| 7112 | param_di_types.append(get_llvm_di_type(g, gen_type)); | |
| 7125 | ZigType *gen_type; | |
| 7126 | if (handle_is_ptr(type_entry)) { | |
| 7127 | gen_type = get_pointer_to_type(g, type_entry, true); | |
| 7128 | gen_param_info->is_byval = true; | |
| 7129 | } else { | |
| 7130 | gen_type = type_entry; | |
| 7131 | } | |
| 7132 | gen_param_info->gen_index = gen_param_types.length; | |
| 7133 | gen_param_info->type = gen_type; | |
| 7134 | gen_param_types.append(get_llvm_type(g, gen_type)); | |
| 7135 | ||
| 7136 | param_di_types.append(get_llvm_di_type(g, gen_type)); | |
| 7137 | } | |
| 7113 | 7138 | } |
| 7114 | 7139 | |
| 7115 | 7140 | if (is_c_abi) { |
src/codegen.cpp+22-15| ... | ... | @@ -1965,10 +1965,12 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ |
| 1965 | 1965 | } |
| 1966 | 1966 | case FnWalkIdInits: { |
| 1967 | 1967 | clear_debug_source_node(g); |
| 1968 | LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); | |
| 1969 | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); | |
| 1970 | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); | |
| 1971 | gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); | |
| 1968 | if (fn_walk->data.inits.fn->resume_blocks.length == 0) { | |
| 1969 | LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); | |
| 1970 | LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); | |
| 1971 | LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); | |
| 1972 | gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); | |
| 1973 | } | |
| 1972 | 1974 | if (var->decl_node) { |
| 1973 | 1975 | gen_var_debug_decl(g, var); |
| 1974 | 1976 | } |
| ... | ... | @@ -2061,7 +2063,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { |
| 2061 | 2063 | assert(variable); |
| 2062 | 2064 | assert(variable->value_ref); |
| 2063 | 2065 | |
| 2064 | if (!handle_is_ptr(variable->var_type)) { | |
| 2066 | if (!handle_is_ptr(variable->var_type) && fn_walk->data.inits.fn->resume_blocks.length == 0) { | |
| 2065 | 2067 | clear_debug_source_node(g); |
| 2066 | 2068 | ZigType *fn_type = fn_table_entry->type_entry; |
| 2067 | 2069 | unsigned gen_arg_index = fn_type->data.fn.gen_param_info[variable->src_arg_index].gen_index; |
| ... | ... | @@ -3471,8 +3473,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3471 | 3473 | if (prefix_arg_err_ret_stack) { |
| 3472 | 3474 | zig_panic("TODO"); |
| 3473 | 3475 | } |
| 3474 | ||
| 3475 | gen_param_values.append(result_loc); | |
| 3476 | 3476 | } else { |
| 3477 | 3477 | if (first_arg_ret) { |
| 3478 | 3478 | gen_param_values.append(result_loc); |
| ... | ... | @@ -3504,6 +3504,15 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3504 | 3504 | LLVMCallConv llvm_cc = get_llvm_cc(g, cc); |
| 3505 | 3505 | LLVMValueRef result; |
| 3506 | 3506 | |
| 3507 | if (instruction->is_async) { | |
| 3508 | for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) { | |
| 3509 | LLVMValueRef arg_ptr = LLVMBuildStructGEP(g->builder, result_loc, coro_arg_start + arg_i, ""); | |
| 3510 | LLVMBuildStore(g->builder, gen_param_values.at(arg_i), arg_ptr); | |
| 3511 | } | |
| 3512 | ZigLLVMBuildCall(g->builder, fn_val, &result_loc, 1, llvm_cc, fn_inline, ""); | |
| 3513 | return nullptr; | |
| 3514 | } | |
| 3515 | ||
| 3507 | 3516 | if (instruction->new_stack == nullptr) { |
| 3508 | 3517 | result = ZigLLVMBuildCall(g->builder, fn_val, |
| 3509 | 3518 | gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, ""); |
| ... | ... | @@ -3519,11 +3528,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3519 | 3528 | LLVMBuildCall(g->builder, stackrestore_fn_val, &old_stack_ref, 1, ""); |
| 3520 | 3529 | } |
| 3521 | 3530 | |
| 3522 | ||
| 3523 | if (instruction->is_async) { | |
| 3524 | return nullptr; | |
| 3525 | } | |
| 3526 | ||
| 3527 | 3531 | if (src_return_type->id == ZigTypeIdUnreachable) { |
| 3528 | 3532 | return LLVMBuildUnreachable(g->builder); |
| 3529 | 3533 | } else if (!ret_has_bits) { |
| ... | ... | @@ -6285,7 +6289,9 @@ static void do_code_gen(CodeGen *g) { |
| 6285 | 6289 | build_all_basic_blocks(g, fn_table_entry); |
| 6286 | 6290 | clear_debug_source_node(g); |
| 6287 | 6291 | |
| 6288 | if (want_sret || fn_table_entry->resume_blocks.length != 0) { | |
| 6292 | bool is_async = cc == CallingConventionAsync || fn_table_entry->resume_blocks.length != 0; | |
| 6293 | ||
| 6294 | if (want_sret || is_async) { | |
| 6289 | 6295 | g->cur_ret_ptr = LLVMGetParam(fn, 0); |
| 6290 | 6296 | } else if (handle_is_ptr(fn_type_id->return_type)) { |
| 6291 | 6297 | g->cur_ret_ptr = build_alloca(g, fn_type_id->return_type, "result", 0); |
| ... | ... | @@ -6303,7 +6309,6 @@ static void do_code_gen(CodeGen *g) { |
| 6303 | 6309 | } |
| 6304 | 6310 | |
| 6305 | 6311 | // error return tracing setup |
| 6306 | bool is_async = cc == CallingConventionAsync; | |
| 6307 | 6312 | bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && !is_async && !have_err_ret_trace_arg; |
| 6308 | 6313 | LLVMValueRef err_ret_array_val = nullptr; |
| 6309 | 6314 | if (have_err_ret_trace_stack) { |
| ... | ... | @@ -6378,7 +6383,9 @@ static void do_code_gen(CodeGen *g) { |
| 6378 | 6383 | FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index]; |
| 6379 | 6384 | assert(gen_info->gen_index != SIZE_MAX); |
| 6380 | 6385 | |
| 6381 | if (handle_is_ptr(var->var_type)) { | |
| 6386 | if (is_async) { | |
| 6387 | var->value_ref = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_arg_start + var_i, ""); | |
| 6388 | } else if (handle_is_ptr(var->var_type)) { | |
| 6382 | 6389 | if (gen_info->is_byval) { |
| 6383 | 6390 | gen_type = var->var_type; |
| 6384 | 6391 | } else { |
test/stage1/behavior/coroutines.zig+19-18| ... | ... | @@ -2,33 +2,34 @@ const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | |
| 5 | var x: i32 = 1; | |
| 5 | var global_x: i32 = 1; | |
| 6 | 6 | |
| 7 | 7 | test "simple coroutine suspend and resume" { |
| 8 | 8 | const p = async simpleAsyncFn(); |
| 9 | expect(x == 2); | |
| 9 | expect(global_x == 2); | |
| 10 | 10 | resume p; |
| 11 | expect(x == 3); | |
| 11 | expect(global_x == 3); | |
| 12 | 12 | } |
| 13 | 13 | fn simpleAsyncFn() void { |
| 14 | x += 1; | |
| 14 | global_x += 1; | |
| 15 | 15 | suspend; |
| 16 | x += 1; | |
| 16 | global_x += 1; | |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | //test "create a coroutine and cancel it" { | |
| 20 | // const p = try async<allocator> simpleAsyncFn(); | |
| 21 | // comptime expect(@typeOf(p) == promise->void); | |
| 22 | // cancel p; | |
| 23 | // expect(x == 2); | |
| 24 | //} | |
| 25 | //async fn simpleAsyncFn() void { | |
| 26 | // x += 1; | |
| 27 | // suspend; | |
| 28 | // x += 1; | |
| 29 | //} | |
| 30 | // | |
| 31 | //test "coroutine suspend, resume, cancel" { | |
| 19 | var global_y: i32 = 1; | |
| 20 | ||
| 21 | test "pass parameter to coroutine" { | |
| 22 | const p = async simpleAsyncFnWithArg(2); | |
| 23 | expect(global_y == 3); | |
| 24 | resume p; | |
| 25 | expect(global_y == 5); | |
| 26 | } | |
| 27 | fn simpleAsyncFnWithArg(delta: i32) void { | |
| 28 | global_y += delta; | |
| 29 | suspend; | |
| 30 | global_y += delta; | |
| 31 | } | |
| 32 | //test "coroutine suspend, resume" { | |
| 32 | 33 | // seq('a'); |
| 33 | 34 | // const p = try async<allocator> testAsyncSeq(); |
| 34 | 35 | // seq('c'); |