From 917a98b722da9a6761abf269727577e76f1df292 Mon Sep 17 00:00:00 2001 From: Natalie Vock Date: Mon, 17 Feb 2025 18:42:48 +0100 Subject: [PATCH] aco: Add ABI and Pseudo CALL format Part-of: --- src/amd/compiler/aco_builder_h.py | 1 + src/amd/compiler/aco_ir.cpp | 1 + src/amd/compiler/aco_ir.h | 106 +++++++++++++++++++++++++++++- src/amd/compiler/aco_opcodes.py | 7 +- 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/src/amd/compiler/aco_builder_h.py b/src/amd/compiler/aco_builder_h.py index bb0de783c86..dd2e1f80c2c 100644 --- a/src/amd/compiler/aco_builder_h.py +++ b/src/amd/compiler/aco_builder_h.py @@ -578,6 +578,7 @@ formats = [("pseudo", [Format.PSEUDO], list(itertools.product(range(5), range(7) ("branch", [Format.PSEUDO_BRANCH], [(0, 0), (0, 1)]), ("barrier", [Format.PSEUDO_BARRIER], [(0, 0)]), ("reduction", [Format.PSEUDO_REDUCTION], [(3, 3)]), + ("call", [Format.PSEUDO_CALL], [(0, 0)]), ("vop1", [Format.VOP1], [(0, 0), (1, 1), (1, 2), (2, 2)]), ("vop1_sdwa", [Format.VOP1, Format.SDWA], [(1, 1)]), ("vop2", [Format.VOP2], itertools.product([1, 2], [2, 3])), diff --git a/src/amd/compiler/aco_ir.cpp b/src/amd/compiler/aco_ir.cpp index bcaf464c9bc..4eab2e51305 100644 --- a/src/amd/compiler/aco_ir.cpp +++ b/src/amd/compiler/aco_ir.cpp @@ -1704,6 +1704,7 @@ get_instr_data_size(Format format) case Format::PSEUDO_BARRIER: return sizeof(Pseudo_barrier_instruction); case Format::PSEUDO_REDUCTION: return sizeof(Pseudo_reduction_instruction); case Format::PSEUDO_BRANCH: return sizeof(Pseudo_branch_instruction); + case Format::PSEUDO_CALL: return sizeof(Pseudo_call_instruction); case Format::DS: return sizeof(DS_instruction); case Format::FLAT: case Format::GLOBAL: diff --git a/src/amd/compiler/aco_ir.h b/src/amd/compiler/aco_ir.h index e760f174259..9eec453c8ae 100644 --- a/src/amd/compiler/aco_ir.h +++ b/src/amd/compiler/aco_ir.h @@ -11,6 +11,7 @@ #include "aco_shader_info.h" #include "aco_util.h" +#include "util/bitset.h" #include "util/compiler.h" #include "util/shader_stats.h" @@ -1114,6 +1115,74 @@ struct RegisterDemand { } }; +struct ABI { + struct GPRRange { + uint16_t sgpr; + uint16_t vgpr; + }; + + struct RegisterBlock { + GPRRange clobbered_size; + GPRRange preserved_size; + bool clobbered_first; + }; + + RegisterBlock block_size; + /* Maximum number of registers allowed to be used by parameters */ + RegisterDemand max_param_demand; + + void preservedRegisters(BITSET_DECLARE(regs, 512), + RegisterDemand reg_limit = RegisterDemand(256, 256)) const + { + unsigned size = DIV_ROUND_UP(512, BITSET_WORDBITS) * sizeof(BITSET_WORD); + memset(regs, 0, size); + + /* Fill out preserved ranges by repeating the register block across the register file */ + + /* SGPR ranges */ + unsigned gpr_offset = block_size.clobbered_first ? block_size.clobbered_size.sgpr : 0; + while (gpr_offset < (unsigned)reg_limit.sgpr) { + unsigned end = MIN2((unsigned)reg_limit.sgpr, gpr_offset + block_size.preserved_size.sgpr); + BITSET_SET_RANGE(regs, gpr_offset, end - 1); + gpr_offset = end + block_size.clobbered_size.sgpr; + } + /* VGPR ranges */ + gpr_offset = block_size.clobbered_first ? block_size.clobbered_size.vgpr : 0; + while (gpr_offset < (unsigned)reg_limit.vgpr) { + unsigned end = MIN2((unsigned)reg_limit.vgpr, gpr_offset + block_size.preserved_size.vgpr); + BITSET_SET_RANGE(regs, 256 + gpr_offset, 256 + end - 1); + gpr_offset = end + block_size.clobbered_size.vgpr; + } + } +}; + +static constexpr ABI rtRaygenABI = { + ABI::RegisterBlock{ + ABI::GPRRange{128u, 256u}, /* clobbered_size */ + ABI::GPRRange{0u, 0u}, /* preserved_size */ + true, /* preserved_first */ + }, + RegisterDemand(32, 32), /* max_param_demand */ +}; + +static constexpr ABI rtTraversalABI = { + ABI::RegisterBlock{ + ABI::GPRRange{128u, 256u}, /* clobbered_size */ + ABI::GPRRange{0u, 0u}, /* preserved_size */ + true, /* preserved_first */ + }, + RegisterDemand(32, 32), /* max_param_demand */ +}; + +static constexpr ABI rtAnyHitABI = { + ABI::RegisterBlock{ + ABI::GPRRange{128u, 256u}, /* clobbered_size */ + ABI::GPRRange{80u, 80u}, /* preserved_size */ + false, /* preserved_first */ + }, + RegisterDemand(32, 32), /* max_param_demand */ +}; + struct Block; struct Instruction; struct Pseudo_instruction; @@ -1129,6 +1198,7 @@ struct FLAT_instruction; struct Pseudo_branch_instruction; struct Pseudo_barrier_instruction; struct Pseudo_reduction_instruction; +struct Pseudo_call_instruction; struct VALU_instruction; struct VINTERP_inreg_instruction; struct VINTRP_instruction; @@ -1329,6 +1399,17 @@ struct Instruction { return *(Pseudo_reduction_instruction*)this; } constexpr bool isReduction() const noexcept { return format == Format::PSEUDO_REDUCTION; } + Pseudo_call_instruction& call() noexcept + { + assert(isCall()); + return *(Pseudo_call_instruction*)this; + } + const Pseudo_call_instruction& call() const noexcept + { + assert(isCall()); + return *(Pseudo_call_instruction*)this; + } + constexpr bool isCall() const noexcept { return format == Format::PSEUDO_CALL; } constexpr bool isVOP3P() const noexcept { return (uint16_t)format & (uint16_t)Format::VOP3P; } VINTERP_inreg_instruction& vinterp_inreg() noexcept { @@ -1816,6 +1897,27 @@ struct Pseudo_reduction_instruction : public Instruction { static_assert(sizeof(Pseudo_reduction_instruction) == sizeof(Instruction) + 4, "Unexpected padding"); +struct Pseudo_call_instruction : public Instruction { + /* The amount of "free" preserved registers we can use to stash caller-owned temporaries instead + * of needing to spill them. This is equal to the amount of preserved registers given by the ABI, + * minus the amount of preserved registers already occupied by call parameters. + */ + RegisterDemand callee_preserved_limit; + /* Register demand of caller-owned temporaries we need to preserve a copy of throughout the + * callee. This includes all live variables not used by the call instruction as well as clobbered + * Operand temporaries (unless the call kills the operand - we don't need to preserve a copy in + * that case). Non-clobbered Operand temporaries are not included in this demand - they occupy a + * preserved register, which is accounted for in the calculation of callee_preserved_limit + * already. + * + * Iff this demand is higher than callee_preserved_limit, some of the temporaries need to be + * spilled to scratch. + */ + RegisterDemand caller_preserved_demand; + + ABI abi; +}; + inline bool Instruction::accessesLDS() const noexcept { @@ -1894,8 +1996,8 @@ memory_sync_info get_sync_info(const Instruction* instr); inline bool is_dead(const std::vector& uses, const Instruction* instr) { - if (instr->definitions.empty() || instr->isBranch() || instr->opcode == aco_opcode::p_startpgm || - instr->opcode == aco_opcode::p_init_scratch || + if (instr->definitions.empty() || instr->isBranch() || instr->isCall() || + instr->opcode == aco_opcode::p_startpgm || instr->opcode == aco_opcode::p_init_scratch || instr->opcode == aco_opcode::p_dual_src_export_gfx11) return false; diff --git a/src/amd/compiler/aco_opcodes.py b/src/amd/compiler/aco_opcodes.py index 36d6da07e29..0a5ea3daf13 100644 --- a/src/amd/compiler/aco_opcodes.py +++ b/src/amd/compiler/aco_opcodes.py @@ -50,6 +50,7 @@ class Format(IntEnum): PSEUDO_BRANCH = auto() PSEUDO_BARRIER = auto() PSEUDO_REDUCTION = auto() + PSEUDO_CALL = auto() # Scalar ALU & Control Formats SOP1 = auto() SOP2 = auto() @@ -93,7 +94,7 @@ class Format(IntEnum): return "salu" elif self in [Format.FLAT, Format.GLOBAL, Format.SCRATCH]: return "flatlike" - elif self in [Format.PSEUDO_BRANCH, Format.PSEUDO_REDUCTION, Format.PSEUDO_BARRIER]: + elif self in [Format.PSEUDO_BRANCH, Format.PSEUDO_REDUCTION, Format.PSEUDO_BARRIER, Format.PSEUDO_CALL]: return self.name.split("_")[-1].lower() else: return self.name.lower() @@ -163,6 +164,8 @@ class Format(IntEnum): elif self == Format.PSEUDO_BARRIER: return [('memory_sync_info', 'sync', None), ('sync_scope', 'exec_scope', 'scope_invocation')] + elif self == Format.PSEUDO_CALL: + return [('ABI', 'abi', None)] elif self == Format.VINTRP: return [('unsigned', 'attribute', None), ('unsigned', 'component', None), @@ -376,6 +379,8 @@ insn("p_cbranch_nz", format=Format.PSEUDO_BRANCH) insn("p_barrier", format=Format.PSEUDO_BARRIER) +insn("p_call", format=Format.PSEUDO_CALL) + # Primitive Ordered Pixel Shading pseudo-instructions. # For querying whether the current wave can enter the ordered section on GFX9-10.3, doing