diff options
| -rw-r--r-- | src/video_core/engines/shader_bytecode.h | 357 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 49 | 
2 files changed, 201 insertions, 205 deletions
| diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index e6c2fd367..f4dfef76a 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -4,10 +4,16 @@  #pragma once +#include <bitset>  #include <cstring>  #include <map>  #include <string> +#include <vector> + +#include <boost/optional.hpp> +  #include "common/bit_field.h" +#include "common/common_types.h"  namespace Tegra {  namespace Shader { @@ -89,188 +95,12 @@ union Uniform {      BitField<34, 5, u64> index;  }; -union OpCode { -    enum class Id : u64 { -        TEXS = 0x6C, -        IPA = 0xE0, -        FMUL32_IMM = 0x1E, -        FFMA_IMM = 0x65, -        FFMA_CR = 0x93, -        FFMA_RC = 0xA3, -        FFMA_RR = 0xB3, - -        FADD_C = 0x98B, -        FMUL_C = 0x98D, -        MUFU = 0xA10, -        FADD_R = 0xB8B, -        FMUL_R = 0xB8D, -        LD_A = 0x1DFB, -        ST_A = 0x1DFE, - -        FSETP_R = 0x5BB, -        FSETP_C = 0x4BB, -        FSETP_IMM = 0x36B, -        FSETP_NEG_IMM = 0x37B, -        EXIT = 0xE30, -        KIL = 0xE33, - -        FMUL_IMM = 0x70D, -        FMUL_IMM_x = 0x72D, -        FADD_IMM = 0x70B, -        FADD_IMM_x = 0x72B, -    }; - -    enum class Type { -        Trivial, -        Arithmetic, -        Ffma, -        Flow, -        Memory, -        FloatPredicate, -        Unknown, -    }; - -    struct Info { -        Type type; -        std::string name; -    }; - -    OpCode() = default; - -    constexpr OpCode(Id value) : value(static_cast<u64>(value)) {} - -    constexpr OpCode(u64 value) : value{value} {} - -    constexpr Id EffectiveOpCode() const { -        switch (op1) { -        case Id::TEXS: -            return op1; -        } - -        switch (op2) { -        case Id::IPA: -        case Id::FMUL32_IMM: -            return op2; -        } - -        switch (op3) { -        case Id::FFMA_IMM: -        case Id::FFMA_CR: -        case Id::FFMA_RC: -        case Id::FFMA_RR: -            return op3; -        } - -        switch (op4) { -        case Id::EXIT: -        case Id::FSETP_R: -        case Id::FSETP_C: -        case Id::KIL: -            return op4; -        case Id::FSETP_IMM: -        case Id::FSETP_NEG_IMM: -            return Id::FSETP_IMM; -        } - -        switch (op5) { -        case Id::MUFU: -        case Id::LD_A: -        case Id::ST_A: -        case Id::FADD_R: -        case Id::FADD_C: -        case Id::FMUL_R: -        case Id::FMUL_C: -            return op5; - -        case Id::FMUL_IMM: -        case Id::FMUL_IMM_x: -            return Id::FMUL_IMM; - -        case Id::FADD_IMM: -        case Id::FADD_IMM_x: -            return Id::FADD_IMM; -        } - -        return static_cast<Id>(value); -    } - -    static const Info& GetInfo(const OpCode& opcode) { -        static const std::map<Id, Info> info_table{BuildInfoTable()}; -        const auto& search{info_table.find(opcode.EffectiveOpCode())}; -        if (search != info_table.end()) { -            return search->second; -        } - -        static const Info unknown{Type::Unknown, "UNK"}; -        return unknown; -    } - -    constexpr operator Id() const { -        return static_cast<Id>(value); -    } - -    constexpr OpCode operator<<(size_t bits) const { -        return value << bits; -    } - -    constexpr OpCode operator>>(size_t bits) const { -        return value >> bits; -    } - -    template <typename T> -    constexpr u64 operator-(const T& oth) const { -        return value - oth; -    } - -    constexpr u64 operator&(const OpCode& oth) const { -        return value & oth.value; -    } - -    constexpr u64 operator~() const { -        return ~value; -    } - -    static std::map<Id, Info> BuildInfoTable() { -        std::map<Id, Info> info_table; -        info_table[Id::TEXS] = {Type::Memory, "texs"}; -        info_table[Id::LD_A] = {Type::Memory, "ld_a"}; -        info_table[Id::ST_A] = {Type::Memory, "st_a"}; -        info_table[Id::MUFU] = {Type::Arithmetic, "mufu"}; -        info_table[Id::FFMA_IMM] = {Type::Ffma, "ffma_imm"}; -        info_table[Id::FFMA_CR] = {Type::Ffma, "ffma_cr"}; -        info_table[Id::FFMA_RC] = {Type::Ffma, "ffma_rc"}; -        info_table[Id::FFMA_RR] = {Type::Ffma, "ffma_rr"}; -        info_table[Id::FADD_R] = {Type::Arithmetic, "fadd_r"}; -        info_table[Id::FADD_C] = {Type::Arithmetic, "fadd_c"}; -        info_table[Id::FADD_IMM] = {Type::Arithmetic, "fadd_imm"}; -        info_table[Id::FMUL_R] = {Type::Arithmetic, "fmul_r"}; -        info_table[Id::FMUL_C] = {Type::Arithmetic, "fmul_c"}; -        info_table[Id::FMUL_IMM] = {Type::Arithmetic, "fmul_imm"}; -        info_table[Id::FMUL32_IMM] = {Type::Arithmetic, "fmul32_imm"}; -        info_table[Id::FSETP_C] = {Type::FloatPredicate, "fsetp_c"}; -        info_table[Id::FSETP_R] = {Type::FloatPredicate, "fsetp_r"}; -        info_table[Id::FSETP_IMM] = {Type::FloatPredicate, "fsetp_imm"}; -        info_table[Id::EXIT] = {Type::Trivial, "exit"}; -        info_table[Id::IPA] = {Type::Trivial, "ipa"}; -        info_table[Id::KIL] = {Type::Flow, "kil"}; -        return info_table; -    } - -    BitField<57, 7, Id> op1; -    BitField<56, 8, Id> op2; -    BitField<55, 9, Id> op3; -    BitField<52, 12, Id> op4; -    BitField<51, 13, Id> op5; -    u64 value{}; -}; -static_assert(sizeof(OpCode) == 0x8, "Incorrect structure size"); -  } // namespace Shader  } // namespace Tegra  namespace std { -// TODO(bunne): The below is forbidden by the C++ standard, but works fine. See #330. +// TODO(bunnei): The below is forbidden by the C++ standard, but works fine. See #330.  template <>  struct make_unsigned<Tegra::Shader::Attribute> {      using type = Tegra::Shader::Attribute; @@ -281,11 +111,6 @@ struct make_unsigned<Tegra::Shader::Register> {      using type = Tegra::Shader::Register;  }; -template <> -struct make_unsigned<Tegra::Shader::OpCode> { -    using type = Tegra::Shader::OpCode; -}; -  } // namespace std  namespace Tegra { @@ -324,11 +149,12 @@ enum class SubOp : u64 {  union Instruction {      Instruction& operator=(const Instruction& instr) { -        hex = instr.hex; +        value = instr.value;          return *this;      } -    OpCode opcode; +    constexpr Instruction(u64 value) : value{value} {} +      BitField<0, 8, Register> gpr0;      BitField<8, 8, Register> gpr8;      union { @@ -340,6 +166,7 @@ union Instruction {      BitField<20, 7, SubOp> sub_op;      BitField<28, 8, Register> gpr28;      BitField<39, 8, Register> gpr39; +    BitField<48, 16, u64> opcode;      union {          BitField<20, 19, u64> imm20_19; @@ -395,11 +222,171 @@ union Instruction {      Uniform uniform;      Sampler sampler; -    u64 hex; +    u64 value;  };  static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size");  static_assert(std::is_standard_layout<Instruction>::value,                "Structure does not have standard layout"); +class OpCode { +public: +    enum class Id { +        KIL, +        LD_A, +        ST_A, +        TEXS, +        EXIT, +        IPA, +        FFMA_IMM, +        FFMA_CR, +        FFMA_RC, +        FFMA_RR, +        FADD_C, +        FADD_R, +        FADD_IMM, +        FMUL_C, +        FMUL_R, +        FMUL_IMM, +        FMUL32_IMM, +        MUFU, +        FSETP_R, +        FSETP_C, +        FSETP_IMM, +    }; + +    enum class Type { +        Trivial, +        Arithmetic, +        Ffma, +        Flow, +        Memory, +        FloatPredicate, +        Unknown, +    }; + +    class Matcher { +    public: +        Matcher(const char* const name, u16 mask, u16 expected, OpCode::Id id, OpCode::Type type) +            : name{name}, mask{mask}, expected{expected}, id{id}, type{type} {} + +        const char* GetName() const { +            return name; +        } + +        u16 GetMask() const { +            return mask; +        } + +        Id GetId() const { +            return id; +        } + +        Type GetType() const { +            return type; +        } + +        /** +         * Tests to see if the given instruction is the instruction this matcher represents. +         * @param instruction The instruction to test +         * @returns true if the given instruction matches. +         */ +        bool Matches(u16 instruction) const { +            return (instruction & mask) == expected; +        } + +    private: +        const char* name; +        u16 mask; +        u16 expected; +        Id id; +        Type type; +    }; + +    static boost::optional<const Matcher&> Decode(Instruction instr) { +        static const auto table{GetDecodeTable()}; + +        const auto matches_instruction = [instr](const auto& matcher) { +            return matcher.Matches(static_cast<u16>(instr.opcode)); +        }; + +        auto iter = std::find_if(table.begin(), table.end(), matches_instruction); +        return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none; +    } + +private: +    struct Detail { +    private: +        static constexpr size_t opcode_bitsize = 16; + +        /** +         * Generates the mask and the expected value after masking from a given bitstring. +         * A '0' in a bitstring indicates that a zero must be present at that bit position. +         * A '1' in a bitstring indicates that a one must be present at that bit position. +         */ +        static auto GetMaskAndExpect(const char* const bitstring) { +            u16 mask = 0, expect = 0; +            for (size_t i = 0; i < opcode_bitsize; i++) { +                const size_t bit_position = opcode_bitsize - i - 1; +                switch (bitstring[i]) { +                case '0': +                    mask |= 1 << bit_position; +                    break; +                case '1': +                    expect |= 1 << bit_position; +                    mask |= 1 << bit_position; +                    break; +                default: +                    // Ignore +                    break; +                } +            } +            return std::make_tuple(mask, expect); +        } + +    public: +        /// Creates a matcher that can match and parse instructions based on bitstring. +        static auto GetMatcher(const char* const bitstring, OpCode::Id op, OpCode::Type type, +                               const char* const name) { +            const auto mask_expect = GetMaskAndExpect(bitstring); +            return Matcher(name, std::get<0>(mask_expect), std::get<1>(mask_expect), op, type); +        } +    }; + +    static std::vector<Matcher> GetDecodeTable() { +        std::vector<Matcher> table = { +#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) +            INST("111000110011----", Id::KIL, Type::Flow, "KIL"), +            INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), +            INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), +            INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), +            INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), +            INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), +            INST("001100101-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), +            INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), +            INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), +            INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"), +            INST("0100110001011---", Id::FADD_C, Type::Arithmetic, "FADD_C"), +            INST("0101110001011---", Id::FADD_R, Type::Arithmetic, "FADD_R"), +            INST("0011100-01011---", Id::FADD_IMM, Type::Arithmetic, "FADD_IMM"), +            INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"), +            INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"), +            INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"), +            INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"), +            INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), +            INST("010110111011----", Id::FSETP_R, Type::FloatPredicate, "FSETP_R"), +            INST("010010111011----", Id::FSETP_C, Type::FloatPredicate, "FSETP_C"), +            INST("0011011-1011----", Id::FSETP_IMM, Type::FloatPredicate, "FSETP_IMM"), +        }; +#undef INST +        std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) { +            // If a matcher has more bits in its mask it is more specific, so it +            // should come first. +            return std::bitset<16>(a.GetMask()).count() > std::bitset<16>(b.GetMask()).count(); +        }); + +        return table; +    } +}; +  } // namespace Shader  } // namespace Tegra diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 2395945c3..5919b3c9e 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -97,11 +97,12 @@ private:              return exit_method;          for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { -            const Instruction instr = {program_code[offset]}; -            switch (instr.opcode.EffectiveOpCode()) { -            case OpCode::Id::EXIT: { -                return exit_method = ExitMethod::AlwaysEnd; -            } +            if (const auto opcode = OpCode::Decode({program_code[offset]})) { +                switch (opcode->GetId()) { +                case OpCode::Id::EXIT: { +                    return exit_method = ExitMethod::AlwaysEnd; +                } +                }              }          }          return exit_method = ExitMethod::AlwaysReturn; @@ -332,12 +333,20 @@ private:       */      u32 CompileInstr(u32 offset) {          // Ignore sched instructions when generating code. -        if (IsSchedInstruction(offset)) +        if (IsSchedInstruction(offset)) {              return offset + 1; +        }          const Instruction instr = {program_code[offset]}; +        const auto opcode = OpCode::Decode(instr); + +        // Decoding failure +        if (!opcode) { +            NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", instr.value); +            UNREACHABLE(); +        } -        shader.AddLine("// " + std::to_string(offset) + ": " + OpCode::GetInfo(instr.opcode).name); +        shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName());          using Tegra::Shader::Pred;          ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, @@ -349,7 +358,7 @@ private:              ++shader.scope;          } -        switch (OpCode::GetInfo(instr.opcode).type) { +        switch (opcode->GetType()) {          case OpCode::Type::Arithmetic: {              std::string dest = GetRegister(instr.gpr0);              std::string op_a = instr.alu.negate_a ? "-" : ""; @@ -374,7 +383,7 @@ private:                  op_b = "abs(" + op_b + ")";              } -            switch (instr.opcode.EffectiveOpCode()) { +            switch (opcode->GetId()) {              case OpCode::Id::FMUL_C:              case OpCode::Id::FMUL_R:              case OpCode::Id::FMUL_IMM: { @@ -424,8 +433,8 @@ private:              }              default: {                  NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {} ({}): {}", -                               static_cast<unsigned>(instr.opcode.EffectiveOpCode()), -                               OpCode::GetInfo(instr.opcode).name, instr.hex); +                               static_cast<unsigned>(opcode->GetId()), opcode->GetName(), +                               instr.value);                  UNREACHABLE();              }              } @@ -437,7 +446,7 @@ private:              std::string op_b = instr.ffma.negate_b ? "-" : "";              std::string op_c = instr.ffma.negate_c ? "-" : ""; -            switch (instr.opcode.EffectiveOpCode()) { +            switch (opcode->GetId()) {              case OpCode::Id::FFMA_CR: {                  op_b += GetUniform(instr.uniform);                  op_c += GetRegister(instr.gpr39); @@ -460,8 +469,8 @@ private:              }              default: {                  NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {} ({}): {}", -                               static_cast<unsigned>(instr.opcode.EffectiveOpCode()), -                               OpCode::GetInfo(instr.opcode).name, instr.hex); +                               static_cast<unsigned>(opcode->GetId()), opcode->GetName(), +                               instr.value);                  UNREACHABLE();              }              } @@ -473,7 +482,7 @@ private:              std::string gpr0 = GetRegister(instr.gpr0);              const Attribute::Index attribute = instr.attribute.fmt20.index; -            switch (instr.opcode.EffectiveOpCode()) { +            switch (opcode->GetId()) {              case OpCode::Id::LD_A: {                  ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested");                  SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4); @@ -505,8 +514,8 @@ private:              }              default: {                  NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {} ({}): {}", -                               static_cast<unsigned>(instr.opcode.EffectiveOpCode()), -                               OpCode::GetInfo(instr.opcode).name, instr.hex); +                               static_cast<unsigned>(opcode->GetId()), opcode->GetName(), +                               instr.value);                  UNREACHABLE();              }              } @@ -564,7 +573,7 @@ private:              break;          }          default: { -            switch (instr.opcode.EffectiveOpCode()) { +            switch (opcode->GetId()) {              case OpCode::Id::EXIT: {                  ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex),                             "Predicated exits not implemented"); @@ -584,8 +593,8 @@ private:              }              default: {                  NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {} ({}): {}", -                               static_cast<unsigned>(instr.opcode.EffectiveOpCode()), -                               OpCode::GetInfo(instr.opcode).name, instr.hex); +                               static_cast<unsigned>(opcode->GetId()), opcode->GetName(), +                               instr.value);                  UNREACHABLE();              }              } | 
