2 * Copyright © 2010 Intel Corporation
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:
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
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.
25 * \file ir_if_return.cpp
27 * This pass tries to normalize functions to always return from one
28 * place by moving around blocks of code in if statements.
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.
36 #include "glsl_types.h"
39 class ir_if_return_visitor
: public ir_hierarchical_visitor
{
41 ir_if_return_visitor()
43 this->progress
= false;
46 ir_visitor_status
visit_enter(ir_function_signature
*);
47 ir_visitor_status
visit_leave(ir_if
*);
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
);
58 do_if_return(exec_list
*instructions
)
60 ir_if_return_visitor v
;
64 visit_list_elements(&v
, instructions
);
71 * Removes any instructions after a (unconditional) return, since they will
75 truncate_after_instruction(ir_instruction
*ir
)
80 while (!ir
->get_next()->is_tail_sentinel())
81 ((ir_instruction
*)ir
->get_next())->remove();
85 * Returns an ir_instruction of the first ir_return in the exec_list, or NULL.
88 find_return_in_block(exec_list
*instructions
)
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
;
100 ir_if_return_visitor::move_returns_after_block(ir_instruction
*ir
,
101 ir_return
*then_return
,
102 ir_return
*else_return
)
105 if (!then_return
->value
) {
106 then_return
->remove();
107 else_return
->remove();
108 ir
->insert_after(new(ir
) ir_return(NULL
));
110 ir_assignment
*assign
;
111 ir_variable
*new_var
= new(ir
) ir_variable(then_return
->value
->type
,
114 ir
->insert_before(new_var
);
116 assign
= new(ir
) ir_assignment(new(ir
) ir_dereference_variable(new_var
),
117 then_return
->value
, NULL
);
118 then_return
->replace_with(assign
);
120 assign
= new(ir
) ir_assignment(new(ir
) ir_dereference_variable(new_var
),
121 else_return
->value
, NULL
);
122 else_return
->replace_with(assign
);
124 ir_dereference_variable
*deref
= new(ir
) ir_dereference_variable(new_var
);
125 ir
->insert_after(new(ir
) ir_return(deref
));
127 this->progress
= true;
131 ir_if_return_visitor::move_outer_block_inside(ir_instruction
*ir
,
132 exec_list
*inner_block
)
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();
139 inner_block
->push_tail(move_ir
);
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.
148 return visit_continue
;
152 /* Normalize a function to always have a return statement at the end.
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.
159 ir_if_return_visitor::visit_enter(ir_function_signature
*ir
)
164 return visit_continue_with_parent
;
165 if (strcmp(ir
->function_name(), "main") == 0)
166 return visit_continue_with_parent
;
168 ret
= find_return_in_block(&ir
->body
);
171 truncate_after_instruction(ret
);
173 if (ir
->return_type
->is_void()) {
174 ir
->body
.push_tail(new(ir
) ir_return(NULL
));
176 /* Probably, if we've got a function with a return value
177 * hitting this point, it's something like:
179 * float reduce_below_half(float val)
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.
195 ir_variable
*undef
= new(ir
) ir_variable(ir
->return_type
,
198 ir
->body
.push_tail(undef
);
200 ir_dereference_variable
*deref
= new(ir
) ir_dereference_variable(undef
);
201 ir
->body
.push_tail(new(ir
) ir_return(deref
));
205 return visit_continue
;
209 ir_if_return_visitor::visit_leave(ir_if
*ir
)
211 ir_return
*then_return
;
212 ir_return
*else_return
;
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
;
219 /* Trim off any trailing instructions after the return statements
222 truncate_after_instruction(then_return
);
223 truncate_after_instruction(else_return
);
225 /* If both sides return, then we can move the returns to a single
226 * one outside the if statement.
228 if (then_return
&& else_return
) {
229 move_returns_after_block(ir
, then_return
, else_return
);
230 return visit_continue
;
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.
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.
241 return move_outer_block_inside(ir
, &ir
->else_instructions
);
244 return move_outer_block_inside(ir
, &ir
->then_instructions
);