agx: Remove logical_end instructions

They're more trouble than they're worth for us. They were originally lifted
unthinkingly from ACO, where I assume they're necessary for software CF
lowering, but they're just an inconvenient convenience for us. Remove em.

Signed-off-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25052>
This commit is contained in:
Alyssa Rosenzweig
2023-08-29 14:40:25 -04:00
committed by Marge Bot
parent a2e5d1ddd1
commit f9343fe5ca
7 changed files with 44 additions and 80 deletions

View File

@@ -1718,20 +1718,6 @@ agx_emit_tex(agx_builder *b, nir_tex_instr *instr)
I->mask = agx_expand_tex_to(b, &instr->def, tmp, masked); I->mask = agx_expand_tex_to(b, &instr->def, tmp, masked);
} }
/*
* Mark the logical end of the current block by emitting a p_logical_end marker.
* Note if an unconditional jump is emitted (for instance, to break out of a
* loop from inside an if), the block has already reached its logical end so we
* don't re-emit p_logical_end. The validator checks this, and correct register
* allocation depends on it.
*/
static void
agx_emit_logical_end(agx_builder *b)
{
if (!b->shader->current_block->unconditional_jumps)
agx_logical_end(b);
}
/* /*
* NIR loops are treated as a pair of AGX loops: * NIR loops are treated as a pair of AGX loops:
* *
@@ -1768,9 +1754,6 @@ agx_emit_jump(agx_builder *b, nir_jump_instr *instr)
/* Update the counter and flush */ /* Update the counter and flush */
agx_nest(b, nestings); agx_nest(b, nestings);
/* Jumps must come at the end of a block */
agx_emit_logical_end(b);
agx_pop_exec(b, 0); agx_pop_exec(b, 0);
ctx->current_block->unconditional_jumps = true; ctx->current_block->unconditional_jumps = true;
@@ -1925,7 +1908,6 @@ emit_if(agx_context *ctx, nir_if *nif)
agx_builder _b = agx_init_builder(ctx, agx_after_block(first_block)); agx_builder _b = agx_init_builder(ctx, agx_after_block(first_block));
agx_index cond = agx_src_index(&nif->condition); agx_index cond = agx_src_index(&nif->condition);
agx_emit_logical_end(&_b);
agx_if_icmp(&_b, cond, agx_zero(), 1, AGX_ICOND_UEQ, true); agx_if_icmp(&_b, cond, agx_zero(), 1, AGX_ICOND_UEQ, true);
ctx->loop_nesting++; ctx->loop_nesting++;
@@ -1934,7 +1916,6 @@ emit_if(agx_context *ctx, nir_if *nif)
agx_block *end_then = ctx->current_block; agx_block *end_then = ctx->current_block;
_b.cursor = agx_after_block(ctx->current_block); _b.cursor = agx_after_block(ctx->current_block);
agx_emit_logical_end(&_b);
agx_block *else_block = emit_cf_list(ctx, &nif->else_list); agx_block *else_block = emit_cf_list(ctx, &nif->else_list);
agx_block *end_else = ctx->current_block; agx_block *end_else = ctx->current_block;
@@ -1953,7 +1934,6 @@ emit_if(agx_context *ctx, nir_if *nif)
agx_block_add_successor(end_else, ctx->after_block); agx_block_add_successor(end_else, ctx->after_block);
_b.cursor = agx_after_block(ctx->current_block); _b.cursor = agx_after_block(ctx->current_block);
agx_emit_logical_end(&_b);
agx_pop_exec(&_b, 1); agx_pop_exec(&_b, 1);
ctx->loop_nesting--; ctx->loop_nesting--;
} }
@@ -1974,7 +1954,6 @@ emit_loop(agx_context *ctx, nir_loop *nloop)
/* Make room for break/continue nesting (TODO: skip if no divergent CF) */ /* Make room for break/continue nesting (TODO: skip if no divergent CF) */
agx_builder _b = agx_init_builder(ctx, agx_after_block(ctx->current_block)); agx_builder _b = agx_init_builder(ctx, agx_after_block(ctx->current_block));
agx_emit_logical_end(&_b);
agx_push_exec(&_b, 2); agx_push_exec(&_b, 2);
/* Fallthrough to body */ /* Fallthrough to body */
@@ -1988,7 +1967,6 @@ emit_loop(agx_context *ctx, nir_loop *nloop)
/* Fix up the nesting counter via an always true while_icmp, and branch back /* Fix up the nesting counter via an always true while_icmp, and branch back
* to start of loop if any lanes are active */ * to start of loop if any lanes are active */
_b.cursor = agx_after_block(ctx->current_block); _b.cursor = agx_after_block(ctx->current_block);
agx_emit_logical_end(&_b);
agx_while_icmp(&_b, agx_zero(), agx_zero(), 2, AGX_ICOND_UEQ, false); agx_while_icmp(&_b, agx_zero(), agx_zero(), 2, AGX_ICOND_UEQ, false);
agx_jmp_exec_any(&_b, start_block); agx_jmp_exec_any(&_b, start_block);
agx_pop_exec(&_b, 2); agx_pop_exec(&_b, 2);
@@ -2526,7 +2504,6 @@ agx_compile_function_nir(nir_shader *nir, nir_function_impl *impl,
*/ */
agx_block *last_block = list_last_entry(&ctx->blocks, agx_block, link); agx_block *last_block = list_last_entry(&ctx->blocks, agx_block, link);
agx_builder _b = agx_init_builder(ctx, agx_after_block(last_block)); agx_builder _b = agx_init_builder(ctx, agx_after_block(last_block));
agx_logical_end(&_b);
agx_stop(&_b); agx_stop(&_b);
/* Index blocks now that we're done emitting so the order is consistent */ /* Index blocks now that we're done emitting so the order is consistent */

View File

@@ -670,24 +670,6 @@ agx_after_instr(agx_instr *instr)
}; };
} }
/*
* Get a cursor inserting at the logical end of the block. In particular, this
* is before branches or control flow instructions, which occur after the
* logical end but before the physical end.
*/
static inline agx_cursor
agx_after_block_logical(agx_block *block)
{
/* Search for a p_logical_end */
agx_foreach_instr_in_block_rev(block, I) {
if (I->op == AGX_OPCODE_LOGICAL_END)
return agx_before_instr(I);
}
/* If there's no p_logical_end, use the physical end */
return agx_after_block(block);
}
static inline agx_cursor static inline agx_cursor
agx_before_nonempty_block(agx_block *block) agx_before_nonempty_block(agx_block *block)
{ {
@@ -706,6 +688,42 @@ agx_before_block(agx_block *block)
return agx_before_nonempty_block(block); return agx_before_nonempty_block(block);
} }
static inline bool
instr_after_logical_end(const agx_instr *I)
{
switch (I->op) {
case AGX_OPCODE_JMP_EXEC_ANY:
case AGX_OPCODE_JMP_EXEC_NONE:
case AGX_OPCODE_POP_EXEC:
case AGX_OPCODE_IF_ICMP:
case AGX_OPCODE_WHILE_ICMP:
case AGX_OPCODE_IF_FCMP:
case AGX_OPCODE_WHILE_FCMP:
case AGX_OPCODE_STOP:
return true;
default:
return false;
}
}
/*
* Get a cursor inserting at the logical end of the block. In particular, this
* is before branches or control flow instructions, which occur after the
* logical end but before the physical end.
*/
static inline agx_cursor
agx_after_block_logical(agx_block *block)
{
/* Search for the first instruction that's not past the logical end */
agx_foreach_instr_in_block_rev(block, I) {
if (!instr_after_logical_end(I))
return agx_after_instr(I);
}
/* If we got here, the block is either empty or entirely control flow */
return agx_before_block(block);
}
/* IR builder in terms of cursor infrastructure */ /* IR builder in terms of cursor infrastructure */
typedef struct { typedef struct {

View File

@@ -410,10 +410,6 @@ op("xor", _, srcs = 2)
op("and", _, srcs = 2) op("and", _, srcs = 2)
op("or", _, srcs = 2) op("or", _, srcs = 2)
# Indicates the logical end of the block, before final branches/control flow
op("logical_end", _, dests = 0, srcs = 0, can_eliminate = False,
schedule_class = "invalid")
op("collect", _, srcs = VARIABLE) op("collect", _, srcs = VARIABLE)
op("split", _, srcs = 1, dests = VARIABLE) op("split", _, srcs = 1, dests = VARIABLE)
op("phi", _, srcs = VARIABLE, schedule_class = "preload") op("phi", _, srcs = VARIABLE, schedule_class = "preload")

View File

@@ -12,7 +12,6 @@
* Detect blocks with the sole contents: * Detect blocks with the sole contents:
* *
* else n=1 * else n=1
* logical_end
* pop_exec n=1 * pop_exec n=1
* *
* The else instruction is a no-op. To see that, consider the pseudocode for the * The else instruction is a no-op. To see that, consider the pseudocode for the
@@ -50,7 +49,6 @@
enum block_state { enum block_state {
STATE_ELSE = 0, STATE_ELSE = 0,
STATE_LOGICAL_END,
STATE_POP_EXEC, STATE_POP_EXEC,
STATE_DONE, STATE_DONE,
@@ -66,9 +64,6 @@ state_for_instr(const agx_instr *I)
case AGX_OPCODE_ELSE_FCMP: case AGX_OPCODE_ELSE_FCMP:
return (I->nest == 1) ? STATE_ELSE : STATE_NONE; return (I->nest == 1) ? STATE_ELSE : STATE_NONE;
case AGX_OPCODE_LOGICAL_END:
return STATE_LOGICAL_END;
case AGX_OPCODE_POP_EXEC: case AGX_OPCODE_POP_EXEC:
return (I->nest == 1) ? STATE_POP_EXEC : STATE_NONE; return (I->nest == 1) ? STATE_POP_EXEC : STATE_NONE;

View File

@@ -984,15 +984,6 @@ agx_pack_binary(agx_context *ctx, struct util_dynarray *emission)
struct util_dynarray fixups; struct util_dynarray fixups;
util_dynarray_init(&fixups, ctx); util_dynarray_init(&fixups, ctx);
agx_foreach_block(ctx, block) {
agx_foreach_instr_in_block_rev(block, I) {
if (I->op == AGX_OPCODE_LOGICAL_END) {
agx_remove_instruction(I);
break;
}
}
}
agx_foreach_block(ctx, block) { agx_foreach_block(ctx, block) {
/* Relative to the start of the binary, the block begins at the current /* Relative to the start of the binary, the block begins at the current
* number of bytes emitted */ * number of bytes emitted */

View File

@@ -59,7 +59,7 @@ create_dag(agx_context *ctx, agx_block *block, void *memctx)
agx_foreach_instr_in_block(block, I) { agx_foreach_instr_in_block(block, I) {
/* Don't touch control flow */ /* Don't touch control flow */
if (I->op == AGX_OPCODE_LOGICAL_END) if (instr_after_logical_end(I))
break; break;
struct sched_node *node = rzalloc(memctx, struct sched_node); struct sched_node *node = rzalloc(memctx, struct sched_node);

View File

@@ -17,13 +17,12 @@
/* /*
* If a block contains phi nodes, they must come at the start of the block. If a * If a block contains phi nodes, they must come at the start of the block. If a
* block contains control flow, it must come after a p_logical_end marker. * block contains control flow, it must come at the beginning/end as applicable.
* Therefore the form of a valid block is: * Therefore the form of a valid block is:
* *
* Control flow instructions (else) * Control flow instructions (else)
* Phi nodes * Phi nodes
* General instructions * General instructions
* Logical end
* Control flow instructions (except else) * Control flow instructions (except else)
* *
* Validate that this form is satisfied. * Validate that this form is satisfied.
@@ -58,24 +57,12 @@ agx_validate_block_form(agx_block *block)
break; break;
default: default:
agx_validate_assert(state != AGX_BLOCK_STATE_CF); if (instr_after_logical_end(I)) {
state = AGX_BLOCK_STATE_BODY; state = AGX_BLOCK_STATE_CF;
break; } else {
agx_validate_assert(state != AGX_BLOCK_STATE_CF);
case AGX_OPCODE_LOGICAL_END: state = AGX_BLOCK_STATE_BODY;
agx_validate_assert(state != AGX_BLOCK_STATE_CF); }
state = AGX_BLOCK_STATE_CF;
break;
case AGX_OPCODE_JMP_EXEC_ANY:
case AGX_OPCODE_JMP_EXEC_NONE:
case AGX_OPCODE_POP_EXEC:
case AGX_OPCODE_IF_ICMP:
case AGX_OPCODE_WHILE_ICMP:
case AGX_OPCODE_IF_FCMP:
case AGX_OPCODE_WHILE_FCMP:
case AGX_OPCODE_STOP:
agx_validate_assert(state == AGX_BLOCK_STATE_CF);
break; break;
} }
} }