authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-10 01:12:22-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-10 01:38:40-05:00
log84e952c230ddb9c2bd232958010d2045384532eb
tree23e153f531fd723e19314320fb87424301d92f21
parent3b3649b86f74d08013b669a6a4eac573f8d7fa23

fix await multithreaded data race

coro return was reading from a value that coro await was writing to. that wasn't how it was designed to work, it was an implementation mistake. this commit also has some work-in-progress code for fixing error return traces across suspend points.

6 files changed, 110 insertions(+), 21 deletions(-)

src/all_types.hpp+9-1
......@@ -61,6 +61,7 @@ struct IrExecutable {
6161 IrInstruction *coro_handle;
6262 IrInstruction *coro_awaiter_field_ptr; // this one is shared and in the promise
6363 IrInstruction *coro_result_ptr_field_ptr;
64 IrInstruction *coro_result_field_ptr;
6465 IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise
6566 IrBasicBlock *coro_early_final;
6667 IrBasicBlock *coro_normal_final;
......@@ -1281,7 +1282,7 @@ struct FnTableEntry {
12811282 bool is_cold;
12821283
12831284 ZigList<FnExport> export_list;
1284 bool calls_errorable_function;
1285 bool calls_or_awaits_errorable_fn;
12851286};
12861287
12871288uint32_t fn_table_entry_hash(FnTableEntry*);
......@@ -2038,6 +2039,7 @@ enum IrInstructionId {
20382039 IrInstructionIdCoroAllocHelper,
20392040 IrInstructionIdAtomicRmw,
20402041 IrInstructionIdPromiseResultType,
2042 IrInstructionIdAwaitBookkeeping,
20412043};
20422044
20432045struct IrInstruction {
......@@ -2985,6 +2987,12 @@ struct IrInstructionPromiseResultType {
29852987 IrInstruction *promise_type;
29862988};
29872989
2990struct IrInstructionAwaitBookkeeping {
2991 IrInstruction base;
2992
2993 IrInstruction *promise_result_type;
2994};
2995
29882996static const size_t slice_ptr_index = 0;
29892997static const size_t slice_len_index = 1;
29902998
src/analyze.cpp+5-3
......@@ -5856,9 +5856,11 @@ uint32_t get_coro_frame_align_bytes(CodeGen *g) {
58565856 return g->pointer_size_bytes * 2;
58575857}
58585858
5859bool type_can_fail(TypeTableEntry *type_entry) {
5860 return type_entry->id == TypeTableEntryIdErrorUnion || type_entry->id == TypeTableEntryIdErrorSet;
5861}
5862
58595863bool fn_type_can_fail(FnTypeId *fn_type_id) {
5860 TypeTableEntry *return_type = fn_type_id->return_type;
5861 return return_type->id == TypeTableEntryIdErrorUnion || return_type->id == TypeTableEntryIdErrorSet ||
5862 fn_type_id->cc == CallingConventionAsync;
5864 return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync;
58635865}
58645866
src/analyze.hpp+1
......@@ -195,6 +195,7 @@ TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry);
195195
196196uint32_t get_coro_frame_align_bytes(CodeGen *g);
197197bool fn_type_can_fail(FnTypeId *fn_type_id);
198bool type_can_fail(TypeTableEntry *type_entry);
198199bool fn_eval_cacheable(Scope *scope);
199200
200201#endif
src/codegen.cpp+2-1
......@@ -4251,6 +4251,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
42514251 case IrInstructionIdExport:
42524252 case IrInstructionIdErrorUnion:
42534253 case IrInstructionIdPromiseResultType:
4254 case IrInstructionIdAwaitBookkeeping:
42544255 zig_unreachable();
42554256
42564257 case IrInstructionIdReturn:
......@@ -5279,7 +5280,7 @@ static void do_code_gen(CodeGen *g) {
52795280 uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
52805281 if (err_ret_trace_arg_index != UINT32_MAX) {
52815282 g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
5282 } else if (g->have_err_ret_tracing && fn_table_entry->calls_errorable_function) {
5283 } else if (g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn) {
52835284 // TODO call graph analysis to find out what this number needs to be for every function
52845285 static const size_t stack_trace_ptr_count = 30;
52855286
src/ir.cpp+84-16
......@@ -707,6 +707,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultTyp
707707 return IrInstructionIdPromiseResultType;
708708}
709709
710static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitBookkeeping *) {
711 return IrInstructionIdAwaitBookkeeping;
712}
713
710714template<typename T>
711715static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
712716 T *special_instruction = allocate<T>(1);
......@@ -2656,6 +2660,17 @@ static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope,
26562660 return &instruction->base;
26572661}
26582662
2663static IrInstruction *ir_build_await_bookkeeping(IrBuilder *irb, Scope *scope, AstNode *source_node,
2664 IrInstruction *promise_result_type)
2665{
2666 IrInstructionAwaitBookkeeping *instruction = ir_build_instruction<IrInstructionAwaitBookkeeping>(irb, scope, source_node);
2667 instruction->promise_result_type = promise_result_type;
2668
2669 ir_ref_instruction(promise_result_type, irb->current_basic_block);
2670
2671 return &instruction->base;
2672}
2673
26592674static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
26602675 results[ReturnKindUnconditional] = 0;
26612676 results[ReturnKindError] = 0;
......@@ -2734,13 +2749,16 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
27342749 FnTableEntry *fn_entry = exec_fn_entry(irb->exec);
27352750 bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
27362751 if (!is_async) {
2752 //if (irb->codegen->have_err_ret_tracing) {
2753 // IrInstruction *stack_trace_ptr = ir_build_error_return_trace_nonnull(irb, scope, node);
2754 // ir_build_save_err_ret_addr(irb, scope, node, stack_trace_ptr);
2755 //}
27372756 IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value);
27382757 return_inst->is_gen = is_generated_code;
27392758 return return_inst;
27402759 }
27412760
2742 IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr);
2743 ir_build_store_ptr(irb, scope, node, result_ptr, return_value);
2761 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
27442762 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node,
27452763 get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
27462764 // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig
......@@ -2756,6 +2774,22 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
27562774 // the above blocks are rendered by ir_gen after the rest of codegen
27572775}
27582776
2777//static void ir_gen_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *node, bool is_async) {
2778// if (!irb->codegen->have_err_ret_tracing)
2779// return;
2780//
2781// if (is_async) {
2782// IrInstruction *err_ret_addr_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_err_ret_addr_ptr);
2783// IrInstruction *return_address_ptr = ir_build_return_address(irb, scope, node);
2784// IrInstruction *return_address_usize = ir_build_ptr_to_int(irb, scope, node, return_address_ptr);
2785// ir_build_store_ptr(irb, scope, node, err_ret_addr_ptr, return_address_usize);
2786// return;
2787// }
2788//
2789// IrInstruction *stack_trace_ptr = ir_build_error_return_trace_nonnull(irb, scope, node);
2790// ir_build_save_err_ret_addr(irb, scope, node, stack_trace_ptr);
2791//}
2792
27592793static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
27602794 assert(node->type == NodeTypeReturnExpr);
27612795
......@@ -2791,9 +2825,13 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
27912825
27922826 size_t defer_counts[2];
27932827 ir_count_defers(irb, scope, outer_scope, defer_counts);
2794 if (defer_counts[ReturnKindError] > 0) {
2828 bool have_err_defers = defer_counts[ReturnKindError] > 0;
2829 if (have_err_defers || irb->codegen->have_err_ret_tracing) {
27952830 IrBasicBlock *err_block = ir_create_basic_block(irb, scope, "ErrRetErr");
27962831 IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk");
2832 if (!have_err_defers) {
2833 ir_gen_defers_for_block(irb, scope, outer_scope, false);
2834 }
27972835
27982836 IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value);
27992837
......@@ -2808,11 +2846,16 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
28082846 IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt");
28092847
28102848 ir_set_cursor_at_end_and_append_block(irb, err_block);
2811 ir_gen_defers_for_block(irb, scope, outer_scope, true);
2849 if (have_err_defers) {
2850 ir_gen_defers_for_block(irb, scope, outer_scope, true);
2851 }
2852 //ir_gen_save_err_ret_addr(irb, scope, node, is_async);
28122853 ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
28132854
28142855 ir_set_cursor_at_end_and_append_block(irb, ok_block);
2815 ir_gen_defers_for_block(irb, scope, outer_scope, false);
2856 if (have_err_defers) {
2857 ir_gen_defers_for_block(irb, scope, outer_scope, false);
2858 }
28162859 ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
28172860
28182861 ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
......@@ -2834,7 +2877,12 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
28342877
28352878 IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn");
28362879 IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue");
2837 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope));
2880 IrInstruction *is_comptime;
2881 if (ir_should_inline(irb->exec, scope)) {
2882 is_comptime = ir_build_const_bool(irb, scope, node, true);
2883 } else {
2884 is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val);
2885 }
28382886 ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
28392887
28402888 ir_set_cursor_at_end_and_append_block(irb, return_block);
......@@ -6002,6 +6050,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
60026050 IrInstruction *undefined_value = ir_build_const_undefined(irb, parent_scope, node);
60036051 IrInstruction *target_promise_type = ir_build_typeof(irb, parent_scope, node, target_inst);
60046052 IrInstruction *promise_result_type = ir_build_promise_result_type(irb, parent_scope, node, target_promise_type);
6053 ir_build_await_bookkeeping(irb, parent_scope, node, promise_result_type);
60056054 ir_build_var_decl(irb, parent_scope, node, result_var, promise_result_type, nullptr, undefined_value);
60066055 IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var, false, false);
60076056 ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr);
......@@ -6271,7 +6320,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
62716320 IrInstruction *coro_id;
62726321 IrInstruction *u8_ptr_type;
62736322 IrInstruction *const_bool_false;
6274 IrInstruction *coro_result_field_ptr;
62756323 TypeTableEntry *return_type;
62766324 Buf *result_ptr_field_name;
62776325 VariableTableEntry *coro_size_var;
......@@ -6325,10 +6373,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
63256373 irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
63266374 awaiter_handle_field_name);
63276375 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
6328 coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
6376 irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
63296377 result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
63306378 irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
6331 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, coro_result_field_ptr);
6379 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr);
63326380
63336381
63346382 irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal");
......@@ -6368,14 +6416,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
63686416 ir_build_unreachable(irb, scope, node);
63696417
63706418 ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_normal_final);
6371 ir_build_br(irb, scope, node, check_free_block, const_bool_false);
6372
6373 ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);
63746419 if (type_has_bits(return_type)) {
63756420 IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr);
63766421 IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, result_ptr);
63776422 IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type,
6378 coro_result_field_ptr);
6423 irb->exec->coro_result_field_ptr);
63796424 IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node,
63806425 fn_entry->type_entry->data.fn.fn_type_id.return_type);
63816426 IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst);
......@@ -6383,6 +6428,9 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
63836428 }
63846429 ir_build_br(irb, scope, node, check_free_block, const_bool_false);
63856430
6431 ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);
6432 ir_build_br(irb, scope, node, check_free_block, const_bool_false);
6433
63866434 ir_set_cursor_at_end_and_append_block(irb, check_free_block);
63876435 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
63886436 IrInstruction **incoming_values = allocate<IrInstruction *>(2);
......@@ -11405,7 +11453,7 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
1140511453 FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
1140611454 TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
1140711455 TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
11408 if (fn_entry == nullptr || !fn_entry->calls_errorable_function || !ira->codegen->have_err_ret_tracing) {
11456 if (fn_entry == nullptr || !fn_entry->calls_or_awaits_errorable_fn || !ira->codegen->have_err_ret_tracing) {
1140911457 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
1141011458 out_val->data.x_maybe = nullptr;
1141111459 return nullable_type;
......@@ -12085,7 +12133,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1208512133
1208612134 TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
1208712135 if (fn_type_can_fail(&impl_fn->type_entry->data.fn.fn_type_id)) {
12088 parent_fn_entry->calls_errorable_function = true;
12136 parent_fn_entry->calls_or_awaits_errorable_fn = true;
1208912137 }
1209012138
1209112139 size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count;
......@@ -12111,7 +12159,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1211112159 assert(fn_type_id->return_type != nullptr);
1211212160 assert(parent_fn_entry != nullptr);
1211312161 if (fn_type_can_fail(fn_type_id)) {
12114 parent_fn_entry->calls_errorable_function = true;
12162 parent_fn_entry->calls_or_awaits_errorable_fn = true;
1211512163 }
1211612164
1211712165
......@@ -17655,6 +17703,22 @@ static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira
1765517703 return ira->codegen->builtin_types.entry_type;
1765617704}
1765717705
17706static TypeTableEntry *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, IrInstructionAwaitBookkeeping *instruction) {
17707 TypeTableEntry *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->other);
17708 if (type_is_invalid(promise_result_type))
17709 return ira->codegen->builtin_types.entry_invalid;
17710
17711 FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
17712 assert(fn_entry != nullptr);
17713
17714 if (type_can_fail(promise_result_type)) {
17715 fn_entry->calls_or_awaits_errorable_fn = true;
17716 }
17717
17718 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
17719 out_val->type = ira->codegen->builtin_types.entry_void;
17720 return out_val->type;
17721}
1765817722
1765917723static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
1766017724 switch (instruction->id) {
......@@ -17672,6 +17736,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1767217736 case IrInstructionIdErrWrapPayload:
1767317737 case IrInstructionIdCast:
1767417738 zig_unreachable();
17739
1767517740 case IrInstructionIdReturn:
1767617741 return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
1767717742 case IrInstructionIdConst:
......@@ -17890,6 +17955,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1789017955 return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
1789117956 case IrInstructionIdPromiseResultType:
1789217957 return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);
17958 case IrInstructionIdAwaitBookkeeping:
17959 return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)instruction);
1789317960 }
1789417961 zig_unreachable();
1789517962}
......@@ -18014,6 +18081,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
1801418081 case IrInstructionIdCoroResume:
1801518082 case IrInstructionIdCoroSave:
1801618083 case IrInstructionIdCoroAllocHelper:
18084 case IrInstructionIdAwaitBookkeeping:
1801718085 return true;
1801818086
1801918087 case IrInstructionIdPhi:
src/ir_print.cpp+9
......@@ -1155,6 +1155,12 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio
11551155 fprintf(irp->f, ")");
11561156}
11571157
1158static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) {
1159 fprintf(irp->f, "@awaitBookkeeping(");
1160 ir_print_other_instruction(irp, instruction->promise_result_type);
1161 fprintf(irp->f, ")");
1162}
1163
11581164static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
11591165 ir_print_prefix(irp, instruction);
11601166 switch (instruction->id) {
......@@ -1523,6 +1529,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
15231529 case IrInstructionIdPromiseResultType:
15241530 ir_print_promise_result_type(irp, (IrInstructionPromiseResultType *)instruction);
15251531 break;
1532 case IrInstructionIdAwaitBookkeeping:
1533 ir_print_await_bookkeeping(irp, (IrInstructionAwaitBookkeeping *)instruction);
1534 break;
15261535 }
15271536 fprintf(irp->f, "\n");
15281537}