diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 2c707414e..0c239d4b1 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -1598,16 +1598,31 @@ void OpDispatchBuilder::SHLDOp(OpcodeArgs) { else Shift = _And(Shift, _Constant(0x1F)); + auto CondJump = _CondJump(Shift); + + // Do nothing if shift count is zero + auto JumpTarget = CreateNewCodeBlock(); + SetTrueJumpTarget(CondJump, JumpTarget); + SetCurrentCodeBlock(JumpTarget); + + auto ShiftRight = _Sub(_Constant(Size), Shift); OrderedNode *Res{}; auto Tmp1 = _Lshl(Dest, Shift); auto Tmp2 = _Lshr(Src, ShiftRight); + Res = _Or(Tmp1, Tmp2); StoreResult(GPRClass, Op, Res, -1); - GenerateFlags_ShiftLeft(Op, _Bfe(Size, 0, Res), _Bfe(Size, 0, Dest), _Bfe(Size, 0, Src)); + GenerateFlags_ShiftLeft(Op, _Bfe(Size, 0, Res), _Bfe(Size, 0, Dest), _Bfe(Size, 0, Shift)); + + auto Jump = _Jump(); + auto NextJumpTarget = CreateNewCodeBlock(); + SetJumpTarget(Jump, NextJumpTarget); + SetFalseJumpTarget(CondJump, NextJumpTarget); + SetCurrentCodeBlock(NextJumpTarget); } void OpDispatchBuilder::SHLDImmediateOp(OpcodeArgs) { @@ -1625,17 +1640,19 @@ void OpDispatchBuilder::SHLDImmediateOp(OpcodeArgs) { else Shift &= 0x1F; - OrderedNode *ShiftLeft = _Constant(Shift); - auto ShiftRight = _Constant(Size - Shift); + if (Shift != 0) { + OrderedNode *ShiftLeft = _Constant(Shift); + auto ShiftRight = _Constant(Size - Shift); - OrderedNode *Res{}; - auto Tmp1 = _Lshl(Dest, ShiftLeft); - auto Tmp2 = _Lshr(Src, ShiftRight); - Res = _Or(Tmp1, Tmp2); + OrderedNode *Res{}; + auto Tmp1 = _Lshl(Dest, ShiftLeft); + auto Tmp2 = _Lshr(Src, ShiftRight); + Res = _Or(Tmp1, Tmp2); - StoreResult(GPRClass, Op, Res, -1); + StoreResult(GPRClass, Op, Res, -1); - GenerateFlags_ShiftLeftImmediate(Op, _Bfe(Size, 0, Res), _Bfe(Size, 0, Dest), Shift); + GenerateFlags_ShiftLeftImmediate(Op, _Bfe(Size, 0, Res), _Bfe(Size, 0, Dest), Shift); + } } void OpDispatchBuilder::SHRDOp(OpcodeArgs) { @@ -1652,6 +1669,14 @@ void OpDispatchBuilder::SHRDOp(OpcodeArgs) { else Shift = _And(Shift, _Constant(0x1F)); + + auto CondJump = _CondJump(Shift); + + // Do nothing if shift count is zero + auto JumpTarget = CreateNewCodeBlock(); + SetTrueJumpTarget(CondJump, JumpTarget); + SetCurrentCodeBlock(JumpTarget); + auto ShiftLeft = _Sub(_Constant(Size), Shift); OrderedNode *Res{}; @@ -1660,7 +1685,13 @@ void OpDispatchBuilder::SHRDOp(OpcodeArgs) { Res = _Or(Tmp1, Tmp2); StoreResult(GPRClass, Op, Res, -1); - GenerateFlags_ShiftRight(Op, _Bfe(Size, 0, Res), _Bfe(Size, 0, Dest), _Bfe(Size, 0, Src)); + GenerateFlags_ShiftRight(Op, _Bfe(Size, 0, Res), _Bfe(Size, 0, Dest), _Bfe(Size, 0, Shift)); + + auto Jump = _Jump(); + auto NextJumpTarget = CreateNewCodeBlock(); + SetJumpTarget(Jump, NextJumpTarget); + SetFalseJumpTarget(CondJump, NextJumpTarget); + SetCurrentCodeBlock(NextJumpTarget); } void OpDispatchBuilder::SHRDImmediateOp(OpcodeArgs) { @@ -1678,17 +1709,19 @@ void OpDispatchBuilder::SHRDImmediateOp(OpcodeArgs) { else Shift = Op->Src[1].TypeLiteral.Literal & 0x1F; - OrderedNode *ShiftRight = _Constant(Shift); - auto ShiftLeft = _Constant(Size - Shift); + if (Shift != 0) { + OrderedNode *ShiftRight = _Constant(Shift); + auto ShiftLeft = _Constant(Size - Shift); - OrderedNode *Res{}; - auto Tmp1 = _Lshr(Dest, ShiftRight); - auto Tmp2 = _Lshl(Src, ShiftLeft); - Res = _Or(Tmp1, Tmp2); + OrderedNode *Res{}; + auto Tmp1 = _Lshr(Dest, ShiftRight); + auto Tmp2 = _Lshl(Src, ShiftLeft); + Res = _Or(Tmp1, Tmp2); - StoreResult(GPRClass, Op, Res, -1); + StoreResult(GPRClass, Op, Res, -1); - GenerateFlags_ShiftRightImmediate(Op, _Bfe(Size, 0, Res), _Bfe(Size, 0, Dest), Shift); + GenerateFlags_ShiftRightImmediate(Op, _Bfe(Size, 0, Res), _Bfe(Size, 0, Dest), Shift); + } } template @@ -4863,16 +4896,14 @@ void OpDispatchBuilder::GenerateFlags_ShiftLeft(FEXCore::X86Tables::DecodedOp Op // SF { - auto SignBitConst = _Constant(GetSrcSize(Op) * 8 - 1); + SetRFLAG(_Bfe(1, GetSrcSize(Op) * 8 - 1, Res)); + } - auto LshrOp = _Lshr(Res, SignBitConst); - SetRFLAG(LshrOp); - - // OF + // OF + { // In the case of left shift. OF is only set from the result of XOR // When Shift > 1 then OF is undefined - auto SourceBit = _Bfe(1, GetSrcSize(Op) * 8 - 1, Src1); - SetRFLAG(_Xor(SourceBit, LshrOp)); + SetRFLAG(_Bfe(1, GetSrcSize(Op) * 8 - 1, _Xor(Src1, Res))); } auto Jump = _Jump(); @@ -4923,17 +4954,15 @@ void OpDispatchBuilder::GenerateFlags_ShiftRight(FEXCore::X86Tables::DecodedOp O // SF { - auto SignBitConst = _Constant(GetSrcSize(Op) * 8 - 1); - - auto LshrOp = _Lshr(Res, SignBitConst); - SetRFLAG(LshrOp); + SetRFLAG(_Bfe(1, GetSrcSize(Op) * 8 - 1, Res)); } // OF { // Only defined when Shift is 1 else undefined - // Is set to the MSB of the original value - SetRFLAG(_Bfe(1, GetSrcSize(Op) * 8 - 1, Src1)); + // OF flag is set if a sign change occurred + + SetRFLAG(_Bfe(1, GetSrcSize(Op) * 8 - 1, _Xor(Src1, Res))); } auto Jump = _Jump(); diff --git a/unittests/ASM/TwoByte/0F_A5_2.asm b/unittests/ASM/TwoByte/0F_A5_2.asm new file mode 100644 index 000000000..4e5f6f1ef --- /dev/null +++ b/unittests/ASM/TwoByte/0F_A5_2.asm @@ -0,0 +1,22 @@ +%ifdef CONFIG +{ + "RegData": { + "R15": "0xFFFFFFFFFFFFFFFF", + "R14": "0x4141414141410000", + "R13": "0", + "R12": "0" + } +} +%endif + +mov cl, 0 +mov r15, -1 +mov r14, 0x4141414141410000 +mov r13, 0 +mov r12, 0 + +shld r14w, r15w, cl +shld r13d, r15d, cl +shld r12, r15, cl + +hlt diff --git a/unittests/ASM/TwoByte/0F_A5_3.asm b/unittests/ASM/TwoByte/0F_A5_3.asm new file mode 100644 index 000000000..b696bd608 --- /dev/null +++ b/unittests/ASM/TwoByte/0F_A5_3.asm @@ -0,0 +1,35 @@ +%ifdef CONFIG +{ + "RegData": { + "R15": "0xFFFFFFFFFFFFFFFF", + "R14": "0x4141414141410000", + "R13": "0", + "R12": "0", + "R11": "0" + } +} +%endif + +mov cl, 0 +mov r15, -1 +mov r14, 0x4141414141410000 +mov r13, 0 +mov r12, 0 +mov r11, 0 + +; Get the incoming flags +mov rax, 0 +lahf +mov r11, rax + +shld r14w, r15w, cl +shld r13d, r15d, cl +shld r12, r15, cl + +; Get the outgoing flags +; None should have changed +mov rax, 0 +lahf +xor r11, rax + +hlt diff --git a/unittests/ASM/TwoByte/0F_A5_4.asm b/unittests/ASM/TwoByte/0F_A5_4.asm new file mode 100644 index 000000000..3da70fa7c --- /dev/null +++ b/unittests/ASM/TwoByte/0F_A5_4.asm @@ -0,0 +1,113 @@ +%ifdef CONFIG +{ + "RegData": { + "r10": "1", + "RBX": "1", + "RDX": "1", + "RSI": "1", + "RBP": "1", + "RSP": "0", + "R8": "0", + "r9": "0" + } +} +%endif + +mov cl, 1 +mov r15, -1 +mov r14, 0xFFFFFFFFFFFF4000 +mov r13, 0xFFFFFFFF40000000 +mov r12, 0x4000000000000000 + +mov r10, 0 +mov rbx, 0 +mov rdx, 0 + +mov rsi, 0 +mov rdi, 0 +mov rbp, 0 + +mov rsp, 0 +mov r8, 0 +mov r9, 0 + +mov r11, 1 + +; Sign from 0->1 should set OF + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shld r14w, r15w, cl +cmovo r10, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shld r13d, r15d, cl +cmovo rbx, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shld r12, r15, cl +cmovo rdx, r11 + +; Sign from 1->0 should set OF +mov r15, -1 +mov r14, 0xFFFFFFFFFFFF8000 +mov r13, 0xFFFFFFFF80000000 +mov r12, 0x8000000000000000 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shld r14w, r15w, cl +cmovo rsi, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shld r13d, r15d, cl +cmovo rdi, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shld r12, r15, cl +cmovo rbp, r11 + +; Sign from 0->0 should NOT set OF +mov r15, -1 +mov r14, 0xFFFFFFFFFFFF0000 +mov r13, 0xFFFFFFFF00000000 +mov r12, 0x0000000000000000 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shld r14w, r15w, cl +cmovo rsp, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shld r13d, r15d, cl +cmovo r8, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shld r12, r15, cl +cmovo r9, r11 + +hlt diff --git a/unittests/ASM/TwoByte/0F_A5_5.asm b/unittests/ASM/TwoByte/0F_A5_5.asm new file mode 100644 index 000000000..97138920f --- /dev/null +++ b/unittests/ASM/TwoByte/0F_A5_5.asm @@ -0,0 +1,29 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "1", + "RBX": "1", + "RDX": "1" + } +} +%endif + +mov cl, 2 +mov r15, -1 +mov r14, 0x4141414141414000 +mov r13, 0xFFFFFFFF40000000 +mov r12, 0x4000000000000000 + +mov rax, 0 +mov rbx, 0 +mov rdx, 0 +mov rsi, 1 + +shld r14w, r15w, cl +cmovc rax, rsi +shld r13d, r15d, cl +cmovc rbx, rsi +shld r12, r15, cl +cmovc rdx, rsi + +hlt diff --git a/unittests/ASM/TwoByte/0F_A5_6.asm b/unittests/ASM/TwoByte/0F_A5_6.asm new file mode 100644 index 000000000..d9e58b9cc --- /dev/null +++ b/unittests/ASM/TwoByte/0F_A5_6.asm @@ -0,0 +1,29 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "1", + "RBX": "1", + "RDX": "1" + } +} +%endif + +mov cl, 2 +mov r15, 0 +mov r14, 0x4141414141414000 +mov r13, 0xFFFFFFFF40000000 +mov r12, 0x4000000000000000 + +mov rax, 0 +mov rbx, 0 +mov rdx, 0 +mov rsi, 1 + +shld r14w, r15w, cl +cmovz rax, rsi +shld r13d, r15d, cl +cmovz rbx, rsi +shld r12, r15, cl +cmovz rdx, rsi + +hlt diff --git a/unittests/ASM/TwoByte/0F_A5_7.asm b/unittests/ASM/TwoByte/0F_A5_7.asm new file mode 100644 index 000000000..fa358078e --- /dev/null +++ b/unittests/ASM/TwoByte/0F_A5_7.asm @@ -0,0 +1,29 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "1", + "RBX": "1", + "RDX": "1" + } +} +%endif + +mov cl, 2 +mov r15, 0 +mov r14, 0x4141414141412000 +mov r13, 0xFFFFFFFF20000000 +mov r12, 0x2000000000000000 + +mov rax, 0 +mov rbx, 0 +mov rdx, 0 +mov rsi, 1 + +shld r14w, r15w, cl +cmovs rax, rsi +shld r13d, r15d, cl +cmovs rbx, rsi +shld r12, r15, cl +cmovs rdx, rsi + +hlt diff --git a/unittests/ASM/TwoByte/0F_AD_2.asm b/unittests/ASM/TwoByte/0F_AD_2.asm new file mode 100644 index 000000000..223ac68c9 --- /dev/null +++ b/unittests/ASM/TwoByte/0F_AD_2.asm @@ -0,0 +1,22 @@ +%ifdef CONFIG +{ + "RegData": { + "R15": "0xFFFFFFFFFFFFFFFF", + "R14": "0x4141414141410000", + "R13": "0", + "R12": "0" + } +} +%endif + +mov cl, 0 +mov r15, -1 +mov r14, 0x4141414141410000 +mov r13, 0 +mov r12, 0 + +shrd r14w, r15w, cl +shrd r13d, r15d, cl +shrd r12, r15, cl + +hlt diff --git a/unittests/ASM/TwoByte/0F_AD_3.asm b/unittests/ASM/TwoByte/0F_AD_3.asm new file mode 100644 index 000000000..fec536c1a --- /dev/null +++ b/unittests/ASM/TwoByte/0F_AD_3.asm @@ -0,0 +1,35 @@ +%ifdef CONFIG +{ + "RegData": { + "R15": "0xFFFFFFFFFFFFFFFF", + "R14": "0x4141414141410000", + "R13": "0", + "R12": "0", + "R11": "0" + } +} +%endif + +mov cl, 0 +mov r15, -1 +mov r14, 0x4141414141410000 +mov r13, 0 +mov r12, 0 +mov r11, 0 + +; Get the incoming flags +mov rax, 0 +lahf +mov r11, rax + +shrd r14w, r15w, cl +shrd r13d, r15d, cl +shrd r12, r15, cl + +; Get the outgoing flags +; None should have changed +mov rax, 0 +lahf +xor r11, rax + +hlt diff --git a/unittests/ASM/TwoByte/0F_AD_4.asm b/unittests/ASM/TwoByte/0F_AD_4.asm new file mode 100644 index 000000000..2aa4d1e9e --- /dev/null +++ b/unittests/ASM/TwoByte/0F_AD_4.asm @@ -0,0 +1,113 @@ +%ifdef CONFIG +{ + "RegData": { + "r10": "1", + "RBX": "1", + "RDX": "1", + "RSI": "1", + "RBP": "1", + "RSP": "0", + "R8": "0", + "r9": "0" + } +} +%endif + +mov cl, 1 +mov r15, -1 +mov r14, 0xFFFFFFFFFFFF0000 +mov r13, 0xFFFFFFFF00000000 +mov r12, 0 + +mov r10, 0 +mov rbx, 0 +mov rdx, 0 + +mov rsi, 0 +mov rdi, 0 +mov rbp, 0 + +mov rsp, 0 +mov r8, 0 +mov r9, 0 + +mov r11, 1 + +; Sign from 0->1 should set OF + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shrd r14w, r15w, cl +cmovo r10, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shrd r13d, r15d, cl +cmovo rbx, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shrd r12, r15, cl +cmovo rdx, r11 + +; Sign from 1->0 should set OF +mov r15, 0 +mov r14, 0xFFFFFFFFFFFF8000 +mov r13, 0xFFFFFFFF80000000 +mov r12, 0x8000000000000000 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shrd r14w, r15w, cl +cmovo rsi, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shrd r13d, r15d, cl +cmovo rdi, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shrd r12, r15, cl +cmovo rbp, r11 + +; Sign from 0->0 should NOT set OF +mov r15, 0xFFFFFFFFFFFFFFFE +mov r14, 0xFFFFFFFFFFFF0000 +mov r13, 0xFFFFFFFF00000000 +mov r12, 0x0000000000000000 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shrd r14w, r15w, cl +cmovo rsp, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shrd r13d, r15d, cl +cmovo r8, r11 + +; Let's clear OF really quick +mov rax, 0 +ror rax, 1 + +shrd r12, r15, cl +cmovo r9, r11 + +hlt diff --git a/unittests/ASM/TwoByte/0F_AD_5.asm b/unittests/ASM/TwoByte/0F_AD_5.asm new file mode 100644 index 000000000..75eb73af6 --- /dev/null +++ b/unittests/ASM/TwoByte/0F_AD_5.asm @@ -0,0 +1,29 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "1", + "RBX": "1", + "RDX": "1" + } +} +%endif + +mov cl, 2 +mov r15, -1 +mov r14, 0x4141414141410002 +mov r13, 0xFFFFFFFF00000002 +mov r12, 0x0000000000000002 + +mov rax, 0 +mov rbx, 0 +mov rdx, 0 +mov rsi, 1 + +shrd r14w, r15w, cl +cmovc rax, rsi +shrd r13d, r15d, cl +cmovc rbx, rsi +shrd r12, r15, cl +cmovc rdx, rsi + +hlt diff --git a/unittests/ASM/TwoByte/0F_AD_6.asm b/unittests/ASM/TwoByte/0F_AD_6.asm new file mode 100644 index 000000000..84699c14e --- /dev/null +++ b/unittests/ASM/TwoByte/0F_AD_6.asm @@ -0,0 +1,29 @@ +%ifdef CONFIG +{ + "RegData": { + "RAX": "1", + "RBX": "1", + "RDX": "1" + } +} +%endif + +mov cl, 2 +mov r15, 0 +mov r14, 0x4141414141410002 +mov r13, 0xFFFFFFFF00000002 +mov r12, 0x0000000000000002 + +mov rax, 0 +mov rbx, 0 +mov rdx, 0 +mov rsi, 1 + +shrd r14w, r15w, cl +cmovz rax, rsi +shrd r13d, r15d, cl +cmovz rbx, rsi +shrd r12, r15, cl +cmovz rdx, rsi + +hlt diff --git a/unittests/ASM/TwoByte/0F_AD_7.asm b/unittests/ASM/TwoByte/0F_AD_7.asm new file mode 100644 index 000000000..1d0a7db60 --- /dev/null +++ b/unittests/ASM/TwoByte/0F_AD_7.asm @@ -0,0 +1,30 @@ + +%ifdef CONFIG +{ + "RegData": { + "RAX": "1", + "RBX": "1", + "RDX": "1" + } +} +%endif + +mov cl, 2 +mov r15, 0xFFFFFFFFFFFFFFF2 +mov r14, 0x4141414141410000 +mov r13, 0xFFFFFFFF00000000 +mov r12, 0x0000000000000000 + +mov rax, 0 +mov rbx, 0 +mov rdx, 0 +mov rsi, 1 + +shrd r14w, r15w, cl +cmovs rax, rsi +shrd r13d, r15d, cl +cmovs rbx, rsi +shrd r12, r15, cl +cmovs rdx, rsi + +hlt