pan/bi: Add bi_instr_schedulable predicate

Using the previously defined checks for architectural scheduling
constraints, define one top-level predicate to check if an instruction
on the worklist is ready for scheduling.

Signed-off-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/8354>
This commit is contained in:
Alyssa Rosenzweig 2021-01-06 14:55:02 -05:00 committed by Marge Bot
parent 2537a53d5c
commit a303076c1a
1 changed files with 88 additions and 0 deletions

View File

@ -677,6 +677,94 @@ bi_writes_reg(bi_instr *instr)
!bi_opcode_props[instr->op].sr_write);
}
/* Instruction placement entails two questions: what subset of instructions in
* the block can legally be scheduled? and of those which is the best? That is,
* we seek to maximize a cost function on a subset of the worklist satisfying a
* particular predicate. The necessary predicate is determined entirely by
* Bifrost's architectural limitations and is described in the accompanying
* whitepaper. The cost function is a heuristic. */
static bool
bi_instr_schedulable(bi_instr *instr,
struct bi_clause_state *clause,
struct bi_tuple_state *tuple,
bool fma)
{
/* The units must match */
if ((fma && !bi_can_fma(instr)) || (!fma && !bi_can_add(instr)))
return false;
/* There can only be one message-passing instruction per clause */
if (bi_must_message(instr) && clause->message)
return false;
/* Some instructions have placement requirements */
if (bi_must_last(instr) && !tuple->last)
return false;
/* Message-passing instructions are not guaranteed write within the
* same clause (most likely they will not), so if a later instruction
* in the clause reads from the destination, the message-passing
* instruction can't be scheduled */
if (bi_opcode_props[instr->op].sr_write) {
for (unsigned i = 0; i < clause->read_count; ++i) {
if (bi_is_equiv(instr->dest[0], clause->reads[i]))
return false;
}
}
/* If FAU is already assigned, we may not disrupt that. Do a
* non-disruptive test update */
if (!bi_update_fau(clause, tuple, instr, fma, false))
return false;
/* If this choice of FMA would force a staging passthrough, the ADD
* instruction must support such a passthrough */
if (tuple->add && bi_has_staging_passthrough_hazard(instr->dest[0], tuple->add))
return false;
/* If this choice of destination would force a cross-tuple passthrough, the next tuple must support that */
if (tuple->prev && bi_has_cross_passthrough_hazard(tuple->prev, instr))
return false;
/* Register file writes are limited, TODO don't count tempable things */
unsigned total_writes = tuple->reg.nr_writes;
if (bi_writes_reg(instr))
total_writes++;
/* Last tuple in a clause can only write a single value */
if (tuple->last && total_writes > 1)
return false;
/* Register file reads are limited, so count unique */
unsigned unique_new_srcs = 0;
bi_foreach_src(instr, s) {
if (bi_tuple_is_new_src(instr, &tuple->reg, s))
unique_new_srcs++;
}
unsigned total_srcs = tuple->reg.nr_reads + unique_new_srcs;
/* TODO: spill to moves */
if (total_srcs > 3)
return false;
/* Count effective reads for the successor */
unsigned succ_reads = bi_count_succ_reads(instr->dest[0],
tuple->add ? tuple->add->dest[0] : bi_null(),
tuple->prev_reads, tuple->nr_prev_reads);
/* Successor must satisfy R+W <= 4, so we require W <= 4-R */
if (total_writes > MAX2(4 - (signed) succ_reads, 0))
return false;
return true;
}
#ifndef NDEBUG
static bi_builder *