diff --git a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp index e5bf19018..44c2b4c2e 100644 --- a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp +++ b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp @@ -3206,6 +3206,56 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { memcpy(GDP, Tmp, Op->RegisterSize); break; } + case IR::OP_FCMP: { + auto Op = IROp->C(); + uint32_t ResultFlags{}; + if (Op->ElementSize == 4) { + float Src1 = *GetSrc(Op->Header.Args[0]); + float Src2 = *GetSrc(Op->Header.Args[1]); + if (Op->Flags & (1 << FCMP_FLAG_LT)) { + if (Src1 < Src2) { + ResultFlags |= (1 << FCMP_FLAG_LT); + } + } + if (Op->Flags & (1 << FCMP_FLAG_UNORDERED)) { + if (std::isnan(Src1) || std::isnan(Src2)) { + ResultFlags |= (1 << FCMP_FLAG_UNORDERED); + } + } + if (Op->Flags & (1 << FCMP_FLAG_EQ)) { + if (Src1 == Src2) { + ResultFlags |= (1 << FCMP_FLAG_EQ); + } + } + } + else { + double Src1 = *GetSrc(Op->Header.Args[0]); + double Src2 = *GetSrc(Op->Header.Args[1]); + if (Op->Flags & (1 << FCMP_FLAG_LT)) { + if (Src1 < Src2) { + ResultFlags |= (1 << FCMP_FLAG_LT); + } + } + if (Op->Flags & (1 << FCMP_FLAG_UNORDERED)) { + if (std::isnan(Src1) || std::isnan(Src2)) { + ResultFlags |= (1 << FCMP_FLAG_UNORDERED); + } + } + if (Op->Flags & (1 << FCMP_FLAG_EQ)) { + if (Src1 == Src2) { + ResultFlags |= (1 << FCMP_FLAG_EQ); + } + } + } + + GD = ResultFlags; + break; + } + case IR::OP_GETHOSTFLAG: { + auto Op = IROp->C(); + GD = (*GetSrc(Op->Header.Args[0]) >> Op->Flag) & 1; + break; + } #define DO_SCALAR_COMPARE_OP(size, type, type2, func) \ case size: { \ auto *Dst_d = reinterpret_cast(Tmp); \ diff --git a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp index 75a16bbfe..43cd265fe 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp @@ -2929,6 +2929,42 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const } break; } + case IR::OP_FCMP: { + auto Op = IROp->C(); + + if (Op->ElementSize == 4) { + fcmp(GetSrc(Op->Header.Args[0].ID()).S(), GetSrc(Op->Header.Args[1].ID()).S()); + } + else { + fcmp(GetSrc(Op->Header.Args[0].ID()).D(), GetSrc(Op->Header.Args[1].ID()).D()); + } + auto Dst = GetSrc(Node); + eor(Dst, Dst, Dst); + + if (Op->Flags & (1 << FCMP_FLAG_LT)) { + cset(TMP2, Condition::mi); + lsl(TMP2, TMP2, FCMP_FLAG_LT); + orr(Dst, Dst, TMP2); + } + if (Op->Flags & (1 << FCMP_FLAG_EQ)) { + cset(TMP2, Condition::eq); + lsl(TMP2, TMP2, FCMP_FLAG_EQ); + orr(Dst, Dst, TMP2); + } + if (Op->Flags & (1 << FCMP_FLAG_EQ)) { + cset(TMP2, Condition::vs); + lsl(TMP2, TMP2, FCMP_FLAG_EQ); + orr(Dst, Dst, TMP2); + } + + break; + } + case IR::OP_GETHOSTFLAG: { + auto Op = IROp->C(); + + ubfx(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), Op->Flag, 1); + break; + } case IR::OP_VZIP: { auto Op = IROp->C(); LogMan::Throw::A(Op->RegisterSize == 16, "Can't handle register size of: %d", Op->RegisterSize); diff --git a/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp b/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp index 140776674..8809ed01e 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp @@ -2796,7 +2796,47 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const } break; } + case IR::OP_FCMP: { + auto Op = IROp->C(); + if (Op->ElementSize == 4) { + ucomiss(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID())); + } + else { + ucomisd(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID())); + } + mov (rax, 0); + + if (Op->Flags & (1 << FCMP_FLAG_LT)) { + mov(rcx, 0); + setb(cl); + shl(rcx, FCMP_FLAG_LT); + or(rax, rcx); + } + if (Op->Flags & (1 << FCMP_FLAG_UNORDERED)) { + mov(rcx, 0); + setp(cl); + shl(rcx, FCMP_FLAG_UNORDERED); + or(rax, rcx); + } + if (Op->Flags & (1 << FCMP_FLAG_EQ)) { + mov(rcx, 0); + setz(cl); + shl(rcx, FCMP_FLAG_EQ); + or(rax, rcx); + } + mov (GetDst(Node), rax); + break; + } + case IR::OP_GETHOSTFLAG: { + auto Op = IROp->C(); + + mov(rax, GetSrc(Op->Header.Args[0].ID())); + shr(rax, Op->Flag); + and(rax, 1); + mov(GetDst(Node), rax); + break; + } case IR::OP_VZIP: { auto Op = IROp->C(); movapd(xmm15, GetSrc(Op->Header.Args[0].ID())); diff --git a/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp b/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp index 8de8b8c7d..9bddb6c3f 100644 --- a/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp +++ b/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp @@ -1806,6 +1806,47 @@ void LLVMJITCore::HandleIR(FEXCore::IR::IRListView const *IR, IR::NodeWrap SetDest(*WrapperOp, Result); break; } + case IR::OP_FCMP: { + auto Op = IROp->C(); + auto Src1 = GetSrc(Op->Header.Args[0]); + auto Src2 = GetSrc(Op->Header.Args[1]); + + // Cast to the type we want + Src1 = CastScalarToType(Src1, false, OpSize * 8, Op->ElementSize); + Src2 = CastScalarToType(Src2, false, OpSize * 8, Op->ElementSize); + + llvm::Value *Result = JITState.IRBuilder->getInt64(0); + if (Op->Flags & (1 << IR::FCMP_FLAG_LT)) { + auto FCmp = JITState.IRBuilder->CreateFCmpOLT(Src1, Src2); + FCmp = JITState.IRBuilder->CreateZExt(FCmp, Result->getType()); + FCmp = JITState.IRBuilder->CreateShl(FCmp, JITState.IRBuilder->getInt64(IR::FCMP_FLAG_LT)); + Result = JITState.IRBuilder->CreateOr(Result, FCmp); + } + if (Op->Flags & (1 << IR::FCMP_FLAG_UNORDERED)) { + auto FCmp = JITState.IRBuilder->CreateFCmpUNO(Src1, Src2); + FCmp = JITState.IRBuilder->CreateZExt(FCmp, Result->getType()); + FCmp = JITState.IRBuilder->CreateShl(FCmp, JITState.IRBuilder->getInt64(IR::FCMP_FLAG_UNORDERED)); + Result = JITState.IRBuilder->CreateOr(Result, FCmp); + } + if (Op->Flags & (1 << IR::FCMP_FLAG_EQ)) { + auto FCmp = JITState.IRBuilder->CreateFCmpUEQ(Src1, Src2); + FCmp = JITState.IRBuilder->CreateZExt(FCmp, Result->getType()); + FCmp = JITState.IRBuilder->CreateShl(FCmp, JITState.IRBuilder->getInt64(IR::FCMP_FLAG_EQ)); + Result = JITState.IRBuilder->CreateOr(Result, FCmp); + } + + SetDest(*WrapperOp, Result); + break; + } + case IR::OP_GETHOSTFLAG: { + auto Op = IROp->C(); + auto Src = GetSrc(Op->Header.Args[0]); + auto Result = JITState.IRBuilder->CreateLShr(Src, JITState.IRBuilder->getInt64(Op->Flag)); + Result = JITState.IRBuilder->CreateAnd(Result, JITState.IRBuilder->getInt64(1)); + + SetDest(*WrapperOp, Result); + break; + } case IR::OP_CREATEVECTOR2: { auto Op = IROp->C(); LogMan::Throw::A(OpSize <= 16, "Can't handle a vector of size: %d", OpSize); diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 13ef7553e..9deb1ad82 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -4409,6 +4409,31 @@ void OpDispatchBuilder::PAlignrOp(OpcodeArgs) { StoreResult(FPRClass, Op, Res, -1); } +template +void OpDispatchBuilder::UCOMISxOp(OpcodeArgs) { + OrderedNode *Src1 = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1); + OrderedNode *Src2 = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1); + OrderedNode *Res = _FCmp(Src1, Src2, ElementSize, + (1 << FCMP_FLAG_EQ) | + (1 << FCMP_FLAG_LT) | + (1 << FCMP_FLAG_UNORDERED)); + + OrderedNode *HostFlag_CF = _GetHostFlag(Res, FCMP_FLAG_LT); + OrderedNode *HostFlag_ZF = _GetHostFlag(Res, FCMP_FLAG_EQ); + OrderedNode *HostFlag_Unordered = _GetHostFlag(Res, FCMP_FLAG_UNORDERED); + HostFlag_CF = _Or(HostFlag_CF, HostFlag_Unordered); + HostFlag_ZF = _Or(HostFlag_ZF, HostFlag_Unordered); + + SetRFLAG(HostFlag_CF); + SetRFLAG(HostFlag_ZF); + SetRFLAG(HostFlag_Unordered); + + auto ZeroConst = _Constant(0); + SetRFLAG(ZeroConst); + SetRFLAG(ZeroConst); + SetRFLAG(ZeroConst); +} + void OpDispatchBuilder::LDMXCSR(OpcodeArgs) { } @@ -4647,6 +4672,7 @@ void InstallOpcodeHandlers() { {0x16, 1, &OpDispatchBuilder::MOVLHPSOp}, {0x17, 1, &OpDispatchBuilder::MOVUPSOp}, {0x28, 2, &OpDispatchBuilder::MOVUPSOp}, + {0x2E, 2, &OpDispatchBuilder::UCOMISxOp<4>}, {0x50, 1, &OpDispatchBuilder::MOVMSKOp<4>}, {0x51, 1, &OpDispatchBuilder::VectorUnaryOp}, {0x52, 1, &OpDispatchBuilder::VectorUnaryOp}, @@ -4831,6 +4857,7 @@ void InstallOpcodeHandlers() { {0x16, 2, &OpDispatchBuilder::MOVHPDOp}, {0x19, 7, &OpDispatchBuilder::NOPOp}, {0x28, 2, &OpDispatchBuilder::MOVAPSOp}, + {0x2E, 2, &OpDispatchBuilder::UCOMISxOp<8>}, {0x40, 16, &OpDispatchBuilder::CMOVOp}, {0x50, 1, &OpDispatchBuilder::MOVMSKOp<8>}, diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index 4fd880248..1cd432a60 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -269,6 +269,8 @@ public: void FXRStoreOp(OpcodeArgs); void PAlignrOp(OpcodeArgs); + template + void UCOMISxOp(OpcodeArgs); void LDMXCSR(OpcodeArgs); void STMXCSR(OpcodeArgs); diff --git a/External/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp b/External/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp index 4c91236c5..948f375b2 100644 --- a/External/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp +++ b/External/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp @@ -44,8 +44,8 @@ void InitializeSecondaryTables() { {0x2B, 1, X86InstInfo{"MOVNTPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}}, {0x2C, 1, X86InstInfo{"CVTTPS2PI", TYPE_MMX, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, {0x2D, 1, X86InstInfo{"CVTPS2PI", TYPE_MMX, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, - {0x2E, 1, X86InstInfo{"UCOMISS", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, - {0x2F, 1, X86InstInfo{"COMISS", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, + {0x2E, 1, X86InstInfo{"UCOMISS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, + {0x2F, 1, X86InstInfo{"COMISS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, {0x30, 1, X86InstInfo{"WRMSR", TYPE_PRIV, FLAGS_NO_OVERLAY, 0, nullptr}}, {0x31, 1, X86InstInfo{"RDTSC", TYPE_INST, FLAGS_DEBUG | FLAGS_NO_OVERLAY, 0, nullptr}}, @@ -426,8 +426,8 @@ void InitializeSecondaryTables() { {0x2B, 1, X86InstInfo{"MOVNTPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}}, {0x2C, 1, X86InstInfo{"CVTTPD2PI", TYPE_MMX, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, {0x2D, 1, X86InstInfo{"CVTPD2PI", TYPE_MMX, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, - {0x2E, 1, X86InstInfo{"UCOMISD", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, - {0x2F, 1, X86InstInfo{"COMISD", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, + {0x2E, 1, X86InstInfo{"UCOMISD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, + {0x2F, 1, X86InstInfo{"COMISD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}}, {0x30, 16, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0, nullptr}}, {0x40, 16, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0, nullptr}}, diff --git a/External/FEXCore/Source/Interface/IR/IR.json b/External/FEXCore/Source/Interface/IR/IR.json index 7079d9b26..951092d24 100644 --- a/External/FEXCore/Source/Interface/IR/IR.json +++ b/External/FEXCore/Source/Interface/IR/IR.json @@ -15,7 +15,11 @@ "constexpr static uint8_t COND_SGT = 12", "constexpr static uint8_t COND_SLE = 13", "static constexpr FEXCore::IR::RegisterClassType GPRClass {0}", - "static constexpr FEXCore::IR::RegisterClassType FPRClass {1}" + "static constexpr FEXCore::IR::RegisterClassType FPRClass {1}", + + "constexpr static uint8_t FCMP_FLAG_EQ = 0", + "constexpr static uint8_t FCMP_FLAG_LT = 1", + "constexpr static uint8_t FCMP_FLAG_UNORDERED = 2" ], "Ops": { @@ -562,6 +566,24 @@ ] }, + "ExtractElement": { + "HasDest": true, + "DestSize": "GetOpSize(ssa0)", + "SSAArgs": "1", + "Args": [ + "uint8_t", "Idx" + ] + }, + + "FCmp": { + "HasDest": true, + "SSAArgs": "2", + "Args": [ + "uint8_t", "ElementSize", + "uint32_t", "Flags" + ] + }, + "Print": { "SSAArgs": "1" }, @@ -1241,6 +1263,22 @@ ] }, + "GetHostFlag": { + "HasDest": true, + "SSAArgs": "1", + "Args": [ + "uint8_t", "Flag" + ] + }, + + "CreatesFlags": { + "HasDest": true, + "SSAArgs": "1", + "Args": [ + "uint32_t", "Flags" + ] + }, + "Last": { "Last": true, "Args": [] diff --git a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp index b4cd88f31..da620e8c6 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp +++ b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp @@ -238,7 +238,9 @@ namespace { return GetRegClassFromNode(ListBegin, DataBegin, Op->PhiBegin); } default: - if (IROp->Op > IR::OP_PRINT) + if (IROp->Op >= IR::OP_GETHOSTFLAG) + return IR::RegisterAllocationPass::FLAGSClass; + else if (IROp->Op > IR::OP_PRINT) return IR::RegisterAllocationPass::FPRClass; else return IR::RegisterAllocationPass::GPRClass; diff --git a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.h b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.h index 40e95070d..dad76f37f 100644 --- a/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.h +++ b/External/FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.h @@ -9,6 +9,7 @@ class RegisterAllocationPass : public FEXCore::IR::Pass { public: static constexpr uint32_t GPRClass = 0; static constexpr uint32_t FPRClass = 1; + static constexpr uint32_t FLAGSClass = 2; bool HasFullRA() const { return HadFullRA; } uint32_t SpillSlots() const { return SpillSlotCount; }