summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2010-07-29 12:36:06 -0700
committerEric Anholt <eric@anholt.net>2010-07-29 15:19:37 -0700
commit18964618a14996fff7ab5a5db75c85cd64865ef4 (patch)
treeb5e84939bcb8b2410b14fa245a5a72b32c78ecca
parent92a3768cef122c699b0121ab168def6eaa0faa7a (diff)
glsl2: Make ir_if_return handle if () { return } else { not return }
This makes many remaining functions inlinable. Fixes for i965: glsl1-function with early return (1) glsl1-function with early return (2)
-rw-r--r--src/glsl/ir_if_return.cpp103
1 files changed, 77 insertions, 26 deletions
diff --git a/src/glsl/ir_if_return.cpp b/src/glsl/ir_if_return.cpp
index 8b8ff85d82..4d59e70786 100644
--- a/src/glsl/ir_if_return.cpp
+++ b/src/glsl/ir_if_return.cpp
@@ -24,15 +24,11 @@
/**
* \file ir_if_return.cpp
*
- * If a function includes an if statement that returns from both
- * branches, then make the branches write the return val to a temp and
- * return the temp after the if statement.
+ * This pass tries to normalize functions to always return from one place.
*
- * This allows inlinining in the common case of short functions that
- * return one of two values based on a condition. This helps on
- * hardware with no branching support, and may even be a useful
- * transform on hardware supporting control flow by masked returns
- * with normal returns.
+ * This helps on hardware with no branching support, and may even be a
+ * useful transform on hardware supporting control flow by turning
+ * masked returns into normal returns.
*/
#include "ir.h"
@@ -46,6 +42,11 @@ public:
ir_visitor_status visit_enter(ir_if *);
+ void move_outer_block_inside(ir_instruction *ir,
+ exec_list *inner_block);
+ void move_returns_after_block(ir_instruction *ir,
+ ir_return *then_return,
+ ir_return *else_return);
bool progress;
};
@@ -69,6 +70,9 @@ do_if_return(exec_list *instructions)
static void
truncate_after_instruction(ir_instruction *ir)
{
+ if (!ir)
+ return;
+
while (!ir->get_next()->is_tail_sentinel())
((ir_instruction *)ir->get_next())->remove();
}
@@ -88,24 +92,11 @@ find_return_in_block(exec_list *instructions)
return NULL;
}
-ir_visitor_status
-ir_if_return_visitor::visit_enter(ir_if *ir)
+void
+ir_if_return_visitor::move_returns_after_block(ir_instruction *ir,
+ ir_return *then_return,
+ ir_return *else_return)
{
- ir_return *then_return = NULL;
- ir_return *else_return = NULL;
-
- then_return = find_return_in_block(&ir->then_instructions);
- else_return = find_return_in_block(&ir->else_instructions);
- if (!then_return || !else_return)
- return visit_continue;
-
- /* Trim off any trailing instructions after the return statements
- * on both sides.
- */
- truncate_after_instruction(then_return);
- truncate_after_instruction(else_return);
-
- this->progress = true;
if (!then_return->value) {
then_return->remove();
@@ -129,6 +120,66 @@ ir_if_return_visitor::visit_enter(ir_if *ir)
ir_dereference_variable *deref = new(ir) ir_dereference_variable(new_var);
ir->insert_after(new(ir) ir_return(deref));
}
+ this->progress = true;
+}
+
+void
+ir_if_return_visitor::move_outer_block_inside(ir_instruction *ir,
+ exec_list *inner_block)
+{
+ if (!ir->get_next()->is_tail_sentinel())
+ this->progress = true;
+
+ while (!ir->get_next()->is_tail_sentinel()) {
+ ir_instruction *move_ir = (ir_instruction *)ir->get_next();
+
+ move_ir->remove();
+ inner_block->push_tail(move_ir);
+ }
+}
+
+ir_visitor_status
+ir_if_return_visitor::visit_enter(ir_if *ir)
+{
+ ir_return *then_return;
+ ir_return *else_return;
+
+ then_return = find_return_in_block(&ir->then_instructions);
+ else_return = find_return_in_block(&ir->else_instructions);
+ if (!then_return && !else_return)
+ return visit_continue;
+
+ /* Trim off any trailing instructions after the return statements
+ * on both sides.
+ */
+ truncate_after_instruction(then_return);
+ truncate_after_instruction(else_return);
+
+ /* If both sides return, then we can move the returns to a single
+ * one outside the if statement.
+ */
+ if (then_return && else_return) {
+ move_returns_after_block(ir, then_return, else_return);
+ return visit_continue;
+ }
- return visit_continue;
+ /* If only one side returns, then the block of code after the "if"
+ * is only executed by the other side, so those instructions don't
+ * need to be anywhere but that other side.
+ *
+ * This will usually pull a return statement up into the other
+ * side, so we'll trigger the above case on the next pass.
+ */
+ if (then_return) {
+ move_outer_block_inside(ir, &ir->else_instructions);
+ } else {
+ assert(else_return);
+ move_outer_block_inside(ir, &ir->then_instructions);
+ }
+
+ /* If we move the instructions following ir inside the block, it
+ * will confuse the exec_list iteration in the parent that visited
+ * us. So stop the visit at this point.
+ */
+ return visit_stop;
}