// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include #include #include #include "common/common_types.h" #include "common/polyfill_ranges.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/frontend/maxwell/decode.h" #include "shader_recompiler/frontend/maxwell/opcodes.h" namespace Shader::Maxwell { namespace { struct MaskValue { u64 mask; u64 value; }; constexpr MaskValue MaskValueFromEncoding(const char* encoding) { u64 mask{}; u64 value{}; u64 bit{u64(1) << 63}; while (*encoding) { switch (*encoding) { case '0': mask |= bit; break; case '1': mask |= bit; value |= bit; break; case '-': break; case ' ': break; default: throw LogicError("Invalid encoding character '{}'", *encoding); } ++encoding; if (*encoding != ' ') { bit >>= 1; } } return MaskValue{.mask = mask, .value = value}; } struct InstEncoding { MaskValue mask_value; Opcode opcode; }; constexpr std::array UNORDERED_ENCODINGS{ #define INST(name, cute, encode) \ InstEncoding{ \ .mask_value{MaskValueFromEncoding(encode)}, \ .opcode = Opcode::name, \ }, #include "maxwell.inc" #undef INST }; constexpr auto SortedEncodings() { std::array encodings{UNORDERED_ENCODINGS}; std::ranges::sort(encodings, [](const InstEncoding& lhs, const InstEncoding& rhs) { return std::popcount(lhs.mask_value.mask) > std::popcount(rhs.mask_value.mask); }); return encodings; } constexpr auto ENCODINGS{SortedEncodings()}; constexpr int WidestLeftBits() { int bits{64}; for (const InstEncoding& encoding : ENCODINGS) { bits = std::min(bits, std::countr_zero(encoding.mask_value.mask)); } return 64 - bits; } constexpr int WIDEST_LEFT_BITS{WidestLeftBits()}; constexpr int MASK_SHIFT{64 - WIDEST_LEFT_BITS}; constexpr size_t ToFastLookupIndex(u64 value) { return static_cast(value >> MASK_SHIFT); } constexpr size_t FastLookupSize() { size_t max_width{}; for (const InstEncoding& encoding : ENCODINGS) { max_width = std::max(max_width, ToFastLookupIndex(encoding.mask_value.mask)); } return max_width + 1; } constexpr size_t FAST_LOOKUP_SIZE{FastLookupSize()}; struct InstInfo { [[nodiscard]] u64 Mask() const noexcept { return static_cast(high_mask) << MASK_SHIFT; } [[nodiscard]] u64 Value() const noexcept { return static_cast(high_value) << MASK_SHIFT; } u16 high_mask; u16 high_value; Opcode opcode; }; constexpr auto MakeFastLookupTableIndex(size_t index) { std::array encodings{}; size_t element{}; for (const auto& encoding : ENCODINGS) { const size_t mask{ToFastLookupIndex(encoding.mask_value.mask)}; const size_t value{ToFastLookupIndex(encoding.mask_value.value)}; if ((index & mask) == value) { encodings.at(element) = InstInfo{ .high_mask = static_cast(encoding.mask_value.mask >> MASK_SHIFT), .high_value = static_cast(encoding.mask_value.value >> MASK_SHIFT), .opcode = encoding.opcode, }; ++element; } } return encodings; } /*constexpr*/ auto MakeFastLookupTable() { auto encodings{std::make_unique, FAST_LOOKUP_SIZE>>()}; for (size_t index = 0; index < FAST_LOOKUP_SIZE; ++index) { (*encodings)[index] = MakeFastLookupTableIndex(index); } return encodings; } const auto FAST_LOOKUP_TABLE{MakeFastLookupTable()}; } // Anonymous namespace Opcode Decode(u64 insn) { const auto& table{(*FAST_LOOKUP_TABLE)[ToFastLookupIndex(insn)]}; const auto it{std::ranges::find_if( table, [insn](const InstInfo& info) { return (insn & info.Mask()) == info.Value(); })}; if (it == table.end()) { throw NotImplementedException("Instruction 0x{:016x} is unknown / unimplemented", insn); } return it->opcode; } } // namespace Shader::Maxwell