diff --git a/src/amd/compiler/aco_lower_phis.cpp b/src/amd/compiler/aco_lower_phis.cpp index 6b8f611ecc7..f20f4f3c96e 100644 --- a/src/amd/compiler/aco_lower_phis.cpp +++ b/src/amd/compiler/aco_lower_phis.cpp @@ -192,6 +192,50 @@ build_merge_code(Program* program, ssa_state* state, Block* block, Operand cur) return; } +void +build_const_else_merge_code(Program* program, Block& invert_block, aco_ptr& phi) +{ + /* When the else-side operand of a binary merge phi is constant, + * we can use a simpler way to lower the phi by emitting some + * instructions to the invert block instead. + * This allows us to actually delete the else block when it's empty. + */ + assert(invert_block.kind & block_kind_invert); + Builder bld(program); + Operand then = phi->operands[0]; + const Operand els = phi->operands[1]; + + /* Only -1 (all lanes true) and 0 (all lanes false) constants are supported here. */ + assert(!then.isConstant() || then.constantEquals(0) || then.constantEquals(-1)); + assert(els.constantEquals(0) || els.constantEquals(-1)); + + if (!then.isConstant()) { + /* Left-hand operand is not constant, so we need to emit a phi to access it. */ + bld.reset(&invert_block.instructions, invert_block.instructions.begin()); + then = bld.pseudo(aco_opcode::p_linear_phi, bld.def(bld.lm), then, Operand(bld.lm)); + } + + auto after_phis = + std::find_if(invert_block.instructions.begin(), invert_block.instructions.end(), + [](const aco_ptr& instr) -> bool { return !is_phi(instr.get()); }); + bld.reset(&invert_block.instructions, after_phis); + + Temp tmp; + if (then.constantEquals(-1) && els.constantEquals(0)) { + tmp = bld.copy(bld.def(bld.lm), Operand(exec, bld.lm)); + } else { + Builder::WaveSpecificOpcode opc = els.constantEquals(0) ? Builder::s_and : Builder::s_orn2; + tmp = bld.sop2(opc, bld.def(bld.lm), bld.def(s1, scc), then, Operand(exec, bld.lm)); + } + + /* We can't delete the original phi because that'd invalidate the iterator in lower_phis, + * so just make it a trivial phi instead. + */ + phi->opcode = aco_opcode::p_linear_phi; + phi->operands[0] = Operand(tmp); + phi->operands[1] = Operand(tmp); +} + void init_any_pred_defined(Program* program, ssa_state* state, Block* block, aco_ptr& phi) { @@ -268,6 +312,12 @@ lower_divergent_bool_phi(Program* program, ssa_state* state, Block* block, return; } + if (phi->operands.size() == 2 && phi->operands[1].isConstant() && + (block->kind & block_kind_merge)) { + build_const_else_merge_code(program, program->blocks[block->linear_idom], phi); + return; + } + /* do this here to avoid resizing in case of no boolean phis */ state->visited.resize(program->blocks.size()); state->outputs.resize(program->blocks.size());