Home | History | Annotate | Download | only in glsl
      1 /*
      2  * Copyright  2010 Intel Corporation
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     21  * DEALINGS IN THE SOFTWARE.
     22  */
     23 
     24 /**
     25  * \file ir_if_return.cpp
     26  *
     27  * This pass tries to normalize functions to always return from one
     28  * place by moving around blocks of code in if statements.
     29  *
     30  * This helps on hardware with no branching support, and may even be a
     31  * useful transform on hardware supporting control flow by turning
     32  * masked returns into normal returns.
     33  */
     34 
     35 #include <string.h>
     36 #include "glsl_types.h"
     37 #include "ir.h"
     38 
     39 class ir_if_return_visitor : public ir_hierarchical_visitor {
     40 public:
     41    ir_if_return_visitor()
     42    {
     43       this->progress = false;
     44    }
     45 
     46    ir_visitor_status visit_enter(ir_function_signature *);
     47    ir_visitor_status visit_leave(ir_if *);
     48 
     49    ir_visitor_status move_outer_block_inside(ir_instruction *ir,
     50 					     exec_list *inner_block);
     51    void move_returns_after_block(ir_instruction *ir,
     52 				 ir_return *then_return,
     53 				 ir_return *else_return);
     54    bool progress;
     55 };
     56 
     57 bool
     58 do_if_return(exec_list *instructions)
     59 {
     60    ir_if_return_visitor v;
     61 
     62    do {
     63       v.progress = false;
     64       visit_list_elements(&v, instructions);
     65    } while (v.progress);
     66 
     67    return v.progress;
     68 }
     69 
     70 /**
     71  * Removes any instructions after a (unconditional) return, since they will
     72  * never be executed.
     73  */
     74 static void
     75 truncate_after_instruction(ir_instruction *ir)
     76 {
     77    if (!ir)
     78       return;
     79 
     80    while (!ir->get_next()->is_tail_sentinel())
     81       ((ir_instruction *)ir->get_next())->remove();
     82 }
     83 
     84 /**
     85  * Returns an ir_instruction of the first ir_return in the exec_list, or NULL.
     86  */
     87 static ir_return *
     88 find_return_in_block(exec_list *instructions)
     89 {
     90    foreach_iter(exec_list_iterator, iter, *instructions) {
     91       ir_instruction *ir = (ir_instruction *)iter.get();
     92       if (ir->ir_type == ir_type_return)
     93 	 return (ir_return *)ir;
     94    }
     95 
     96    return NULL;
     97 }
     98 
     99 void
    100 ir_if_return_visitor::move_returns_after_block(ir_instruction *ir,
    101 					       ir_return *then_return,
    102 					       ir_return *else_return)
    103 {
    104 
    105    if (!then_return->value) {
    106       then_return->remove();
    107       else_return->remove();
    108       ir->insert_after(new(ir) ir_return(NULL));
    109    } else {
    110       ir_assignment *assign;
    111       ir_variable *new_var = new(ir) ir_variable(then_return->value->type,
    112 						 "if_return_tmp",
    113 						 ir_var_temporary);
    114       ir->insert_before(new_var);
    115 
    116       assign = new(ir) ir_assignment(new(ir) ir_dereference_variable(new_var),
    117 				     then_return->value, NULL);
    118       then_return->replace_with(assign);
    119 
    120       assign = new(ir) ir_assignment(new(ir) ir_dereference_variable(new_var),
    121 				     else_return->value, NULL);
    122       else_return->replace_with(assign);
    123 
    124       ir_dereference_variable *deref = new(ir) ir_dereference_variable(new_var);
    125       ir->insert_after(new(ir) ir_return(deref));
    126    }
    127    this->progress = true;
    128 }
    129 
    130 ir_visitor_status
    131 ir_if_return_visitor::move_outer_block_inside(ir_instruction *ir,
    132 					      exec_list *inner_block)
    133 {
    134    if (!ir->get_next()->is_tail_sentinel()) {
    135       while (!ir->get_next()->is_tail_sentinel()) {
    136 	 ir_instruction *move_ir = (ir_instruction *)ir->get_next();
    137 
    138 	 move_ir->remove();
    139 	 inner_block->push_tail(move_ir);
    140       }
    141 
    142       /* If we move the instructions following ir inside the block, it
    143        * will confuse the exec_list iteration in the parent that visited
    144        * us.  So stop the visit at this point.
    145        */
    146       return visit_stop;
    147    } else {
    148       return visit_continue;
    149    }
    150 }
    151 
    152 /* Normalize a function to always have a return statement at the end.
    153  *
    154  * This avoids the ir_if handler needing to know whether it is at the
    155  * top level of the function to know if there's an implicit return at
    156  * the end of the outer block.
    157  */
    158 ir_visitor_status
    159 ir_if_return_visitor::visit_enter(ir_function_signature *ir)
    160 {
    161    ir_return *ret;
    162 
    163    if (!ir->is_defined)
    164       return visit_continue_with_parent;
    165    if (strcmp(ir->function_name(), "main") == 0)
    166       return visit_continue_with_parent;
    167 
    168    ret = find_return_in_block(&ir->body);
    169 
    170    if (ret) {
    171       truncate_after_instruction(ret);
    172    } else {
    173       if (ir->return_type->is_void()) {
    174 	 ir->body.push_tail(new(ir) ir_return(NULL));
    175       } else {
    176 	 /* Probably, if we've got a function with a return value
    177 	  * hitting this point, it's something like:
    178 	  *
    179 	  * float reduce_below_half(float val)
    180 	  * {
    181 	  *         while () {
    182 	  *                 if (val >= 0.5)
    183 	  *                         val /= 2.0;
    184 	  *                 else
    185 	  *                         return val;
    186 	  *         }
    187 	  * }
    188 	  *
    189 	  * So we gain a junk return statement of an undefined value
    190 	  * at the end that never gets executed.  However, a backend
    191 	  * using this pass is probably desperate to get rid of
    192 	  * function calls, so go ahead and do it for their sake in
    193 	  * case it fixes apps.
    194 	  */
    195 	 ir_variable *undef = new(ir) ir_variable(ir->return_type,
    196 						  "if_return_undef",
    197 						  ir_var_temporary);
    198 	 ir->body.push_tail(undef);
    199 
    200 	 ir_dereference_variable *deref = new(ir) ir_dereference_variable(undef);
    201 	 ir->body.push_tail(new(ir) ir_return(deref));
    202       }
    203    }
    204 
    205    return visit_continue;
    206 }
    207 
    208 ir_visitor_status
    209 ir_if_return_visitor::visit_leave(ir_if *ir)
    210 {
    211    ir_return *then_return;
    212    ir_return *else_return;
    213 
    214    then_return = find_return_in_block(&ir->then_instructions);
    215    else_return = find_return_in_block(&ir->else_instructions);
    216    if (!then_return && !else_return)
    217       return visit_continue;
    218 
    219    /* Trim off any trailing instructions after the return statements
    220     * on both sides.
    221     */
    222    truncate_after_instruction(then_return);
    223    truncate_after_instruction(else_return);
    224 
    225    /* If both sides return, then we can move the returns to a single
    226     * one outside the if statement.
    227     */
    228    if (then_return && else_return) {
    229       move_returns_after_block(ir, then_return, else_return);
    230       return visit_continue;
    231    }
    232 
    233    /* If only one side returns, then the block of code after the "if"
    234     * is only executed by the other side, so those instructions don't
    235     * need to be anywhere but that other side.
    236     *
    237     * This will usually pull a return statement up into the other
    238     * side, so we'll trigger the above case on the next pass.
    239     */
    240    if (then_return) {
    241       return move_outer_block_inside(ir, &ir->else_instructions);
    242    } else {
    243       assert(else_return);
    244       return move_outer_block_inside(ir, &ir->then_instructions);
    245    }
    246 }
    247