freedreno/ir3: eliminate unused false-deps

Previously false-dependencies would get flagged as used, even if the
only "use" was a false dep to (for example) prevent a load from being
scheduled after a store.

In addition to being pointless instructions, in some cases they can
cause problems.  For example, ldg (and similar instructions) depend on
an immed arg getting CP'd into the instruction, but this doesn't happen
if an instruction is otherwise unused.  Which can result in undefined
results (overwriting unintended registers).

Signed-off-by: Rob Clark <robdclark@gmail.com>
This commit is contained in:
Rob Clark 2018-03-06 09:53:45 -05:00
parent 4f78383809
commit bd2ca2bcdd
2 changed files with 31 additions and 11 deletions

View File

@ -607,7 +607,7 @@ ir3_cp(struct ir3 *ir, struct ir3_shader_variant *so)
list_for_each_entry (struct ir3_instruction, instr, &block->instr_list, node) {
struct ir3_instruction *src;
/* by the way, we don't acount for false-dep's, so the CP
/* by the way, we don't account for false-dep's, so the CP
* pass should always happen before false-dep's are inserted
*/
debug_assert(instr->deps_count == 0);

View File

@ -133,13 +133,18 @@ ir3_insert_by_depth(struct ir3_instruction *instr, struct list_head *list)
}
static void
ir3_instr_depth(struct ir3_instruction *instr, unsigned boost)
ir3_instr_depth(struct ir3_instruction *instr, unsigned boost, bool falsedep)
{
struct ir3_instruction *src;
/* if we've already visited this instruction, bail now: */
if (ir3_instr_check_mark(instr))
if (falsedep) {
/* don't mark falsedep's as used, but process them normally: */
if (instr->flags & IR3_INSTR_MARK)
return;
} else if (ir3_instr_check_mark(instr)) {
return;
}
instr->depth = 0;
@ -147,7 +152,7 @@ ir3_instr_depth(struct ir3_instruction *instr, unsigned boost)
unsigned sd;
/* visit child to compute it's depth: */
ir3_instr_depth(src, boost);
ir3_instr_depth(src, boost, __is_false_dep(instr, i));
/* for array writes, no need to delay on previous write: */
if (i == 0)
@ -165,9 +170,10 @@ ir3_instr_depth(struct ir3_instruction *instr, unsigned boost)
ir3_insert_by_depth(instr, &instr->block->instr_list);
}
static void
static bool
remove_unused_by_block(struct ir3_block *block)
{
bool progress = false;
list_for_each_entry_safe (struct ir3_instruction, instr, &block->instr_list, node) {
if (!ir3_instr_check_mark(instr)) {
if (instr->opc == OPC_END)
@ -178,32 +184,35 @@ remove_unused_by_block(struct ir3_block *block)
instr->flags |= IR3_INSTR_UNUSED;
/* and remove from instruction list: */
list_delinit(&instr->node);
progress = true;
}
}
return progress;
}
void
ir3_depth(struct ir3 *ir)
static bool
compute_depth_and_remove_unused(struct ir3 *ir)
{
unsigned i;
bool progress = false;
ir3_clear_mark(ir);
for (i = 0; i < ir->noutputs; i++)
if (ir->outputs[i])
ir3_instr_depth(ir->outputs[i], 0);
ir3_instr_depth(ir->outputs[i], 0, false);
list_for_each_entry (struct ir3_block, block, &ir->block_list, node) {
for (i = 0; i < block->keeps_count; i++)
ir3_instr_depth(block->keeps[i], 0);
ir3_instr_depth(block->keeps[i], 0, false);
/* We also need to account for if-condition: */
if (block->condition)
ir3_instr_depth(block->condition, 6);
ir3_instr_depth(block->condition, 6, false);
}
/* mark un-used instructions: */
list_for_each_entry (struct ir3_block, block, &ir->block_list, node) {
remove_unused_by_block(block);
progress |= remove_unused_by_block(block);
}
/* note that we can end up with unused indirects, but we should
@ -221,4 +230,15 @@ ir3_depth(struct ir3 *ir)
if (in && (in->flags & IR3_INSTR_UNUSED))
ir->inputs[i] = NULL;
}
return progress;
}
void
ir3_depth(struct ir3 *ir)
{
bool progress;
do {
progress = compute_depth_and_remove_unused(ir);
} while (progress);
}