diff options
Diffstat (limited to 'src/shader_recompiler/backend/glasm/reg_alloc.h')
-rw-r--r-- | src/shader_recompiler/backend/glasm/reg_alloc.h | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/src/shader_recompiler/backend/glasm/reg_alloc.h b/src/shader_recompiler/backend/glasm/reg_alloc.h new file mode 100644 index 000000000..82aec66c6 --- /dev/null +++ b/src/shader_recompiler/backend/glasm/reg_alloc.h @@ -0,0 +1,303 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <bitset> + +#include <fmt/format.h> + +#include "common/bit_cast.h" +#include "common/bit_field.h" +#include "common/common_types.h" +#include "shader_recompiler/exception.h" + +namespace Shader::IR { +class Inst; +class Value; +} // namespace Shader::IR + +namespace Shader::Backend::GLASM { + +class EmitContext; + +enum class Type : u32 { + Void, + Register, + U32, + U64, +}; + +struct Id { + union { + u32 raw; + BitField<0, 1, u32> is_valid; + BitField<1, 1, u32> is_long; + BitField<2, 1, u32> is_spill; + BitField<3, 1, u32> is_condition_code; + BitField<4, 1, u32> is_null; + BitField<5, 27, u32> index; + }; + + bool operator==(Id rhs) const noexcept { + return raw == rhs.raw; + } + bool operator!=(Id rhs) const noexcept { + return !operator==(rhs); + } +}; +static_assert(sizeof(Id) == sizeof(u32)); + +struct Value { + Type type; + union { + Id id; + u32 imm_u32; + u64 imm_u64; + }; + + bool operator==(const Value& rhs) const noexcept { + if (type != rhs.type) { + return false; + } + switch (type) { + case Type::Void: + return true; + case Type::Register: + return id == rhs.id; + case Type::U32: + return imm_u32 == rhs.imm_u32; + case Type::U64: + return imm_u64 == rhs.imm_u64; + } + return false; + } + bool operator!=(const Value& rhs) const noexcept { + return !operator==(rhs); + } +}; +struct Register : Value {}; +struct ScalarRegister : Value {}; +struct ScalarU32 : Value {}; +struct ScalarS32 : Value {}; +struct ScalarF32 : Value {}; +struct ScalarF64 : Value {}; + +class RegAlloc { +public: + RegAlloc() = default; + + Register Define(IR::Inst& inst); + + Register LongDefine(IR::Inst& inst); + + [[nodiscard]] Value Peek(const IR::Value& value); + + Value Consume(const IR::Value& value); + + void Unref(IR::Inst& inst); + + [[nodiscard]] Register AllocReg(); + + [[nodiscard]] Register AllocLongReg(); + + void FreeReg(Register reg); + + void InvalidateConditionCodes() { + // This does nothing for now + } + + [[nodiscard]] size_t NumUsedRegisters() const noexcept { + return num_used_registers; + } + + [[nodiscard]] size_t NumUsedLongRegisters() const noexcept { + return num_used_long_registers; + } + + [[nodiscard]] bool IsEmpty() const noexcept { + return register_use.none() && long_register_use.none(); + } + + /// Returns true if the instruction is expected to be aliased to another + static bool IsAliased(const IR::Inst& inst); + + /// Returns the underlying value out of an alias sequence + static IR::Inst& AliasInst(IR::Inst& inst); + +private: + static constexpr size_t NUM_REGS = 4096; + static constexpr size_t NUM_ELEMENTS = 4; + + Value MakeImm(const IR::Value& value); + + Register Define(IR::Inst& inst, bool is_long); + + Value PeekInst(IR::Inst& inst); + + Value ConsumeInst(IR::Inst& inst); + + Id Alloc(bool is_long); + + void Free(Id id); + + size_t num_used_registers{}; + size_t num_used_long_registers{}; + std::bitset<NUM_REGS> register_use{}; + std::bitset<NUM_REGS> long_register_use{}; +}; + +template <bool scalar, typename FormatContext> +auto FormatTo(FormatContext& ctx, Id id) { + if (id.is_condition_code != 0) { + throw NotImplementedException("Condition code emission"); + } + if (id.is_spill != 0) { + throw NotImplementedException("Spill emission"); + } + if constexpr (scalar) { + if (id.is_null != 0) { + return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC.x" : "RC.x"); + } + if (id.is_long != 0) { + return fmt::format_to(ctx.out(), "D{}.x", id.index.Value()); + } else { + return fmt::format_to(ctx.out(), "R{}.x", id.index.Value()); + } + } else { + if (id.is_null != 0) { + return fmt::format_to(ctx.out(), "{}", id.is_long != 0 ? "DC" : "RC"); + } + if (id.is_long != 0) { + return fmt::format_to(ctx.out(), "D{}", id.index.Value()); + } else { + return fmt::format_to(ctx.out(), "R{}", id.index.Value()); + } + } +} + +} // namespace Shader::Backend::GLASM + +template <> +struct fmt::formatter<Shader::Backend::GLASM::Id> { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(Shader::Backend::GLASM::Id id, FormatContext& ctx) { + return Shader::Backend::GLASM::FormatTo<true>(ctx, id); + } +}; + +template <> +struct fmt::formatter<Shader::Backend::GLASM::Register> { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(const Shader::Backend::GLASM::Register& value, FormatContext& ctx) { + if (value.type != Shader::Backend::GLASM::Type::Register) { + throw Shader::InvalidArgument("Register value type is not register"); + } + return Shader::Backend::GLASM::FormatTo<false>(ctx, value.id); + } +}; + +template <> +struct fmt::formatter<Shader::Backend::GLASM::ScalarRegister> { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(const Shader::Backend::GLASM::ScalarRegister& value, FormatContext& ctx) { + if (value.type != Shader::Backend::GLASM::Type::Register) { + throw Shader::InvalidArgument("Register value type is not register"); + } + return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id); + } +}; + +template <> +struct fmt::formatter<Shader::Backend::GLASM::ScalarU32> { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(const Shader::Backend::GLASM::ScalarU32& value, FormatContext& ctx) { + switch (value.type) { + case Shader::Backend::GLASM::Type::Void: + break; + case Shader::Backend::GLASM::Type::Register: + return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id); + case Shader::Backend::GLASM::Type::U32: + return fmt::format_to(ctx.out(), "{}", value.imm_u32); + case Shader::Backend::GLASM::Type::U64: + break; + } + throw Shader::InvalidArgument("Invalid value type {}", value.type); + } +}; + +template <> +struct fmt::formatter<Shader::Backend::GLASM::ScalarS32> { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(const Shader::Backend::GLASM::ScalarS32& value, FormatContext& ctx) { + switch (value.type) { + case Shader::Backend::GLASM::Type::Void: + break; + case Shader::Backend::GLASM::Type::Register: + return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id); + case Shader::Backend::GLASM::Type::U32: + return fmt::format_to(ctx.out(), "{}", static_cast<s32>(value.imm_u32)); + case Shader::Backend::GLASM::Type::U64: + break; + } + throw Shader::InvalidArgument("Invalid value type {}", value.type); + } +}; + +template <> +struct fmt::formatter<Shader::Backend::GLASM::ScalarF32> { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(const Shader::Backend::GLASM::ScalarF32& value, FormatContext& ctx) { + switch (value.type) { + case Shader::Backend::GLASM::Type::Void: + break; + case Shader::Backend::GLASM::Type::Register: + return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id); + case Shader::Backend::GLASM::Type::U32: + return fmt::format_to(ctx.out(), "{}", Common::BitCast<f32>(value.imm_u32)); + case Shader::Backend::GLASM::Type::U64: + break; + } + throw Shader::InvalidArgument("Invalid value type {}", value.type); + } +}; + +template <> +struct fmt::formatter<Shader::Backend::GLASM::ScalarF64> { + constexpr auto parse(format_parse_context& ctx) { + return ctx.begin(); + } + template <typename FormatContext> + auto format(const Shader::Backend::GLASM::ScalarF64& value, FormatContext& ctx) { + switch (value.type) { + case Shader::Backend::GLASM::Type::Void: + break; + case Shader::Backend::GLASM::Type::Register: + return Shader::Backend::GLASM::FormatTo<true>(ctx, value.id); + case Shader::Backend::GLASM::Type::U32: + break; + case Shader::Backend::GLASM::Type::U64: + return fmt::format_to(ctx.out(), "{}", Common::BitCast<f64>(value.imm_u64)); + } + throw Shader::InvalidArgument("Invalid value type {}", value.type); + } +}; |