ir3: Readd support for translating NIR phi nodes
This is roughly based on the support removed a while ago, but it handles sources better by associating each source with a predecessor block. Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9842>
This commit is contained in:
committed by
Emma Anholt
parent
0ef021be4a
commit
e1d7240576
@@ -2749,6 +2749,60 @@ emit_tex_txs(struct ir3_context *ctx, nir_tex_instr *tex)
|
||||
ir3_put_dst(ctx, &tex->dest);
|
||||
}
|
||||
|
||||
/* phi instructions are left partially constructed. We don't resolve
|
||||
* their srcs until the end of the shader, since (eg. loops) one of
|
||||
* the phi's srcs might be defined after the phi due to back edges in
|
||||
* the CFG.
|
||||
*/
|
||||
static void
|
||||
emit_phi(struct ir3_context *ctx, nir_phi_instr *nphi)
|
||||
{
|
||||
struct ir3_instruction *phi, **dst;
|
||||
|
||||
/* NOTE: phi's should be lowered to scalar at this point */
|
||||
compile_assert(ctx, nphi->dest.ssa.num_components == 1);
|
||||
|
||||
dst = ir3_get_dst(ctx, &nphi->dest, 1);
|
||||
|
||||
phi = ir3_instr_create(ctx->block, OPC_META_PHI,
|
||||
1 + exec_list_length(&nphi->srcs));
|
||||
__ssa_dst(phi);
|
||||
phi->phi.nphi = nphi;
|
||||
|
||||
dst[0] = phi;
|
||||
|
||||
ir3_put_dst(ctx, &nphi->dest);
|
||||
}
|
||||
|
||||
static struct ir3_block *get_block(struct ir3_context *ctx, const nir_block *nblock);
|
||||
|
||||
static void
|
||||
resolve_phis(struct ir3_context *ctx, struct ir3_block *block)
|
||||
{
|
||||
foreach_instr (phi, &block->instr_list) {
|
||||
if (phi->opc != OPC_META_PHI)
|
||||
break;
|
||||
|
||||
nir_phi_instr *nphi = phi->phi.nphi;
|
||||
|
||||
for (unsigned i = 0; i < block->predecessors_count; i++) {
|
||||
struct ir3_block *pred = block->predecessors[i];
|
||||
nir_foreach_phi_src(nsrc, nphi) {
|
||||
if (get_block(ctx, nsrc->pred) == pred) {
|
||||
if (nsrc->src.ssa->parent_instr->type == nir_instr_type_ssa_undef) {
|
||||
/* Create an ir3 undef */
|
||||
ir3_reg_create(phi, INVALID_REG, phi->regs[0]->flags);
|
||||
} else {
|
||||
struct ir3_instruction *src = ir3_get_src(ctx, &nsrc->src)[0];
|
||||
__ssa_src(phi, src, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
emit_jump(struct ir3_context *ctx, nir_jump_instr *jump)
|
||||
{
|
||||
@@ -2810,8 +2864,7 @@ emit_instr(struct ir3_context *ctx, nir_instr *instr)
|
||||
emit_jump(ctx, nir_instr_as_jump(instr));
|
||||
break;
|
||||
case nir_instr_type_phi:
|
||||
/* we have converted phi webs to regs in NIR by now */
|
||||
ir3_context_error(ctx, "Unexpected NIR instruction type: %d\n", instr->type);
|
||||
emit_phi(ctx, nir_instr_as_phi(instr));
|
||||
break;
|
||||
case nir_instr_type_call:
|
||||
case nir_instr_type_parallel_copy:
|
||||
@@ -3092,6 +3145,9 @@ emit_function(struct ir3_context *ctx, nir_function_impl *impl)
|
||||
}
|
||||
|
||||
setup_predecessors(ctx->ir);
|
||||
foreach_block (block, &ctx->ir->block_list) {
|
||||
resolve_phis(ctx, block);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -1014,6 +1014,13 @@ is_output_only(struct ir3_instruction *instr)
|
||||
static void
|
||||
sched_node_add_deps(struct ir3_instruction *instr)
|
||||
{
|
||||
/* There's nothing to do for phi nodes, since they always go first. And
|
||||
* phi nodes can reference sources later in the same block, so handling
|
||||
* sources is not only unnecessary but could cause problems.
|
||||
*/
|
||||
if (instr->opc == OPC_META_PHI)
|
||||
return;
|
||||
|
||||
/* Since foreach_ssa_src() already handles false-dep's we can construct
|
||||
* the DAG easily in a single pass.
|
||||
*/
|
||||
@@ -1100,17 +1107,19 @@ sched_block(struct ir3_sched_ctx *ctx, struct ir3_block *block)
|
||||
ctx->remaining_tex++;
|
||||
}
|
||||
|
||||
/* First schedule all meta:input instructions, followed by
|
||||
* tex-prefetch. We want all of the instructions that load
|
||||
* values into registers before the shader starts to go
|
||||
* before any other instructions. But in particular we
|
||||
* want inputs to come before prefetches. This is because
|
||||
* a FS's bary_ij input may not actually be live in the
|
||||
* shader, but it should not be scheduled on top of any
|
||||
* other input (but can be overwritten by a tex prefetch)
|
||||
/* First schedule all meta:input and meta:phi instructions, followed by
|
||||
* tex-prefetch. We want all of the instructions that load values into
|
||||
* registers before the shader starts to go before any other instructions.
|
||||
* But in particular we want inputs to come before prefetches. This is
|
||||
* because a FS's bary_ij input may not actually be live in the shader,
|
||||
* but it should not be scheduled on top of any other input (but can be
|
||||
* overwritten by a tex prefetch)
|
||||
*
|
||||
* Note: Because the first block cannot have predecessors, meta:input and
|
||||
* meta:phi cannot exist in the same block.
|
||||
*/
|
||||
foreach_instr_safe (instr, &ctx->unscheduled_list)
|
||||
if (instr->opc == OPC_META_INPUT)
|
||||
if (instr->opc == OPC_META_INPUT || instr->opc == OPC_META_PHI)
|
||||
schedule(ctx, instr);
|
||||
|
||||
foreach_instr_safe (instr, &ctx->unscheduled_list)
|
||||
|
||||
@@ -72,6 +72,32 @@ validate_src(struct ir3_validate_ctx *ctx, struct ir3_register *reg)
|
||||
validate_assert(ctx, reg_class_flags(src->regs[0]) == reg_class_flags(reg));
|
||||
}
|
||||
|
||||
/* phi sources are logically read at the end of the predecessor basic block,
|
||||
* and we have to validate them then in order to correctly validate that the
|
||||
* use comes after the definition for loop phis.
|
||||
*/
|
||||
static void
|
||||
validate_phi_src(struct ir3_validate_ctx *ctx, struct ir3_block *block, struct ir3_block *pred)
|
||||
{
|
||||
unsigned pred_idx = ir3_block_get_pred_index(block, pred);
|
||||
|
||||
foreach_instr (phi, &block->instr_list) {
|
||||
if (phi->opc != OPC_META_PHI)
|
||||
break;
|
||||
|
||||
ctx->current_instr = phi;
|
||||
validate_assert(ctx, phi->regs_count == block->predecessors_count + 1);
|
||||
validate_src(ctx, phi->regs[1 + pred_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
validate_phi(struct ir3_validate_ctx *ctx, struct ir3_instruction *phi)
|
||||
{
|
||||
_mesa_set_add(ctx->defs, phi);
|
||||
validate_assert(ctx, writes_gpr(phi));
|
||||
}
|
||||
|
||||
#define validate_reg_size(ctx, reg, type) \
|
||||
validate_assert(ctx, type_size(type) == (((reg)->flags & IR3_REG_HALF) ? 16 : 32))
|
||||
|
||||
@@ -215,9 +241,29 @@ ir3_validate(struct ir3 *ir)
|
||||
ctx->defs = _mesa_pointer_set_create(ctx);
|
||||
|
||||
foreach_block (block, &ir->block_list) {
|
||||
/* We require that the first block does not have any predecessors,
|
||||
* which allows us to assume that phi nodes and meta:input's do not
|
||||
* appear in the same basic block.
|
||||
*/
|
||||
validate_assert(ctx,
|
||||
block != ir3_start_block(ir) || block->predecessors_count == 0);
|
||||
|
||||
struct ir3_instruction *prev = NULL;
|
||||
foreach_instr (instr, &block->instr_list) {
|
||||
ctx->current_instr = instr;
|
||||
validate_instr(ctx, instr);
|
||||
if (instr->opc == OPC_META_PHI) {
|
||||
/* phis must be the first in the block */
|
||||
validate_assert(ctx, prev == NULL || prev->opc == OPC_META_PHI);
|
||||
validate_phi(ctx, instr);
|
||||
} else {
|
||||
validate_instr(ctx, instr);
|
||||
}
|
||||
prev = instr;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 2; i++) {
|
||||
if (block->successors[i])
|
||||
validate_phi_src(ctx, block->successors[i], block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user