Commit e00b93a1 authored by Paul Berry's avatar Paul Berry

glsl/loops: replace loop controls with a normative bound.

This patch replaces the ir_loop fields "from", "to", "increment",
"counter", and "cmp" with a single integer ("normative_bound") that
serves the same purpose.

I've used the name "normative_bound" to emphasize the fact that the
back-end is required to emit code to prevent the loop from running
more than normative_bound times.  (By contrast, an "informative" bound
would be a bound that is informational only).
Reviewed-by: default avatarJordan Justen <jordan.l.justen@intel.com>
Reviewed-by: default avatarIan Romanick <ian.d.romanick@intel.com>
parent 2c17f97f
......@@ -1277,11 +1277,7 @@ ir_constant::is_basis() const
ir_loop::ir_loop()
{
this->ir_type = ir_type_loop;
this->cmp = ir_unop_neg;
this->from = NULL;
this->to = NULL;
this->increment = NULL;
this->counter = NULL;
this->normative_bound = -1;
}
......
......@@ -86,6 +86,7 @@ enum ir_node_type {
ir_type_max /**< maximum ir_type enum number, for validation */
};
/**
* Base class of all IR instructions
*/
......@@ -1025,54 +1026,11 @@ public:
exec_list body_instructions;
/**
* \name Loop counter and controls
*
* Represents a loop like a FORTRAN \c do-loop.
*
* \note
* If \c from and \c to are the same value, the loop will execute once.
*/
/*@{*/
/**
* Value which should be assigned to \c counter before the first iteration
* of the loop. Must be non-null whenever \c counter is non-null, and vice
* versa.
*/
ir_rvalue *from;
/**
* Value which \c counter should be compared to in order to determine
* whether to exit the loop. Must be non-null whenever \c counter is
* non-null, and vice versa.
* Normative bound for the loop. If this value is >= 0, the back-end
* should generate instructions to ensure that the loop executes no more
* than this many times.
*/
ir_rvalue *to;
/**
* Value which should be added to \c counter at the end of each loop
* iteration. Must be non-null whenever \c counter is non-null, and vice
* versa.
*/
ir_rvalue *increment;
/**
* Variable which counts loop iterations. This is a brand new ir_variable
* declaration (not a reference to a previously declared ir_variable, as in
* ir_dereference_variable).
*/
ir_variable *counter;
/**
* Comparison operation in the loop terminator.
*
* If any of the loop control fields are non-\c NULL, this field must be
* one of \c ir_binop_less, \c ir_binop_greater, \c ir_binop_lequal,
* \c ir_binop_gequal, \c ir_binop_equal, or \c ir_binop_nequal.
*
* Ignored if \c counter is NULL.
*/
int cmp;
/*@}*/
int normative_bound;
};
......
......@@ -158,21 +158,13 @@ ir_loop::clone(void *mem_ctx, struct hash_table *ht) const
{
ir_loop *new_loop = new(mem_ctx) ir_loop();
if (this->from)
new_loop->from = this->from->clone(mem_ctx, ht);
if (this->to)
new_loop->to = this->to->clone(mem_ctx, ht);
if (this->increment)
new_loop->increment = this->increment->clone(mem_ctx, ht);
if (this->counter)
new_loop->counter = this->counter->clone(mem_ctx, ht);
new_loop->normative_bound = this->normative_bound;
foreach_iter(exec_list_iterator, iter, this->body_instructions) {
ir_instruction *ir = (ir_instruction *)iter.get();
new_loop->body_instructions.push_tail(ir->clone(mem_ctx, ht));
}
new_loop->cmp = this->cmp;
return new_loop;
}
......
......@@ -87,36 +87,10 @@ ir_loop::accept(ir_hierarchical_visitor *v)
if (s != visit_continue)
return (s == visit_continue_with_parent) ? visit_continue : s;
if (this->counter) {
s = this->counter->accept(v);
if (s != visit_continue)
return (s == visit_continue_with_parent) ? visit_continue : s;
}
s = visit_list_elements(v, &this->body_instructions);
if (s == visit_stop)
return s;
if (s != visit_continue_with_parent) {
if (this->from) {
s = this->from->accept(v);
if (s != visit_continue)
return (s == visit_continue_with_parent) ? visit_continue : s;
}
if (this->to) {
s = this->to->accept(v);
if (s != visit_continue)
return (s == visit_continue_with_parent) ? visit_continue : s;
}
if (this->increment) {
s = this->increment->accept(v);
if (s != visit_continue)
return (s == visit_continue_with_parent) ? visit_continue : s;
}
}
return v->visit_leave(this);
}
......
......@@ -524,17 +524,8 @@ void
ir_print_visitor::visit(ir_loop *ir)
{
printf("(loop (");
if (ir->counter != NULL)
ir->counter->accept(this);
printf(") (");
if (ir->from != NULL)
ir->from->accept(this);
printf(") (");
if (ir->to != NULL)
ir->to->accept(this);
printf(") (");
if (ir->increment != NULL)
ir->increment->accept(this);
if (ir->normative_bound >= 0)
printf("%d", ir->normative_bound);
printf(") (\n");
indentation++;
......
......@@ -488,18 +488,34 @@ ir_reader::read_if(s_expression *expr, ir_loop *loop_ctx)
ir_loop *
ir_reader::read_loop(s_expression *expr)
{
s_expression *s_counter, *s_from, *s_to, *s_inc, *s_body;
s_expression *s_bound_expr, *s_body, *s_bound;
s_pattern pat[] = { "loop", s_counter, s_from, s_to, s_inc, s_body };
if (!MATCH(expr, pat)) {
ir_read_error(expr, "expected (loop <counter> <from> <to> "
"<increment> <body>)");
s_pattern loop_pat[] = { "loop", s_bound_expr, s_body };
s_pattern no_bound_pat[] = { };
s_pattern bound_pat[] = { s_bound };
if (!MATCH(expr, loop_pat)) {
ir_read_error(expr, "expected (loop <bound> <body>)");
return NULL;
}
// FINISHME: actually read the count/from/to fields.
ir_loop *loop = new(mem_ctx) ir_loop;
if (MATCH(s_bound_expr, no_bound_pat)) {
loop->normative_bound = -1;
} else if (MATCH(s_bound_expr, bound_pat)) {
s_int *value = SX_AS_INT(s_bound);
if (value == NULL) {
ir_read_error(s_bound_expr, "malformed loop bound");
delete loop;
return NULL;
}
loop->normative_bound = value->value();
} else {
ir_read_error(s_bound_expr, "malformed loop bound");
delete loop;
return NULL;
}
read_instructions(&loop->body_instructions, s_body, loop);
if (state->error) {
delete loop;
......
......@@ -63,8 +63,6 @@ public:
virtual ir_visitor_status visit_enter(ir_if *ir);
virtual ir_visitor_status visit_enter(ir_loop *ir);
virtual ir_visitor_status visit_leave(ir_loop *ir);
virtual ir_visitor_status visit_enter(ir_function *ir);
virtual ir_visitor_status visit_leave(ir_function *ir);
virtual ir_visitor_status visit_enter(ir_function_signature *ir);
......@@ -149,54 +147,6 @@ ir_validate::visit_enter(ir_if *ir)
}
ir_visitor_status
ir_validate::visit_enter(ir_loop *ir)
{
if (ir->counter != NULL && hash_table_find(ht, ir->counter) != NULL) {
printf("ir_loop @ %p specifies already-declared variable `%s' @ %p\n",
(void *) ir, ir->counter->name, (void *) ir->counter);
abort();
}
return visit_continue;
}
ir_visitor_status
ir_validate::visit_leave(ir_loop *ir)
{
if (ir->counter != NULL) {
if ((ir->from == NULL) || (ir->to == NULL) || (ir->increment == NULL)) {
printf("ir_loop has invalid loop controls:\n"
" counter: %p\n"
" from: %p\n"
" to: %p\n"
" increment: %p\n",
(void *) ir->counter, (void *) ir->from, (void *) ir->to,
(void *) ir->increment);
abort();
}
if ((ir->cmp < ir_binop_less) || (ir->cmp > ir_binop_nequal)) {
printf("ir_loop has invalid comparitor %d\n", ir->cmp);
abort();
}
} else {
if ((ir->from != NULL) || (ir->to != NULL) || (ir->increment != NULL)) {
printf("ir_loop has invalid loop controls:\n"
" counter: %p\n"
" from: %p\n"
" to: %p\n"
" increment: %p\n",
(void *) ir->counter, (void *) ir->from, (void *) ir->to,
(void *) ir->increment);
abort();
}
}
return visit_continue;
}
ir_visitor_status
ir_validate::visit_enter(ir_function *ir)
{
......
......@@ -132,24 +132,3 @@ ir_variable_refcount_visitor::visit_leave(ir_assignment *ir)
return visit_continue;
}
ir_visitor_status
ir_variable_refcount_visitor::visit_leave(ir_loop *ir)
{
/* If the loop has a counter variable, it is implicitly referenced and
* assigned to. Note that since the LHS of an assignment is counted as a
* reference, we actually have to increment referenced_count by 2 so that
* later code will know that the variable isn't just assigned to.
*/
if (ir->counter != NULL) {
ir_variable_refcount_entry *entry =
this->get_variable_entry(ir->counter);
if (entry) {
entry->referenced_count += 2;
entry->assigned_count++;
}
}
return visit_continue;
}
......@@ -60,7 +60,6 @@ public:
virtual ir_visitor_status visit_enter(ir_function_signature *);
virtual ir_visitor_status visit_leave(ir_assignment *);
virtual ir_visitor_status visit_leave(ir_loop *);
ir_variable_refcount_entry *get_variable_entry(ir_variable *var);
......
......@@ -44,8 +44,7 @@ analyze_loop_variables(exec_list *instructions);
*
* (if (expression bool ...) (break))
*
* and fill in the \c ir_loop::from, \c ir_loop::to, and \c ir_loop::counter
* fields of the \c ir_loop.
* and fill in the \c normative_bound field of the \c ir_loop.
*
* In this process, some conditional break-statements may be eliminated
* altogether. For example, if it is provable that one loop exit condition will
......
......@@ -187,13 +187,11 @@ loop_control_visitor::visit_leave(ir_loop *ir)
* i is a loop induction variable, c is a constant, and < is any relative
* operator.
*/
int max_iterations = ls->max_iterations;
unsigned max_iterations =
ls->max_iterations < 0 ? INT_MAX : ls->max_iterations;
if(ir->from && ir->to && ir->increment)
max_iterations = calculate_iterations(ir->from, ir->to, ir->increment, (ir_expression_operation)ir->cmp);
if(max_iterations < 0)
max_iterations = INT_MAX;
if (ir->normative_bound >= 0)
max_iterations = ir->normative_bound;
foreach_list(node, &ls->terminators) {
loop_terminator *t = (loop_terminator *) node;
......@@ -248,14 +246,11 @@ loop_control_visitor::visit_leave(ir_loop *ir)
cmp);
if (iterations >= 0) {
/* If the new iteration count is lower than the previously
* believed iteration count, update the loop control values.
* believed iteration count, then add a normative bound to
* this loop.
*/
if (iterations < max_iterations) {
ir->from = init->clone(ir, NULL);
ir->to = limit->clone(ir, NULL);
ir->increment = lv->increment->clone(ir, NULL);
ir->counter = lv->var->clone(ir, NULL);
ir->cmp = cmp;
if ((unsigned) iterations < max_iterations) {
ir->normative_bound = iterations;
max_iterations = iterations;
}
......
......@@ -72,34 +72,34 @@ public:
ir_visitor_status
lower_bounded_loops_visitor::visit_leave(ir_loop *ir)
{
if (ir->counter == NULL)
if (ir->normative_bound < 0)
return visit_continue;
exec_list new_instructions;
ir_factory f(&new_instructions, ralloc_parent(ir));
/* Before the loop, declare the counter and initialize it to "from". */
f.emit(ir->counter);
f.emit(assign(ir->counter, ir->from));
/* Before the loop, declare the counter and initialize it to zero. */
ir_variable *counter = f.make_temp(glsl_type::uint_type, "counter");
f.emit(assign(counter, f.constant(0u)));
ir->insert_before(&new_instructions);
/* At the top of the loop, compare the counter to "to", and break if the
* comparison succeeds.
/* At the top of the loop, compare the counter to normative_bound, and
* break if the comparison succeeds.
*/
ir_loop_jump *brk = new(f.mem_ctx) ir_loop_jump(ir_loop_jump::jump_break);
ir_expression_operation cmp = (ir_expression_operation) ir->cmp;
ir->body_instructions.push_head(if_tree(expr(cmp, ir->counter, ir->to),
brk));
ir_if *if_inst = if_tree(gequal(counter,
f.constant((unsigned) ir->normative_bound)),
brk);
ir->body_instructions.push_head(if_inst);
/* At the bottom of the loop, increment the counter. */
ir->body_instructions.push_tail(assign(ir->counter,
add(ir->counter, ir->increment)));
/* NULL out the counter, from, to, and increment variables. */
ir->counter = NULL;
ir->from = NULL;
ir->to = NULL;
ir->increment = NULL;
ir->body_instructions.push_tail(assign(counter,
add(counter, f.constant(1u))));
/* Since we've explicitly added instructions to terminate the loop, we no
* longer need it to have a normative bound.
*/
ir->normative_bound = -1;
this->progress = true;
return visit_continue;
......
......@@ -8,6 +8,6 @@
((declare (out) float a)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000))) break))))))
EOF
((declare (out) float a)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000))) break))))))
......@@ -8,7 +8,7 @@
((declare (in) float b) (declare (out) float a)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(if (expression bool > (var_ref b) (constant float (0.000000))) (break)
())))))))
......
((declare (in) float b) (declare (out) float a)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(if (expression bool > (var_ref b) (constant float (0.0))) (break)
())))))))
......@@ -9,7 +9,7 @@
((declare (in) float b) (declare (out) float a) (declare (out) float c)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(if (expression bool > (var_ref b) (constant float (0.000000)))
((assign (x) (var_ref c) (constant float (1.000000))) break)
......
((declare (in) float b) (declare (out) float a) (declare (out) float c)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(if (expression bool > (var_ref b) (constant float (0.0)))
((assign (x) (var_ref c) (constant float (1.000000))) break)
......
......@@ -8,7 +8,7 @@
((declare (in) float b) (declare (out) float a)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(if (expression bool > (var_ref b) (constant float (0.000000))) ()
(break))))))))
......
((declare (in) float b) (declare (out) float a)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(if (expression bool > (var_ref b) (constant float (0.0))) ()
(break))))))))
......@@ -9,7 +9,7 @@
((declare (in) float b) (declare (out) float a) (declare (out) float c)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(if (expression bool > (var_ref b) (constant float (0.000000))) ()
((assign (x) (var_ref c) (constant float (1.000000))) break))))))))
......
((declare (in) float b) (declare (out) float a) (declare (out) float c)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(if (expression bool > (var_ref b) (constant float (0.0))) ()
((assign (x) (var_ref c) (constant float (1.000000))) break))))))))
......@@ -12,7 +12,7 @@
(declare (in) float cb)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((if (expression bool > (var_ref a) (constant float (0.000000)))
((if (expression bool > (var_ref ba) (constant float (0.000000)))
((if (expression bool > (var_ref bb) (constant float (0.000000)))
......
......@@ -5,7 +5,7 @@
(signature void (parameters)
((declare (temporary) bool break_flag)
(assign (x) (var_ref break_flag) (constant bool (0)))
(loop () () () ()
(loop ()
((declare (temporary) bool execute_flag)
(assign (x) (var_ref execute_flag) (constant bool (1)))
(if (expression bool > (var_ref a) (constant float (0.0)))
......
......@@ -10,7 +10,7 @@
((declare (in) float aa) (declare (in) float ab) (declare (in) float b)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((if (expression bool > (var_ref aa) (constant float (0.000000)))
((if (expression bool > (var_ref ab) (constant float (0.000000)))
(continue)
......
......@@ -3,7 +3,7 @@
(signature void (parameters)
((declare (temporary) bool break_flag)
(assign (x) (var_ref break_flag) (constant bool (0)))
(loop () () () ()
(loop ()
((declare (temporary) bool execute_flag)
(assign (x) (var_ref execute_flag) (constant bool (1)))
(if (expression bool > (var_ref aa) (constant float (0.0)))
......
......@@ -19,7 +19,7 @@
((return))
()))
())
(loop () () () ()
(loop ()
((if (expression bool > (var_ref b) (constant float (0.000000)))
((if (expression bool > (var_ref c) (constant float (0.000000))) (break)
(continue)))
......
......@@ -14,7 +14,7 @@
()))
())
(if (var_ref execute_flag)
((loop () () () ()
((loop ()
((if (expression bool > (var_ref b) (constant float (0.0)))
((if (expression bool > (var_ref c) (constant float (0.0))) ()
(continue)))
......
......@@ -8,6 +8,6 @@
((declare (out) float a)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000))) continue))))))
EOF
((declare (out) float a)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))))))))
......@@ -8,7 +8,7 @@
((declare (out) float a) (declare (out) float b)
(function sub
(signature float (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(return (constant float (2.000000)))))
(assign (x) (var_ref b) (constant float (3.000000)))
......
((declare (out) float a) (declare (out) float b)
(function sub
(signature float (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(return (constant float (2.000000)))))
(assign (x) (var_ref b) (constant float (3.000000)))
......
......@@ -8,7 +8,7 @@
((declare (out) float a) (declare (out) float b)
(function sub
(signature float (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(return (constant float (2.000000)))))
(assign (x) (var_ref b) (constant float (3.000000)))
......
......@@ -6,7 +6,7 @@
(declare (temporary) float return_value)
(declare (temporary) bool return_flag)
(assign (x) (var_ref return_flag) (constant bool (0)))
(loop () () () ()
(loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(assign (x) (var_ref return_value) (constant float (2.000000)))
(assign (x) (var_ref return_flag) (constant bool (1)))
......
......@@ -8,7 +8,7 @@
((declare (out) float a) (declare (out) float b)
(function sub
(signature float (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(return (constant float (2.000000)))))
(assign (x) (var_ref b) (constant float (3.000000)))
......
......@@ -6,7 +6,7 @@
(declare (temporary) float return_value)
(declare (temporary) bool return_flag)
(assign (x) (var_ref return_flag) (constant bool (0)))
(loop () () () ()
(loop ()
((assign (x) (var_ref a) (constant float (1.000000)))
(assign (x) (var_ref return_value) (constant float (2.000000)))
(assign (x) (var_ref return_flag) (constant bool (1)))
......
......@@ -8,7 +8,7 @@
((declare (out) float a) (declare (out) float b)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000))) (return)))
(assign (x) (var_ref b) (constant float (2.000000)))))))
EOF
((declare (out) float a) (declare (out) float b)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000))) (return)))
(assign (x) (var_ref b) (constant float (2.000000)))))))
......@@ -8,7 +8,7 @@
((declare (out) float a) (declare (out) float b)
(function main
(signature void (parameters)
((loop () () () ()
((loop ()
((assign (x) (var_ref a) (constant float (1.000000))) (return)))
(assign (x) (var_ref b) (constant float (2.000000)))))))
EOF
......@@ -3,7 +3,7 @@
(signature void (parameters)
((declare (temporary) bool return_flag)
(assign (x) (var_ref return_flag) (constant bool (0)))
(loop () () () ()
(loop ()
((assign (x) (var_ref a) (constant float (1.000000)))