unsigned maxd, bool pred);
unsigned ir3_delay_calc(struct ir3_block *block, struct ir3_instruction *instr,
bool soft, bool pred);
+void ir3_remove_nops(struct ir3 *ir);
/* depth calculation: */
struct ir3_shader_variant;
list_delinit(&mov->node);
list_add(&mov->node, &atomic->node);
- /* And because this is after instruction scheduling, we don't really
- * have a good way to know if extra delay slots are needed. For
- * example, if the result is consumed by an stib (storeImage()) there
- * would be no extra delay slots in place already, but 5 are needed.
- * Just plan for the worst and hope nobody looks at the resulting
- * code that is generated :-(
- */
- struct ir3_instruction *nop = ir3_NOP(atomic->block);
- nop->repeat = 5;
-
- list_delinit(&nop->node);
- list_add(&nop->node, &mov->node);
-
return atomic->data = mov;
}
return delay;
}
+
+/**
+ * Remove nop instructions. The scheduler can insert placeholder nop's
+ * so that ir3_delay_calc() can account for nop's that won't be needed
+ * due to nop's triggered by a previous instruction. However, before
+ * legalize, we want to remove these. The legalize pass can insert
+ * some nop's if needed to hold (for example) sync flags. This final
+ * remaining nops are inserted by legalize after this.
+ */
+void
+ir3_remove_nops(struct ir3 *ir)
+{
+ foreach_block (block, &ir->block_list) {
+ foreach_instr_safe (instr, &block->instr_list) {
+ if (instr->opc == OPC_NOP) {
+ list_del(&instr->node);
+ }
+ }
+ }
+
+}
if (list_is_empty(&block->instr_list) && (opc_cat(n->opc) >= 5))
ir3_NOP(block);
- if (is_nop(n) && !list_is_empty(&block->instr_list)) {
- struct ir3_instruction *last = list_last_entry(&block->instr_list,
- struct ir3_instruction, node);
- if (is_nop(last) && (last->repeat < 5)) {
- last->repeat++;
- last->flags |= n->flags;
- continue;
- }
-
- /* NOTE: I think the nopN encoding works for a5xx and
- * probably a4xx, but not a3xx. So far only tested on
- * a6xx.
- */
- if ((ctx->compiler->gpu_id >= 600) && !n->flags && (last->nop < 3) &&
- ((opc_cat(last->opc) == 2) || (opc_cat(last->opc) == 3))) {
- last->nop++;
- continue;
- }
- }
-
if (ctx->compiler->samgq_workaround &&
ctx->type == MESA_SHADER_VERTEX && n->opc == OPC_SAMGQ) {
struct ir3_instruction *samgp;
}
}
+/* Insert nop's required to make this a legal/valid shader program: */
+static void
+nop_sched(struct ir3 *ir)
+{
+ foreach_block (block, &ir->block_list) {
+ struct ir3_instruction *last = NULL;
+ struct list_head instr_list;
+
+ /* remove all the instructions from the list, we'll be adding
+ * them back in as we go
+ */
+ list_replace(&block->instr_list, &instr_list);
+ list_inithead(&block->instr_list);
+
+ foreach_instr_safe (instr, &instr_list) {
+ unsigned delay = ir3_delay_calc(block, instr, false, true);
+
+ /* NOTE: I think the nopN encoding works for a5xx and
+ * probably a4xx, but not a3xx. So far only tested on
+ * a6xx.
+ */
+
+ if ((delay > 0) && (ir->compiler->gpu_id >= 600) && last &&
+ ((opc_cat(last->opc) == 2) || (opc_cat(last->opc) == 3))) {
+ /* the previous cat2/cat3 instruction can encode at most 3 nop's: */
+ unsigned transfer = MIN2(delay, 3 - last->nop);
+ last->nop += transfer;
+ delay -= transfer;
+ }
+
+ if ((delay > 0) && last && (last->opc == OPC_NOP)) {
+ /* the previous nop can encode at most 5 repeats: */
+ unsigned transfer = MIN2(delay, 5 - last->repeat);
+ last->repeat += transfer;
+ delay -= transfer;
+ }
+
+ if (delay > 0) {
+ debug_assert(delay <= 6);
+ ir3_NOP(block)->repeat = delay - 1;
+ }
+
+ list_addtail(&instr->node, &block->instr_list);
+ last = instr;
+ }
+ }
+}
+
void
ir3_legalize(struct ir3 *ir, struct ir3_shader_variant *so, int *max_bary)
{
block->data = rzalloc(ctx, struct ir3_legalize_block_data);
}
+ ir3_remove_nops(ir);
+
/* process each block: */
do {
progress = false;
*max_bary = ctx->max_bary;
+ nop_sched(ir);
+
do {
ir3_count_instructions(ir);
} while(resolve_jumps(ir));
if (instr) {
unsigned delay = ir3_delay_calc(ctx->block, instr, false, false);
-
d("delay=%u", delay);
/* and if we run out of instructions that can be scheduled,
if (block->successors[1]) {
/* if/else, conditional branches to "then" or "else": */
struct ir3_instruction *br;
- unsigned delay = 6;
debug_assert(ctx->pred);
debug_assert(block->condition);
- delay -= ir3_distance(ctx->block, ctx->pred, delay, false);
-
- while (delay > 0) {
- ir3_NOP(block);
- delay--;
- }
-
/* create "else" branch first (since "then" block should
* frequently/always end up being a fall-thru):
*/
*/
}
-/* After scheduling individual blocks, we still could have cases where
- * one (or more) paths into a block, a value produced by a previous
- * has too few delay slots to be legal. We can't deal with this in the
- * first pass, because loops (ie. we can't ensure all predecessor blocks
- * are already scheduled in the first pass). All we can really do at
- * this point is stuff in extra nop's until things are legal.
- */
-static void
-sched_intra_block(struct ir3_sched_ctx *ctx, struct ir3_block *block)
-{
- unsigned n = 0;
-
- ctx->block = block;
-
- foreach_instr_safe (instr, &block->instr_list) {
- unsigned delay = 0;
-
- set_foreach(block->predecessors, entry) {
- struct ir3_block *pred = (struct ir3_block *)entry->key;
- unsigned d = ir3_delay_calc(pred, instr, false, true);
- delay = MAX2(d, delay);
- }
-
- while (delay > n) {
- struct ir3_instruction *nop = ir3_NOP(block);
-
- /* move to before instr: */
- list_delinit(&nop->node);
- list_addtail(&nop->node, &instr->node);
-
- n++;
- }
-
- /* we can bail once we hit worst case delay: */
- if (++n > 6)
- break;
- }
-}
-
int ir3_sched(struct ir3 *ir)
{
struct ir3_sched_ctx ctx = {0};
sched_block(&ctx, block);
}
- foreach_block (block, &ir->block_list) {
- sched_intra_block(&ctx, block);
- }
-
if (ctx.error)
return -1;