diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index f4ef54a4d..aa6ee9601 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -275,7 +275,11 @@ public: void V_MUL_LO_U32(const GcnInst& inst); void V_MUL_HI_U32(bool is_signed, const GcnInst& inst); void V_MAD_U64_U32(const GcnInst& inst); + void V_LSHL_ADD_U32(const GcnInst& inst); + void V_ADD_LSHL_U32(const GcnInst& inst); void V_ADD3_U32(const GcnInst& inst); + void V_LSHL_OR_B32(const GcnInst& inst); + void V_AND_OR_B32(const GcnInst& inst); void V_OR3_B32(const GcnInst& inst); // Vector interpolation diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index c35a534aa..f9f7bee06 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -458,6 +458,14 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_MAD_U64_U32(inst); case Opcode::V_ADD3_U32: return V_ADD3_U32(inst); + case Opcode::V_ADD_LSHL_U32: + return V_ADD_LSHL_U32(inst); + case Opcode::V_LSHL_ADD_U32: + return V_LSHL_ADD_U32(inst); + case Opcode::V_LSHL_OR_B32: + return V_LSHL_OR_B32(inst); + case Opcode::V_AND_OR_B32: + return V_AND_OR_B32(inst); case Opcode::V_OR3_B32: return V_OR3_B32(inst); case Opcode::V_NOP: @@ -1552,6 +1560,30 @@ void Translator::V_MAD_U64_U32(const GcnInst& inst) { ir.SetVcc(did_overflow); } +void Translator::V_ADD_LSHL_U32(const GcnInst& inst) { + const auto src0 = GetSrc(inst.src[0]); + const auto src1 = GetSrc(inst.src[1]); + const auto src2 = GetSrc(inst.src[2]); + + const auto shift = ir.BitwiseAnd(src2, ir.Imm32(0x1F)); + + const auto result = ir.ShiftLeftLogical(ir.IAdd(src0, src1), shift); + + SetDst(inst.dst[0], result); +} + +void Translator::V_LSHL_ADD_U32(const GcnInst& inst) { + const auto src0 = GetSrc(inst.src[0]); + const auto src1 = GetSrc(inst.src[1]); + const auto src2 = GetSrc(inst.src[2]); + + const auto shift = ir.BitwiseAnd(src1, ir.Imm32(0x1F)); + + const auto result = ir.IAdd(ir.ShiftLeftLogical(src0, shift), src2); + + SetDst(inst.dst[0], result); +} + void Translator::V_ADD3_U32(const GcnInst& inst) { const auto src0 = GetSrc(inst.src[0]); const auto src1 = GetSrc(inst.src[1]); @@ -1560,6 +1592,28 @@ void Translator::V_ADD3_U32(const GcnInst& inst) { SetDst(inst.dst[0], ir.IAdd(src0, ir.IAdd(src1, src2))); } +void Translator::V_LSHL_OR_B32(const GcnInst& inst) { + const auto src0 = GetSrc(inst.src[0]); + const auto src1 = GetSrc(inst.src[1]); + const auto src2 = GetSrc(inst.src[2]); + + const auto shift = ir.BitwiseAnd(src1, ir.Imm32(0x1F)); + + const auto result = ir.BitwiseOr(ir.ShiftLeftLogical(src0, shift), src2); + + SetDst(inst.dst[0], result); +} + +void Translator::V_AND_OR_B32(const GcnInst& inst) { + const auto src0 = GetSrc(inst.src[0]); + const auto src1 = GetSrc(inst.src[1]); + const auto src2 = GetSrc(inst.src[2]); + + const auto result = ir.BitwiseOr(ir.BitwiseAnd(src0, src1), src2); + + SetDst(inst.dst[0], result); +} + void Translator::V_OR3_B32(const GcnInst& inst) { const auto src0 = GetSrc(inst.src[0]); const auto src1 = GetSrc(inst.src[1]); diff --git a/tests/gcn/test_gcn_instructions.cpp b/tests/gcn/test_gcn_instructions.cpp index 76659f1d6..59fb95c14 100644 --- a/tests/gcn/test_gcn_instructions.cpp +++ b/tests/gcn/test_gcn_instructions.cpp @@ -244,3 +244,83 @@ TEST_F(GcnTest, or3_u32_4) { EXPECT_TRUE(result.has_value()); EXPECT_EQ(*result, 0xD7575757); } + +TEST_F(GcnTest, and_or_b32_1) { + auto runner = gcn_test::Runner::instance().value(); + + auto spirv = TranslateToSpirv(VOP3A(OpcodeVOP3::V_AND_OR_B32, VOperand8::V0, SOperand9::V0, SOperand9::V1, SOperand9::V2).Get()); + auto result = runner->run(spirv, std::array{0xF0F0F0F0, 0x07070707, 0x11111111}); + + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(*result, 0x11111111); +} + +TEST_F(GcnTest, and_or_b32_2) { + auto runner = gcn_test::Runner::instance().value(); + + auto spirv = TranslateToSpirv(VOP3A(OpcodeVOP3::V_AND_OR_B32, VOperand8::V0, SOperand9::V0, SOperand9::V1, SOperand9::V2).SetOmod(Omod::Mul2).Get()); + auto result = runner->run(spirv, std::array{0x40404040, 0x40404040, 0x40404040}); + + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(*result, 0x40404040); +} + +TEST_F(GcnTest, and_or_b32_3) { + auto runner = gcn_test::Runner::instance().value(); + + auto spirv = TranslateToSpirv(VOP3A(OpcodeVOP3::V_AND_OR_B32, VOperand8::V0, SOperand9::V0, SOperand9::V1, SOperand9::V2).SetClamp(true).Get()); + auto result = runner->run(spirv, std::array{0x40404040, 0x40404040, 0x40404040}); + + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(*result, 0x40404040); +} + +TEST_F(GcnTest, and_or_b32_4) { + auto runner = gcn_test::Runner::instance().value(); + + auto spirv = TranslateToSpirv(VOP3A(OpcodeVOP3::V_AND_OR_B32, VOperand8::V0, SOperand9::V0, SOperand9::V1, SOperand9::V2).SetNeg({1,0,0}).Get()); + auto result = runner->run(spirv, std::array{0x07070707, 0x11111111, 0xF0F0F0F0}); + + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(*result, 0xF1F1F1F1); +} + +TEST_F(GcnTest, and_or_b32_5) { + auto runner = gcn_test::Runner::instance().value(); + + auto spirv = TranslateToSpirv(VOP3A(OpcodeVOP3::V_AND_OR_B32, VOperand8::V0, SOperand9::V0, SOperand9::V1, SOperand9::V2).SetNeg({1,0,0}).SetAbs({1,0,0}).Get()); + auto result = runner->run(spirv, std::array{0x77777777, 0xB0B0B0B0, 0x11111111}); + + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(*result, 0xB1313131); +} + +TEST_F(GcnTest, and_or_b32_6) { + auto runner = gcn_test::Runner::instance().value(); + + auto spirv = TranslateToSpirv(VOP3A(OpcodeVOP3::V_AND_OR_B32, VOperand8::V0, SOperand9::V0, SOperand9::V1, SOperand9::V2).SetOmod(Omod::Mul2).Get()); + auto result = runner->run(spirv, std::array{0x40404040, 0xB0B0B0B0, 0x11111111}); + + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(*result, 0x11111111); +} + +TEST_F(GcnTest, and_or_b32_7) { + auto runner = gcn_test::Runner::instance().value(); + + auto spirv = TranslateToSpirv(VOP3A(OpcodeVOP3::V_AND_OR_B32, VOperand8::V0, SOperand9::V0, SOperand9::V1, SOperand9::V2).SetOmod(Omod::Div2).Get()); + auto result = runner->run(spirv, std::array{0xB0B0B0B0, 0x77777777, 0x40404040}); + + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(*result, 0x70707070); +} + +TEST_F(GcnTest, and_or_b32_8) { + auto runner = gcn_test::Runner::instance().value(); + + auto spirv = TranslateToSpirv(VOP3A(OpcodeVOP3::V_AND_OR_B32, VOperand8::V0, SOperand9::V0, SOperand9::V1, SOperand9::V2).SetAbs({1,1,0}).Get()); + auto result = runner->run(spirv, std::array{0xB0B0B0B0, 0x11111111, 0x11111111}); + + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(*result, 0x11111111); +}