Files
third_party_mesa3d/src/glsl/loop_unroll.cpp
Eric Anholt 10ef949424 glsl: Hide many classes local to individual .cpp files in anon namespaces.
This gives the compiler the chance to inline and not export class symbols
even in the absence of LTO.  Saves about 60kb on disk.

Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Ian Romanick <ian.d.romanick@.intel.com>
2013-09-23 12:45:22 -07:00

257 lines
7.2 KiB
C++

/*
* Copyright © 2010 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "glsl_types.h"
#include "loop_analysis.h"
#include "ir_hierarchical_visitor.h"
namespace {
class loop_unroll_visitor : public ir_hierarchical_visitor {
public:
loop_unroll_visitor(loop_state *state, unsigned max_iterations)
{
this->state = state;
this->progress = false;
this->max_iterations = max_iterations;
}
virtual ir_visitor_status visit_leave(ir_loop *ir);
loop_state *state;
bool progress;
unsigned max_iterations;
};
} /* anonymous namespace */
static bool
is_break(ir_instruction *ir)
{
return ir != NULL && ir->ir_type == ir_type_loop_jump
&& ((ir_loop_jump *) ir)->is_break();
}
class loop_unroll_count : public ir_hierarchical_visitor {
public:
int nodes;
bool fail;
loop_unroll_count(exec_list *list)
{
nodes = 0;
fail = false;
run(list);
}
virtual ir_visitor_status visit_enter(ir_assignment *ir)
{
nodes++;
return visit_continue;
}
virtual ir_visitor_status visit_enter(ir_expression *ir)
{
nodes++;
return visit_continue;
}
virtual ir_visitor_status visit_enter(ir_loop *ir)
{
fail = true;
return visit_continue;
}
};
ir_visitor_status
loop_unroll_visitor::visit_leave(ir_loop *ir)
{
loop_variable_state *const ls = this->state->get(ir);
int iterations;
/* If we've entered a loop that hasn't been analyzed, something really,
* really bad has happened.
*/
if (ls == NULL) {
assert(ls != NULL);
return visit_continue;
}
iterations = ls->max_iterations;
/* Don't try to unroll loops where the number of iterations is not known
* at compile-time.
*/
if (iterations < 0)
return visit_continue;
/* Don't try to unroll loops that have zillions of iterations either.
*/
if (iterations > (int) max_iterations)
return visit_continue;
/* Don't try to unroll nested loops and loops with a huge body.
*/
loop_unroll_count count(&ir->body_instructions);
if (count.fail || count.nodes * iterations > (int)max_iterations * 5)
return visit_continue;
if (ls->num_loop_jumps > 1)
return visit_continue;
else if (ls->num_loop_jumps) {
ir_instruction *last_ir = (ir_instruction *) ir->body_instructions.get_tail();
assert(last_ir != NULL);
if (is_break(last_ir)) {
/* If the only loop-jump is a break at the end of the loop, the loop
* will execute exactly once. Remove the break, set the iteration
* count, and fall through to the normal unroller.
*/
last_ir->remove();
iterations = 1;
this->progress = true;
} else {
ir_if *ir_if = NULL;
ir_instruction *break_ir = NULL;
bool continue_from_then_branch = false;
foreach_list(node, &ir->body_instructions) {
/* recognize loops in the form produced by ir_lower_jumps */
ir_instruction *cur_ir = (ir_instruction *) node;
ir_if = cur_ir->as_if();
if (ir_if != NULL) {
/* Determine which if-statement branch, if any, ends with a
* break. The branch that did *not* have the break will get a
* temporary continue inserted in each iteration of the loop
* unroll.
*
* Note that since ls->num_loop_jumps is <= 1, it is impossible
* for both branches to end with a break.
*/
ir_instruction *ir_if_last =
(ir_instruction *) ir_if->then_instructions.get_tail();
if (is_break(ir_if_last)) {
continue_from_then_branch = false;
break_ir = ir_if_last;
break;
} else {
ir_if_last =
(ir_instruction *) ir_if->else_instructions.get_tail();
if (is_break(ir_if_last)) {
break_ir = ir_if_last;
continue_from_then_branch = true;
break;
}
}
}
}
if (break_ir == NULL)
return visit_continue;
/* move instructions after then if in the continue branch */
while (!ir_if->get_next()->is_tail_sentinel()) {
ir_instruction *move_ir = (ir_instruction *) ir_if->get_next();
move_ir->remove();
if (continue_from_then_branch)
ir_if->then_instructions.push_tail(move_ir);
else
ir_if->else_instructions.push_tail(move_ir);
}
/* Remove the break from the if-statement.
*/
break_ir->remove();
void *const mem_ctx = ralloc_parent(ir);
ir_instruction *ir_to_replace = ir;
for (int i = 0; i < iterations; i++) {
exec_list copy_list;
copy_list.make_empty();
clone_ir_list(mem_ctx, &copy_list, &ir->body_instructions);
ir_if = ((ir_instruction *) copy_list.get_tail())->as_if();
assert(ir_if != NULL);
ir_to_replace->insert_before(&copy_list);
ir_to_replace->remove();
/* placeholder that will be removed in the next iteration */
ir_to_replace =
new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_continue);
exec_list *const list = (continue_from_then_branch)
? &ir_if->then_instructions : &ir_if->else_instructions;
list->push_tail(ir_to_replace);
}
ir_to_replace->remove();
this->progress = true;
return visit_continue;
}
}
void *const mem_ctx = ralloc_parent(ir);
for (int i = 0; i < iterations; i++) {
exec_list copy_list;
copy_list.make_empty();
clone_ir_list(mem_ctx, &copy_list, &ir->body_instructions);
ir->insert_before(&copy_list);
}
/* The loop has been replaced by the unrolled copies. Remove the original
* loop from the IR sequence.
*/
ir->remove();
this->progress = true;
return visit_continue;
}
bool
unroll_loops(exec_list *instructions, loop_state *ls, unsigned max_iterations)
{
loop_unroll_visitor v(ls, max_iterations);
v.run(instructions);
return v.progress;
}