authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-01-26 15:34:36-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-01-26 15:34:36-05:00
loge0a422ae7e9716172ef316e88a1050f98fb7f1fa
treeb06e8598885211bfaf4fbabdb712257a54e4fa20
parent34a4d7a2017647e5f88d21cbfce16d8a837d6b4c

fix runtime branching tricking the comptime evaluation

closes #167

3 files changed, 49 insertions(+), 13 deletions(-)

src/all_types.hpp+4
...@@ -1424,6 +1424,10 @@ struct IrBasicBlock {...@@ -1424,6 +1424,10 @@ struct IrBasicBlock {
1424 size_t ref_count;1424 size_t ref_count;
1425 LLVMBasicBlockRef llvm_block;1425 LLVMBasicBlockRef llvm_block;
1426 LLVMBasicBlockRef llvm_exit_block;1426 LLVMBasicBlockRef llvm_exit_block;
1427 // The instruction that referenced this basic block and caused us to
1428 // analyze the basic block. If the same instruction wants us to emit
1429 // the same basic block, then we re-generate it instead of saving it.
1430 IrInstruction *ref_instruction;
1427};1431};
14281432
1429enum IrInstructionId {1433enum IrInstructionId {
src/ir.cpp+29-13
...@@ -5843,13 +5843,16 @@ static bool is_u8(TypeTableEntry *type) {...@@ -5843,13 +5843,16 @@ static bool is_u8(TypeTableEntry *type) {
5843 !type->data.integral.is_signed && type->data.integral.bit_count == 8;5843 !type->data.integral.is_signed && type->data.integral.bit_count == 8;
5844}5844}
58455845
5846static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) {5846static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) {
5847 assert(old_bb);5847 assert(old_bb);
58485848
5849 if (old_bb->other)5849 if (old_bb->other) {
5850 return old_bb->other;5850 if (ref_old_instruction == nullptr || old_bb->other->ref_instruction != ref_old_instruction)
5851 return old_bb->other;
5852 }
58515853
5852 IrBasicBlock *new_bb = ir_build_bb_from(&ira->new_irb, old_bb);5854 IrBasicBlock *new_bb = ir_build_bb_from(&ira->new_irb, old_bb);
5855 new_bb->ref_instruction = ref_old_instruction;
58535856
5854 // We are about to enqueue old_bb for analysis. Before we do so, look over old_bb's5857 // We are about to enqueue old_bb for analysis. Before we do so, look over old_bb's
5855 // instructions and make sure we have enqueued first the blocks which contain5858 // instructions and make sure we have enqueued first the blocks which contain
...@@ -5865,7 +5868,7 @@ static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) {...@@ -5865,7 +5868,7 @@ static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) {
5865 continue;5868 continue;
5866 if (dep_instruction->owner_bb == old_bb)5869 if (dep_instruction->owner_bb == old_bb)
5867 continue;5870 continue;
5868 ir_get_new_bb(ira, dep_instruction->owner_bb);5871 ir_get_new_bb(ira, dep_instruction->owner_bb, nullptr);
5869 }5872 }
5870 }5873 }
5871 ira->old_bb_queue.append(old_bb);5874 ira->old_bb_queue.append(old_bb);
...@@ -5897,7 +5900,8 @@ static void ir_finish_bb(IrAnalyze *ira) {...@@ -5897,7 +5900,8 @@ static void ir_finish_bb(IrAnalyze *ira) {
58975900
5898 if (ira->block_queue_index < ira->old_bb_queue.length) {5901 if (ira->block_queue_index < ira->old_bb_queue.length) {
5899 IrBasicBlock *old_bb = ira->old_bb_queue.at(ira->block_queue_index);5902 IrBasicBlock *old_bb = ira->old_bb_queue.at(ira->block_queue_index);
5900 ira->new_irb.current_basic_block = ir_get_new_bb(ira, old_bb);5903 assert(old_bb->other);
5904 ira->new_irb.current_basic_block = old_bb->other;
59015905
5902 ir_start_bb(ira, old_bb, nullptr);5906 ir_start_bb(ira, old_bb, nullptr);
5903 }5907 }
...@@ -8274,7 +8278,7 @@ static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr...@@ -8274,7 +8278,7 @@ static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr
8274 if (is_comptime || old_dest_block->ref_count == 1)8278 if (is_comptime || old_dest_block->ref_count == 1)
8275 return ir_inline_bb(ira, &br_instruction->base, old_dest_block);8279 return ir_inline_bb(ira, &br_instruction->base, old_dest_block);
82768280
8277 IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block);8281 IrBasicBlock *new_bb = ir_get_new_bb(ira, old_dest_block, &br_instruction->base);
8278 ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb);8282 ir_build_br_from(&ira->new_irb, &br_instruction->base, new_bb);
8279 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);8283 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
8280}8284}
...@@ -8307,7 +8311,7 @@ static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstruct...@@ -8307,7 +8311,7 @@ static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstruct
8307 if (is_comptime || old_dest_block->ref_count == 1)8311 if (is_comptime || old_dest_block->ref_count == 1)
8308 return ir_inline_bb(ira, &cond_br_instruction->base, old_dest_block);8312 return ir_inline_bb(ira, &cond_br_instruction->base, old_dest_block);
83098313
8310 IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block);8314 IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block, &cond_br_instruction->base);
8311 ir_build_br_from(&ira->new_irb, &cond_br_instruction->base, new_dest_block);8315 ir_build_br_from(&ira->new_irb, &cond_br_instruction->base, new_dest_block);
8312 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);8316 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
8313 }8317 }
...@@ -8317,8 +8321,9 @@ static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstruct...@@ -8317,8 +8321,9 @@ static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstruct
8317 if (casted_condition == ira->codegen->invalid_instruction)8321 if (casted_condition == ira->codegen->invalid_instruction)
8318 return ir_unreach_error(ira);8322 return ir_unreach_error(ira);
83198323
8320 IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block);8324 assert(cond_br_instruction->then_block != cond_br_instruction->else_block);
8321 IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block);8325 IrBasicBlock *new_then_block = ir_get_new_bb(ira, cond_br_instruction->then_block, &cond_br_instruction->base);
8326 IrBasicBlock *new_else_block = ir_get_new_bb(ira, cond_br_instruction->else_block, &cond_br_instruction->base);
8322 ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base,8327 ir_build_cond_br_from(&ira->new_irb, &cond_br_instruction->base,
8323 casted_condition, new_then_block, new_else_block, nullptr);8328 casted_condition, new_then_block, new_else_block, nullptr);
8324 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);8329 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
...@@ -9683,7 +9688,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,...@@ -9683,7 +9688,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
9683 if (is_comptime || old_dest_block->ref_count == 1) {9688 if (is_comptime || old_dest_block->ref_count == 1) {
9684 return ir_inline_bb(ira, &switch_br_instruction->base, old_dest_block);9689 return ir_inline_bb(ira, &switch_br_instruction->base, old_dest_block);
9685 } else {9690 } else {
9686 IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block);9691 IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block, &switch_br_instruction->base);
9687 ir_build_br_from(&ira->new_irb, &switch_br_instruction->base, new_dest_block);9692 ir_build_br_from(&ira->new_irb, &switch_br_instruction->base, new_dest_block);
9688 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);9693 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
9689 }9694 }
...@@ -9693,9 +9698,15 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,...@@ -9693,9 +9698,15 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
9693 for (size_t i = 0; i < case_count; i += 1) {9698 for (size_t i = 0; i < case_count; i += 1) {
9694 IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i];9699 IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i];
9695 IrInstructionSwitchBrCase *new_case = &cases[i];9700 IrInstructionSwitchBrCase *new_case = &cases[i];
9696 new_case->block = ir_get_new_bb(ira, old_case->block);9701 new_case->block = ir_get_new_bb(ira, old_case->block, &switch_br_instruction->base);
9697 new_case->value = ira->codegen->invalid_instruction;9702 new_case->value = ira->codegen->invalid_instruction;
96989703
9704 // Calling ir_get_new_bb set the ref_instruction on the new basic block.
9705 // However a switch br may branch to the same basic block which would trigger an
9706 // incorrect re-generation of the block. So we set it to null here and assign
9707 // it back after the loop.
9708 new_case->block->ref_instruction = nullptr;
9709
9699 IrInstruction *old_value = old_case->value;9710 IrInstruction *old_value = old_case->value;
9700 IrInstruction *new_value = old_value->other;9711 IrInstruction *new_value = old_value->other;
9701 if (new_value->value.type->id == TypeTableEntryIdInvalid)9712 if (new_value->value.type->id == TypeTableEntryIdInvalid)
...@@ -9717,7 +9728,12 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,...@@ -9717,7 +9728,12 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
9717 new_case->value = casted_new_value;9728 new_case->value = casted_new_value;
9718 }9729 }
97199730
9720 IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block);9731 for (size_t i = 0; i < case_count; i += 1) {
9732 IrInstructionSwitchBrCase *new_case = &cases[i];
9733 new_case->block->ref_instruction = &switch_br_instruction->base;
9734 }
9735
9736 IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base);
9721 ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base,9737 ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base,
9722 target_value, new_else_block, case_count, cases, nullptr);9738 target_value, new_else_block, case_count, cases, nullptr);
9723 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);9739 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
...@@ -11751,7 +11767,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl...@@ -11751,7 +11767,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
11751 ira->exec_context.mem_slot_list = allocate<ConstExprValue>(ira->exec_context.mem_slot_count);11767 ira->exec_context.mem_slot_list = allocate<ConstExprValue>(ira->exec_context.mem_slot_count);
1175211768
11753 IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0);11769 IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0);
11754 IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb);11770 IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr);
11755 ir_ref_bb(new_entry_bb);11771 ir_ref_bb(new_entry_bb);
11756 ira->new_irb.current_basic_block = new_entry_bb;11772 ira->new_irb.current_basic_block = new_entry_bb;
11757 ira->block_queue_index = 0;11773 ira->block_queue_index = 0;
test/cases/eval.zig+16
...@@ -175,3 +175,19 @@ fn constSlice() {...@@ -175,3 +175,19 @@ fn constSlice() {
175 assert(b[0] == '2');175 assert(b[0] == '2');
176 }176 }
177}177}
178
179fn tryToTrickEvalWithRuntimeIf() {
180 @setFnTest(this);
181
182 assert(testTryToTrickEvalWithRuntimeIf(true) == 10);
183}
184
185fn testTryToTrickEvalWithRuntimeIf(b: bool) -> usize {
186 comptime var i: usize = 0;
187 inline while (i < 10; i += 1) {
188 const result = if (b) false else true;
189 }
190 comptime {
191 return i;
192 }
193}