Merge pull request #570 from lioncash/parallel

thumb32: Implement parallel add/sub instructions
This commit is contained in:
merry 2021-02-01 23:17:19 +00:00 committed by GitHub
commit 644350c7e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 682 additions and 66 deletions

View file

@ -152,6 +152,7 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS)
frontend/A32/translate/impl/thumb16.cpp frontend/A32/translate/impl/thumb16.cpp
frontend/A32/translate/impl/thumb32.cpp frontend/A32/translate/impl/thumb32.cpp
frontend/A32/translate/impl/thumb32_misc.cpp frontend/A32/translate/impl/thumb32_misc.cpp
frontend/A32/translate/impl/thumb32_parallel.cpp
frontend/A32/translate/impl/translate_arm.h frontend/A32/translate/impl/translate_arm.h
frontend/A32/translate/impl/translate_thumb.h frontend/A32/translate/impl/translate_thumb.h
frontend/A32/translate/impl/vfp.cpp frontend/A32/translate/impl/vfp.cpp

View file

@ -235,44 +235,44 @@ std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32
//INST(&V::thumb32_UXTAB, "UXTAB", "111110100101----1111----1-------"), //INST(&V::thumb32_UXTAB, "UXTAB", "111110100101----1111----1-------"),
// Parallel Addition and Subtraction (signed) // Parallel Addition and Subtraction (signed)
//INST(&V::thumb32_SADD16, "SADD16", "111110101001----1111----0000----"), INST(&V::thumb32_SADD16, "SADD16", "111110101001nnnn1111dddd0000mmmm"),
//INST(&V::thumb32_SASX, "SASX", "111110101010----1111----0000----"), INST(&V::thumb32_SASX, "SASX", "111110101010nnnn1111dddd0000mmmm"),
//INST(&V::thumb32_SSAX, "SSAX", "111110101110----1111----0000----"), INST(&V::thumb32_SSAX, "SSAX", "111110101110nnnn1111dddd0000mmmm"),
//INST(&V::thumb32_SSUB16, "SSUB16", "111110101101----1111----0000----"), INST(&V::thumb32_SSUB16, "SSUB16", "111110101101nnnn1111dddd0000mmmm"),
//INST(&V::thumb32_SADD8, "SADD8", "111110101000----1111----0000----"), INST(&V::thumb32_SADD8, "SADD8", "111110101000nnnn1111dddd0000mmmm"),
//INST(&V::thumb32_SSUB8, "SSUB8", "111110101100----1111----0000----"), INST(&V::thumb32_SSUB8, "SSUB8", "111110101100nnnn1111dddd0000mmmm"),
//INST(&V::thumb32_QADD16, "QADD16", "111110101001----1111----0001----"), INST(&V::thumb32_QADD16, "QADD16", "111110101001nnnn1111dddd0001mmmm"),
//INST(&V::thumb32_QASX, "QASX", "111110101010----1111----0001----"), INST(&V::thumb32_QASX, "QASX", "111110101010nnnn1111dddd0001mmmm"),
//INST(&V::thumb32_QSAX, "QSAX", "111110101110----1111----0001----"), INST(&V::thumb32_QSAX, "QSAX", "111110101110nnnn1111dddd0001mmmm"),
//INST(&V::thumb32_QSUB16, "QSUB16", "111110101101----1111----0001----"), INST(&V::thumb32_QSUB16, "QSUB16", "111110101101nnnn1111dddd0001mmmm"),
//INST(&V::thumb32_QADD8, "QADD8", "111110101000----1111----0001----"), INST(&V::thumb32_QADD8, "QADD8", "111110101000nnnn1111dddd0001mmmm"),
//INST(&V::thumb32_QSUB8, "QSUB8", "111110101100----1111----0001----"), INST(&V::thumb32_QSUB8, "QSUB8", "111110101100nnnn1111dddd0001mmmm"),
//INST(&V::thumb32_SHADD16, "SHADD16", "111110101001----1111----0010----"), INST(&V::thumb32_SHADD16, "SHADD16", "111110101001nnnn1111dddd0010mmmm"),
//INST(&V::thumb32_SHASX, "SHASX", "111110101010----1111----0010----"), INST(&V::thumb32_SHASX, "SHASX", "111110101010nnnn1111dddd0010mmmm"),
//INST(&V::thumb32_SHSAX, "SHSAX", "111110101110----1111----0010----"), INST(&V::thumb32_SHSAX, "SHSAX", "111110101110nnnn1111dddd0010mmmm"),
//INST(&V::thumb32_SHSUB16, "SHSUB16", "111110101101----1111----0010----"), INST(&V::thumb32_SHSUB16, "SHSUB16", "111110101101nnnn1111dddd0010mmmm"),
//INST(&V::thumb32_SHADD8, "SHADD8", "111110101000----1111----0010----"), INST(&V::thumb32_SHADD8, "SHADD8", "111110101000nnnn1111dddd0010mmmm"),
//INST(&V::thumb32_SHSUB8, "SHSUB8", "111110101100----1111----0010----"), INST(&V::thumb32_SHSUB8, "SHSUB8", "111110101100nnnn1111dddd0010mmmm"),
// Parallel Addition and Subtraction (unsigned) // Parallel Addition and Subtraction (unsigned)
//INST(&V::thumb32_UADD16, "UADD16", "111110101001----1111----0100----"), INST(&V::thumb32_UADD16, "UADD16", "111110101001nnnn1111dddd0100mmmm"),
//INST(&V::thumb32_UASX, "UASX", "111110101010----1111----0100----"), INST(&V::thumb32_UASX, "UASX", "111110101010nnnn1111dddd0100mmmm"),
//INST(&V::thumb32_USAX, "USAX", "111110101110----1111----0100----"), INST(&V::thumb32_USAX, "USAX", "111110101110nnnn1111dddd0100mmmm"),
//INST(&V::thumb32_USUB16, "USUB16", "111110101101----1111----0100----"), INST(&V::thumb32_USUB16, "USUB16", "111110101101nnnn1111dddd0100mmmm"),
//INST(&V::thumb32_UADD8, "UADD8", "111110101000----1111----0100----"), INST(&V::thumb32_UADD8, "UADD8", "111110101000nnnn1111dddd0100mmmm"),
//INST(&V::thumb32_USUB8, "USUB8", "111110101100----1111----0100----"), INST(&V::thumb32_USUB8, "USUB8", "111110101100nnnn1111dddd0100mmmm"),
//INST(&V::thumb32_UQADD16, "UQADD16", "111110101001----1111----0101----"), INST(&V::thumb32_UQADD16, "UQADD16", "111110101001nnnn1111dddd0101mmmm"),
//INST(&V::thumb32_UQASX, "UQASX", "111110101010----1111----0101----"), INST(&V::thumb32_UQASX, "UQASX", "111110101010nnnn1111dddd0101mmmm"),
//INST(&V::thumb32_UQSAX, "UQSAX", "111110101110----1111----0101----"), INST(&V::thumb32_UQSAX, "UQSAX", "111110101110nnnn1111dddd0101mmmm"),
//INST(&V::thumb32_UQSUB16, "UQSUB16", "111110101101----1111----0101----"), INST(&V::thumb32_UQSUB16, "UQSUB16", "111110101101nnnn1111dddd0101mmmm"),
//INST(&V::thumb32_UQADD8, "UQADD8", "111110101000----1111----0101----"), INST(&V::thumb32_UQADD8, "UQADD8", "111110101000nnnn1111dddd0101mmmm"),
//INST(&V::thumb32_UQSUB8, "UQSUB8", "111110101100----1111----0101----"), INST(&V::thumb32_UQSUB8, "UQSUB8", "111110101100nnnn1111dddd0101mmmm"),
//INST(&V::thumb32_UHADD16, "UHADD16", "111110101001----1111----0110----"), INST(&V::thumb32_UHADD16, "UHADD16", "111110101001nnnn1111dddd0110mmmm"),
//INST(&V::thumb32_UHASX, "UHASX", "111110101010----1111----0110----"), INST(&V::thumb32_UHASX, "UHASX", "111110101010nnnn1111dddd0110mmmm"),
//INST(&V::thumb32_UHSAX, "UHSAX", "111110101110----1111----0110----"), INST(&V::thumb32_UHSAX, "UHSAX", "111110101110nnnn1111dddd0110mmmm"),
//INST(&V::thumb32_UHSUB16, "UHSUB16", "111110101101----1111----0110----"), INST(&V::thumb32_UHSUB16, "UHSUB16", "111110101101nnnn1111dddd0110mmmm"),
//INST(&V::thumb32_UHADD8, "UHADD8", "111110101000----1111----0110----"), INST(&V::thumb32_UHADD8, "UHADD8", "111110101000nnnn1111dddd0110mmmm"),
//INST(&V::thumb32_UHSUB8, "UHSUB8", "111110101100----1111----0110----"), INST(&V::thumb32_UHSUB8, "UHSUB8", "111110101100nnnn1111dddd0110mmmm"),
// Miscellaneous Operations // Miscellaneous Operations
INST(&V::thumb32_QADD, "QADD", "111110101000nnnn1111dddd1000mmmm"), INST(&V::thumb32_QADD, "QADD", "111110101000nnnn1111dddd1000mmmm"),

View file

@ -0,0 +1,521 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_thumb.h"
namespace Dynarmic::A32 {
static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) {
return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result);
}
static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) {
return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result);
}
bool ThumbTranslatorVisitor::thumb32_SADD8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedAddS8(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SADD16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedAddS16(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SASX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedAddSubS16(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SSAX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSubAddS16(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SSUB8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSubS8(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SSUB16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSubS16(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UADD8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedAddU8(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UADD16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedAddU16(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UASX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedAddSubU16(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_USAX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSubAddU16(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_USUB8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSubU8(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_USUB16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSubU16(reg_n, reg_m);
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
bool ThumbTranslatorVisitor::thumb32_QADD8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSaturatedAddS8(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_QADD16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSaturatedAddS16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_QASX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto Rn = ir.GetRegister(n);
const auto Rm = ir.GetRegister(m);
const auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn));
const auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn));
const auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm));
const auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm));
const auto diff = ir.SignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result;
const auto sum = ir.SignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result;
const auto result = Pack2x16To1x32(ir, diff, sum);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_QSAX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto Rn = ir.GetRegister(n);
const auto Rm = ir.GetRegister(m);
const auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn));
const auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn));
const auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm));
const auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm));
const auto sum = ir.SignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
const auto diff = ir.SignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
const auto result = Pack2x16To1x32(ir, sum, diff);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_QSUB8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSaturatedSubS8(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_QSUB16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSaturatedSubS16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UQADD8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSaturatedAddU8(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UQADD16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSaturatedAddU16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UQASX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto Rn = ir.GetRegister(n);
const auto Rm = ir.GetRegister(m);
const auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn));
const auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn));
const auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm));
const auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm));
const auto diff = ir.UnsignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result;
const auto sum = ir.UnsignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result;
const auto result = Pack2x16To1x32(ir, diff, sum);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UQSAX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto Rn = ir.GetRegister(n);
const auto Rm = ir.GetRegister(m);
const auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn));
const auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn));
const auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm));
const auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm));
const auto sum = ir.UnsignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
const auto diff = ir.UnsignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
const auto result = Pack2x16To1x32(ir, sum, diff);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UQSUB8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSaturatedSubU8(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UQSUB16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedSaturatedSubU16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SHADD8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingAddS8(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SHADD16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingAddS16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SHASX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingAddSubS16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SHSAX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingSubAddS16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SHSUB8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingSubS8(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_SHSUB16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingSubS16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UHADD8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingAddU8(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UHADD16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingAddU16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UHASX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingAddSubU16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UHSAX(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingSubAddU16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UHSUB8(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingSubU8(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
bool ThumbTranslatorVisitor::thumb32_UHSUB16(Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto reg_m = ir.GetRegister(m);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.PackedHalvingSubU16(reg_n, reg_m);
ir.SetRegister(d, result);
return true;
}
} // namespace Dynarmic::A32

View file

@ -127,6 +127,46 @@ struct ThumbTranslatorVisitor final {
bool thumb32_REV16(Reg n, Reg d, Reg m); bool thumb32_REV16(Reg n, Reg d, Reg m);
bool thumb32_REVSH(Reg n, Reg d, Reg m); bool thumb32_REVSH(Reg n, Reg d, Reg m);
bool thumb32_SEL(Reg n, Reg d, Reg m); bool thumb32_SEL(Reg n, Reg d, Reg m);
// thumb32 parallel add/sub instructions
bool thumb32_SADD8(Reg n, Reg d, Reg m);
bool thumb32_SADD16(Reg n, Reg d, Reg m);
bool thumb32_SASX(Reg n, Reg d, Reg m);
bool thumb32_SSAX(Reg n, Reg d, Reg m);
bool thumb32_SSUB8(Reg n, Reg d, Reg m);
bool thumb32_SSUB16(Reg n, Reg d, Reg m);
bool thumb32_UADD8(Reg n, Reg d, Reg m);
bool thumb32_UADD16(Reg n, Reg d, Reg m);
bool thumb32_UASX(Reg n, Reg d, Reg m);
bool thumb32_USAX(Reg n, Reg d, Reg m);
bool thumb32_USUB8(Reg n, Reg d, Reg m);
bool thumb32_USUB16(Reg n, Reg d, Reg m);
bool thumb32_QADD8(Reg n, Reg d, Reg m);
bool thumb32_QADD16(Reg n, Reg d, Reg m);
bool thumb32_QASX(Reg n, Reg d, Reg m);
bool thumb32_QSAX(Reg n, Reg d, Reg m);
bool thumb32_QSUB8(Reg n, Reg d, Reg m);
bool thumb32_QSUB16(Reg n, Reg d, Reg m);
bool thumb32_UQADD8(Reg n, Reg d, Reg m);
bool thumb32_UQADD16(Reg n, Reg d, Reg m);
bool thumb32_UQASX(Reg n, Reg d, Reg m);
bool thumb32_UQSAX(Reg n, Reg d, Reg m);
bool thumb32_UQSUB8(Reg n, Reg d, Reg m);
bool thumb32_UQSUB16(Reg n, Reg d, Reg m);
bool thumb32_SHADD8(Reg n, Reg d, Reg m);
bool thumb32_SHADD16(Reg n, Reg d, Reg m);
bool thumb32_SHASX(Reg n, Reg d, Reg m);
bool thumb32_SHSAX(Reg n, Reg d, Reg m);
bool thumb32_SHSUB8(Reg n, Reg d, Reg m);
bool thumb32_SHSUB16(Reg n, Reg d, Reg m);
bool thumb32_UHADD8(Reg n, Reg d, Reg m);
bool thumb32_UHADD16(Reg n, Reg d, Reg m);
bool thumb32_UHASX(Reg n, Reg d, Reg m);
bool thumb32_UHSAX(Reg n, Reg d, Reg m);
bool thumb32_UHSUB8(Reg n, Reg d, Reg m);
bool thumb32_UHSUB16(Reg n, Reg d, Reg m);
}; };
} // namespace Dynarmic::A32 } // namespace Dynarmic::A32

View file

@ -361,6 +361,13 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb][Thumb16
} }
TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") { TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
const auto three_reg_not_r15 = [](u32 inst) {
const auto d = Common::Bits<8, 11>(inst);
const auto m = Common::Bits<0, 3>(inst);
const auto n = Common::Bits<16, 19>(inst);
return d != 15 && m != 15 && n != 15;
};
const std::array instructions = { const std::array instructions = {
ThumbInstGen("111110101011nnnn1111dddd1000mmmm", // CLZ ThumbInstGen("111110101011nnnn1111dddd1000mmmm", // CLZ
[](u32 inst) { [](u32 inst) {
@ -370,33 +377,25 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
return m == n && d != 15 && m != 15; return m == n && d != 15 && m != 15;
}), }),
ThumbInstGen("111110101000nnnn1111dddd1000mmmm", // QADD ThumbInstGen("111110101000nnnn1111dddd1000mmmm", // QADD
[](u32 inst) { three_reg_not_r15),
const auto d = Common::Bits<8, 11>(inst); ThumbInstGen("111110101000nnnn1111dddd0001mmmm", // QADD8
const auto m = Common::Bits<0, 3>(inst); three_reg_not_r15),
const auto n = Common::Bits<16, 19>(inst); ThumbInstGen("111110101001nnnn1111dddd0001mmmm", // QADD16
return d != 15 && m != 15 && n != 15; three_reg_not_r15),
}), ThumbInstGen("111110101010nnnn1111dddd0001mmmm", // QASX
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd1001mmmm", // QDADD ThumbInstGen("111110101000nnnn1111dddd1001mmmm", // QDADD
[](u32 inst) { three_reg_not_r15),
const auto d = Common::Bits<8, 11>(inst);
const auto m = Common::Bits<0, 3>(inst);
const auto n = Common::Bits<16, 19>(inst);
return d != 15 && m != 15 && n != 15;
}),
ThumbInstGen("111110101000nnnn1111dddd1011mmmm", // QDSUB ThumbInstGen("111110101000nnnn1111dddd1011mmmm", // QDSUB
[](u32 inst) { three_reg_not_r15),
const auto d = Common::Bits<8, 11>(inst); ThumbInstGen("111110101110nnnn1111dddd0001mmmm", // QSAX
const auto m = Common::Bits<0, 3>(inst); three_reg_not_r15),
const auto n = Common::Bits<16, 19>(inst);
return d != 15 && m != 15 && n != 15;
}),
ThumbInstGen("111110101000nnnn1111dddd1010mmmm", // QSUB ThumbInstGen("111110101000nnnn1111dddd1010mmmm", // QSUB
[](u32 inst) { three_reg_not_r15),
const auto d = Common::Bits<8, 11>(inst); ThumbInstGen("111110101100nnnn1111dddd0001mmmm", // QSUB8
const auto m = Common::Bits<0, 3>(inst); three_reg_not_r15),
const auto n = Common::Bits<16, 19>(inst); ThumbInstGen("111110101101nnnn1111dddd0001mmmm", // QSUB16
return d != 15 && m != 15 && n != 15; three_reg_not_r15),
}),
ThumbInstGen("111110101001nnnn1111dddd1010mmmm", // RBIT ThumbInstGen("111110101001nnnn1111dddd1010mmmm", // RBIT
[](u32 inst) { [](u32 inst) {
const auto d = Common::Bits<8, 11>(inst); const auto d = Common::Bits<8, 11>(inst);
@ -425,13 +424,68 @@ TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
const auto n = Common::Bits<16, 19>(inst); const auto n = Common::Bits<16, 19>(inst);
return m == n && d != 15 && m != 15; return m == n && d != 15 && m != 15;
}), }),
ThumbInstGen("111110101000nnnn1111dddd0000mmmm", // SADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0000mmmm", // SADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0000mmmm", // SASX
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd1000mmmm", // SEL ThumbInstGen("111110101010nnnn1111dddd1000mmmm", // SEL
[](u32 inst) { three_reg_not_r15),
const auto d = Common::Bits<8, 11>(inst); ThumbInstGen("111110101000nnnn1111dddd0010mmmm", // SHADD8
const auto m = Common::Bits<0, 3>(inst); three_reg_not_r15),
const auto n = Common::Bits<16, 19>(inst); ThumbInstGen("111110101001nnnn1111dddd0010mmmm", // SHADD16
return d != 15 && m != 15 && n != 15; three_reg_not_r15),
}), ThumbInstGen("111110101010nnnn1111dddd0010mmmm", // SHASX
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0010mmmm", // SHSAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0010mmmm", // SHSUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0010mmmm", // SHSUB16
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0000mmmm", // SSAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0000mmmm", // SSUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0000mmmm", // SSUB16
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd0100mmmm", // UADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0100mmmm", // UADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0100mmmm", // UASX
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd0110mmmm", // UHADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0110mmmm", // UHADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0110mmmm", // UHASX
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0110mmmm", // UHSAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0110mmmm", // UHSUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0110mmmm", // UHSUB16
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd0101mmmm", // UQADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0101mmmm", // UQADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0101mmmm", // UQASX
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0101mmmm", // UQSAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0101mmmm", // UQSUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0101mmmm", // UQSUB16
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0100mmmm", // USAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0100mmmm", // USUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0100mmmm", // USUB16
three_reg_not_r15),
}; };
const auto instruction_select = [&]() -> u32 { const auto instruction_select = [&]() -> u32 {