strength_return
};
+namespace {
+
struct block_record
{
/* minimum jump strength (of lowered IR, not pre-lowering IR)
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)
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
* satisfied, because discard statements can't contain other
* statements.
*/
+ (void) ir;
}
enum jump_strength get_jump_strength(ir_instruction* ir)
/* 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_list() instead.
+ * visiting it. So we use foreach_in_list() instead.
*
- * foreach_list() isn't safe if the node being visited gets
+ * 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();
- foreach_list(node, list) {
- ((ir_instruction *) node)->accept(this);
+ foreach_in_list(ir_instruction, node, list) {
+ node->accept(this);
}
block_record ret = this->block;
this->block = saved_block;
* 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:
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.
+ */
}
}
}
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
* 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);
}
};
+} /* 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)
{
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;
}