From 22590dde7798de82c8517c6221d13f210e53e134 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 7 Oct 2023 23:13:47 -0700 Subject: [PATCH] FEXCore: Implements support for RPRES This allows us to use reciprocal instructions which matches precision of what x86 expects rather than converting everything to float divides. Currently no hardware supports this, and even the upcoming X4/A720/A520 won't support it, but it was trivial to implement so wire it up. --- .../Source/Interface/Config/Config.json.in | 7 ++- .../Source/Interface/Core/HostFeatures.cpp | 10 ++++ .../Source/Interface/Core/JIT/Arm64/JIT.cpp | 1 + .../Interface/Core/JIT/Arm64/JITClass.h | 1 + .../Interface/Core/JIT/Arm64/VectorOps.cpp | 46 +++++++++++++++++++ FEXCore/include/FEXCore/Core/HostFeatures.h | 1 + Scripts/InstructionCountParser.py | 3 ++ Source/Tools/CodeSizeValidation/Main.cpp | 7 +++ 8 files changed, 74 insertions(+), 2 deletions(-) diff --git a/FEXCore/Source/Interface/Config/Config.json.in b/FEXCore/Source/Interface/Config/Config.json.in index 558dacc6f..f112d99f3 100644 --- a/FEXCore/Source/Interface/Config/Config.json.in +++ b/FEXCore/Source/Interface/Config/Config.json.in @@ -84,7 +84,9 @@ "ENABLEFLAGM2": "enableflagm2", "DISABLEFLAGM2": "disableflagm2", "ENABLECRYPTO": "enablecrypto", - "DISABLECRYPTO": "disablecrypto" + "DISABLECRYPTO": "disablecrypto", + "ENABLERPRES": "enablerpres", + "DISABLERPRES": "disablerpres" }, "Desc": [ "Allows controlling of the CPU features in the JIT.", @@ -103,7 +105,8 @@ "\t{enable,disable}fcma: Will force enable or disable fcma even if the host doesn't support it", "\t{enable,disable}flagm: Will force enable or disable flagm even if the host doesn't support it", "\t{enable,disable}flagm2: Will force enable or disable flagm2 even if the host doesn't support it", - "\t{enable,disable}crypto: Will force enable or disable crypto extensions even if the host doesn't support it" + "\t{enable,disable}crypto: Will force enable or disable crypto extensions even if the host doesn't support it", + "\t{enable,disable}rpres: Will force enable or disable rpres even if the host doesn't support it" ] } }, diff --git a/FEXCore/Source/Interface/Core/HostFeatures.cpp b/FEXCore/Source/Interface/Core/HostFeatures.cpp index f6c762d78..7dae9455c 100644 --- a/FEXCore/Source/Interface/Core/HostFeatures.cpp +++ b/FEXCore/Source/Interface/Core/HostFeatures.cpp @@ -89,6 +89,7 @@ static void OverrideFeatures(HostFeatures *Features) { ENABLE_DISABLE_OPTION(FlagM, FLAGM); ENABLE_DISABLE_OPTION(FlagM2, FLAGM2); ENABLE_DISABLE_OPTION(Crypto, CRYPTO); + ENABLE_DISABLE_OPTION(RPRES, RPRES); #undef ENABLE_DISABLE_OPTION @@ -186,6 +187,12 @@ static void OverrideFeatures(HostFeatures *Features) { Features->SupportsCRC = false; Features->SupportsPMULL_128Bit = false; } + if (EnableRPRES) { + Features->SupportsRPRES = true; + } + else if (DisableRPRES) { + Features->SupportsRPRES = false; + } } HostFeatures::HostFeatures() { @@ -193,6 +200,8 @@ HostFeatures::HostFeatures() { auto Features = vixl::CPUFeatures::All(); // Vixl simulator doesn't support AFP. Features.Remove(vixl::CPUFeatures::Feature::kAFP); + // Vixl simulator doesn't support RPRES. + Features.Remove(vixl::CPUFeatures::Feature::kRPRES); #elif !defined(_WIN32) auto Features = vixl::CPUFeatures::InferFromOS(); #else @@ -214,6 +223,7 @@ HostFeatures::HostFeatures() { SupportsFCMA = Features.Has(vixl::CPUFeatures::Feature::kFcma); SupportsFlagM = Features.Has(vixl::CPUFeatures::Feature::kFlagM); SupportsFlagM2 = Features.Has(vixl::CPUFeatures::Feature::kAXFlag); + SupportsRPRES = Features.Has(vixl::CPUFeatures::Feature::kRPRES); Supports3DNow = true; SupportsSSE4A = true; diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp index 27b49c9b6..3355e5a73 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp @@ -533,6 +533,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::In , Arm64Emitter(ctx, 0) , HostSupportsSVE128{ctx->HostFeatures.SupportsSVE} , HostSupportsSVE256{ctx->HostFeatures.SupportsAVX} + , HostSupportsRPRES{ctx->HostFeatures.SupportsRPRES} , CTX {ctx} { RAPass = Thread->PassManager->GetPass("RA"); diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/JITClass.h b/FEXCore/Source/Interface/Core/JIT/Arm64/JITClass.h index c464bb59f..054040fc1 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/JITClass.h +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/JITClass.h @@ -58,6 +58,7 @@ private: const bool HostSupportsSVE128{}; const bool HostSupportsSVE256{}; + const bool HostSupportsRPRES{}; ARMEmitter::BiDirectionalLabel *PendingTargetLabel; FEXCore::Context::ContextImpl *CTX; diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/VectorOps.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/VectorOps.cpp index 28d5258fb..21a8b2a51 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/VectorOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/VectorOps.cpp @@ -1115,11 +1115,23 @@ DEF_OP(VFRecp) { if (HostSupportsSVE256 && Is256Bit) { const auto Pred = PRED_TMP_32B.Merging(); + if (ElementSize == 4 && HostSupportsRPRES) { + // RPRES gives enough precision for this. + frecpe(SubRegSize.Vector, Dst.Z(), Vector.Z()); + return; + } + fmov(SubRegSize.Vector, VTMP1.Z(), 1.0); fdiv(SubRegSize.Vector, VTMP1.Z(), Pred, VTMP1.Z(), Vector.Z()); mov(Dst.Z(), VTMP1.Z()); } else { if (IsScalar) { + if (ElementSize == 4 && HostSupportsRPRES) { + // RPRES gives enough precision for this. + frecpe(SubRegSize.Scalar, Dst.S(), Vector.S()); + return; + } + fmov(SubRegSize.Scalar, VTMP1.Q(), 1.0f); switch (ElementSize) { case 2: { @@ -1138,6 +1150,17 @@ DEF_OP(VFRecp) { break; } } else { + if (ElementSize == 4 && HostSupportsRPRES) { + // RPRES gives enough precision for this. + if (OpSize == 8) { + frecpe(SubRegSize.Vector, Dst.D(), Vector.D()); + } + else { + frecpe(SubRegSize.Vector, Dst.Q(), Vector.Q()); + } + return; + } + fmov(SubRegSize.Vector, VTMP1.Q(), 1.0f); fdiv(SubRegSize.Vector, Dst.Q(), VTMP1.Q(), Vector.Q()); } @@ -1208,11 +1231,23 @@ DEF_OP(VFRSqrt) { if (HostSupportsSVE256 && Is256Bit) { const auto Pred = PRED_TMP_32B.Merging(); + if (ElementSize == 4 && HostSupportsRPRES) { + // RPRES gives enough precision for this. + frsqrte(SubRegSize.Vector, Dst.Z(), Vector.Z()); + return; + } + fsqrt(SubRegSize.Vector, VTMP1.Z(), Pred, Vector.Z()); fmov(SubRegSize.Vector, Dst.Z(), 1.0); fdiv(SubRegSize.Vector, Dst.Z(), Pred, Dst.Z(), VTMP1.Z()); } else { if (IsScalar) { + if (ElementSize == 4 && HostSupportsRPRES) { + // RPRES gives enough precision for this. + frsqrte(SubRegSize.Scalar, Dst.S(), Vector.S()); + return; + } + fmov(SubRegSize.Scalar, VTMP1.Q(), 1.0); switch (ElementSize) { case 2: { @@ -1234,6 +1269,17 @@ DEF_OP(VFRSqrt) { break; } } else { + if (ElementSize == 4 && HostSupportsRPRES) { + // RPRES gives enough precision for this. + if (OpSize == 8) { + frsqrte(SubRegSize.Vector, Dst.D(), Vector.D()); + } + else { + frsqrte(SubRegSize.Vector, Dst.Q(), Vector.Q()); + } + return; + } + fmov(SubRegSize.Vector, VTMP1.Q(), 1.0); fsqrt(SubRegSize.Vector, VTMP2.Q(), Vector.Q()); fdiv(SubRegSize.Vector, Dst.Q(), VTMP1.Q(), VTMP2.Q()); diff --git a/FEXCore/include/FEXCore/Core/HostFeatures.h b/FEXCore/include/FEXCore/Core/HostFeatures.h index 6b8c77e8f..064e7d771 100644 --- a/FEXCore/include/FEXCore/Core/HostFeatures.h +++ b/FEXCore/include/FEXCore/Core/HostFeatures.h @@ -36,6 +36,7 @@ class HostFeatures final { bool SupportsFCMA{}; bool SupportsFlagM{}; bool SupportsFlagM2{}; + bool SupportsRPRES{}; // Float exception behaviour bool SupportsAFP{}; diff --git a/Scripts/InstructionCountParser.py b/Scripts/InstructionCountParser.py index 577a88dfe..7f9ba47e1 100755 --- a/Scripts/InstructionCountParser.py +++ b/Scripts/InstructionCountParser.py @@ -49,6 +49,8 @@ class HostFeatures(Flag) : FEATURE_FCMA = (1 << 4) FEATURE_CSSC = (1 << 5) FEATURE_AFP = (1 << 6) + FEATURE_RPRES = (1 << 7) + HostFeaturesLookup = { "SVE128" : HostFeatures.FEATURE_SVE128, @@ -58,6 +60,7 @@ HostFeaturesLookup = { "FCMA" : HostFeatures.FEATURE_FCMA, "CSSC" : HostFeatures.FEATURE_CSSC, "AFP" : HostFeatures.FEATURE_AFP, + "RPRES" : HostFeatures.FEATURE_RPRES, } def GetHostFeatures(data): diff --git a/Source/Tools/CodeSizeValidation/Main.cpp b/Source/Tools/CodeSizeValidation/Main.cpp index 765180d26..e2c81cacf 100644 --- a/Source/Tools/CodeSizeValidation/Main.cpp +++ b/Source/Tools/CodeSizeValidation/Main.cpp @@ -463,6 +463,7 @@ int main(int argc, char **argv, char **const envp) { FEATURE_FCMA = (1U << 4), FEATURE_CSSC = (1U << 5), FEATURE_AFP = (1U << 6), + FEATURE_RPRES = (1U << 7), }; uint64_t SVEWidth = 0; @@ -490,6 +491,9 @@ int main(int argc, char **argv, char **const envp) { if (TestHeaderData->EnabledHostFeatures & FEATURE_AFP) { HostFeatureControl |= static_cast(FEXCore::Config::HostFeatures::ENABLEAFP); } + if (TestHeaderData->EnabledHostFeatures & FEATURE_RPRES) { + HostFeatureControl |= static_cast(FEXCore::Config::HostFeatures::ENABLERPRES); + } // Always enable ARMv8.1 LSE atomics. HostFeatureControl |= static_cast(FEXCore::Config::HostFeatures::ENABLEATOMICS); @@ -517,6 +521,9 @@ int main(int argc, char **argv, char **const envp) { if (TestHeaderData->DisabledHostFeatures & FEATURE_AFP) { HostFeatureControl |= static_cast(FEXCore::Config::HostFeatures::DISABLEAFP); } + if (TestHeaderData->DisabledHostFeatures & FEATURE_RPRES) { + HostFeatureControl |= static_cast(FEXCore::Config::HostFeatures::DISABLERPRES); + } FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_HOSTFEATURES, fextl::fmt::format("{}", HostFeatureControl)); FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_FORCESVEWIDTH, fextl::fmt::format("{}", SVEWidth));