gl_shader_decompiler: Implement VMAD
This commit is contained in:
		| @@ -214,6 +214,18 @@ enum class IMinMaxExchange : u64 { | |||||||
|     XHi = 3, |     XHi = 3, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | enum class VmadType : u64 { | ||||||
|  |     Size16_Low = 0, | ||||||
|  |     Size16_High = 1, | ||||||
|  |     Size32 = 2, | ||||||
|  |     Invalid = 3, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum class VmadShr : u64 { | ||||||
|  |     Shr7 = 1, | ||||||
|  |     Shr15 = 2, | ||||||
|  | }; | ||||||
|  |  | ||||||
| enum class XmadMode : u64 { | enum class XmadMode : u64 { | ||||||
|     None = 0, |     None = 0, | ||||||
|     CLo = 1, |     CLo = 1, | ||||||
| @@ -452,6 +464,7 @@ union Instruction { | |||||||
|     BitField<48, 16, u64> opcode; |     BitField<48, 16, u64> opcode; | ||||||
|  |  | ||||||
|     union { |     union { | ||||||
|  |         BitField<20, 16, u64> imm20_16; | ||||||
|         BitField<20, 19, u64> imm20_19; |         BitField<20, 19, u64> imm20_19; | ||||||
|         BitField<20, 32, s64> imm20_32; |         BitField<20, 32, s64> imm20_32; | ||||||
|         BitField<45, 1, u64> negate_b; |         BitField<45, 1, u64> negate_b; | ||||||
| @@ -493,6 +506,10 @@ union Instruction { | |||||||
|             } |             } | ||||||
|         } lop3; |         } lop3; | ||||||
|  |  | ||||||
|  |         u16 GetImm20_16() const { | ||||||
|  |             return static_cast<u16>(imm20_16); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         u32 GetImm20_19() const { |         u32 GetImm20_19() const { | ||||||
|             u32 imm{static_cast<u32>(imm20_19)}; |             u32 imm{static_cast<u32>(imm20_19)}; | ||||||
|             imm <<= 12; |             imm <<= 12; | ||||||
| @@ -1016,6 +1033,23 @@ union Instruction { | |||||||
|         BitField<47, 2, IsberdShift> shift; |         BitField<47, 2, IsberdShift> shift; | ||||||
|     } isberd; |     } isberd; | ||||||
|  |  | ||||||
|  |     union { | ||||||
|  |         BitField<48, 1, u64> signed_a; | ||||||
|  |         BitField<38, 1, u64> is_byte_chunk_a; | ||||||
|  |         BitField<36, 2, VmadType> type_a; | ||||||
|  |         BitField<36, 2, u64> byte_height_a; | ||||||
|  |  | ||||||
|  |         BitField<49, 1, u64> signed_b; | ||||||
|  |         BitField<50, 1, u64> use_register_b; | ||||||
|  |         BitField<30, 1, u64> is_byte_chunk_b; | ||||||
|  |         BitField<28, 2, VmadType> type_b; | ||||||
|  |         BitField<28, 2, u64> byte_height_b; | ||||||
|  |  | ||||||
|  |         BitField<51, 2, VmadShr> shr; | ||||||
|  |         BitField<55, 1, u64> saturate; // Saturates the result (a * b + c) | ||||||
|  |         BitField<47, 1, u64> cc; | ||||||
|  |     } vmad; | ||||||
|  |  | ||||||
|     union { |     union { | ||||||
|         BitField<20, 16, u64> imm20_16; |         BitField<20, 16, u64> imm20_16; | ||||||
|         BitField<36, 1, u64> product_shift_left; |         BitField<36, 1, u64> product_shift_left; | ||||||
| @@ -1083,6 +1117,7 @@ public: | |||||||
|         IPA, |         IPA, | ||||||
|         OUT_R, // Emit vertex/primitive |         OUT_R, // Emit vertex/primitive | ||||||
|         ISBERD, |         ISBERD, | ||||||
|  |         VMAD, | ||||||
|         FFMA_IMM, // Fused Multiply and Add |         FFMA_IMM, // Fused Multiply and Add | ||||||
|         FFMA_CR, |         FFMA_CR, | ||||||
|         FFMA_RC, |         FFMA_RC, | ||||||
| @@ -1320,6 +1355,7 @@ private: | |||||||
|             INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), |             INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), | ||||||
|             INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), |             INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"), | ||||||
|             INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), |             INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"), | ||||||
|  |             INST("01011111--------", Id::VMAD, Type::Trivial, "VMAD"), | ||||||
|             INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), |             INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), | ||||||
|             INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), |             INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), | ||||||
|             INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), |             INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), | ||||||
|   | |||||||
| @@ -2953,6 +2953,88 @@ private: | |||||||
|                 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); |                 LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed"); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |             case OpCode::Id::VMAD: { | ||||||
|  |                 const bool signed_a = instr.vmad.signed_a == 1; | ||||||
|  |                 const bool signed_b = instr.vmad.signed_b == 1; | ||||||
|  |                 const bool result_signed = signed_a || signed_b; | ||||||
|  |                 boost::optional<std::string> forced_result; | ||||||
|  |  | ||||||
|  |                 auto Unpack = [&](const std::string& op, bool is_chunk, bool is_signed, | ||||||
|  |                                   Tegra::Shader::VmadType type, u64 byte_height) { | ||||||
|  |                     const std::string value = [&]() { | ||||||
|  |                         if (!is_chunk) { | ||||||
|  |                             const auto offset = static_cast<u32>(byte_height * 8); | ||||||
|  |                             return "((" + op + " >> " + std::to_string(offset) + ") & 0xff)"; | ||||||
|  |                         } | ||||||
|  |                         const std::string zero = "0"; | ||||||
|  |  | ||||||
|  |                         switch (type) { | ||||||
|  |                         case Tegra::Shader::VmadType::Size16_Low: | ||||||
|  |                             return '(' + op + " & 0xffff)"; | ||||||
|  |                         case Tegra::Shader::VmadType::Size16_High: | ||||||
|  |                             return '(' + op + " >> 16)"; | ||||||
|  |                         case Tegra::Shader::VmadType::Size32: | ||||||
|  |                             // TODO(Rodrigo): From my hardware tests it becomes a bit "mad" when | ||||||
|  |                             // this type is used (1 * 1 + 0 == 0x5b800000). Until a better | ||||||
|  |                             // explanation is found: assert. | ||||||
|  |                             UNREACHABLE_MSG("Unimplemented"); | ||||||
|  |                             return zero; | ||||||
|  |                         case Tegra::Shader::VmadType::Invalid: | ||||||
|  |                             // Note(Rodrigo): This flag is invalid according to nvdisasm. From my | ||||||
|  |                             // testing (even though it's invalid) this makes the whole instruction | ||||||
|  |                             // assign zero to target register. | ||||||
|  |                             forced_result = boost::make_optional(zero); | ||||||
|  |                             return zero; | ||||||
|  |                         default: | ||||||
|  |                             UNREACHABLE(); | ||||||
|  |                             return zero; | ||||||
|  |                         } | ||||||
|  |                     }(); | ||||||
|  |  | ||||||
|  |                     if (is_signed) { | ||||||
|  |                         return "int(" + value + ')'; | ||||||
|  |                     } | ||||||
|  |                     return value; | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |                 const std::string op_a = Unpack(regs.GetRegisterAsInteger(instr.gpr8, 0, false), | ||||||
|  |                                                 instr.vmad.is_byte_chunk_a != 0, signed_a, | ||||||
|  |                                                 instr.vmad.type_a, instr.vmad.byte_height_a); | ||||||
|  |  | ||||||
|  |                 std::string op_b; | ||||||
|  |                 if (instr.vmad.use_register_b) { | ||||||
|  |                     op_b = Unpack(regs.GetRegisterAsInteger(instr.gpr20, 0, false), | ||||||
|  |                                   instr.vmad.is_byte_chunk_b != 0, signed_b, instr.vmad.type_b, | ||||||
|  |                                   instr.vmad.byte_height_b); | ||||||
|  |                 } else { | ||||||
|  |                     op_b = '(' + | ||||||
|  |                            std::to_string(signed_b ? static_cast<s16>(instr.alu.GetImm20_16()) | ||||||
|  |                                                    : instr.alu.GetImm20_16()) + | ||||||
|  |                            ')'; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39, 0, result_signed); | ||||||
|  |  | ||||||
|  |                 std::string result; | ||||||
|  |                 if (forced_result) { | ||||||
|  |                     result = *forced_result; | ||||||
|  |                 } else { | ||||||
|  |                     result = '(' + op_a + " * " + op_b + " + " + op_c + ')'; | ||||||
|  |  | ||||||
|  |                     switch (instr.vmad.shr) { | ||||||
|  |                     case Tegra::Shader::VmadShr::Shr7: | ||||||
|  |                         result = '(' + result + " >> 7)"; | ||||||
|  |                         break; | ||||||
|  |                     case Tegra::Shader::VmadShr::Shr15: | ||||||
|  |                         result = '(' + result + " >> 15)"; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, | ||||||
|  |                                           instr.vmad.saturate == 1, 0, Register::Size::Word, | ||||||
|  |                                           instr.vmad.cc); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|             default: { |             default: { | ||||||
|                 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); |                 LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); | ||||||
|                 UNREACHABLE(); |                 UNREACHABLE(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user