X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Flower_jumps.cpp;h=ec7a0c537ff11044b3bcaf5b378dd3361b87687d;hb=1e4081f54aa5c6cba566ed549389d847bf7e6799;hp=cbdd8eaa8be34fe4f2cb43354f4a5d2298b88ba2;hpb=03145ba655ad9173a74b853843eccaae78ff392f;p=mesa.git diff --git a/src/glsl/lower_jumps.cpp b/src/glsl/lower_jumps.cpp index cbdd8eaa8be..ec7a0c537ff 100644 --- a/src/glsl/lower_jumps.cpp +++ b/src/glsl/lower_jumps.cpp @@ -133,6 +133,8 @@ enum jump_strength strength_return }; +namespace { + struct block_record { /* minimum jump strength (of lowered IR, not pre-lowering IR) @@ -279,8 +281,13 @@ struct ir_lower_jumps_visitor : public ir_control_flow_visitor { bool lower_main_return; ir_lower_jumps_visitor() + : progress(false), + pull_out_jumps(false), + lower_continue(false), + lower_break(false), + lower_sub_return(false), + lower_main_return(false) { - this->progress = false; } void truncate_after_instruction(exec_node *ir) @@ -341,6 +348,50 @@ struct ir_lower_jumps_visitor : public ir_control_flow_visitor { ir->replace_with(new(ir) ir_loop_jump(ir_loop_jump::jump_break)); } + /** + * Create the necessary instruction to replace a break instruction. + */ + ir_instruction *create_lowered_break() + { + void *ctx = this->function.signature; + return new(ctx) ir_assignment( + new(ctx) ir_dereference_variable(this->loop.get_break_flag()), + new(ctx) ir_constant(true), + 0); + } + + /** + * If the given instruction is a break, lower it to an instruction + * that sets the break flag, without consulting + * should_lower_jump(). + * + * It is safe to pass NULL to this function. + */ + void lower_break_unconditionally(ir_instruction *ir) + { + if (get_jump_strength(ir) != strength_break) { + return; + } + ir->replace_with(create_lowered_break()); + } + + /** + * If the block ends in a conditional or unconditional break, lower + * it, even though should_lower_jump() says it needn't be lowered. + */ + void lower_final_breaks(exec_list *block) + { + ir_instruction *ir = (ir_instruction *) block->get_tail(); + lower_break_unconditionally(ir); + ir_if *ir_if = ir->as_if(); + if (ir_if) { + lower_break_unconditionally( + (ir_instruction *) ir_if->then_instructions.get_tail()); + lower_break_unconditionally( + (ir_instruction *) ir_if->else_instructions.get_tail()); + } + } + virtual void visit(class ir_loop_jump * ir) { /* Eliminate all instructions after each one, since they are @@ -394,6 +445,7 @@ struct ir_lower_jumps_visitor : public ir_control_flow_visitor { * satisfied, because discard statements can't contain other * statements. */ + (void) ir; } enum jump_strength get_jump_strength(ir_instruction* ir) @@ -445,9 +497,20 @@ struct ir_lower_jumps_visitor : public ir_control_flow_visitor { block_record visit_block(exec_list* list) { + /* Note: since visiting a node may change that node's next + * pointer, we can't use visit_exec_list(), because + * visit_exec_list() caches the node's next pointer before + * visiting it. So we use foreach_in_list() instead. + * + * foreach_in_list() isn't safe if the node being visited gets + * removed, but fortunately this visitor doesn't do that. + */ + block_record saved_block = this->block; this->block = block_record(); - visit_exec_list(list, this); + foreach_in_list(ir_instruction, node, list) { + node->accept(this); + } block_record ret = this->block; this->block = saved_block; return ret; @@ -605,7 +668,7 @@ retry: /* we get here if we put code after the if inside a branch */ * The visit() function for the loop will ensure that the * break flag is checked after executing the loop body. */ - jumps[lower]->insert_before(new(ir) ir_assignment(new (ir) ir_dereference_variable(this->loop.get_break_flag()), new (ir) ir_constant(true), 0)); + jumps[lower]->insert_before(create_lowered_break()); goto lower_continue; } else if(jump_strengths[lower] == strength_continue) { lower_continue: @@ -627,7 +690,10 @@ lower_continue: block_records[lower].min_strength = strength_always_clears_execute_flag; block_records[lower].may_clear_execute_flag = true; this->progress = true; - break; + + /* Let the loop run again, in case the other branch of the + * if needs to be lowered too. + */ } } @@ -822,6 +888,9 @@ lower_continue: } if(this->loop.break_flag) { + /* We only get here if we are lowering breaks */ + assert (lower_break); + /* If a break flag was generated while visiting the body of * the loop, then at least one break was lowered, so we need * to generate an if statement at the end of the loop that @@ -829,7 +898,13 @@ lower_continue: * generate won't violate the CONTAINED_JUMPS_LOWERED * postcondition, because should_lower_jump() always returns * false for a break that happens at the end of a loop. + * + * However, if the loop already ends in a conditional or + * unconditional break, then we need to lower that break, + * because it won't be at the end of the loop anymore. */ + lower_final_breaks(&ir->body_instructions); + ir_if* break_if = new(ir) ir_if(new(ir) ir_dereference_variable(this->loop.break_flag)); break_if->then_instructions.push_tail(new(ir) ir_loop_jump(ir_loop_jump::jump_break)); ir->body_instructions.push_tail(break_if); @@ -924,6 +999,8 @@ lower_continue: } }; +} /* anonymous namespace */ + bool do_lower_jumps(exec_list *instructions, bool pull_out_jumps, bool lower_sub_return, bool lower_main_return, bool lower_continue, bool lower_break) { @@ -934,10 +1011,12 @@ do_lower_jumps(exec_list *instructions, bool pull_out_jumps, bool lower_sub_retu v.lower_sub_return = lower_sub_return; v.lower_main_return = lower_main_return; + bool progress_ever = false; do { v.progress = false; visit_exec_list(instructions, &v); + progress_ever = v.progress || progress_ever; } while (v.progress); - return v.progress; + return progress_ever; }