diff options
author | Timur Kristóf <timur.kristof@gmail.com> | 2019-10-24 11:45:27 +0200 |
---|---|---|
committer | Timur Kristóf <timur.kristof@gmail.com> | 2019-10-25 10:10:42 +0200 |
commit | 09d676d81ab6e604f73f65ad696a9996312f93a4 (patch) | |
tree | 9c3e4f3e1323c64ba25952f3a01df40d4867ac53 /src/amd/compiler/aco_insert_NOPs.cpp | |
parent | d6dfce02d074d615a3b88a3fccd8ee8c7e13c010 (diff) |
aco/gfx10: Mitigate SMEMtoVectorWriteHazard.
There is a hazard that happens when an SMEM instruction
reads an SGPR and then a VALU instruction writes that same SGPR.
This commit adds a workaround that avoids the problem.
Signed-off-by: Timur Kristóf <timur.kristof@gmail.com>
Reviewed-by: Daniel Schürmann <daniel@schuermann.dev>
Diffstat (limited to 'src/amd/compiler/aco_insert_NOPs.cpp')
-rw-r--r-- | src/amd/compiler/aco_insert_NOPs.cpp | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/src/amd/compiler/aco_insert_NOPs.cpp b/src/amd/compiler/aco_insert_NOPs.cpp index 034e9e3949e..6924ea152a2 100644 --- a/src/amd/compiler/aco_insert_NOPs.cpp +++ b/src/amd/compiler/aco_insert_NOPs.cpp @@ -43,12 +43,38 @@ struct NOP_ctx { int last_VMEM_since_scalar_write = -1; bool has_VOPC = false; bool has_nonVALU_exec_read = false; + std::bitset<128> sgprs_read_by_SMEM; NOP_ctx(Program* program) : chip_class(program->chip_class) { vcc_physical = program->config->num_sgprs - 2; } }; +template <std::size_t N> +bool check_written_regs(const aco_ptr<Instruction> &instr, const std::bitset<N> &check_regs) +{ + return std::any_of(instr->definitions.begin(), instr->definitions.end(), [&check_regs](const Definition &def) -> bool { + bool writes_any = false; + for (unsigned i = 0; i < def.size(); i++) { + unsigned def_reg = def.physReg() + i; + writes_any |= def_reg < check_regs.size() && check_regs[def_reg]; + } + return writes_any; + }); +} + +template <std::size_t N> +void mark_read_regs(const aco_ptr<Instruction> &instr, std::bitset<N> ®_reads) +{ + for (const Operand &op : instr->operands) { + for (unsigned i = 0; i < op.size(); i++) { + unsigned reg = op.physReg() + i; + if (reg < reg_reads.size()) + reg_reads.set(reg); + } + } +} + bool VALU_writes_sgpr(aco_ptr<Instruction>& instr) { if ((uint32_t) instr->format & (uint32_t) Format::VOPC) @@ -352,6 +378,41 @@ std::pair<int, int> handle_instruction_gfx10(NOP_ctx& ctx, aco_ptr<Instruction>& ctx.has_nonVALU_exec_read = false; } + /* SMEMtoVectorWriteHazard + * Handle any VALU instruction writing an SGPR after an SMEM reads it. + */ + if (instr->format == Format::SMEM) { + /* Remember all SGPRs that are read by the SMEM instruction */ + mark_read_regs(instr, ctx.sgprs_read_by_SMEM); + } else if (VALU_writes_sgpr(instr)) { + /* Check if VALU writes an SGPR that was previously read by SMEM */ + if (check_written_regs(instr, ctx.sgprs_read_by_SMEM)) { + ctx.sgprs_read_by_SMEM.reset(); + + /* Insert s_mov to mitigate the problem */ + aco_ptr<SOP1_instruction> s_mov{create_instruction<SOP1_instruction>(aco_opcode::s_mov_b32, Format::SOP1, 1, 1)}; + s_mov->definitions[0] = Definition(sgpr_null, s1); + s_mov->operands[0] = Operand(0u); + new_instructions.emplace_back(std::move(s_mov)); + } + } else if (instr->isSALU()) { + if (instr->format != Format::SOPP) { + /* SALU can mitigate the hazard */ + ctx.sgprs_read_by_SMEM.reset(); + } else { + /* Reducing lgkmcnt count to 0 always mitigates the hazard. */ + const SOPP_instruction *sopp = static_cast<const SOPP_instruction *>(instr.get()); + if (sopp->opcode == aco_opcode::s_waitcnt_lgkmcnt) { + if (sopp->imm == 0 && sopp->definitions[0].physReg() == sgpr_null) + ctx.sgprs_read_by_SMEM.reset(); + } else if (sopp->opcode == aco_opcode::s_waitcnt) { + unsigned lgkm = (sopp->imm >> 8) & 0x3f; + if (lgkm == 0) + ctx.sgprs_read_by_SMEM.reset(); + } + } + } + return std::make_pair(sNOPs, vNOPs); } |