From f89ef01578d6abb7354401f7fc411089c5741814 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 9 Mar 2020 17:59:29 -0700 Subject: [PATCH] Implements the COMISS instructions These are fairly popular so not having them around is causing headaches. We will really want these in the future to use the host register, btu that is more time consuming than I want to dedicate to at the moment. So the FCMP instruction must be told which flags to generate up front, then it generates the flags in to a GPR which are then tracked and pulled out with a specific IR op. This can be augmented in the future to more easily RA the host flag register --- .../Core/Interpreter/InterpreterCore.cpp | 50 +++++++++++++++++++ .../Source/Interface/Core/JIT/Arm64/JIT.cpp | 36 +++++++++++++ .../Source/Interface/Core/JIT/x86_64/JIT.cpp | 40 +++++++++++++++ .../Interface/Core/LLVMJIT/LLVMCore.cpp | 41 +++++++++++++++ .../Interface/Core/OpcodeDispatcher.cpp | 27 ++++++++++ .../Source/Interface/Core/OpcodeDispatcher.h | 2 + .../Core/X86Tables/SecondaryTables.cpp | 8 +-- External/FEXCore/Source/Interface/IR/IR.json | 40 ++++++++++++++- .../IR/Passes/RegisterAllocationPass.cpp | 4 +- .../IR/Passes/RegisterAllocationPass.h | 1 + 10 files changed, 243 insertions(+), 6 deletions(-) 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; }