101e04c3fSmrg/* 201e04c3fSmrg * Copyright © 2010 Luca Barbieri 301e04c3fSmrg * 401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a 501e04c3fSmrg * copy of this software and associated documentation files (the "Software"), 601e04c3fSmrg * to deal in the Software without restriction, including without limitation 701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the 901e04c3fSmrg * Software is furnished to do so, subject to the following conditions: 1001e04c3fSmrg * 1101e04c3fSmrg * The above copyright notice and this permission notice (including the next 1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the 1301e04c3fSmrg * Software. 1401e04c3fSmrg * 1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2101e04c3fSmrg * DEALINGS IN THE SOFTWARE. 2201e04c3fSmrg */ 2301e04c3fSmrg 2401e04c3fSmrg/** 2501e04c3fSmrg * \file lower_jumps.cpp 2601e04c3fSmrg * 2701e04c3fSmrg * This pass lowers jumps (break, continue, and return) to if/else structures. 2801e04c3fSmrg * 2901e04c3fSmrg * It can be asked to: 3001e04c3fSmrg * 1. Pull jumps out of ifs where possible 3101e04c3fSmrg * 2. Remove all "continue"s, replacing them with an "execute flag" 3201e04c3fSmrg * 3. Replace all "break" with a single conditional one at the end of the loop 3301e04c3fSmrg * 4. Replace all "return"s with a single return at the end of the function, 3401e04c3fSmrg * for the main function and/or other functions 3501e04c3fSmrg * 3601e04c3fSmrg * Applying this pass gives several benefits: 3701e04c3fSmrg * 1. All functions can be inlined. 3801e04c3fSmrg * 2. nv40 and other pre-DX10 chips without "continue" can be supported 3901e04c3fSmrg * 3. nv30 and other pre-DX10 chips with no control flow at all are better 4001e04c3fSmrg * supported 4101e04c3fSmrg * 4201e04c3fSmrg * Continues are lowered by adding a per-loop "execute flag", initialized to 4301e04c3fSmrg * true, that when cleared inhibits all execution until the end of the loop. 4401e04c3fSmrg * 4501e04c3fSmrg * Breaks are lowered to continues, plus setting a "break flag" that is checked 4601e04c3fSmrg * at the end of the loop, and trigger the unique "break". 4701e04c3fSmrg * 4801e04c3fSmrg * Returns are lowered to breaks/continues, plus adding a "return flag" that 4901e04c3fSmrg * causes loops to break again out of their enclosing loops until all the 5001e04c3fSmrg * loops are exited: then the "execute flag" logic will ignore everything 5101e04c3fSmrg * until the end of the function. 5201e04c3fSmrg * 5301e04c3fSmrg * Note that "continue" and "return" can also be implemented by adding 5401e04c3fSmrg * a dummy loop and using break. 5501e04c3fSmrg * However, this is bad for hardware with limited nesting depth, and 5601e04c3fSmrg * prevents further optimization, and thus is not currently performed. 5701e04c3fSmrg */ 5801e04c3fSmrg 5901e04c3fSmrg#include "compiler/glsl_types.h" 6001e04c3fSmrg#include <string.h> 6101e04c3fSmrg#include "ir.h" 6201e04c3fSmrg 6301e04c3fSmrg/** 6401e04c3fSmrg * Enum recording the result of analyzing how control flow might exit 6501e04c3fSmrg * an IR node. 6601e04c3fSmrg * 6701e04c3fSmrg * Each possible value of jump_strength indicates a strictly stronger 6801e04c3fSmrg * guarantee on control flow than the previous value. 6901e04c3fSmrg * 7001e04c3fSmrg * The ordering of strengths roughly reflects the way jumps are 7101e04c3fSmrg * lowered: jumps with higher strength tend to be lowered to jumps of 7201e04c3fSmrg * lower strength. Accordingly, strength is used as a heuristic to 7301e04c3fSmrg * determine which lowering to perform first. 7401e04c3fSmrg * 7501e04c3fSmrg * This enum is also used by get_jump_strength() to categorize 7601e04c3fSmrg * instructions as either break, continue, return, or other. When 7701e04c3fSmrg * used in this fashion, strength_always_clears_execute_flag is not 7801e04c3fSmrg * used. 7901e04c3fSmrg * 8001e04c3fSmrg * The control flow analysis made by this optimization pass makes two 8101e04c3fSmrg * simplifying assumptions: 8201e04c3fSmrg * 8301e04c3fSmrg * - It ignores discard instructions, since they are lowered by a 8401e04c3fSmrg * separate pass (lower_discard.cpp). 8501e04c3fSmrg * 8601e04c3fSmrg * - It assumes it is always possible for control to flow from a loop 8701e04c3fSmrg * to the instruction immediately following it. Technically, this 8801e04c3fSmrg * is not true (since all execution paths through the loop might 8901e04c3fSmrg * jump back to the top, or return from the function). 9001e04c3fSmrg * 9101e04c3fSmrg * Both of these simplifying assumtions are safe, since they can never 9201e04c3fSmrg * cause reachable code to be incorrectly classified as unreachable; 9301e04c3fSmrg * they can only do the opposite. 9401e04c3fSmrg */ 9501e04c3fSmrgenum jump_strength 9601e04c3fSmrg{ 9701e04c3fSmrg /** 9801e04c3fSmrg * Analysis has produced no guarantee on how control flow might 9901e04c3fSmrg * exit this IR node. It might fall out the bottom (with or 10001e04c3fSmrg * without clearing the execute flag, if present), or it might 10101e04c3fSmrg * continue to the top of the innermost enclosing loop, break out 10201e04c3fSmrg * of it, or return from the function. 10301e04c3fSmrg */ 10401e04c3fSmrg strength_none, 10501e04c3fSmrg 10601e04c3fSmrg /** 10701e04c3fSmrg * The only way control can fall out the bottom of this node is 10801e04c3fSmrg * through a code path that clears the execute flag. It might also 10901e04c3fSmrg * continue to the top of the innermost enclosing loop, break out 11001e04c3fSmrg * of it, or return from the function. 11101e04c3fSmrg */ 11201e04c3fSmrg strength_always_clears_execute_flag, 11301e04c3fSmrg 11401e04c3fSmrg /** 11501e04c3fSmrg * Control cannot fall out the bottom of this node. It might 11601e04c3fSmrg * continue to the top of the innermost enclosing loop, break out 11701e04c3fSmrg * of it, or return from the function. 11801e04c3fSmrg */ 11901e04c3fSmrg strength_continue, 12001e04c3fSmrg 12101e04c3fSmrg /** 12201e04c3fSmrg * Control cannot fall out the bottom of this node, or continue the 12301e04c3fSmrg * top of the innermost enclosing loop. It can only break out of 12401e04c3fSmrg * it or return from the function. 12501e04c3fSmrg */ 12601e04c3fSmrg strength_break, 12701e04c3fSmrg 12801e04c3fSmrg /** 12901e04c3fSmrg * Control cannot fall out the bottom of this node, continue to the 13001e04c3fSmrg * top of the innermost enclosing loop, or break out of it. It can 13101e04c3fSmrg * only return from the function. 13201e04c3fSmrg */ 13301e04c3fSmrg strength_return 13401e04c3fSmrg}; 13501e04c3fSmrg 13601e04c3fSmrgnamespace { 13701e04c3fSmrg 13801e04c3fSmrgstruct block_record 13901e04c3fSmrg{ 14001e04c3fSmrg /* minimum jump strength (of lowered IR, not pre-lowering IR) 14101e04c3fSmrg * 14201e04c3fSmrg * If the block ends with a jump, must be the strength of the jump. 14301e04c3fSmrg * Otherwise, the jump would be dead and have been deleted before) 14401e04c3fSmrg * 14501e04c3fSmrg * If the block doesn't end with a jump, it can be different than strength_none if all paths before it lead to some jump 14601e04c3fSmrg * (e.g. an if with a return in one branch, and a break in the other, while not lowering them) 14701e04c3fSmrg * Note that identical jumps are usually unified though. 14801e04c3fSmrg */ 14901e04c3fSmrg jump_strength min_strength; 15001e04c3fSmrg 15101e04c3fSmrg /* can anything clear the execute flag? */ 15201e04c3fSmrg bool may_clear_execute_flag; 15301e04c3fSmrg 15401e04c3fSmrg block_record() 15501e04c3fSmrg { 15601e04c3fSmrg this->min_strength = strength_none; 15701e04c3fSmrg this->may_clear_execute_flag = false; 15801e04c3fSmrg } 15901e04c3fSmrg}; 16001e04c3fSmrg 16101e04c3fSmrgstruct loop_record 16201e04c3fSmrg{ 16301e04c3fSmrg ir_function_signature* signature; 16401e04c3fSmrg ir_loop* loop; 16501e04c3fSmrg 16601e04c3fSmrg /* used to avoid lowering the break used to represent lowered breaks */ 16701e04c3fSmrg unsigned nesting_depth; 16801e04c3fSmrg bool in_if_at_the_end_of_the_loop; 16901e04c3fSmrg 17001e04c3fSmrg bool may_set_return_flag; 17101e04c3fSmrg 17201e04c3fSmrg ir_variable* break_flag; 17301e04c3fSmrg ir_variable* execute_flag; /* cleared to emulate continue */ 17401e04c3fSmrg 17501e04c3fSmrg loop_record(ir_function_signature* p_signature = 0, ir_loop* p_loop = 0) 17601e04c3fSmrg { 17701e04c3fSmrg this->signature = p_signature; 17801e04c3fSmrg this->loop = p_loop; 17901e04c3fSmrg this->nesting_depth = 0; 18001e04c3fSmrg this->in_if_at_the_end_of_the_loop = false; 18101e04c3fSmrg this->may_set_return_flag = false; 18201e04c3fSmrg this->break_flag = 0; 18301e04c3fSmrg this->execute_flag = 0; 18401e04c3fSmrg } 18501e04c3fSmrg 18601e04c3fSmrg ir_variable* get_execute_flag() 18701e04c3fSmrg { 18801e04c3fSmrg /* also supported for the "function loop" */ 18901e04c3fSmrg if(!this->execute_flag) { 19001e04c3fSmrg exec_list& list = this->loop ? this->loop->body_instructions : signature->body; 19101e04c3fSmrg this->execute_flag = new(this->signature) ir_variable(glsl_type::bool_type, "execute_flag", ir_var_temporary); 19201e04c3fSmrg list.push_head(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(execute_flag), new(this->signature) ir_constant(true))); 19301e04c3fSmrg list.push_head(this->execute_flag); 19401e04c3fSmrg } 19501e04c3fSmrg return this->execute_flag; 19601e04c3fSmrg } 19701e04c3fSmrg 19801e04c3fSmrg ir_variable* get_break_flag() 19901e04c3fSmrg { 20001e04c3fSmrg assert(this->loop); 20101e04c3fSmrg if(!this->break_flag) { 20201e04c3fSmrg this->break_flag = new(this->signature) ir_variable(glsl_type::bool_type, "break_flag", ir_var_temporary); 20301e04c3fSmrg this->loop->insert_before(this->break_flag); 20401e04c3fSmrg this->loop->insert_before(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(break_flag), new(this->signature) ir_constant(false))); 20501e04c3fSmrg } 20601e04c3fSmrg return this->break_flag; 20701e04c3fSmrg } 20801e04c3fSmrg}; 20901e04c3fSmrg 21001e04c3fSmrgstruct function_record 21101e04c3fSmrg{ 21201e04c3fSmrg ir_function_signature* signature; 21301e04c3fSmrg ir_variable* return_flag; /* used to break out of all loops and then jump to the return instruction */ 21401e04c3fSmrg ir_variable* return_value; 21501e04c3fSmrg bool lower_return; 21601e04c3fSmrg unsigned nesting_depth; 21701e04c3fSmrg 21801e04c3fSmrg function_record(ir_function_signature* p_signature = 0, 21901e04c3fSmrg bool lower_return = false) 22001e04c3fSmrg { 22101e04c3fSmrg this->signature = p_signature; 22201e04c3fSmrg this->return_flag = 0; 22301e04c3fSmrg this->return_value = 0; 22401e04c3fSmrg this->nesting_depth = 0; 22501e04c3fSmrg this->lower_return = lower_return; 22601e04c3fSmrg } 22701e04c3fSmrg 22801e04c3fSmrg ir_variable* get_return_flag() 22901e04c3fSmrg { 23001e04c3fSmrg if(!this->return_flag) { 23101e04c3fSmrg this->return_flag = new(this->signature) ir_variable(glsl_type::bool_type, "return_flag", ir_var_temporary); 23201e04c3fSmrg this->signature->body.push_head(new(this->signature) ir_assignment(new(this->signature) ir_dereference_variable(return_flag), new(this->signature) ir_constant(false))); 23301e04c3fSmrg this->signature->body.push_head(this->return_flag); 23401e04c3fSmrg } 23501e04c3fSmrg return this->return_flag; 23601e04c3fSmrg } 23701e04c3fSmrg 23801e04c3fSmrg ir_variable* get_return_value() 23901e04c3fSmrg { 24001e04c3fSmrg if(!this->return_value) { 24101e04c3fSmrg assert(!this->signature->return_type->is_void()); 24201e04c3fSmrg return_value = new(this->signature) ir_variable(this->signature->return_type, "return_value", ir_var_temporary); 24301e04c3fSmrg this->signature->body.push_head(this->return_value); 24401e04c3fSmrg } 24501e04c3fSmrg return this->return_value; 24601e04c3fSmrg } 24701e04c3fSmrg}; 24801e04c3fSmrg 24901e04c3fSmrgstruct ir_lower_jumps_visitor : public ir_control_flow_visitor { 25001e04c3fSmrg /* Postconditions: on exit of any visit() function: 25101e04c3fSmrg * 25201e04c3fSmrg * ANALYSIS: this->block.min_strength, 25301e04c3fSmrg * this->block.may_clear_execute_flag, and 25401e04c3fSmrg * this->loop.may_set_return_flag are updated to reflect the 25501e04c3fSmrg * characteristics of the visited statement. 25601e04c3fSmrg * 25701e04c3fSmrg * DEAD_CODE_ELIMINATION: If this->block.min_strength is not 25801e04c3fSmrg * strength_none, the visited node is at the end of its exec_list. 25901e04c3fSmrg * In other words, any unreachable statements that follow the 26001e04c3fSmrg * visited statement in its exec_list have been removed. 26101e04c3fSmrg * 26201e04c3fSmrg * CONTAINED_JUMPS_LOWERED: If the visited statement contains other 26301e04c3fSmrg * statements, then should_lower_jump() is false for all of the 26401e04c3fSmrg * return, break, or continue statements it contains. 26501e04c3fSmrg * 26601e04c3fSmrg * Note that visiting a jump does not lower it. That is the 26701e04c3fSmrg * responsibility of the statement (or function signature) that 26801e04c3fSmrg * contains the jump. 26901e04c3fSmrg */ 27001e04c3fSmrg 2717ec681f3Smrg using ir_control_flow_visitor::visit; 2727ec681f3Smrg 27301e04c3fSmrg bool progress; 27401e04c3fSmrg 27501e04c3fSmrg struct function_record function; 27601e04c3fSmrg struct loop_record loop; 27701e04c3fSmrg struct block_record block; 27801e04c3fSmrg 27901e04c3fSmrg bool pull_out_jumps; 28001e04c3fSmrg bool lower_continue; 28101e04c3fSmrg bool lower_break; 28201e04c3fSmrg bool lower_sub_return; 28301e04c3fSmrg bool lower_main_return; 28401e04c3fSmrg 28501e04c3fSmrg ir_lower_jumps_visitor() 28601e04c3fSmrg : progress(false), 28701e04c3fSmrg pull_out_jumps(false), 28801e04c3fSmrg lower_continue(false), 28901e04c3fSmrg lower_break(false), 29001e04c3fSmrg lower_sub_return(false), 29101e04c3fSmrg lower_main_return(false) 29201e04c3fSmrg { 29301e04c3fSmrg } 29401e04c3fSmrg 29501e04c3fSmrg void truncate_after_instruction(exec_node *ir) 29601e04c3fSmrg { 29701e04c3fSmrg if (!ir) 29801e04c3fSmrg return; 29901e04c3fSmrg 30001e04c3fSmrg while (!ir->get_next()->is_tail_sentinel()) { 30101e04c3fSmrg ((ir_instruction *)ir->get_next())->remove(); 30201e04c3fSmrg this->progress = true; 30301e04c3fSmrg } 30401e04c3fSmrg } 30501e04c3fSmrg 30601e04c3fSmrg void move_outer_block_inside(ir_instruction *ir, exec_list *inner_block) 30701e04c3fSmrg { 30801e04c3fSmrg while (!ir->get_next()->is_tail_sentinel()) { 30901e04c3fSmrg ir_instruction *move_ir = (ir_instruction *)ir->get_next(); 31001e04c3fSmrg 31101e04c3fSmrg move_ir->remove(); 31201e04c3fSmrg inner_block->push_tail(move_ir); 31301e04c3fSmrg } 31401e04c3fSmrg } 31501e04c3fSmrg 31601e04c3fSmrg /** 31701e04c3fSmrg * Insert the instructions necessary to lower a return statement, 31801e04c3fSmrg * before the given return instruction. 31901e04c3fSmrg */ 32001e04c3fSmrg void insert_lowered_return(ir_return *ir) 32101e04c3fSmrg { 32201e04c3fSmrg ir_variable* return_flag = this->function.get_return_flag(); 32301e04c3fSmrg if(!this->function.signature->return_type->is_void()) { 32401e04c3fSmrg ir_variable* return_value = this->function.get_return_value(); 32501e04c3fSmrg ir->insert_before( 32601e04c3fSmrg new(ir) ir_assignment( 32701e04c3fSmrg new (ir) ir_dereference_variable(return_value), 32801e04c3fSmrg ir->value)); 32901e04c3fSmrg } 33001e04c3fSmrg ir->insert_before( 33101e04c3fSmrg new(ir) ir_assignment( 33201e04c3fSmrg new (ir) ir_dereference_variable(return_flag), 33301e04c3fSmrg new (ir) ir_constant(true))); 33401e04c3fSmrg this->loop.may_set_return_flag = true; 33501e04c3fSmrg } 33601e04c3fSmrg 33701e04c3fSmrg /** 33801e04c3fSmrg * If the given instruction is a return, lower it to instructions 33901e04c3fSmrg * that store the return value (if there is one), set the return 34001e04c3fSmrg * flag, and then break. 34101e04c3fSmrg * 34201e04c3fSmrg * It is safe to pass NULL to this function. 34301e04c3fSmrg */ 34401e04c3fSmrg void lower_return_unconditionally(ir_instruction *ir) 34501e04c3fSmrg { 34601e04c3fSmrg if (get_jump_strength(ir) != strength_return) { 34701e04c3fSmrg return; 34801e04c3fSmrg } 34901e04c3fSmrg insert_lowered_return((ir_return*)ir); 35001e04c3fSmrg ir->replace_with(new(ir) ir_loop_jump(ir_loop_jump::jump_break)); 35101e04c3fSmrg } 35201e04c3fSmrg 35301e04c3fSmrg /** 35401e04c3fSmrg * Create the necessary instruction to replace a break instruction. 35501e04c3fSmrg */ 35601e04c3fSmrg ir_instruction *create_lowered_break() 35701e04c3fSmrg { 35801e04c3fSmrg void *ctx = this->function.signature; 35901e04c3fSmrg return new(ctx) ir_assignment( 36001e04c3fSmrg new(ctx) ir_dereference_variable(this->loop.get_break_flag()), 36101e04c3fSmrg new(ctx) ir_constant(true)); 36201e04c3fSmrg } 36301e04c3fSmrg 36401e04c3fSmrg /** 36501e04c3fSmrg * If the given instruction is a break, lower it to an instruction 36601e04c3fSmrg * that sets the break flag, without consulting 36701e04c3fSmrg * should_lower_jump(). 36801e04c3fSmrg * 36901e04c3fSmrg * It is safe to pass NULL to this function. 37001e04c3fSmrg */ 37101e04c3fSmrg void lower_break_unconditionally(ir_instruction *ir) 37201e04c3fSmrg { 37301e04c3fSmrg if (get_jump_strength(ir) != strength_break) { 37401e04c3fSmrg return; 37501e04c3fSmrg } 37601e04c3fSmrg ir->replace_with(create_lowered_break()); 37701e04c3fSmrg } 37801e04c3fSmrg 37901e04c3fSmrg /** 38001e04c3fSmrg * If the block ends in a conditional or unconditional break, lower 38101e04c3fSmrg * it, even though should_lower_jump() says it needn't be lowered. 38201e04c3fSmrg */ 38301e04c3fSmrg void lower_final_breaks(exec_list *block) 38401e04c3fSmrg { 38501e04c3fSmrg ir_instruction *ir = (ir_instruction *) block->get_tail(); 38601e04c3fSmrg lower_break_unconditionally(ir); 38701e04c3fSmrg ir_if *ir_if = ir->as_if(); 38801e04c3fSmrg if (ir_if) { 38901e04c3fSmrg lower_break_unconditionally( 39001e04c3fSmrg (ir_instruction *) ir_if->then_instructions.get_tail()); 39101e04c3fSmrg lower_break_unconditionally( 39201e04c3fSmrg (ir_instruction *) ir_if->else_instructions.get_tail()); 39301e04c3fSmrg } 39401e04c3fSmrg } 39501e04c3fSmrg 39601e04c3fSmrg virtual void visit(class ir_loop_jump * ir) 39701e04c3fSmrg { 39801e04c3fSmrg /* Eliminate all instructions after each one, since they are 39901e04c3fSmrg * unreachable. This satisfies the DEAD_CODE_ELIMINATION 40001e04c3fSmrg * postcondition. 40101e04c3fSmrg */ 40201e04c3fSmrg truncate_after_instruction(ir); 40301e04c3fSmrg 40401e04c3fSmrg /* Set this->block.min_strength based on this instruction. This 40501e04c3fSmrg * satisfies the ANALYSIS postcondition. It is not necessary to 40601e04c3fSmrg * update this->block.may_clear_execute_flag or 40701e04c3fSmrg * this->loop.may_set_return_flag, because an unlowered jump 40801e04c3fSmrg * instruction can't change any flags. 40901e04c3fSmrg */ 41001e04c3fSmrg this->block.min_strength = ir->is_break() ? strength_break : strength_continue; 41101e04c3fSmrg 41201e04c3fSmrg /* The CONTAINED_JUMPS_LOWERED postcondition is already 41301e04c3fSmrg * satisfied, because jump statements can't contain other 41401e04c3fSmrg * statements. 41501e04c3fSmrg */ 41601e04c3fSmrg } 41701e04c3fSmrg 41801e04c3fSmrg virtual void visit(class ir_return * ir) 41901e04c3fSmrg { 42001e04c3fSmrg /* Eliminate all instructions after each one, since they are 42101e04c3fSmrg * unreachable. This satisfies the DEAD_CODE_ELIMINATION 42201e04c3fSmrg * postcondition. 42301e04c3fSmrg */ 42401e04c3fSmrg truncate_after_instruction(ir); 42501e04c3fSmrg 42601e04c3fSmrg /* Set this->block.min_strength based on this instruction. This 42701e04c3fSmrg * satisfies the ANALYSIS postcondition. It is not necessary to 42801e04c3fSmrg * update this->block.may_clear_execute_flag or 42901e04c3fSmrg * this->loop.may_set_return_flag, because an unlowered return 43001e04c3fSmrg * instruction can't change any flags. 43101e04c3fSmrg */ 43201e04c3fSmrg this->block.min_strength = strength_return; 43301e04c3fSmrg 43401e04c3fSmrg /* The CONTAINED_JUMPS_LOWERED postcondition is already 43501e04c3fSmrg * satisfied, because jump statements can't contain other 43601e04c3fSmrg * statements. 43701e04c3fSmrg */ 43801e04c3fSmrg } 43901e04c3fSmrg 44001e04c3fSmrg virtual void visit(class ir_discard * ir) 44101e04c3fSmrg { 44201e04c3fSmrg /* Nothing needs to be done. The ANALYSIS and 44301e04c3fSmrg * DEAD_CODE_ELIMINATION postconditions are already satisfied, 44401e04c3fSmrg * because discard statements are ignored by this optimization 44501e04c3fSmrg * pass. The CONTAINED_JUMPS_LOWERED postcondition is already 44601e04c3fSmrg * satisfied, because discard statements can't contain other 44701e04c3fSmrg * statements. 44801e04c3fSmrg */ 44901e04c3fSmrg (void) ir; 45001e04c3fSmrg } 45101e04c3fSmrg 45201e04c3fSmrg enum jump_strength get_jump_strength(ir_instruction* ir) 45301e04c3fSmrg { 45401e04c3fSmrg if(!ir) 45501e04c3fSmrg return strength_none; 45601e04c3fSmrg else if(ir->ir_type == ir_type_loop_jump) { 45701e04c3fSmrg if(((ir_loop_jump*)ir)->is_break()) 45801e04c3fSmrg return strength_break; 45901e04c3fSmrg else 46001e04c3fSmrg return strength_continue; 46101e04c3fSmrg } else if(ir->ir_type == ir_type_return) 46201e04c3fSmrg return strength_return; 46301e04c3fSmrg else 46401e04c3fSmrg return strength_none; 46501e04c3fSmrg } 46601e04c3fSmrg 46701e04c3fSmrg bool should_lower_jump(ir_jump* ir) 46801e04c3fSmrg { 46901e04c3fSmrg unsigned strength = get_jump_strength(ir); 47001e04c3fSmrg bool lower; 47101e04c3fSmrg switch(strength) 47201e04c3fSmrg { 47301e04c3fSmrg case strength_none: 47401e04c3fSmrg lower = false; /* don't change this, code relies on it */ 47501e04c3fSmrg break; 47601e04c3fSmrg case strength_continue: 47701e04c3fSmrg lower = lower_continue; 47801e04c3fSmrg break; 47901e04c3fSmrg case strength_break: 48001e04c3fSmrg assert(this->loop.loop); 48101e04c3fSmrg /* never lower "canonical break" */ 48201e04c3fSmrg if(ir->get_next()->is_tail_sentinel() && (this->loop.nesting_depth == 0 48301e04c3fSmrg || (this->loop.nesting_depth == 1 && this->loop.in_if_at_the_end_of_the_loop))) 48401e04c3fSmrg lower = false; 48501e04c3fSmrg else 48601e04c3fSmrg lower = lower_break; 48701e04c3fSmrg break; 48801e04c3fSmrg case strength_return: 48901e04c3fSmrg /* never lower return at the end of a this->function */ 49001e04c3fSmrg if(this->function.nesting_depth == 0 && ir->get_next()->is_tail_sentinel()) 49101e04c3fSmrg lower = false; 49201e04c3fSmrg else 49301e04c3fSmrg lower = this->function.lower_return; 49401e04c3fSmrg break; 49501e04c3fSmrg } 49601e04c3fSmrg return lower; 49701e04c3fSmrg } 49801e04c3fSmrg 49901e04c3fSmrg block_record visit_block(exec_list* list) 50001e04c3fSmrg { 50101e04c3fSmrg /* Note: since visiting a node may change that node's next 50201e04c3fSmrg * pointer, we can't use visit_exec_list(), because 50301e04c3fSmrg * visit_exec_list() caches the node's next pointer before 50401e04c3fSmrg * visiting it. So we use foreach_in_list() instead. 50501e04c3fSmrg * 50601e04c3fSmrg * foreach_in_list() isn't safe if the node being visited gets 50701e04c3fSmrg * removed, but fortunately this visitor doesn't do that. 50801e04c3fSmrg */ 50901e04c3fSmrg 51001e04c3fSmrg block_record saved_block = this->block; 51101e04c3fSmrg this->block = block_record(); 51201e04c3fSmrg foreach_in_list(ir_instruction, node, list) { 51301e04c3fSmrg node->accept(this); 51401e04c3fSmrg } 51501e04c3fSmrg block_record ret = this->block; 51601e04c3fSmrg this->block = saved_block; 51701e04c3fSmrg return ret; 51801e04c3fSmrg } 51901e04c3fSmrg 52001e04c3fSmrg virtual void visit(ir_if *ir) 52101e04c3fSmrg { 52201e04c3fSmrg if(this->loop.nesting_depth == 0 && ir->get_next()->is_tail_sentinel()) 52301e04c3fSmrg this->loop.in_if_at_the_end_of_the_loop = true; 52401e04c3fSmrg 52501e04c3fSmrg ++this->function.nesting_depth; 52601e04c3fSmrg ++this->loop.nesting_depth; 52701e04c3fSmrg 52801e04c3fSmrg block_record block_records[2]; 52901e04c3fSmrg ir_jump* jumps[2]; 53001e04c3fSmrg 53101e04c3fSmrg /* Recursively lower nested jumps. This satisfies the 53201e04c3fSmrg * CONTAINED_JUMPS_LOWERED postcondition, except in the case of 53301e04c3fSmrg * unconditional jumps at the end of ir->then_instructions and 53401e04c3fSmrg * ir->else_instructions, which are handled below. 53501e04c3fSmrg */ 53601e04c3fSmrg block_records[0] = visit_block(&ir->then_instructions); 53701e04c3fSmrg block_records[1] = visit_block(&ir->else_instructions); 53801e04c3fSmrg 53901e04c3fSmrgretry: /* we get here if we put code after the if inside a branch */ 54001e04c3fSmrg 54101e04c3fSmrg /* Determine which of ir->then_instructions and 54201e04c3fSmrg * ir->else_instructions end with an unconditional jump. 54301e04c3fSmrg */ 54401e04c3fSmrg for(unsigned i = 0; i < 2; ++i) { 54501e04c3fSmrg exec_list& list = i ? ir->else_instructions : ir->then_instructions; 54601e04c3fSmrg jumps[i] = 0; 54701e04c3fSmrg if(!list.is_empty() && get_jump_strength((ir_instruction*)list.get_tail())) 54801e04c3fSmrg jumps[i] = (ir_jump*)list.get_tail(); 54901e04c3fSmrg } 55001e04c3fSmrg 55101e04c3fSmrg /* Loop until we have satisfied the CONTAINED_JUMPS_LOWERED 55201e04c3fSmrg * postcondition by lowering jumps in both then_instructions and 55301e04c3fSmrg * else_instructions. 55401e04c3fSmrg */ 55501e04c3fSmrg for(;;) { 55601e04c3fSmrg /* Determine the types of the jumps that terminate 55701e04c3fSmrg * ir->then_instructions and ir->else_instructions. 55801e04c3fSmrg */ 55901e04c3fSmrg jump_strength jump_strengths[2]; 56001e04c3fSmrg 56101e04c3fSmrg for(unsigned i = 0; i < 2; ++i) { 56201e04c3fSmrg if(jumps[i]) { 56301e04c3fSmrg jump_strengths[i] = block_records[i].min_strength; 56401e04c3fSmrg assert(jump_strengths[i] == get_jump_strength(jumps[i])); 56501e04c3fSmrg } else 56601e04c3fSmrg jump_strengths[i] = strength_none; 56701e04c3fSmrg } 56801e04c3fSmrg 56901e04c3fSmrg /* If both code paths end in a jump, and the jumps are the 57001e04c3fSmrg * same, and we are pulling out jumps, replace them with a 57101e04c3fSmrg * single jump that comes after the if instruction. The new 57201e04c3fSmrg * jump will be visited next, and it will be lowered if 57301e04c3fSmrg * necessary by the loop or conditional that encloses it. 57401e04c3fSmrg */ 57501e04c3fSmrg if(pull_out_jumps && jump_strengths[0] == jump_strengths[1]) { 57601e04c3fSmrg bool unify = true; 57701e04c3fSmrg if(jump_strengths[0] == strength_continue) 57801e04c3fSmrg ir->insert_after(new(ir) ir_loop_jump(ir_loop_jump::jump_continue)); 57901e04c3fSmrg else if(jump_strengths[0] == strength_break) 58001e04c3fSmrg ir->insert_after(new(ir) ir_loop_jump(ir_loop_jump::jump_break)); 58101e04c3fSmrg /* FINISHME: unify returns with identical expressions */ 58201e04c3fSmrg else if(jump_strengths[0] == strength_return && this->function.signature->return_type->is_void()) 58301e04c3fSmrg ir->insert_after(new(ir) ir_return(NULL)); 58401e04c3fSmrg else 58501e04c3fSmrg unify = false; 58601e04c3fSmrg 58701e04c3fSmrg if(unify) { 58801e04c3fSmrg jumps[0]->remove(); 58901e04c3fSmrg jumps[1]->remove(); 59001e04c3fSmrg this->progress = true; 59101e04c3fSmrg 59201e04c3fSmrg /* Update jumps[] to reflect the fact that the jumps 59301e04c3fSmrg * are gone, and update block_records[] to reflect the 59401e04c3fSmrg * fact that control can now flow to the next 59501e04c3fSmrg * instruction. 59601e04c3fSmrg */ 59701e04c3fSmrg jumps[0] = 0; 59801e04c3fSmrg jumps[1] = 0; 59901e04c3fSmrg block_records[0].min_strength = strength_none; 60001e04c3fSmrg block_records[1].min_strength = strength_none; 60101e04c3fSmrg 60201e04c3fSmrg /* The CONTAINED_JUMPS_LOWERED postcondition is now 60301e04c3fSmrg * satisfied, so we can break out of the loop. 60401e04c3fSmrg */ 60501e04c3fSmrg break; 60601e04c3fSmrg } 60701e04c3fSmrg } 60801e04c3fSmrg 60901e04c3fSmrg /* lower a jump: if both need to lowered, start with the strongest one, so that 61001e04c3fSmrg * we might later unify the lowered version with the other one 61101e04c3fSmrg */ 61201e04c3fSmrg bool should_lower[2]; 61301e04c3fSmrg for(unsigned i = 0; i < 2; ++i) 61401e04c3fSmrg should_lower[i] = should_lower_jump(jumps[i]); 61501e04c3fSmrg 61601e04c3fSmrg int lower; 61701e04c3fSmrg if(should_lower[1] && should_lower[0]) 61801e04c3fSmrg lower = jump_strengths[1] > jump_strengths[0]; 61901e04c3fSmrg else if(should_lower[0]) 62001e04c3fSmrg lower = 0; 62101e04c3fSmrg else if(should_lower[1]) 62201e04c3fSmrg lower = 1; 62301e04c3fSmrg else 62401e04c3fSmrg /* Neither code path ends in a jump that needs to be 62501e04c3fSmrg * lowered, so the CONTAINED_JUMPS_LOWERED postcondition 62601e04c3fSmrg * is satisfied and we can break out of the loop. 62701e04c3fSmrg */ 62801e04c3fSmrg break; 62901e04c3fSmrg 63001e04c3fSmrg if(jump_strengths[lower] == strength_return) { 63101e04c3fSmrg /* To lower a return, we create a return flag (if the 63201e04c3fSmrg * function doesn't have one already) and add instructions 63301e04c3fSmrg * that: 1. store the return value (if this function has a 63401e04c3fSmrg * non-void return) and 2. set the return flag 63501e04c3fSmrg */ 63601e04c3fSmrg insert_lowered_return((ir_return*)jumps[lower]); 63701e04c3fSmrg if(this->loop.loop) { 63801e04c3fSmrg /* If we are in a loop, replace the return instruction 63901e04c3fSmrg * with a break instruction, and then loop so that the 64001e04c3fSmrg * break instruction can be lowered if necessary. 64101e04c3fSmrg */ 64201e04c3fSmrg ir_loop_jump* lowered = 0; 64301e04c3fSmrg lowered = new(ir) ir_loop_jump(ir_loop_jump::jump_break); 64401e04c3fSmrg /* Note: we must update block_records and jumps to 64501e04c3fSmrg * reflect the fact that the control path has been 64601e04c3fSmrg * altered from a return to a break. 64701e04c3fSmrg */ 64801e04c3fSmrg block_records[lower].min_strength = strength_break; 64901e04c3fSmrg jumps[lower]->replace_with(lowered); 65001e04c3fSmrg jumps[lower] = lowered; 65101e04c3fSmrg } else { 65201e04c3fSmrg /* If we are not in a loop, we then proceed as we would 65301e04c3fSmrg * for a continue statement (set the execute flag to 65401e04c3fSmrg * false to prevent the rest of the function from 65501e04c3fSmrg * executing). 65601e04c3fSmrg */ 65701e04c3fSmrg goto lower_continue; 65801e04c3fSmrg } 65901e04c3fSmrg this->progress = true; 66001e04c3fSmrg } else if(jump_strengths[lower] == strength_break) { 66101e04c3fSmrg /* To lower a break, we create a break flag (if the loop 66201e04c3fSmrg * doesn't have one already) and add an instruction that 66301e04c3fSmrg * sets it. 66401e04c3fSmrg * 66501e04c3fSmrg * Then we proceed as we would for a continue statement 66601e04c3fSmrg * (set the execute flag to false to prevent the rest of 66701e04c3fSmrg * the loop body from executing). 66801e04c3fSmrg * 66901e04c3fSmrg * The visit() function for the loop will ensure that the 67001e04c3fSmrg * break flag is checked after executing the loop body. 67101e04c3fSmrg */ 67201e04c3fSmrg jumps[lower]->insert_before(create_lowered_break()); 67301e04c3fSmrg goto lower_continue; 67401e04c3fSmrg } else if(jump_strengths[lower] == strength_continue) { 67501e04c3fSmrglower_continue: 67601e04c3fSmrg /* To lower a continue, we create an execute flag (if the 67701e04c3fSmrg * loop doesn't have one already) and replace the continue 67801e04c3fSmrg * with an instruction that clears it. 67901e04c3fSmrg * 68001e04c3fSmrg * Note that this code path gets exercised when lowering 68101e04c3fSmrg * return statements that are not inside a loop, so 68201e04c3fSmrg * this->loop must be initialized even outside of loops. 68301e04c3fSmrg */ 68401e04c3fSmrg ir_variable* execute_flag = this->loop.get_execute_flag(); 68501e04c3fSmrg jumps[lower]->replace_with(new(ir) ir_assignment(new (ir) ir_dereference_variable(execute_flag), new (ir) ir_constant(false))); 68601e04c3fSmrg /* Note: we must update block_records and jumps to reflect 68701e04c3fSmrg * the fact that the control path has been altered to an 68801e04c3fSmrg * instruction that clears the execute flag. 68901e04c3fSmrg */ 69001e04c3fSmrg jumps[lower] = 0; 69101e04c3fSmrg block_records[lower].min_strength = strength_always_clears_execute_flag; 69201e04c3fSmrg block_records[lower].may_clear_execute_flag = true; 69301e04c3fSmrg this->progress = true; 69401e04c3fSmrg 69501e04c3fSmrg /* Let the loop run again, in case the other branch of the 69601e04c3fSmrg * if needs to be lowered too. 69701e04c3fSmrg */ 69801e04c3fSmrg } 69901e04c3fSmrg } 70001e04c3fSmrg 70101e04c3fSmrg /* move out a jump out if possible */ 70201e04c3fSmrg if(pull_out_jumps) { 70301e04c3fSmrg /* If one of the branches ends in a jump, and control cannot 70401e04c3fSmrg * fall out the bottom of the other branch, then we can move 70501e04c3fSmrg * the jump after the if. 70601e04c3fSmrg * 70701e04c3fSmrg * Set move_out to the branch we are moving a jump out of. 70801e04c3fSmrg */ 70901e04c3fSmrg int move_out = -1; 71001e04c3fSmrg if(jumps[0] && block_records[1].min_strength >= strength_continue) 71101e04c3fSmrg move_out = 0; 71201e04c3fSmrg else if(jumps[1] && block_records[0].min_strength >= strength_continue) 71301e04c3fSmrg move_out = 1; 71401e04c3fSmrg 71501e04c3fSmrg if(move_out >= 0) 71601e04c3fSmrg { 71701e04c3fSmrg jumps[move_out]->remove(); 71801e04c3fSmrg ir->insert_after(jumps[move_out]); 71901e04c3fSmrg /* Note: we must update block_records and jumps to reflect 72001e04c3fSmrg * the fact that the jump has been moved out of the if. 72101e04c3fSmrg */ 72201e04c3fSmrg jumps[move_out] = 0; 72301e04c3fSmrg block_records[move_out].min_strength = strength_none; 72401e04c3fSmrg this->progress = true; 72501e04c3fSmrg } 72601e04c3fSmrg } 72701e04c3fSmrg 72801e04c3fSmrg /* Now satisfy the ANALYSIS postcondition by setting 72901e04c3fSmrg * this->block.min_strength and 73001e04c3fSmrg * this->block.may_clear_execute_flag based on the 73101e04c3fSmrg * characteristics of the two branches. 73201e04c3fSmrg */ 73301e04c3fSmrg if(block_records[0].min_strength < block_records[1].min_strength) 73401e04c3fSmrg this->block.min_strength = block_records[0].min_strength; 73501e04c3fSmrg else 73601e04c3fSmrg this->block.min_strength = block_records[1].min_strength; 73701e04c3fSmrg this->block.may_clear_execute_flag = this->block.may_clear_execute_flag || block_records[0].may_clear_execute_flag || block_records[1].may_clear_execute_flag; 73801e04c3fSmrg 73901e04c3fSmrg /* Now we need to clean up the instructions that follow the 74001e04c3fSmrg * if. 74101e04c3fSmrg * 74201e04c3fSmrg * If those instructions are unreachable, then satisfy the 74301e04c3fSmrg * DEAD_CODE_ELIMINATION postcondition by eliminating them. 74401e04c3fSmrg * Otherwise that postcondition is already satisfied. 74501e04c3fSmrg */ 74601e04c3fSmrg if(this->block.min_strength) 74701e04c3fSmrg truncate_after_instruction(ir); 74801e04c3fSmrg else if(this->block.may_clear_execute_flag) 74901e04c3fSmrg { 75001e04c3fSmrg /* If the "if" instruction might clear the execute flag, then 75101e04c3fSmrg * we need to guard any instructions that follow so that they 75201e04c3fSmrg * are only executed if the execute flag is set. 75301e04c3fSmrg * 75401e04c3fSmrg * If one of the branches of the "if" always clears the 75501e04c3fSmrg * execute flag, and the other branch never clears it, then 75601e04c3fSmrg * this is easy: just move all the instructions following the 75701e04c3fSmrg * "if" into the branch that never clears it. 75801e04c3fSmrg */ 75901e04c3fSmrg int move_into = -1; 76001e04c3fSmrg if(block_records[0].min_strength && !block_records[1].may_clear_execute_flag) 76101e04c3fSmrg move_into = 1; 76201e04c3fSmrg else if(block_records[1].min_strength && !block_records[0].may_clear_execute_flag) 76301e04c3fSmrg move_into = 0; 76401e04c3fSmrg 76501e04c3fSmrg if(move_into >= 0) { 76601e04c3fSmrg assert(!block_records[move_into].min_strength && !block_records[move_into].may_clear_execute_flag); /* otherwise, we just truncated */ 76701e04c3fSmrg 76801e04c3fSmrg exec_list* list = move_into ? &ir->else_instructions : &ir->then_instructions; 76901e04c3fSmrg exec_node* next = ir->get_next(); 77001e04c3fSmrg if(!next->is_tail_sentinel()) { 77101e04c3fSmrg move_outer_block_inside(ir, list); 77201e04c3fSmrg 77301e04c3fSmrg /* If any instructions moved, then we need to visit 77401e04c3fSmrg * them (since they are now inside the "if"). Since 77501e04c3fSmrg * block_records[move_into] is in its default state 77601e04c3fSmrg * (see assertion above), we can safely replace 77701e04c3fSmrg * block_records[move_into] with the result of this 77801e04c3fSmrg * analysis. 77901e04c3fSmrg */ 78001e04c3fSmrg exec_list list; 78101e04c3fSmrg list.head_sentinel.next = next; 78201e04c3fSmrg block_records[move_into] = visit_block(&list); 78301e04c3fSmrg 78401e04c3fSmrg /* 78501e04c3fSmrg * Then we need to re-start our jump lowering, since one 78601e04c3fSmrg * of the instructions we moved might be a jump that 78701e04c3fSmrg * needs to be lowered. 78801e04c3fSmrg */ 78901e04c3fSmrg this->progress = true; 79001e04c3fSmrg goto retry; 79101e04c3fSmrg } 79201e04c3fSmrg } else { 79301e04c3fSmrg /* If we get here, then the simple case didn't apply; we 79401e04c3fSmrg * need to actually guard the instructions that follow. 79501e04c3fSmrg * 79601e04c3fSmrg * To avoid creating unnecessarily-deep nesting, first 79701e04c3fSmrg * look through the instructions that follow and unwrap 79801e04c3fSmrg * any instructions that that are already wrapped in the 79901e04c3fSmrg * appropriate guard. 80001e04c3fSmrg */ 80101e04c3fSmrg ir_instruction* ir_after; 80201e04c3fSmrg for(ir_after = (ir_instruction*)ir->get_next(); !ir_after->is_tail_sentinel();) 80301e04c3fSmrg { 80401e04c3fSmrg ir_if* ir_if = ir_after->as_if(); 80501e04c3fSmrg if(ir_if && ir_if->else_instructions.is_empty()) { 80601e04c3fSmrg ir_dereference_variable* ir_if_cond_deref = ir_if->condition->as_dereference_variable(); 80701e04c3fSmrg if(ir_if_cond_deref && ir_if_cond_deref->var == this->loop.execute_flag) { 80801e04c3fSmrg ir_instruction* ir_next = (ir_instruction*)ir_after->get_next(); 80901e04c3fSmrg ir_after->insert_before(&ir_if->then_instructions); 81001e04c3fSmrg ir_after->remove(); 81101e04c3fSmrg ir_after = ir_next; 81201e04c3fSmrg continue; 81301e04c3fSmrg } 81401e04c3fSmrg } 81501e04c3fSmrg ir_after = (ir_instruction*)ir_after->get_next(); 81601e04c3fSmrg 81701e04c3fSmrg /* only set this if we find any unprotected instruction */ 81801e04c3fSmrg this->progress = true; 81901e04c3fSmrg } 82001e04c3fSmrg 82101e04c3fSmrg /* Then, wrap all the instructions that follow in a single 82201e04c3fSmrg * guard. 82301e04c3fSmrg */ 82401e04c3fSmrg if(!ir->get_next()->is_tail_sentinel()) { 82501e04c3fSmrg assert(this->loop.execute_flag); 82601e04c3fSmrg ir_if* if_execute = new(ir) ir_if(new(ir) ir_dereference_variable(this->loop.execute_flag)); 82701e04c3fSmrg move_outer_block_inside(ir, &if_execute->then_instructions); 82801e04c3fSmrg ir->insert_after(if_execute); 82901e04c3fSmrg } 83001e04c3fSmrg } 83101e04c3fSmrg } 83201e04c3fSmrg --this->loop.nesting_depth; 83301e04c3fSmrg --this->function.nesting_depth; 83401e04c3fSmrg } 83501e04c3fSmrg 83601e04c3fSmrg virtual void visit(ir_loop *ir) 83701e04c3fSmrg { 83801e04c3fSmrg /* Visit the body of the loop, with a fresh data structure in 83901e04c3fSmrg * this->loop so that the analysis we do here won't bleed into 84001e04c3fSmrg * enclosing loops. 84101e04c3fSmrg * 84201e04c3fSmrg * We assume that all code after a loop is reachable from the 84301e04c3fSmrg * loop (see comments on enum jump_strength), so the 84401e04c3fSmrg * DEAD_CODE_ELIMINATION postcondition is automatically 84501e04c3fSmrg * satisfied, as is the block.min_strength portion of the 84601e04c3fSmrg * ANALYSIS postcondition. 84701e04c3fSmrg * 84801e04c3fSmrg * The block.may_clear_execute_flag portion of the ANALYSIS 84901e04c3fSmrg * postcondition is automatically satisfied because execute 85001e04c3fSmrg * flags do not propagate outside of loops. 85101e04c3fSmrg * 85201e04c3fSmrg * The loop.may_set_return_flag portion of the ANALYSIS 85301e04c3fSmrg * postcondition is handled below. 85401e04c3fSmrg */ 85501e04c3fSmrg ++this->function.nesting_depth; 85601e04c3fSmrg loop_record saved_loop = this->loop; 85701e04c3fSmrg this->loop = loop_record(this->function.signature, ir); 85801e04c3fSmrg 85901e04c3fSmrg /* Recursively lower nested jumps. This satisfies the 86001e04c3fSmrg * CONTAINED_JUMPS_LOWERED postcondition, except in the case of 86101e04c3fSmrg * an unconditional continue or return at the bottom of the 86201e04c3fSmrg * loop, which are handled below. 86301e04c3fSmrg */ 86401e04c3fSmrg block_record body = visit_block(&ir->body_instructions); 86501e04c3fSmrg 86601e04c3fSmrg /* If the loop ends in an unconditional continue, eliminate it 86701e04c3fSmrg * because it is redundant. 86801e04c3fSmrg */ 86901e04c3fSmrg ir_instruction *ir_last 87001e04c3fSmrg = (ir_instruction *) ir->body_instructions.get_tail(); 87101e04c3fSmrg if (get_jump_strength(ir_last) == strength_continue) { 87201e04c3fSmrg ir_last->remove(); 87301e04c3fSmrg } 87401e04c3fSmrg 87501e04c3fSmrg /* If the loop ends in an unconditional return, and we are 87601e04c3fSmrg * lowering returns, lower it. 87701e04c3fSmrg */ 87801e04c3fSmrg if (this->function.lower_return) 87901e04c3fSmrg lower_return_unconditionally(ir_last); 88001e04c3fSmrg 88101e04c3fSmrg if(body.min_strength >= strength_break) { 88201e04c3fSmrg /* FINISHME: If the min_strength of the loop body is 88301e04c3fSmrg * strength_break or strength_return, that means that it 88401e04c3fSmrg * isn't a loop at all, since control flow always leaves the 88501e04c3fSmrg * body of the loop via break or return. In principle the 88601e04c3fSmrg * loop could be eliminated in this case. This optimization 88701e04c3fSmrg * is not implemented yet. 88801e04c3fSmrg */ 88901e04c3fSmrg } 89001e04c3fSmrg 89101e04c3fSmrg if(this->loop.break_flag) { 89201e04c3fSmrg /* We only get here if we are lowering breaks */ 89301e04c3fSmrg assert (lower_break); 89401e04c3fSmrg 89501e04c3fSmrg /* If a break flag was generated while visiting the body of 89601e04c3fSmrg * the loop, then at least one break was lowered, so we need 89701e04c3fSmrg * to generate an if statement at the end of the loop that 89801e04c3fSmrg * does a "break" if the break flag is set. The break we 89901e04c3fSmrg * generate won't violate the CONTAINED_JUMPS_LOWERED 90001e04c3fSmrg * postcondition, because should_lower_jump() always returns 90101e04c3fSmrg * false for a break that happens at the end of a loop. 90201e04c3fSmrg * 90301e04c3fSmrg * However, if the loop already ends in a conditional or 90401e04c3fSmrg * unconditional break, then we need to lower that break, 90501e04c3fSmrg * because it won't be at the end of the loop anymore. 90601e04c3fSmrg */ 90701e04c3fSmrg lower_final_breaks(&ir->body_instructions); 90801e04c3fSmrg 90901e04c3fSmrg ir_if* break_if = new(ir) ir_if(new(ir) ir_dereference_variable(this->loop.break_flag)); 91001e04c3fSmrg break_if->then_instructions.push_tail(new(ir) ir_loop_jump(ir_loop_jump::jump_break)); 91101e04c3fSmrg ir->body_instructions.push_tail(break_if); 91201e04c3fSmrg } 91301e04c3fSmrg 91401e04c3fSmrg /* If the body of the loop may set the return flag, then at 91501e04c3fSmrg * least one return was lowered to a break, so we need to ensure 91601e04c3fSmrg * that the return flag is checked after the body of the loop is 91701e04c3fSmrg * executed. 91801e04c3fSmrg */ 91901e04c3fSmrg if(this->loop.may_set_return_flag) { 92001e04c3fSmrg assert(this->function.return_flag); 92101e04c3fSmrg /* Generate the if statement to check the return flag */ 92201e04c3fSmrg ir_if* return_if = new(ir) ir_if(new(ir) ir_dereference_variable(this->function.return_flag)); 92301e04c3fSmrg /* Note: we also need to propagate the knowledge that the 92401e04c3fSmrg * return flag may get set to the outer context. This 92501e04c3fSmrg * satisfies the loop.may_set_return_flag part of the 92601e04c3fSmrg * ANALYSIS postcondition. 92701e04c3fSmrg */ 92801e04c3fSmrg saved_loop.may_set_return_flag = true; 92901e04c3fSmrg if(saved_loop.loop) 93001e04c3fSmrg /* If this loop is nested inside another one, then the if 93101e04c3fSmrg * statement that we generated should break out of that 93201e04c3fSmrg * loop if the return flag is set. Caller will lower that 93301e04c3fSmrg * break statement if necessary. 93401e04c3fSmrg */ 93501e04c3fSmrg return_if->then_instructions.push_tail(new(ir) ir_loop_jump(ir_loop_jump::jump_break)); 93601e04c3fSmrg else { 93701e04c3fSmrg /* Otherwise, ensure that the instructions that follow are only 93801e04c3fSmrg * executed if the return flag is clear. We can do that by moving 93901e04c3fSmrg * those instructions into the else clause of the generated if 94001e04c3fSmrg * statement. 94101e04c3fSmrg */ 94201e04c3fSmrg move_outer_block_inside(ir, &return_if->else_instructions); 94301e04c3fSmrg 94401e04c3fSmrg /* In case the loop is embedded inside an if add a new return to 94501e04c3fSmrg * the return flag then branch and let a future pass tidy it up. 94601e04c3fSmrg */ 94701e04c3fSmrg if (this->function.signature->return_type->is_void()) 94801e04c3fSmrg return_if->then_instructions.push_tail(new(ir) ir_return(NULL)); 94901e04c3fSmrg else { 95001e04c3fSmrg assert(this->function.return_value); 95101e04c3fSmrg ir_variable* return_value = this->function.return_value; 95201e04c3fSmrg return_if->then_instructions.push_tail( 95301e04c3fSmrg new(ir) ir_return(new(ir) ir_dereference_variable(return_value))); 95401e04c3fSmrg } 95501e04c3fSmrg } 95601e04c3fSmrg 95701e04c3fSmrg ir->insert_after(return_if); 95801e04c3fSmrg } 95901e04c3fSmrg 96001e04c3fSmrg this->loop = saved_loop; 96101e04c3fSmrg --this->function.nesting_depth; 96201e04c3fSmrg } 96301e04c3fSmrg 96401e04c3fSmrg virtual void visit(ir_function_signature *ir) 96501e04c3fSmrg { 96601e04c3fSmrg /* these are not strictly necessary */ 96701e04c3fSmrg assert(!this->function.signature); 96801e04c3fSmrg assert(!this->loop.loop); 96901e04c3fSmrg 97001e04c3fSmrg bool lower_return; 97101e04c3fSmrg if (strcmp(ir->function_name(), "main") == 0) 97201e04c3fSmrg lower_return = lower_main_return; 97301e04c3fSmrg else 97401e04c3fSmrg lower_return = lower_sub_return; 97501e04c3fSmrg 97601e04c3fSmrg function_record saved_function = this->function; 97701e04c3fSmrg loop_record saved_loop = this->loop; 97801e04c3fSmrg this->function = function_record(ir, lower_return); 97901e04c3fSmrg this->loop = loop_record(ir); 98001e04c3fSmrg 98101e04c3fSmrg assert(!this->loop.loop); 98201e04c3fSmrg 98301e04c3fSmrg /* Visit the body of the function to lower any jumps that occur 98401e04c3fSmrg * in it, except possibly an unconditional return statement at 98501e04c3fSmrg * the end of it. 98601e04c3fSmrg */ 98701e04c3fSmrg visit_block(&ir->body); 98801e04c3fSmrg 98901e04c3fSmrg /* If the body ended in an unconditional return of non-void, 99001e04c3fSmrg * then we don't need to lower it because it's the one canonical 99101e04c3fSmrg * return. 99201e04c3fSmrg * 99301e04c3fSmrg * If the body ended in a return of void, eliminate it because 99401e04c3fSmrg * it is redundant. 99501e04c3fSmrg */ 99601e04c3fSmrg if (ir->return_type->is_void() && 99701e04c3fSmrg get_jump_strength((ir_instruction *) ir->body.get_tail())) { 99801e04c3fSmrg ir_jump *jump = (ir_jump *) ir->body.get_tail(); 99901e04c3fSmrg assert (jump->ir_type == ir_type_return); 100001e04c3fSmrg jump->remove(); 100101e04c3fSmrg } 100201e04c3fSmrg 100301e04c3fSmrg if(this->function.return_value) 100401e04c3fSmrg ir->body.push_tail(new(ir) ir_return(new (ir) ir_dereference_variable(this->function.return_value))); 100501e04c3fSmrg 100601e04c3fSmrg this->loop = saved_loop; 100701e04c3fSmrg this->function = saved_function; 100801e04c3fSmrg } 100901e04c3fSmrg 101001e04c3fSmrg virtual void visit(class ir_function * ir) 101101e04c3fSmrg { 101201e04c3fSmrg visit_block(&ir->signatures); 101301e04c3fSmrg } 101401e04c3fSmrg}; 101501e04c3fSmrg 101601e04c3fSmrg} /* anonymous namespace */ 101701e04c3fSmrg 101801e04c3fSmrgbool 101901e04c3fSmrgdo_lower_jumps(exec_list *instructions, bool pull_out_jumps, bool lower_sub_return, bool lower_main_return, bool lower_continue, bool lower_break) 102001e04c3fSmrg{ 102101e04c3fSmrg ir_lower_jumps_visitor v; 102201e04c3fSmrg v.pull_out_jumps = pull_out_jumps; 102301e04c3fSmrg v.lower_continue = lower_continue; 102401e04c3fSmrg v.lower_break = lower_break; 102501e04c3fSmrg v.lower_sub_return = lower_sub_return; 102601e04c3fSmrg v.lower_main_return = lower_main_return; 102701e04c3fSmrg 102801e04c3fSmrg bool progress_ever = false; 102901e04c3fSmrg do { 103001e04c3fSmrg v.progress = false; 103101e04c3fSmrg visit_exec_list(instructions, &v); 103201e04c3fSmrg progress_ever = v.progress || progress_ever; 103301e04c3fSmrg } while (v.progress); 103401e04c3fSmrg 103501e04c3fSmrg return progress_ever; 103601e04c3fSmrg} 1037