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