diff options
| -rw-r--r-- | src/video_core/engines/shader_bytecode.h | 404 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 55 |
2 files changed, 249 insertions, 210 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index e6c2fd367..5a006aee5 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -4,10 +4,16 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <bitset> | ||
| 7 | #include <cstring> | 8 | #include <cstring> |
| 8 | #include <map> | 9 | #include <map> |
| 9 | #include <string> | 10 | #include <string> |
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include <boost/optional.hpp> | ||
| 14 | |||
| 10 | #include "common/bit_field.h" | 15 | #include "common/bit_field.h" |
| 16 | #include "common/common_types.h" | ||
| 11 | 17 | ||
| 12 | namespace Tegra { | 18 | namespace Tegra { |
| 13 | namespace Shader { | 19 | namespace Shader { |
| @@ -89,188 +95,12 @@ union Uniform { | |||
| 89 | BitField<34, 5, u64> index; | 95 | BitField<34, 5, u64> index; |
| 90 | }; | 96 | }; |
| 91 | 97 | ||
| 92 | union OpCode { | ||
| 93 | enum class Id : u64 { | ||
| 94 | TEXS = 0x6C, | ||
| 95 | IPA = 0xE0, | ||
| 96 | FMUL32_IMM = 0x1E, | ||
| 97 | FFMA_IMM = 0x65, | ||
| 98 | FFMA_CR = 0x93, | ||
| 99 | FFMA_RC = 0xA3, | ||
| 100 | FFMA_RR = 0xB3, | ||
| 101 | |||
| 102 | FADD_C = 0x98B, | ||
| 103 | FMUL_C = 0x98D, | ||
| 104 | MUFU = 0xA10, | ||
| 105 | FADD_R = 0xB8B, | ||
| 106 | FMUL_R = 0xB8D, | ||
| 107 | LD_A = 0x1DFB, | ||
| 108 | ST_A = 0x1DFE, | ||
| 109 | |||
| 110 | FSETP_R = 0x5BB, | ||
| 111 | FSETP_C = 0x4BB, | ||
| 112 | FSETP_IMM = 0x36B, | ||
| 113 | FSETP_NEG_IMM = 0x37B, | ||
| 114 | EXIT = 0xE30, | ||
| 115 | KIL = 0xE33, | ||
| 116 | |||
| 117 | FMUL_IMM = 0x70D, | ||
| 118 | FMUL_IMM_x = 0x72D, | ||
| 119 | FADD_IMM = 0x70B, | ||
| 120 | FADD_IMM_x = 0x72B, | ||
| 121 | }; | ||
| 122 | |||
| 123 | enum class Type { | ||
| 124 | Trivial, | ||
| 125 | Arithmetic, | ||
| 126 | Ffma, | ||
| 127 | Flow, | ||
| 128 | Memory, | ||
| 129 | FloatPredicate, | ||
| 130 | Unknown, | ||
| 131 | }; | ||
| 132 | |||
| 133 | struct Info { | ||
| 134 | Type type; | ||
| 135 | std::string name; | ||
| 136 | }; | ||
| 137 | |||
| 138 | OpCode() = default; | ||
| 139 | |||
| 140 | constexpr OpCode(Id value) : value(static_cast<u64>(value)) {} | ||
| 141 | |||
| 142 | constexpr OpCode(u64 value) : value{value} {} | ||
| 143 | |||
| 144 | constexpr Id EffectiveOpCode() const { | ||
| 145 | switch (op1) { | ||
| 146 | case Id::TEXS: | ||
| 147 | return op1; | ||
| 148 | } | ||
| 149 | |||
| 150 | switch (op2) { | ||
| 151 | case Id::IPA: | ||
| 152 | case Id::FMUL32_IMM: | ||
| 153 | return op2; | ||
| 154 | } | ||
| 155 | |||
| 156 | switch (op3) { | ||
| 157 | case Id::FFMA_IMM: | ||
| 158 | case Id::FFMA_CR: | ||
| 159 | case Id::FFMA_RC: | ||
| 160 | case Id::FFMA_RR: | ||
| 161 | return op3; | ||
| 162 | } | ||
| 163 | |||
| 164 | switch (op4) { | ||
| 165 | case Id::EXIT: | ||
| 166 | case Id::FSETP_R: | ||
| 167 | case Id::FSETP_C: | ||
| 168 | case Id::KIL: | ||
| 169 | return op4; | ||
| 170 | case Id::FSETP_IMM: | ||
| 171 | case Id::FSETP_NEG_IMM: | ||
| 172 | return Id::FSETP_IMM; | ||
| 173 | } | ||
| 174 | |||
| 175 | switch (op5) { | ||
| 176 | case Id::MUFU: | ||
| 177 | case Id::LD_A: | ||
| 178 | case Id::ST_A: | ||
| 179 | case Id::FADD_R: | ||
| 180 | case Id::FADD_C: | ||
| 181 | case Id::FMUL_R: | ||
| 182 | case Id::FMUL_C: | ||
| 183 | return op5; | ||
| 184 | |||
| 185 | case Id::FMUL_IMM: | ||
| 186 | case Id::FMUL_IMM_x: | ||
| 187 | return Id::FMUL_IMM; | ||
| 188 | |||
| 189 | case Id::FADD_IMM: | ||
| 190 | case Id::FADD_IMM_x: | ||
| 191 | return Id::FADD_IMM; | ||
| 192 | } | ||
| 193 | |||
| 194 | return static_cast<Id>(value); | ||
| 195 | } | ||
| 196 | |||
| 197 | static const Info& GetInfo(const OpCode& opcode) { | ||
| 198 | static const std::map<Id, Info> info_table{BuildInfoTable()}; | ||
| 199 | const auto& search{info_table.find(opcode.EffectiveOpCode())}; | ||
| 200 | if (search != info_table.end()) { | ||
| 201 | return search->second; | ||
| 202 | } | ||
| 203 | |||
| 204 | static const Info unknown{Type::Unknown, "UNK"}; | ||
| 205 | return unknown; | ||
| 206 | } | ||
| 207 | |||
| 208 | constexpr operator Id() const { | ||
| 209 | return static_cast<Id>(value); | ||
| 210 | } | ||
| 211 | |||
| 212 | constexpr OpCode operator<<(size_t bits) const { | ||
| 213 | return value << bits; | ||
| 214 | } | ||
| 215 | |||
| 216 | constexpr OpCode operator>>(size_t bits) const { | ||
| 217 | return value >> bits; | ||
| 218 | } | ||
| 219 | |||
| 220 | template <typename T> | ||
| 221 | constexpr u64 operator-(const T& oth) const { | ||
| 222 | return value - oth; | ||
| 223 | } | ||
| 224 | |||
| 225 | constexpr u64 operator&(const OpCode& oth) const { | ||
| 226 | return value & oth.value; | ||
| 227 | } | ||
| 228 | |||
| 229 | constexpr u64 operator~() const { | ||
| 230 | return ~value; | ||
| 231 | } | ||
| 232 | |||
| 233 | static std::map<Id, Info> BuildInfoTable() { | ||
| 234 | std::map<Id, Info> info_table; | ||
| 235 | info_table[Id::TEXS] = {Type::Memory, "texs"}; | ||
| 236 | info_table[Id::LD_A] = {Type::Memory, "ld_a"}; | ||
| 237 | info_table[Id::ST_A] = {Type::Memory, "st_a"}; | ||
| 238 | info_table[Id::MUFU] = {Type::Arithmetic, "mufu"}; | ||
| 239 | info_table[Id::FFMA_IMM] = {Type::Ffma, "ffma_imm"}; | ||
| 240 | info_table[Id::FFMA_CR] = {Type::Ffma, "ffma_cr"}; | ||
| 241 | info_table[Id::FFMA_RC] = {Type::Ffma, "ffma_rc"}; | ||
| 242 | info_table[Id::FFMA_RR] = {Type::Ffma, "ffma_rr"}; | ||
| 243 | info_table[Id::FADD_R] = {Type::Arithmetic, "fadd_r"}; | ||
| 244 | info_table[Id::FADD_C] = {Type::Arithmetic, "fadd_c"}; | ||
| 245 | info_table[Id::FADD_IMM] = {Type::Arithmetic, "fadd_imm"}; | ||
| 246 | info_table[Id::FMUL_R] = {Type::Arithmetic, "fmul_r"}; | ||
| 247 | info_table[Id::FMUL_C] = {Type::Arithmetic, "fmul_c"}; | ||
| 248 | info_table[Id::FMUL_IMM] = {Type::Arithmetic, "fmul_imm"}; | ||
| 249 | info_table[Id::FMUL32_IMM] = {Type::Arithmetic, "fmul32_imm"}; | ||
| 250 | info_table[Id::FSETP_C] = {Type::FloatPredicate, "fsetp_c"}; | ||
| 251 | info_table[Id::FSETP_R] = {Type::FloatPredicate, "fsetp_r"}; | ||
| 252 | info_table[Id::FSETP_IMM] = {Type::FloatPredicate, "fsetp_imm"}; | ||
| 253 | info_table[Id::EXIT] = {Type::Trivial, "exit"}; | ||
| 254 | info_table[Id::IPA] = {Type::Trivial, "ipa"}; | ||
| 255 | info_table[Id::KIL] = {Type::Flow, "kil"}; | ||
| 256 | return info_table; | ||
| 257 | } | ||
| 258 | |||
| 259 | BitField<57, 7, Id> op1; | ||
| 260 | BitField<56, 8, Id> op2; | ||
| 261 | BitField<55, 9, Id> op3; | ||
| 262 | BitField<52, 12, Id> op4; | ||
| 263 | BitField<51, 13, Id> op5; | ||
| 264 | u64 value{}; | ||
| 265 | }; | ||
| 266 | static_assert(sizeof(OpCode) == 0x8, "Incorrect structure size"); | ||
| 267 | |||
| 268 | } // namespace Shader | 98 | } // namespace Shader |
| 269 | } // namespace Tegra | 99 | } // namespace Tegra |
| 270 | 100 | ||
| 271 | namespace std { | 101 | namespace std { |
| 272 | 102 | ||
| 273 | // TODO(bunne): The below is forbidden by the C++ standard, but works fine. See #330. | 103 | // TODO(bunnei): The below is forbidden by the C++ standard, but works fine. See #330. |
| 274 | template <> | 104 | template <> |
| 275 | struct make_unsigned<Tegra::Shader::Attribute> { | 105 | struct make_unsigned<Tegra::Shader::Attribute> { |
| 276 | using type = Tegra::Shader::Attribute; | 106 | using type = Tegra::Shader::Attribute; |
| @@ -281,11 +111,6 @@ struct make_unsigned<Tegra::Shader::Register> { | |||
| 281 | using type = Tegra::Shader::Register; | 111 | using type = Tegra::Shader::Register; |
| 282 | }; | 112 | }; |
| 283 | 113 | ||
| 284 | template <> | ||
| 285 | struct make_unsigned<Tegra::Shader::OpCode> { | ||
| 286 | using type = Tegra::Shader::OpCode; | ||
| 287 | }; | ||
| 288 | |||
| 289 | } // namespace std | 114 | } // namespace std |
| 290 | 115 | ||
| 291 | namespace Tegra { | 116 | namespace Tegra { |
| @@ -324,11 +149,12 @@ enum class SubOp : u64 { | |||
| 324 | 149 | ||
| 325 | union Instruction { | 150 | union Instruction { |
| 326 | Instruction& operator=(const Instruction& instr) { | 151 | Instruction& operator=(const Instruction& instr) { |
| 327 | hex = instr.hex; | 152 | value = instr.value; |
| 328 | return *this; | 153 | return *this; |
| 329 | } | 154 | } |
| 330 | 155 | ||
| 331 | OpCode opcode; | 156 | constexpr Instruction(u64 value) : value{value} {} |
| 157 | |||
| 332 | BitField<0, 8, Register> gpr0; | 158 | BitField<0, 8, Register> gpr0; |
| 333 | BitField<8, 8, Register> gpr8; | 159 | BitField<8, 8, Register> gpr8; |
| 334 | union { | 160 | union { |
| @@ -340,6 +166,7 @@ union Instruction { | |||
| 340 | BitField<20, 7, SubOp> sub_op; | 166 | BitField<20, 7, SubOp> sub_op; |
| 341 | BitField<28, 8, Register> gpr28; | 167 | BitField<28, 8, Register> gpr28; |
| 342 | BitField<39, 8, Register> gpr39; | 168 | BitField<39, 8, Register> gpr39; |
| 169 | BitField<48, 16, u64> opcode; | ||
| 343 | 170 | ||
| 344 | union { | 171 | union { |
| 345 | BitField<20, 19, u64> imm20_19; | 172 | BitField<20, 19, u64> imm20_19; |
| @@ -395,11 +222,218 @@ union Instruction { | |||
| 395 | Uniform uniform; | 222 | Uniform uniform; |
| 396 | Sampler sampler; | 223 | Sampler sampler; |
| 397 | 224 | ||
| 398 | u64 hex; | 225 | u64 value; |
| 399 | }; | 226 | }; |
| 400 | static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size"); | 227 | static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size"); |
| 401 | static_assert(std::is_standard_layout<Instruction>::value, | 228 | static_assert(std::is_standard_layout<Instruction>::value, |
| 402 | "Structure does not have standard layout"); | 229 | "Structure does not have standard layout"); |
| 403 | 230 | ||
| 231 | class OpCode { | ||
| 232 | public: | ||
| 233 | enum class Id { | ||
| 234 | KIL, | ||
| 235 | LD_A, | ||
| 236 | ST_A, | ||
| 237 | TEXQ, // Texture Query | ||
| 238 | TEXS, // Texture Fetch with scalar/non-vec4 source/destinations | ||
| 239 | TLDS, // Texture Load with scalar/non-vec4 source/destinations | ||
| 240 | EXIT, | ||
| 241 | IPA, | ||
| 242 | FFMA_IMM, // Fused Multiply and Add | ||
| 243 | FFMA_CR, | ||
| 244 | FFMA_RC, | ||
| 245 | FFMA_RR, | ||
| 246 | FADD_C, | ||
| 247 | FADD_R, | ||
| 248 | FADD_IMM, | ||
| 249 | FMUL_C, | ||
| 250 | FMUL_R, | ||
| 251 | FMUL_IMM, | ||
| 252 | FMUL32_IMM, | ||
| 253 | MUFU, // Multi-Function Operator | ||
| 254 | RRO, // Range Reduction Operator | ||
| 255 | F2F_C, | ||
| 256 | F2F_R, | ||
| 257 | F2F_IMM, | ||
| 258 | F2I_C, | ||
| 259 | F2I_R, | ||
| 260 | F2I_IMM, | ||
| 261 | I2F_C, | ||
| 262 | I2F_R, | ||
| 263 | I2F_IMM, | ||
| 264 | LOP32I, | ||
| 265 | MOV_C, | ||
| 266 | MOV_R, | ||
| 267 | MOV_IMM, | ||
| 268 | MOV32I, | ||
| 269 | SHR_C, | ||
| 270 | SHR_R, | ||
| 271 | SHR_IMM, | ||
| 272 | FSETP_C, // Set Predicate | ||
| 273 | FSETP_R, | ||
| 274 | FSETP_IMM, | ||
| 275 | ISETP_C, | ||
| 276 | ISETP_IMM, | ||
| 277 | ISETP_R, | ||
| 278 | }; | ||
| 279 | |||
| 280 | enum class Type { | ||
| 281 | Trivial, | ||
| 282 | Arithmetic, | ||
| 283 | Ffma, | ||
| 284 | Flow, | ||
| 285 | Memory, | ||
| 286 | FloatPredicate, | ||
| 287 | IntegerPredicate, | ||
| 288 | Unknown, | ||
| 289 | }; | ||
| 290 | |||
| 291 | class Matcher { | ||
| 292 | public: | ||
| 293 | Matcher(const char* const name, u16 mask, u16 expected, OpCode::Id id, OpCode::Type type) | ||
| 294 | : name{name}, mask{mask}, expected{expected}, id{id}, type{type} {} | ||
| 295 | |||
| 296 | const char* GetName() const { | ||
| 297 | return name; | ||
| 298 | } | ||
| 299 | |||
| 300 | u16 GetMask() const { | ||
| 301 | return mask; | ||
| 302 | } | ||
| 303 | |||
| 304 | Id GetId() const { | ||
| 305 | return id; | ||
| 306 | } | ||
| 307 | |||
| 308 | Type GetType() const { | ||
| 309 | return type; | ||
| 310 | } | ||
| 311 | |||
| 312 | /** | ||
| 313 | * Tests to see if the given instruction is the instruction this matcher represents. | ||
| 314 | * @param instruction The instruction to test | ||
| 315 | * @returns true if the given instruction matches. | ||
| 316 | */ | ||
| 317 | bool Matches(u16 instruction) const { | ||
| 318 | return (instruction & mask) == expected; | ||
| 319 | } | ||
| 320 | |||
| 321 | private: | ||
| 322 | const char* name; | ||
| 323 | u16 mask; | ||
| 324 | u16 expected; | ||
| 325 | Id id; | ||
| 326 | Type type; | ||
| 327 | }; | ||
| 328 | |||
| 329 | static boost::optional<const Matcher&> Decode(Instruction instr) { | ||
| 330 | static const auto table{GetDecodeTable()}; | ||
| 331 | |||
| 332 | const auto matches_instruction = [instr](const auto& matcher) { | ||
| 333 | return matcher.Matches(static_cast<u16>(instr.opcode)); | ||
| 334 | }; | ||
| 335 | |||
| 336 | auto iter = std::find_if(table.begin(), table.end(), matches_instruction); | ||
| 337 | return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none; | ||
| 338 | } | ||
| 339 | |||
| 340 | private: | ||
| 341 | struct Detail { | ||
| 342 | private: | ||
| 343 | static constexpr size_t opcode_bitsize = 16; | ||
| 344 | |||
| 345 | /** | ||
| 346 | * Generates the mask and the expected value after masking from a given bitstring. | ||
| 347 | * A '0' in a bitstring indicates that a zero must be present at that bit position. | ||
| 348 | * A '1' in a bitstring indicates that a one must be present at that bit position. | ||
| 349 | */ | ||
| 350 | static auto GetMaskAndExpect(const char* const bitstring) { | ||
| 351 | u16 mask = 0, expect = 0; | ||
| 352 | for (size_t i = 0; i < opcode_bitsize; i++) { | ||
| 353 | const size_t bit_position = opcode_bitsize - i - 1; | ||
| 354 | switch (bitstring[i]) { | ||
| 355 | case '0': | ||
| 356 | mask |= 1 << bit_position; | ||
| 357 | break; | ||
| 358 | case '1': | ||
| 359 | expect |= 1 << bit_position; | ||
| 360 | mask |= 1 << bit_position; | ||
| 361 | break; | ||
| 362 | default: | ||
| 363 | // Ignore | ||
| 364 | break; | ||
| 365 | } | ||
| 366 | } | ||
| 367 | return std::make_tuple(mask, expect); | ||
| 368 | } | ||
| 369 | |||
| 370 | public: | ||
| 371 | /// Creates a matcher that can match and parse instructions based on bitstring. | ||
| 372 | static auto GetMatcher(const char* const bitstring, OpCode::Id op, OpCode::Type type, | ||
| 373 | const char* const name) { | ||
| 374 | const auto mask_expect = GetMaskAndExpect(bitstring); | ||
| 375 | return Matcher(name, std::get<0>(mask_expect), std::get<1>(mask_expect), op, type); | ||
| 376 | } | ||
| 377 | }; | ||
| 378 | |||
| 379 | static std::vector<Matcher> GetDecodeTable() { | ||
| 380 | std::vector<Matcher> table = { | ||
| 381 | #define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) | ||
| 382 | INST("111000110011----", Id::KIL, Type::Flow, "KIL"), | ||
| 383 | INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), | ||
| 384 | INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), | ||
| 385 | INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"), | ||
| 386 | INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), | ||
| 387 | INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), | ||
| 388 | INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), | ||
| 389 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), | ||
| 390 | INST("001100101-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), | ||
| 391 | INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), | ||
| 392 | INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), | ||
| 393 | INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"), | ||
| 394 | INST("0100110001011---", Id::FADD_C, Type::Arithmetic, "FADD_C"), | ||
| 395 | INST("0101110001011---", Id::FADD_R, Type::Arithmetic, "FADD_R"), | ||
| 396 | INST("0011100-01011---", Id::FADD_IMM, Type::Arithmetic, "FADD_IMM"), | ||
| 397 | INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"), | ||
| 398 | INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"), | ||
| 399 | INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"), | ||
| 400 | INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"), | ||
| 401 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), | ||
| 402 | INST("0101110010010---", Id::RRO, Type::Arithmetic, "RRO"), | ||
| 403 | INST("0100110010101---", Id::F2F_C, Type::Arithmetic, "F2F_C"), | ||
| 404 | INST("0101110010101---", Id::F2F_R, Type::Arithmetic, "F2F_R"), | ||
| 405 | INST("0011100-10101---", Id::F2F_IMM, Type::Arithmetic, "F2F_IMM"), | ||
| 406 | INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"), | ||
| 407 | INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"), | ||
| 408 | INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"), | ||
| 409 | INST("0100110010111---", Id::I2F_C, Type::Arithmetic, "I2F_C"), | ||
| 410 | INST("0101110010111---", Id::I2F_R, Type::Arithmetic, "I2F_R"), | ||
| 411 | INST("0011100-10111---", Id::I2F_IMM, Type::Arithmetic, "I2F_IMM"), | ||
| 412 | INST("000001----------", Id::LOP32I, Type::Arithmetic, "LOP32I"), | ||
| 413 | INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), | ||
| 414 | INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), | ||
| 415 | INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), | ||
| 416 | INST("000000010000----", Id::MOV32I, Type::Arithmetic, "MOV32I"), | ||
| 417 | INST("0100110000101---", Id::SHR_C, Type::Arithmetic, "SHR_C"), | ||
| 418 | INST("0101110000101---", Id::SHR_R, Type::Arithmetic, "SHR_R"), | ||
| 419 | INST("0011100-00101---", Id::SHR_IMM, Type::Arithmetic, "SHR_IMM"), | ||
| 420 | INST("010010111011----", Id::FSETP_C, Type::FloatPredicate, "FSETP_C"), | ||
| 421 | INST("010110111011----", Id::FSETP_R, Type::FloatPredicate, "FSETP_R"), | ||
| 422 | INST("0011011-1011----", Id::FSETP_IMM, Type::FloatPredicate, "FSETP_IMM"), | ||
| 423 | INST("010010110110----", Id::ISETP_C, Type::IntegerPredicate, "ISETP_C"), | ||
| 424 | INST("010110110110----", Id::ISETP_R, Type::IntegerPredicate, "ISETP_R"), | ||
| 425 | INST("0011011-0110----", Id::ISETP_IMM, Type::IntegerPredicate, "ISETP_IMM"), | ||
| 426 | }; | ||
| 427 | #undef INST | ||
| 428 | std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) { | ||
| 429 | // If a matcher has more bits in its mask it is more specific, so it | ||
| 430 | // should come first. | ||
| 431 | return std::bitset<16>(a.GetMask()).count() > std::bitset<16>(b.GetMask()).count(); | ||
| 432 | }); | ||
| 433 | |||
| 434 | return table; | ||
| 435 | } | ||
| 436 | }; | ||
| 437 | |||
| 404 | } // namespace Shader | 438 | } // namespace Shader |
| 405 | } // namespace Tegra | 439 | } // 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..086424395 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: | |||
| 97 | return exit_method; | 97 | return exit_method; |
| 98 | 98 | ||
| 99 | for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { | 99 | for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { |
| 100 | const Instruction instr = {program_code[offset]}; | 100 | if (const auto opcode = OpCode::Decode({program_code[offset]})) { |
| 101 | switch (instr.opcode.EffectiveOpCode()) { | 101 | switch (opcode->GetId()) { |
| 102 | case OpCode::Id::EXIT: { | 102 | case OpCode::Id::EXIT: { |
| 103 | return exit_method = ExitMethod::AlwaysEnd; | 103 | return exit_method = ExitMethod::AlwaysEnd; |
| 104 | } | 104 | } |
| 105 | } | ||
| 105 | } | 106 | } |
| 106 | } | 107 | } |
| 107 | return exit_method = ExitMethod::AlwaysReturn; | 108 | return exit_method = ExitMethod::AlwaysReturn; |
| @@ -332,12 +333,20 @@ private: | |||
| 332 | */ | 333 | */ |
| 333 | u32 CompileInstr(u32 offset) { | 334 | u32 CompileInstr(u32 offset) { |
| 334 | // Ignore sched instructions when generating code. | 335 | // Ignore sched instructions when generating code. |
| 335 | if (IsSchedInstruction(offset)) | 336 | if (IsSchedInstruction(offset)) { |
| 336 | return offset + 1; | 337 | return offset + 1; |
| 338 | } | ||
| 337 | 339 | ||
| 338 | const Instruction instr = {program_code[offset]}; | 340 | const Instruction instr = {program_code[offset]}; |
| 341 | const auto opcode = OpCode::Decode(instr); | ||
| 339 | 342 | ||
| 340 | shader.AddLine("// " + std::to_string(offset) + ": " + OpCode::GetInfo(instr.opcode).name); | 343 | // Decoding failure |
| 344 | if (!opcode) { | ||
| 345 | NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); | ||
| 346 | UNREACHABLE(); | ||
| 347 | } | ||
| 348 | |||
| 349 | shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName()); | ||
| 341 | 350 | ||
| 342 | using Tegra::Shader::Pred; | 351 | using Tegra::Shader::Pred; |
| 343 | ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, | 352 | ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, |
| @@ -349,7 +358,7 @@ private: | |||
| 349 | ++shader.scope; | 358 | ++shader.scope; |
| 350 | } | 359 | } |
| 351 | 360 | ||
| 352 | switch (OpCode::GetInfo(instr.opcode).type) { | 361 | switch (opcode->GetType()) { |
| 353 | case OpCode::Type::Arithmetic: { | 362 | case OpCode::Type::Arithmetic: { |
| 354 | std::string dest = GetRegister(instr.gpr0); | 363 | std::string dest = GetRegister(instr.gpr0); |
| 355 | std::string op_a = instr.alu.negate_a ? "-" : ""; | 364 | std::string op_a = instr.alu.negate_a ? "-" : ""; |
| @@ -374,7 +383,7 @@ private: | |||
| 374 | op_b = "abs(" + op_b + ")"; | 383 | op_b = "abs(" + op_b + ")"; |
| 375 | } | 384 | } |
| 376 | 385 | ||
| 377 | switch (instr.opcode.EffectiveOpCode()) { | 386 | switch (opcode->GetId()) { |
| 378 | case OpCode::Id::FMUL_C: | 387 | case OpCode::Id::FMUL_C: |
| 379 | case OpCode::Id::FMUL_R: | 388 | case OpCode::Id::FMUL_R: |
| 380 | case OpCode::Id::FMUL_IMM: { | 389 | case OpCode::Id::FMUL_IMM: { |
| @@ -416,16 +425,18 @@ private: | |||
| 416 | SetDest(0, dest, "min(" + op_a + "," + op_b + ")", 1, 1, instr.alu.abs_d); | 425 | SetDest(0, dest, "min(" + op_a + "," + op_b + ")", 1, 1, instr.alu.abs_d); |
| 417 | break; | 426 | break; |
| 418 | default: | 427 | default: |
| 419 | NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {}", | 428 | NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", |
| 420 | static_cast<unsigned>(instr.sub_op.Value())); | 429 | static_cast<unsigned>(instr.sub_op.Value())); |
| 421 | UNREACHABLE(); | 430 | UNREACHABLE(); |
| 422 | } | 431 | } |
| 423 | break; | 432 | break; |
| 424 | } | 433 | } |
| 434 | case OpCode::Id::RRO: { | ||
| 435 | NGLOG_DEBUG(HW_GPU, "Skipping RRO instruction"); | ||
| 436 | break; | ||
| 437 | } | ||
| 425 | default: { | 438 | default: { |
| 426 | NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {} ({}): {}", | 439 | NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName()); |
| 427 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | ||
| 428 | OpCode::GetInfo(instr.opcode).name, instr.hex); | ||
| 429 | UNREACHABLE(); | 440 | UNREACHABLE(); |
| 430 | } | 441 | } |
| 431 | } | 442 | } |
| @@ -437,7 +448,7 @@ private: | |||
| 437 | std::string op_b = instr.ffma.negate_b ? "-" : ""; | 448 | std::string op_b = instr.ffma.negate_b ? "-" : ""; |
| 438 | std::string op_c = instr.ffma.negate_c ? "-" : ""; | 449 | std::string op_c = instr.ffma.negate_c ? "-" : ""; |
| 439 | 450 | ||
| 440 | switch (instr.opcode.EffectiveOpCode()) { | 451 | switch (opcode->GetId()) { |
| 441 | case OpCode::Id::FFMA_CR: { | 452 | case OpCode::Id::FFMA_CR: { |
| 442 | op_b += GetUniform(instr.uniform); | 453 | op_b += GetUniform(instr.uniform); |
| 443 | op_c += GetRegister(instr.gpr39); | 454 | op_c += GetRegister(instr.gpr39); |
| @@ -459,9 +470,7 @@ private: | |||
| 459 | break; | 470 | break; |
| 460 | } | 471 | } |
| 461 | default: { | 472 | default: { |
| 462 | NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {} ({}): {}", | 473 | NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName()); |
| 463 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | ||
| 464 | OpCode::GetInfo(instr.opcode).name, instr.hex); | ||
| 465 | UNREACHABLE(); | 474 | UNREACHABLE(); |
| 466 | } | 475 | } |
| 467 | } | 476 | } |
| @@ -473,7 +482,7 @@ private: | |||
| 473 | std::string gpr0 = GetRegister(instr.gpr0); | 482 | std::string gpr0 = GetRegister(instr.gpr0); |
| 474 | const Attribute::Index attribute = instr.attribute.fmt20.index; | 483 | const Attribute::Index attribute = instr.attribute.fmt20.index; |
| 475 | 484 | ||
| 476 | switch (instr.opcode.EffectiveOpCode()) { | 485 | switch (opcode->GetId()) { |
| 477 | case OpCode::Id::LD_A: { | 486 | case OpCode::Id::LD_A: { |
| 478 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); | 487 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); |
| 479 | SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4); | 488 | SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4); |
| @@ -504,9 +513,7 @@ private: | |||
| 504 | break; | 513 | break; |
| 505 | } | 514 | } |
| 506 | default: { | 515 | default: { |
| 507 | NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {} ({}): {}", | 516 | NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); |
| 508 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | ||
| 509 | OpCode::GetInfo(instr.opcode).name, instr.hex); | ||
| 510 | UNREACHABLE(); | 517 | UNREACHABLE(); |
| 511 | } | 518 | } |
| 512 | } | 519 | } |
| @@ -564,7 +571,7 @@ private: | |||
| 564 | break; | 571 | break; |
| 565 | } | 572 | } |
| 566 | default: { | 573 | default: { |
| 567 | switch (instr.opcode.EffectiveOpCode()) { | 574 | switch (opcode->GetId()) { |
| 568 | case OpCode::Id::EXIT: { | 575 | case OpCode::Id::EXIT: { |
| 569 | ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex), | 576 | ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex), |
| 570 | "Predicated exits not implemented"); | 577 | "Predicated exits not implemented"); |
| @@ -583,9 +590,7 @@ private: | |||
| 583 | break; | 590 | break; |
| 584 | } | 591 | } |
| 585 | default: { | 592 | default: { |
| 586 | NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {} ({}): {}", | 593 | NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); |
| 587 | static_cast<unsigned>(instr.opcode.EffectiveOpCode()), | ||
| 588 | OpCode::GetInfo(instr.opcode).name, instr.hex); | ||
| 589 | UNREACHABLE(); | 594 | UNREACHABLE(); |
| 590 | } | 595 | } |
| 591 | } | 596 | } |