From 6b538506f2ae77cb7dd54ee2768946c3155ba529 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 2 Dec 2020 18:35:55 +0100 Subject: [PATCH] aco/ra: Fix register allocation for subdword operands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ACO attempts to store the output of an instruction in the same register occupied by its operands where possible. Importantly this only works if the operands are large enough to store the result register size. The code failed to consider subdword operands when checking for this, causing entire register slots to be freed up even though subdword parts were still used. In Mafia 3, this affected the following code: v2b: %363:v[2][0:16], v2b: %362:v[2][16:32] = p_split_vector %360:v[2] v1: %116:v[2] = v_cvt_f32_f16 %362:v[2][16:32] v1: %117:v[2] = v_cvt_f32_f16 %363:v[2][0:16] where v[2] is allocated to %116 even though its original lower 16 bits are still used in the instruction after. Reviewed-by: Daniel Schürmann Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/3717 Fixes: 031edbc4a54d5685b05e244f8aa1e094ec246eb5 Part-of: --- src/amd/compiler/aco_register_allocation.cpp | 31 ++++++++++++-------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/amd/compiler/aco_register_allocation.cpp b/src/amd/compiler/aco_register_allocation.cpp index dd59239e29b..a75de106955 100644 --- a/src/amd/compiler/aco_register_allocation.cpp +++ b/src/amd/compiler/aco_register_allocation.cpp @@ -158,6 +158,7 @@ public: return res; } + /* Returns true if any of the bytes in the given range are allocated or blocked */ bool test(PhysReg start, unsigned num_bytes) { for (PhysReg i = start; i.reg_b < start.reg_b + num_bytes; i = PhysReg(i + 1)) { if (regs[i] & 0x0FFFFFFF) @@ -968,15 +969,21 @@ std::pair get_reg_impl(ra_ctx& ctx, /* mark and count killed operands */ unsigned killed_ops = 0; + std::bitset<256> is_killed_operand; /* per-register */ for (unsigned j = 0; !is_phi(instr) && j < instr->operands.size(); j++) { - if (instr->operands[j].isTemp() && - instr->operands[j].isFirstKillBeforeDef() && - instr->operands[j].physReg() >= lb && - instr->operands[j].physReg() < ub && - !reg_file.test(instr->operands[j].physReg(), instr->operands[j].bytes())) { - assert(instr->operands[j].isFixed()); - tmp_file.block(instr->operands[j].physReg(), instr->operands[j].regClass()); - killed_ops += instr->operands[j].getTemp().size(); + Operand& op = instr->operands[j]; + if (op.isTemp() && + op.isFirstKillBeforeDef() && + op.physReg() >= lb && + op.physReg() < ub && + !reg_file.test(PhysReg{op.physReg().reg()}, align(op.bytes() + op.physReg().byte(), 4))) { + assert(op.isFixed()); + + for (unsigned i = 0; i < op.size(); ++i) { + is_killed_operand[(op.physReg() & 0xff) + i] = true; + } + + killed_ops += op.getTemp().size(); } } @@ -1015,11 +1022,8 @@ std::pair get_reg_impl(ra_ctx& ctx, bool found = true; bool aligned = rc == RegClass::v4 && reg_lo % 4 == 0; for (unsigned j = reg_lo; found && j <= reg_hi; j++) { - if (tmp_file[j] == 0 || tmp_file[j] == last_var) - continue; - /* dead operands effectively reduce the number of estimated moves */ - if (tmp_file.is_blocked(PhysReg{j})) { + if (is_killed_operand[j & 0xFF]) { if (remaining_op_moves) { k--; remaining_op_moves--; @@ -1027,6 +1031,9 @@ std::pair get_reg_impl(ra_ctx& ctx, continue; } + if (tmp_file[j] == 0 || tmp_file[j] == last_var) + continue; + if (tmp_file[j] == 0xF0000000) { k += 1; n++;