authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-09-06 16:17:39-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-09-07 00:13:12-04:00
log9ca8d9e21ad657b023c23db5c440fb79a3303771
tree1c11fb3cb0473fcf7e0a8008bae3ace68592c564
parent9423d382fb1c9e95f1d8cd7f6db8a917b082a97a
signaturelock-open Commit is signed but in an unrecognized format.

fix await used in an expression generating bad LLVM


4 files changed, 113 insertions(+), 44 deletions(-)

src/analyze.cpp+81-31
...@@ -4232,31 +4232,40 @@ static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode...@@ -4232,31 +4232,40 @@ static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode
4232{4232{
4233 if (modifier == CallModifierNoAsync)4233 if (modifier == CallModifierNoAsync)
4234 return ErrorNone;4234 return ErrorNone;
4235 if (callee->type_entry->data.fn.fn_type_id.cc != CallingConventionUnspecified)4235 bool callee_is_async = false;
4236 return ErrorNone;4236 switch (callee->type_entry->data.fn.fn_type_id.cc) {
4237 if (callee->anal_state == FnAnalStateReady) {4237 case CallingConventionUnspecified:
4238 analyze_fn_body(g, callee);4238 break;
4239 if (callee->anal_state == FnAnalStateInvalid) {4239 case CallingConventionAsync:
4240 return ErrorSemanticAnalyzeFail;4240 callee_is_async = true;
4241 }4241 break;
4242 default:
4243 return ErrorNone;
4242 }4244 }
4243 bool callee_is_async;4245 if (!callee_is_async) {
4244 if (callee->anal_state == FnAnalStateComplete) {4246 if (callee->anal_state == FnAnalStateReady) {
4245 analyze_fn_async(g, callee, true);4247 analyze_fn_body(g, callee);
4246 if (callee->anal_state == FnAnalStateInvalid) {4248 if (callee->anal_state == FnAnalStateInvalid) {
4247 return ErrorSemanticAnalyzeFail;4249 return ErrorSemanticAnalyzeFail;
4250 }
4248 }4251 }
4249 callee_is_async = fn_is_async(callee);4252 if (callee->anal_state == FnAnalStateComplete) {
4250 } else {4253 analyze_fn_async(g, callee, true);
4251 // If it's already been determined, use that value. Otherwise4254 if (callee->anal_state == FnAnalStateInvalid) {
4252 // assume non-async, emit an error later if it turned out to be async.4255 return ErrorSemanticAnalyzeFail;
4253 if (callee->inferred_async_node == nullptr ||4256 }
4254 callee->inferred_async_node == inferred_async_checking)4257 callee_is_async = fn_is_async(callee);
4255 {
4256 callee->assumed_non_async = call_node;
4257 callee_is_async = false;
4258 } else {4258 } else {
4259 callee_is_async = callee->inferred_async_node != inferred_async_none;4259 // If it's already been determined, use that value. Otherwise
4260 // assume non-async, emit an error later if it turned out to be async.
4261 if (callee->inferred_async_node == nullptr ||
4262 callee->inferred_async_node == inferred_async_checking)
4263 {
4264 callee->assumed_non_async = call_node;
4265 callee_is_async = false;
4266 } else {
4267 callee_is_async = callee->inferred_async_node != inferred_async_none;
4268 }
4260 }4269 }
4261 }4270 }
4262 if (callee_is_async) {4271 if (callee_is_async) {
...@@ -4333,6 +4342,8 @@ static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame) {...@@ -4333,6 +4342,8 @@ static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame) {
4333 }4342 }
4334 for (size_t i = 0; i < fn->await_list.length; i += 1) {4343 for (size_t i = 0; i < fn->await_list.length; i += 1) {
4335 IrInstructionAwaitGen *await = fn->await_list.at(i);4344 IrInstructionAwaitGen *await = fn->await_list.at(i);
4345 // TODO If this is a noasync await, it doesn't count
4346 // https://github.com/ziglang/zig/issues/3157
4336 switch (analyze_callee_async(g, fn, await->target_fn, await->base.source_node, must_not_be_async,4347 switch (analyze_callee_async(g, fn, await->target_fn, await->base.source_node, must_not_be_async,
4337 CallModifierNone))4348 CallModifierNone))
4338 {4349 {
...@@ -5771,15 +5782,39 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {...@@ -5771,15 +5782,39 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
5771 if (!fn_is_async(callee))5782 if (!fn_is_async(callee))
5772 continue;5783 continue;
57735784
5774 IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1);5785 call->frame_result_loc = ir_create_alloca(g, call->base.scope, call->base.source_node, fn,
5775 alloca_gen->base.id = IrInstructionIdAllocaGen;5786 callee_frame_type, "");
5776 alloca_gen->base.source_node = call->base.source_node;5787 }
5777 alloca_gen->base.scope = call->base.scope;5788 // Since this frame is async, an await might represent a suspend point, and
5778 alloca_gen->base.value.type = get_pointer_to_type(g, callee_frame_type, false);5789 // therefore need to spill.
5779 alloca_gen->base.ref_count = 1;5790 for (size_t i = 0; i < fn->await_list.length; i += 1) {
5780 alloca_gen->name_hint = "";5791 IrInstructionAwaitGen *await = fn->await_list.at(i);
5781 fn->alloca_gen_list.append(alloca_gen);5792 // TODO If this is a noasync await, it doesn't need to spill
5782 call->frame_result_loc = &alloca_gen->base;5793 // https://github.com/ziglang/zig/issues/3157
5794 if (await->result_loc != nullptr) {
5795 // If there's a result location, that is the spill
5796 continue;
5797 }
5798 if (!type_has_bits(await->base.value.type))
5799 continue;
5800 if (await->base.value.special != ConstValSpecialRuntime)
5801 continue;
5802 if (await->base.ref_count == 0)
5803 continue;
5804 if (await->target_fn != nullptr) {
5805 // we might not need to suspend
5806 analyze_fn_async(g, await->target_fn, false);
5807 if (await->target_fn->anal_state == FnAnalStateInvalid) {
5808 frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
5809 return ErrorSemanticAnalyzeFail;
5810 }
5811 if (!fn_is_async(await->target_fn)) {
5812 // This await does not represent a suspend point. No spill needed.
5813 continue;
5814 }
5815 }
5816 await->result_loc = ir_create_alloca(g, await->base.scope, await->base.source_node, fn,
5817 await->base.value.type, "");
5783 }5818 }
5784 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;5819 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
5785 ZigType *ptr_return_type = get_pointer_to_type(g, fn_type_id->return_type, false);5820 ZigType *ptr_return_type = get_pointer_to_type(g, fn_type_id->return_type, false);
...@@ -8505,3 +8540,18 @@ void src_assert(bool ok, AstNode *source_node) {...@@ -8505,3 +8540,18 @@ void src_assert(bool ok, AstNode *source_node) {
8505 const char *msg = "assertion failed. This is a bug in the Zig compiler.";8540 const char *msg = "assertion failed. This is a bug in the Zig compiler.";
8506 stage2_panic(msg, strlen(msg));8541 stage2_panic(msg, strlen(msg));
8507}8542}
8543
8544IrInstruction *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn,
8545 ZigType *var_type, const char *name_hint)
8546{
8547 IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1);
8548 alloca_gen->base.id = IrInstructionIdAllocaGen;
8549 alloca_gen->base.source_node = source_node;
8550 alloca_gen->base.scope = scope;
8551 alloca_gen->base.value.type = get_pointer_to_type(g, var_type, false);
8552 alloca_gen->base.ref_count = 1;
8553 alloca_gen->name_hint = name_hint;
8554 fn->alloca_gen_list.append(alloca_gen);
8555 return &alloca_gen->base;
8556}
8557
src/analyze.hpp+4
...@@ -258,4 +258,8 @@ ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field);...@@ -258,4 +258,8 @@ ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field);
258258
259void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn);259void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn);
260260
261IrInstruction *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn,
262 ZigType *var_type, const char *name_hint);
263
264
261#endif265#endif
src/codegen.cpp+12-13
...@@ -1661,6 +1661,14 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {...@@ -1661,6 +1661,14 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
1661 if (!type_has_bits(instruction->value.type))1661 if (!type_has_bits(instruction->value.type))
1662 return nullptr;1662 return nullptr;
1663 if (!instruction->llvm_value) {1663 if (!instruction->llvm_value) {
1664 if (instruction->id == IrInstructionIdAwaitGen) {
1665 IrInstructionAwaitGen *await = reinterpret_cast<IrInstructionAwaitGen*>(instruction);
1666 if (await->result_loc != nullptr) {
1667 instruction->llvm_value = get_handle_value(g, ir_llvm_value(g, await->result_loc),
1668 await->result_loc->value.type->data.pointer.child_type, await->result_loc->value.type);
1669 return instruction->llvm_value;
1670 }
1671 }
1664 src_assert(instruction->value.special != ConstValSpecialRuntime, instruction->source_node);1672 src_assert(instruction->value.special != ConstValSpecialRuntime, instruction->source_node);
1665 assert(instruction->value.type);1673 assert(instruction->value.type);
1666 render_const_val(g, &instruction->value, "");1674 render_const_val(g, &instruction->value, "");
...@@ -5645,7 +5653,6 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst...@@ -5645,7 +5653,6 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
5645 // At this point resuming the function will continue from resume_bb.5653 // At this point resuming the function will continue from resume_bb.
5646 // This code is as if it is running inside the suspend block.5654 // This code is as if it is running inside the suspend block.
56475655
5648
5649 // supply the awaiter return pointer5656 // supply the awaiter return pointer
5650 if (type_has_bits(result_type)) {5657 if (type_has_bits(result_type)) {
5651 LLVMValueRef awaiter_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_ret_start + 1, "");5658 LLVMValueRef awaiter_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_ret_start + 1, "");
...@@ -5703,9 +5710,8 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst...@@ -5703,9 +5710,8 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
5703 LLVMBuildBr(g->builder, end_bb);5710 LLVMBuildBr(g->builder, end_bb);
57045711
5705 LLVMPositionBuilderAtEnd(g->builder, end_bb);5712 LLVMPositionBuilderAtEnd(g->builder, end_bb);
5706 if (type_has_bits(result_type) && result_loc != nullptr) {5713 // Rely on the spill for the llvm_value to be populated.
5707 return get_handle_value(g, result_loc, result_type, ptr_result_type);5714 // See the implementation of ir_llvm_value.
5708 }
5709 return nullptr;5715 return nullptr;
5710}5716}
57115717
...@@ -7153,15 +7159,8 @@ static void do_code_gen(CodeGen *g) {...@@ -7153,15 +7159,8 @@ static void do_code_gen(CodeGen *g) {
7153 if (call->frame_result_loc != nullptr)7159 if (call->frame_result_loc != nullptr)
7154 continue;7160 continue;
7155 ZigType *callee_frame_type = get_fn_frame_type(g, call->fn_entry);7161 ZigType *callee_frame_type = get_fn_frame_type(g, call->fn_entry);
7156 IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1);7162 call->frame_result_loc = ir_create_alloca(g, call->base.scope, call->base.source_node,
7157 alloca_gen->base.id = IrInstructionIdAllocaGen;7163 fn_table_entry, callee_frame_type, "");
7158 alloca_gen->base.source_node = call->base.source_node;
7159 alloca_gen->base.scope = call->base.scope;
7160 alloca_gen->base.value.type = get_pointer_to_type(g, callee_frame_type, false);
7161 alloca_gen->base.ref_count = 1;
7162 alloca_gen->name_hint = "";
7163 fn_table_entry->alloca_gen_list.append(alloca_gen);
7164 call->frame_result_loc = &alloca_gen->base;
7165 }7164 }
7166 // allocate temporary stack data7165 // allocate temporary stack data
7167 for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_gen_list.length; alloca_i += 1) {7166 for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_gen_list.length; alloca_i += 1) {
test/stage1/behavior/async_fn.zig+16
...@@ -1108,3 +1108,19 @@ test "noasync function call" {...@@ -1108,3 +1108,19 @@ test "noasync function call" {
1108 };1108 };
1109 S.doTheTest();1109 S.doTheTest();
1110}1110}
1111
1112test "await used in expression and awaiting fn with no suspend but async calling convention" {
1113 const S = struct {
1114 fn atest() void {
1115 var f1 = async add(1, 2);
1116 var f2 = async add(3, 4);
1117
1118 const sum = (await f1) + (await f2);
1119 expect(sum == 10);
1120 }
1121 async fn add(a: i32, b: i32) i32 {
1122 return a + b;
1123 }
1124 };
1125 _ = async S.atest();
1126}