From 1eb470083c71fb9de48d05880e7e839defdb689a Mon Sep 17 00:00:00 2001 From: Alyssa Rosenzweig Date: Tue, 10 Jun 2025 16:39:48 -0400 Subject: [PATCH] IR: merge div/rem opcodes it's simpler & faster to calculate both together, matching the x86 semantic. Signed-off-by: Alyssa Rosenzweig --- FEXCore/Source/Interface/Core/JIT/ALUOps.cpp | 265 ++++-------------- .../Interface/Core/OpcodeDispatcher.cpp | 105 +++---- FEXCore/Source/Interface/IR/IR.json | 68 ++--- 3 files changed, 112 insertions(+), 326 deletions(-) diff --git a/FEXCore/Source/Interface/Core/JIT/ALUOps.cpp b/FEXCore/Source/Interface/Core/JIT/ALUOps.cpp index dbf9d8fbd..5a178d83c 100644 --- a/FEXCore/Source/Interface/Core/JIT/ALUOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/ALUOps.cpp @@ -409,7 +409,8 @@ DEF_OP(Div) { const auto OpSize = IROp->Size; const auto EmitSize = ConvertSize(IROp); - const auto Dst = GetReg(Node); + const auto Quotient = GetReg(Op->OutQuotient); + const auto Remainder = GetReg(Op->OutRemainder); auto Src1 = GetReg(Op->Src1); auto Src2 = GetReg(Op->Src2); @@ -427,7 +428,8 @@ DEF_OP(Div) { Src2 = TMP2; } - sdiv(EmitSize, Dst, Src1, Src2); + sdiv(EmitSize, Quotient, Src1, Src2); + msub(EmitSize, Remainder, Quotient, Src2, Src1); } DEF_OP(UDiv) { @@ -438,7 +440,8 @@ DEF_OP(UDiv) { const auto OpSize = IROp->Size; const auto EmitSize = ConvertSize(IROp); - const auto Dst = GetReg(Node); + const auto Quotient = GetReg(Op->OutQuotient); + const auto Remainder = GetReg(Op->OutRemainder); auto Src1 = GetReg(Op->Src1); auto Src2 = GetReg(Op->Src2); @@ -456,65 +459,8 @@ DEF_OP(UDiv) { Src2 = TMP2; } - udiv(EmitSize, Dst, Src1, Src2); -} - -DEF_OP(Rem) { - auto Op = IROp->C(); - // Each source is OpSize in size - // So you can have up to a 128bit divide from x86-64 - const auto OpSize = IROp->Size; - const auto EmitSize = ConvertSize(IROp); - - const auto Dst = GetReg(Node); - auto Src1 = GetReg(Op->Src1); - auto Src2 = GetReg(Op->Src2); - - if (OpSize == IR::OpSize::i8Bit) { - sxtb(EmitSize, TMP1, Src1); - sxtb(EmitSize, TMP2, Src2); - - Src1 = TMP1; - Src2 = TMP2; - } else if (OpSize == IR::OpSize::i16Bit) { - sxth(EmitSize, TMP1, Src1); - sxth(EmitSize, TMP2, Src2); - - Src1 = TMP1; - Src2 = TMP2; - } - - sdiv(EmitSize, TMP1, Src1, Src2); - msub(EmitSize, Dst, TMP1, Src2, Src1); -} - -DEF_OP(URem) { - auto Op = IROp->C(); - // Each source is OpSize in size - // So you can have up to a 128bit divide from x86-64 - const auto OpSize = IROp->Size; - const auto EmitSize = ConvertSize(IROp); - - const auto Dst = GetReg(Node); - auto Src1 = GetReg(Op->Src1); - auto Src2 = GetReg(Op->Src2); - - if (OpSize == IR::OpSize::i8Bit) { - uxtb(EmitSize, TMP1, Src1); - uxtb(EmitSize, TMP2, Src2); - - Src1 = TMP1; - Src2 = TMP2; - } else if (OpSize == IR::OpSize::i16Bit) { - uxth(EmitSize, TMP1, Src1); - uxth(EmitSize, TMP2, Src2); - - Src1 = TMP1; - Src2 = TMP2; - } - - udiv(EmitSize, TMP3, Src1, Src2); - msub(EmitSize, Dst, TMP3, Src2, Src1); + udiv(EmitSize, Quotient, Src1, Src2); + msub(EmitSize, Remainder, Quotient, Src2, Src1); } DEF_OP(MulH) { @@ -960,7 +906,8 @@ DEF_OP(LDiv) { const auto OpSize = IROp->Size; const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit; - const auto Dst = GetReg(Node); + const auto Quotient = GetReg(Op->OutQuotient); + const auto Remainder = GetReg(Op->OutRemainder); const auto Upper = GetReg(Op->Upper); const auto Lower = GetReg(Op->Lower); const auto Divisor = GetReg(Op->Divisor); @@ -972,7 +919,8 @@ DEF_OP(LDiv) { uxth(EmitSize, TMP1, Lower); bfi(EmitSize, TMP1, Upper, 16, 16); sxth(EmitSize, TMP2, Divisor); - sdiv(EmitSize, Dst, TMP1, TMP2); + sdiv(EmitSize, Quotient, TMP1, TMP2); + msub(EmitSize, Remainder, Quotient, TMP2, TMP1); break; } case IR::OpSize::i32Bit: { @@ -980,7 +928,8 @@ DEF_OP(LDiv) { mov(EmitSize, TMP1, Lower); bfi(EmitSize, TMP1, Upper, 32, 32); sxtw(TMP2, Divisor.W()); - sdiv(EmitSize, Dst, TMP1, TMP2); + sdiv(EmitSize, Quotient, TMP1, TMP2); + msub(EmitSize, Remainder, Quotient, TMP2, TMP1); break; } case IR::OpSize::i64Bit: { @@ -1008,7 +957,17 @@ DEF_OP(LDiv) { ldr(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16); // Move result to its destination register - mov(EmitSize, Dst, TMP1); + mov(EmitSize, Quotient, TMP1); + + // TODO: Merge division/remainder + mov(EmitSize, TMP1, Upper); + mov(EmitSize, TMP2, Lower); + mov(EmitSize, TMP3, Divisor); + ldr(TMP4, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.AArch64.LREMHandler)); + str(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16); + blr(TMP4); + ldr(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16); + mov(EmitSize, Remainder, TMP1); // Skip 64-bit path b(&LongDIVRet); @@ -1016,7 +975,10 @@ DEF_OP(LDiv) { Bind(&Only64Bit); // 64-Bit only - { sdiv(EmitSize, Dst, Lower, Divisor); } + { + sdiv(EmitSize, Quotient, Lower, Divisor); + msub(EmitSize, Remainder, Quotient, Divisor, Lower); + } Bind(&LongDIVRet); break; @@ -1030,7 +992,8 @@ DEF_OP(LUDiv) { const auto OpSize = IROp->Size; const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit; - const auto Dst = GetReg(Node); + const auto Quotient = GetReg(Op->OutQuotient); + const auto Remainder = GetReg(Op->OutRemainder); const auto Upper = GetReg(Op->Upper); const auto Lower = GetReg(Op->Lower); const auto Divisor = GetReg(Op->Divisor); @@ -1041,14 +1004,16 @@ DEF_OP(LUDiv) { case IR::OpSize::i16Bit: { uxth(EmitSize, TMP1, Lower); bfi(EmitSize, TMP1, Upper, 16, 16); - udiv(EmitSize, Dst, TMP1, Divisor); + udiv(EmitSize, Quotient, TMP1, Divisor); + msub(EmitSize, Remainder, Quotient, Divisor, TMP1); break; } case IR::OpSize::i32Bit: { // TODO: 32-bit operation should be guaranteed not to leave garbage in the upper bits. mov(EmitSize, TMP1, Lower); bfi(EmitSize, TMP1, Upper, 32, 32); - udiv(EmitSize, Dst, TMP1, Divisor); + udiv(EmitSize, Quotient, TMP1, Divisor); + msub(EmitSize, Remainder, Quotient, Divisor, TMP1); break; } case IR::OpSize::i64Bit: { @@ -1072,7 +1037,17 @@ DEF_OP(LUDiv) { ldr(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16); // Move result to its destination register - mov(EmitSize, Dst, TMP1); + mov(EmitSize, Quotient, TMP1); + + // TODO: Merge remainder with the division + mov(EmitSize, TMP1, Upper); + mov(EmitSize, TMP2, Lower); + mov(EmitSize, TMP3, Divisor); + ldr(TMP4, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.AArch64.LUREMHandler)); + str(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16); + blr(TMP4); + ldr(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16); + mov(EmitSize, Remainder, TMP1); // Skip 64-bit path b(&LongDIVRet); @@ -1080,7 +1055,10 @@ DEF_OP(LUDiv) { Bind(&Only64Bit); // 64-Bit only - { udiv(EmitSize, Dst, Lower, Divisor); } + { + udiv(EmitSize, Quotient, Lower, Divisor); + msub(EmitSize, Remainder, Quotient, Divisor, Lower); + } Bind(&LongDIVRet); break; @@ -1089,149 +1067,6 @@ DEF_OP(LUDiv) { } } -DEF_OP(LRem) { - auto Op = IROp->C(); - const auto OpSize = IROp->Size; - const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit; - - const auto Dst = GetReg(Node); - const auto Upper = GetReg(Op->Upper); - const auto Lower = GetReg(Op->Lower); - const auto Divisor = GetReg(Op->Divisor); - - // Each source is OpSize in size - // So you can have up to a 128bit divide from x86-64 - switch (OpSize) { - case IR::OpSize::i16Bit: { - uxth(EmitSize, TMP1, Lower); - bfi(EmitSize, TMP1, Upper, 16, 16); - sxth(EmitSize, TMP2, Divisor); - sdiv(EmitSize, TMP3, TMP1, TMP2); - msub(EmitSize, Dst, TMP3, TMP2, TMP1); - break; - } - case IR::OpSize::i32Bit: { - // TODO: 32-bit operation should be guaranteed not to leave garbage in the upper bits. - mov(EmitSize, TMP1, Lower); - bfi(EmitSize, TMP1, Upper, 32, 32); - sxtw(TMP3, Divisor.W()); - sdiv(EmitSize, TMP2, TMP1, TMP3); - msub(EmitSize, Dst, TMP2, TMP3, TMP1); - break; - } - case IR::OpSize::i64Bit: { - ARMEmitter::ForwardLabel Only64Bit {}; - ARMEmitter::ForwardLabel LongDIVRet {}; - - // Check if the upper bits match the top bit of the lower 64-bits - // Sign extend the top bit of lower bits - sbfx(EmitSize, TMP1, Lower, 63, 1); - eor(EmitSize, TMP1, TMP1, Upper); - - // If the sign bit matches then the result is zero - cbz(EmitSize, TMP1, &Only64Bit); - - // Long divide - { - mov(EmitSize, TMP1, Upper); - mov(EmitSize, TMP2, Lower); - mov(EmitSize, TMP3, Divisor); - - ldr(TMP4, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.AArch64.LREMHandler)); - - str(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16); - blr(TMP4); - ldr(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16); - - // Move result to its destination register - mov(EmitSize, Dst, TMP1); - - // Skip 64-bit path - b(&LongDIVRet); - } - - Bind(&Only64Bit); - // 64-Bit only - { - sdiv(EmitSize, TMP1, Lower, Divisor); - msub(EmitSize, Dst, TMP1, Divisor, Lower); - } - Bind(&LongDIVRet); - break; - } - default: LOGMAN_MSG_A_FMT("Unknown LREM Size: {}", OpSize); break; - } -} - -DEF_OP(LURem) { - auto Op = IROp->C(); - const auto OpSize = IROp->Size; - const auto EmitSize = OpSize >= IR::OpSize::i32Bit ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit; - - const auto Dst = GetReg(Node); - const auto Upper = GetReg(Op->Upper); - const auto Lower = GetReg(Op->Lower); - const auto Divisor = GetReg(Op->Divisor); - - // Each source is OpSize in size - // So you can have up to a 128bit divide from x86-64 - switch (OpSize) { - case IR::OpSize::i16Bit: { - uxth(EmitSize, TMP1, Lower); - bfi(EmitSize, TMP1, Upper, 16, 16); - udiv(EmitSize, TMP2, TMP1, Divisor); - msub(EmitSize, Dst, TMP2, Divisor, TMP1); - break; - } - case IR::OpSize::i32Bit: { - // TODO: 32-bit operation should be guaranteed not to leave garbage in the upper bits. - mov(EmitSize, TMP1, Lower); - bfi(EmitSize, TMP1, Upper, 32, 32); - udiv(EmitSize, TMP2, TMP1, Divisor); - msub(EmitSize, Dst, TMP2, Divisor, TMP1); - break; - } - case IR::OpSize::i64Bit: { - ARMEmitter::ForwardLabel Only64Bit {}; - ARMEmitter::ForwardLabel LongDIVRet {}; - - // Check the upper bits for zero - // If the upper bits are zero then we can do a 64-bit divide - cbz(EmitSize, Upper, &Only64Bit); - - // Long divide - { - mov(EmitSize, TMP1, Upper); - mov(EmitSize, TMP2, Lower); - mov(EmitSize, TMP3, Divisor); - - ldr(TMP4, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.AArch64.LUREMHandler)); - - str(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16); - blr(TMP4); - ldr(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16); - - // Move result to its destination register - mov(EmitSize, Dst, TMP1); - - // Skip 64-bit path - b(&LongDIVRet); - } - - Bind(&Only64Bit); - // 64-Bit only - { - udiv(EmitSize, TMP1, Lower, Divisor); - msub(EmitSize, Dst, TMP1, Divisor, Lower); - } - - Bind(&LongDIVRet); - break; - } - default: LOGMAN_MSG_A_FMT("Unknown LUREM Size: {}", OpSize); break; - } -} - DEF_OP(Not) { auto Op = IROp->C(); diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 30a8ff648..c81c9144b 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -2852,9 +2852,10 @@ void OpDispatchBuilder::AASOp(OpcodeArgs) { void OpDispatchBuilder::AAMOp(OpcodeArgs) { auto AL = LoadGPRRegister(X86State::REG_RAX, OpSize::i8Bit); auto Imm8 = _Constant(Op->Src[0].Data.Literal.Value & 0xFF); - auto UDivOp = _UDiv(OpSize::i64Bit, AL, Imm8); - auto URemOp = _URem(OpSize::i64Bit, AL, Imm8); - auto Res = _AddShift(OpSize::i64Bit, URemOp, UDivOp, ShiftType::LSL, 8); + Ref Quotient = _AllocateGPR(true); + Ref Remainder = _AllocateGPR(true); + _UDiv(OpSize::i64Bit, AL, Imm8, Quotient, Remainder); + auto Res = _AddShift(OpSize::i64Bit, Remainder, Quotient, ShiftType::LSL, 8); StoreGPRRegister(X86State::REG_RAX, Res, OpSize::i16Bit); SetNZ_ZeroCV(OpSize::i8Bit, Res); @@ -3585,48 +3586,39 @@ void OpDispatchBuilder::DIVOp(OpcodeArgs) { Ref Divisor = LoadSource(GPRClass, Op, Op->Dest, Op->Flags); const auto GPRSize = CTX->GetGPROpSize(); - const auto Size = OpSizeFromSrc(Op); + auto Size = OpSizeFromSrc(Op); + + if (Size == OpSize::i64Bit && !CTX->Config.Is64BitMode) { + LogMan::Msg::EFmt("Doesn't exist in 32bit mode"); + DecodeFailure = true; + return; + } + + Ref Quotient = _AllocateGPR(true); + Ref Remainder = _AllocateGPR(true); if (Size == OpSize::i8Bit) { Ref Src1 = LoadGPRRegister(X86State::REG_RAX, OpSize::i16Bit); - auto UDivOp = _UDiv(OpSize::i16Bit, Src1, Divisor); - auto URemOp = _URem(OpSize::i16Bit, Src1, Divisor); + _UDiv(OpSize::i16Bit, Src1, Divisor, Quotient, Remainder); // AX[15:0] = concat - auto ResultAX = _Bfi(GPRSize, 8, 8, UDivOp, URemOp); + auto ResultAX = _Bfi(GPRSize, 8, 8, Quotient, Remainder); StoreGPRRegister(X86State::REG_RAX, ResultAX, OpSize::i16Bit); - } else if (Size == OpSize::i16Bit) { - Ref Src1 = LoadGPRRegister(X86State::REG_RAX); - Ref Src2 = LoadGPRRegister(X86State::REG_RDX); - auto UDivOp = _LUDiv(OpSize::i16Bit, Src1, Src2, Divisor); - auto URemOp = _LURem(OpSize::i16Bit, Src1, Src2, Divisor); - - StoreGPRRegister(X86State::REG_RAX, UDivOp, Size); - StoreGPRRegister(X86State::REG_RDX, URemOp, Size); - } else if (Size == OpSize::i32Bit) { + } else { Ref Src1 = LoadGPRRegister(X86State::REG_RAX); Ref Src2 = LoadGPRRegister(X86State::REG_RDX); - Ref UDivOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LUDiv(OpSize::i32Bit, Src1, Src2, Divisor)); - Ref URemOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LURem(OpSize::i32Bit, Src1, Src2, Divisor)); + _LUDiv(Size, Src1, Src2, Divisor, Quotient, Remainder); - StoreGPRRegister(X86State::REG_RAX, UDivOp); - StoreGPRRegister(X86State::REG_RDX, URemOp); - } else if (Size == OpSize::i64Bit) { - if (!CTX->Config.Is64BitMode) { - LogMan::Msg::EFmt("Doesn't exist in 32bit mode"); - DecodeFailure = true; - return; + if (Size == OpSize::i32Bit) { + Quotient = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Quotient); + Remainder = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Remainder); + Size = OpSize::iInvalid; } - Ref Src1 = LoadGPRRegister(X86State::REG_RAX); - Ref Src2 = LoadGPRRegister(X86State::REG_RDX); - auto UDivOp = _LUDiv(OpSize::i64Bit, Src1, Src2, Divisor); - auto URemOp = _LURem(OpSize::i64Bit, Src1, Src2, Divisor); - - StoreGPRRegister(X86State::REG_RAX, UDivOp); - StoreGPRRegister(X86State::REG_RDX, URemOp); + StoreGPRRegister(X86State::REG_RAX, Quotient, Size); + StoreGPRRegister(X86State::REG_RDX, Remainder, Size); } } @@ -3635,50 +3627,41 @@ void OpDispatchBuilder::IDIVOp(OpcodeArgs) { Ref Divisor = LoadSource(GPRClass, Op, Op->Dest, Op->Flags); const auto GPRSize = CTX->GetGPROpSize(); - const auto Size = OpSizeFromSrc(Op); + auto Size = OpSizeFromSrc(Op); + + if (Size == OpSize::i64Bit && !CTX->Config.Is64BitMode) { + LogMan::Msg::EFmt("Doesn't exist in 32bit mode"); + DecodeFailure = true; + return; + } + + Ref Quotient = _AllocateGPR(true); + Ref Remainder = _AllocateGPR(true); if (Size == OpSize::i8Bit) { Ref Src1 = LoadGPRRegister(X86State::REG_RAX); Src1 = _Sbfe(OpSize::i64Bit, 16, 0, Src1); Divisor = _Sbfe(OpSize::i64Bit, 8, 0, Divisor); - auto UDivOp = _Div(OpSize::i64Bit, Src1, Divisor); - auto URemOp = _Rem(OpSize::i64Bit, Src1, Divisor); + _Div(OpSize::i64Bit, Src1, Divisor, Quotient, Remainder); // AX[15:0] = concat - auto ResultAX = _Bfi(GPRSize, 8, 8, UDivOp, URemOp); + auto ResultAX = _Bfi(GPRSize, 8, 8, Quotient, Remainder); StoreGPRRegister(X86State::REG_RAX, ResultAX, OpSize::i16Bit); - } else if (Size == OpSize::i16Bit) { - Ref Src1 = LoadGPRRegister(X86State::REG_RAX); - Ref Src2 = LoadGPRRegister(X86State::REG_RDX); - auto UDivOp = _LDiv(OpSize::i16Bit, Src1, Src2, Divisor); - auto URemOp = _LRem(OpSize::i16Bit, Src1, Src2, Divisor); - - StoreGPRRegister(X86State::REG_RAX, UDivOp, Size); - StoreGPRRegister(X86State::REG_RDX, URemOp, Size); - } else if (Size == OpSize::i32Bit) { + } else { Ref Src1 = LoadGPRRegister(X86State::REG_RAX); Ref Src2 = LoadGPRRegister(X86State::REG_RDX); - Ref UDivOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LDiv(OpSize::i32Bit, Src1, Src2, Divisor)); - Ref URemOp = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, _LRem(OpSize::i32Bit, Src1, Src2, Divisor)); + _LDiv(Size, Src1, Src2, Divisor, Quotient, Remainder); - StoreGPRRegister(X86State::REG_RAX, UDivOp); - StoreGPRRegister(X86State::REG_RDX, URemOp); - } else if (Size == OpSize::i64Bit) { - if (!CTX->Config.Is64BitMode) { - LogMan::Msg::EFmt("Doesn't exist in 32bit mode"); - DecodeFailure = true; - return; + if (Size == OpSize::i32Bit) { + Quotient = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Quotient); + Remainder = _Bfe(OpSize::i32Bit, IR::OpSizeAsBits(Size), 0, Remainder); + Size = OpSize::iInvalid; } - Ref Src1 = LoadGPRRegister(X86State::REG_RAX); - Ref Src2 = LoadGPRRegister(X86State::REG_RDX); - auto UDivOp = _LDiv(OpSize::i64Bit, Src1, Src2, Divisor); - auto URemOp = _LRem(OpSize::i64Bit, Src1, Src2, Divisor); - - StoreGPRRegister(X86State::REG_RAX, UDivOp); - StoreGPRRegister(X86State::REG_RDX, URemOp); + StoreGPRRegister(X86State::REG_RAX, Quotient, Size); + StoreGPRRegister(X86State::REG_RDX, Remainder, Size); } } diff --git a/FEXCore/Source/Interface/IR/IR.json b/FEXCore/Source/Interface/IR/IR.json index 6aea440e9..2df5a30be 100644 --- a/FEXCore/Source/Interface/IR/IR.json +++ b/FEXCore/Source/Interface/IR/IR.json @@ -1459,34 +1459,20 @@ ], "DestSize": "FEXCore::IR::OpSize::i64Bit" }, - "GPR = Div OpSize:#Size, GPR:$Src1, GPR:$Src2": { - "Desc": ["Integer signed division" - ], - "DestSize": "Size", - "EmitValidation": [ - "Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" - ] - }, - "GPR = UDiv OpSize:#Size, GPR:$Src1, GPR:$Src2": { + "GPR:$Quotient, GPR:$Remainder = UDiv OpSize:#Size, GPR:$Src1, GPR:$Src2": { "Desc": ["Integer unsigned division" ], "DestSize": "Size", + "HasSideEffects": true, "EmitValidation": [ "Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" ] }, - "GPR = Rem OpSize:#Size, GPR:$Src1, GPR:$Src2": { - "Desc": ["Integer signed remainder" - ], - "DestSize": "Size", - "EmitValidation": [ - "Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" - ] - }, - "GPR = URem OpSize:#Size, GPR:$Src1, GPR:$Src2": { - "Desc": ["Integer unsigned remainder" + "GPR:$Quotient, GPR:$Remainder = Div OpSize:#Size, GPR:$Src1, GPR:$Src2": { + "Desc": ["Integer signed division" ], "DestSize": "Size", + "HasSideEffects": true, "EmitValidation": [ "Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" ] @@ -1635,42 +1621,24 @@ ] }, - "GPR = LDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": { + "GPR:$Quotient, GPR:$Remainder = LDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": { + "Desc": ["Integer long signed division returning lower bits", + "The Lower and Upper registers will be concated together to generate a dividend twice the size", + "Then the divisor divides the temporary dividend and returns the results in the original sized register" + ], + "DestSize": "Size", + "HasSideEffects": true, + "EmitValidation": [ + "Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" + ] + }, + "GPR:$Quotient, GPR:$Remainder = LUDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": { "Desc": ["Integer long unsigned division returning lower bits", "The Lower and Upper registers will be concated together to generate a dividend twice the size", "Then the divisor divides the temporary dividend and returns the results in the original sized register" ], "DestSize": "Size", - "EmitValidation": [ - "Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" - ] - }, - "GPR = LUDiv OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": { - "Desc": ["Integer long unsigned division returning lower bits", - "The Lower and Upper registers will be concated together to generate a dividend twice the size", - "Then the divisor divides the temporary dividend and returns the results in the original sized register" - ], - "DestSize": "Size", - "EmitValidation": [ - "Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" - ] - }, - "GPR = LRem OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": { - "Desc": ["Integer long signed remainder returning lower bits", - "The Lower and Upper registers will be concated together to generate a dividend twice the size", - "Then the divisor divides the temporary dividend and returns the remainder results in the original sized register" - ], - "DestSize": "Size", - "EmitValidation": [ - "Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" - ] - }, - "GPR = LURem OpSize:#Size, GPR:$Lower, GPR:$Upper, GPR:$Divisor": { - "Desc": ["Integer long unsigned remainder returning lower bits", - "The Lower and Upper registers will be concated together to generate a dividend twice the size", - "Then the divisor divides the temporary dividend and returns the remainder results in the original sized register" - ], - "DestSize": "Size", + "HasSideEffects": true, "EmitValidation": [ "Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" ]