diff --git a/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp b/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp index 5ef80d5ee..e2c4075d2 100644 --- a/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp +++ b/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp @@ -551,7 +551,10 @@ void Dispatcher::EmitDispatcher() { EmitF64F2XM1(); EmitF64Scale(); EmitF64Atan(); - EmitF64FYL2X(); + F64Log2Constants Log2C; + EmitF64FYL2X(Log2C); + EmitF64FYL2XP1(Log2C); + EmitF64Log2Constants(Log2C); EmitF64FPREM(); EmitF64FPREM1(); @@ -1606,18 +1609,13 @@ void Dispatcher::EmitF64Atan() { // JIT-inlined double-precision y * log2(x) for the F64 reduced precision x87 path. // Input: VTMP1 = x, VTMP2 = y. Output: VTMP1 = y * log2(x). -// Algorithm: atanh-based log via s = f/(2+f) with 9-term polynomial, scaled by 1/ln(2), -// then multiplied by y. -void Dispatcher::EmitF64FYL2X() { +// Constants in `C` are shared with EmitF64FYL2XP1 and emitted by EmitF64Log2Constants. +void Dispatcher::EmitF64FYL2X(F64Log2Constants& C) { F64FYL2XHandlerAddress = GetCursorAddress(); constexpr auto Accum = ARMEmitter::VReg::v2; ARMEmitter::ForwardLabel Fallback; - ARMEmitter::ForwardLabel NoNorm; - ARMEmitter::ForwardLabel Sqrt2Label, Log2eLabel; - ARMEmitter::ForwardLabel BiasLabel; - ARMEmitter::ForwardLabel P0Label, P1Label, P2Label, P3Label, P4Label, P5Label, P6Label, P7Label, P8Label; str(ARMEmitter::QReg::q2, ARMEmitter::Reg::rsp, -16); @@ -1634,63 +1632,58 @@ void Dispatcher::EmitF64FYL2X() { (void)b(ARMEmitter::Condition::CC_EQ, &Fallback); fmov(ARMEmitter::Size::i64Bit, TMP3, VTMP2.D()); - // Extract k and normalize mantissa m into [1.0, 2.0). + // k = unbiased exponent; m bits = mantissa | (0x3FF << 52). sub(ARMEmitter::Size::i64Bit, TMP2, TMP2, 1023); ubfx(ARMEmitter::Size::i64Bit, TMP1, TMP1, 0, 52); - ldr(TMP4, &BiasLabel); + movz(ARMEmitter::Size::i64Bit, TMP4, 0x3FF0, 48); orr(ARMEmitter::Size::i64Bit, TMP1, TMP1, TMP4); + + // Index = top 6 mantissa bits (bits 51..46 of m). + ubfx(ARMEmitter::Size::i64Bit, TMP4, TMP1, 46, 6); + + // Set m as F64 in VTMP1. fmov(ARMEmitter::Size::i64Bit, VTMP1.D(), TMP1); - // If m > sqrt(2), halve m and increment k. - ldr(VTMP2.D(), &Sqrt2Label); - fcmp(VTMP1.D(), VTMP2.D()); - (void)b(ARMEmitter::Condition::CC_LE, &NoNorm); - fmov(ARMEmitter::ScalarRegSize::i64Bit, VTMP2, 0.5f); - fmul(VTMP1.D(), VTMP1.D(), VTMP2.D()); - add(ARMEmitter::Size::i64Bit, TMP2, TMP2, 1); - (void)Bind(&NoNorm); + // Load (recip, logc) from LUT[index]. + (void)adr(TMP1, &C.Table); + add(ARMEmitter::Size::i64Bit, TMP1, TMP1, TMP4, ARMEmitter::ShiftType::LSL, 4); + ldp(VTMP2.D(), Accum.D(), TMP1, 0); - // f = m - 1; s = f / (2 + f); TMP1 stashes s, VTMP1 holds s^2. - fmov(ARMEmitter::ScalarRegSize::i64Bit, VTMP2, 1.0f); + // Stash logc bits in TMP4 so Accum can be reused for the polynomial. + fmov(ARMEmitter::Size::i64Bit, TMP4, Accum.D()); + + // r = recip * m - 1. + fmul(VTMP1.D(), VTMP2.D(), VTMP1.D()); + ldr(VTMP2.D(), &C.One); fsub(VTMP1.D(), VTMP1.D(), VTMP2.D()); - fmov(ARMEmitter::ScalarRegSize::i64Bit, VTMP2, 2.0f); - fadd(VTMP2.D(), VTMP1.D(), VTMP2.D()); - fdiv(Accum.D(), VTMP1.D(), VTMP2.D()); - fmul(VTMP1.D(), Accum.D(), Accum.D()); - fmov(ARMEmitter::Size::i64Bit, TMP1, Accum.D()); - // 9-term Horner from 1/19 down to 1/3. - ldr(Accum.D(), &P8Label); - ldr(VTMP2.D(), &P7Label); + // Horner: poly = a0 + r*(a1 + r*(a2 + ... + r*a7)). + ldr(Accum.D(), &C.A7); + ldr(VTMP2.D(), &C.A6); fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); - ldr(VTMP2.D(), &P6Label); + ldr(VTMP2.D(), &C.A5); fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); - ldr(VTMP2.D(), &P5Label); + ldr(VTMP2.D(), &C.A4); fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); - ldr(VTMP2.D(), &P4Label); + ldr(VTMP2.D(), &C.A3); fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); - ldr(VTMP2.D(), &P3Label); + ldr(VTMP2.D(), &C.A2); fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); - ldr(VTMP2.D(), &P2Label); + ldr(VTMP2.D(), &C.A1); fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); - ldr(VTMP2.D(), &P1Label); - fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); - ldr(VTMP2.D(), &P0Label); + ldr(VTMP2.D(), &C.A0); fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); - // ln(1+f) = 2 * s * (1 + s^2 * P(s^2)). - fmul(Accum.D(), VTMP1.D(), Accum.D()); - fmov(ARMEmitter::ScalarRegSize::i64Bit, VTMP2, 1.0f); - fadd(Accum.D(), Accum.D(), VTMP2.D()); - fmov(ARMEmitter::Size::i64Bit, VTMP2.D(), TMP1); - fmul(Accum.D(), VTMP2.D(), Accum.D()); - fadd(Accum.D(), Accum.D(), Accum.D()); + // log2(1+r) = r * Accum. + fmul(VTMP1.D(), VTMP1.D(), Accum.D()); - // log2(x) = k + ln(1+f)/ln(2); multiply by y. - ldr(VTMP1.D(), &Log2eLabel); - fmul(VTMP1.D(), Accum.D(), VTMP1.D()); + // log2(x) = log2(1+r) + log2(center[i]) + k. + fmov(ARMEmitter::Size::i64Bit, VTMP2.D(), TMP4); + fadd(VTMP1.D(), VTMP1.D(), VTMP2.D()); scvtf(ARMEmitter::Size::i64Bit, VTMP2.D(), TMP2); fadd(VTMP1.D(), VTMP1.D(), VTMP2.D()); + + // result = y * log2(x). fmov(ARMEmitter::Size::i64Bit, VTMP2.D(), TMP3); fmul(VTMP1.D(), VTMP1.D(), VTMP2.D()); @@ -1711,33 +1704,267 @@ void Dispatcher::EmitF64FYL2X() { blr(TMP1); ldr(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16); ret(); +} - // Constant pool: bias=1.0, sqrt(2), log2(e)=1/ln(2), P8..P0 = 1/19, 1/17, 1/15, ..., 1/3. +// JIT-inlined double-precision y * log2(1 + x) for the F64 reduced precision x87 path. +// Input: VTMP1 = x, VTMP2 = y. Output: VTMP1 = y * log2(1 + x). +// Computes v = 1 + x in F64 and runs the same LUT-based log2 as F64FYL2X. +// Loses 1-2 ulps of precision near x=0 (FYL2XP1's original purpose) but +// matches main's lowering and avoids the range-check cliff into a fallback. +void Dispatcher::EmitF64FYL2XP1(F64Log2Constants& C) { + F64FYL2XP1HandlerAddress = GetCursorAddress(); + + constexpr auto Accum = ARMEmitter::VReg::v2; + + ARMEmitter::ForwardLabel Fallback; + + str(ARMEmitter::QReg::q2, ARMEmitter::Reg::rsp, -16); + + mrs(TMP1, ARMEmitter::SystemRegister::NZCV); + str(TMP1.W(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.flags[24])); + + // v = 1 + x in Accum so VTMP1/VTMP2 still hold the original x/y at the fallback. + ldr(Accum.D(), &C.One); + fadd(Accum.D(), VTMP1.D(), Accum.D()); + + // Reject v <= 0, subnormal, NaN, Inf via v's bits in TMP1. + fmov(ARMEmitter::Size::i64Bit, TMP1, Accum.D()); + (void)tbnz(TMP1, 63, &Fallback); + lsr(ARMEmitter::Size::i64Bit, TMP2, TMP1, 52); + (void)cbz(ARMEmitter::Size::i64Bit, TMP2, &Fallback); + cmp(ARMEmitter::Size::i64Bit, TMP2, 0x7FF); + (void)b(ARMEmitter::Condition::CC_EQ, &Fallback); + fmov(ARMEmitter::Size::i64Bit, TMP3, VTMP2.D()); + + // k = unbiased exponent; m bits = mantissa | (0x3FF << 52). + sub(ARMEmitter::Size::i64Bit, TMP2, TMP2, 1023); + ubfx(ARMEmitter::Size::i64Bit, TMP1, TMP1, 0, 52); + movz(ARMEmitter::Size::i64Bit, TMP4, 0x3FF0, 48); + orr(ARMEmitter::Size::i64Bit, TMP1, TMP1, TMP4); + + // Index = top 6 mantissa bits (bits 51..46 of m). + ubfx(ARMEmitter::Size::i64Bit, TMP4, TMP1, 46, 6); + + // Set m as F64 in VTMP1. + fmov(ARMEmitter::Size::i64Bit, VTMP1.D(), TMP1); + + // Load (recip, logc) from LUT[index]. + (void)adr(TMP1, &C.Table); + add(ARMEmitter::Size::i64Bit, TMP1, TMP1, TMP4, ARMEmitter::ShiftType::LSL, 4); + ldp(VTMP2.D(), Accum.D(), TMP1, 0); + + // Stash logc bits in TMP4 so Accum can be reused for the polynomial. + fmov(ARMEmitter::Size::i64Bit, TMP4, Accum.D()); + + // r = recip * m - 1. + fmul(VTMP1.D(), VTMP2.D(), VTMP1.D()); + ldr(VTMP2.D(), &C.One); + fsub(VTMP1.D(), VTMP1.D(), VTMP2.D()); + + // Horner: poly = a0 + r*(a1 + r*(a2 + ... + r*a7)). + ldr(Accum.D(), &C.A7); + ldr(VTMP2.D(), &C.A6); + fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); + ldr(VTMP2.D(), &C.A5); + fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); + ldr(VTMP2.D(), &C.A4); + fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); + ldr(VTMP2.D(), &C.A3); + fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); + ldr(VTMP2.D(), &C.A2); + fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); + ldr(VTMP2.D(), &C.A1); + fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); + ldr(VTMP2.D(), &C.A0); + fmadd(Accum.D(), VTMP1.D(), Accum.D(), VTMP2.D()); + + // log2(1+r) = r * Accum. + fmul(VTMP1.D(), VTMP1.D(), Accum.D()); + + // log2(v) = log2(1+r) + log2(center[i]) + k. + fmov(ARMEmitter::Size::i64Bit, VTMP2.D(), TMP4); + fadd(VTMP1.D(), VTMP1.D(), VTMP2.D()); + scvtf(ARMEmitter::Size::i64Bit, VTMP2.D(), TMP2); + fadd(VTMP1.D(), VTMP1.D(), VTMP2.D()); + + // result = y * log2(v). + fmov(ARMEmitter::Size::i64Bit, VTMP2.D(), TMP3); + fmul(VTMP1.D(), VTMP1.D(), VTMP2.D()); + + ldr(TMP1.W(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.flags[24])); + msr(ARMEmitter::SystemRegister::NZCV, TMP1); + + ldr(ARMEmitter::QReg::q2, ARMEmitter::Reg::rsp, 16); + ret(); + + // Fallback path: VTMP1/VTMP2 still hold the original x/y. + (void)Bind(&Fallback); + ldr(TMP1.W(), STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.flags[24])); + msr(ARMEmitter::SystemRegister::NZCV, TMP1); + ldr(ARMEmitter::QReg::q2, ARMEmitter::Reg::rsp, 16); + str(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16); + ldr(TMP1, STATE_PTR(CpuStateFrame, Pointers.FallbackHandlerPointers[FEXCore::Core::OPINDEX_F64FYL2XP1].ABIHandler)); + ldr(TMP4, STATE_PTR(CpuStateFrame, Pointers.FallbackHandlerPointers[FEXCore::Core::OPINDEX_F64FYL2XP1].Func)); + blr(TMP1); + ldr(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16); + ret(); +} + +// Shared constants pool for the LUT-based F64 log2 path used by FYL2X and FYL2XP1. +// Emitted once after both handlers; their forward `ldr`/`adr` references are +// patched here. Layout: One (1.0), 8 Horner coefficients A0..A7, then a 64-entry +// LUT of (1/center[i], log2(center[i])) pairs where center[i] = 1 + (i+0.5)/64. +void Dispatcher::EmitF64Log2Constants(F64Log2Constants& C) { Align(16); - (void)Bind(&BiasLabel); - dc64(0x3FF0'0000'0000'0000ULL); - (void)Bind(&Sqrt2Label); - dc64(0x3FF6'A09E'667F'3BCDULL); - (void)Bind(&Log2eLabel); - dc64(0x3FF7'1547'652B'82FEULL); - (void)Bind(&P8Label); - dc64(0x3FAA'F286'BCA1'AF28ULL); - (void)Bind(&P7Label); - dc64(0x3FAE'1E1E'1E1E'1E1EULL); - (void)Bind(&P6Label); - dc64(0x3FB1'1111'1111'1111ULL); - (void)Bind(&P5Label); - dc64(0x3FB3'B13B'13B1'3B14ULL); - (void)Bind(&P4Label); - dc64(0x3FB7'45D1'745D'1746ULL); - (void)Bind(&P3Label); - dc64(0x3FBC'71C7'1C71'C71CULL); - (void)Bind(&P2Label); - dc64(0x3FC2'4924'9249'2492ULL); - (void)Bind(&P1Label); - dc64(0x3FC9'9999'9999'999AULL); - (void)Bind(&P0Label); - dc64(0x3FD5'5555'5555'5555ULL); + (void)Bind(&C.One); + dc64(0x3FF0000000000000ULL); // 1.0 + (void)Bind(&C.A0); + dc64(0x3FF71547652B82FEULL); // log2(e) * 1/1 + (void)Bind(&C.A1); + dc64(0xBFE71547652B82FEULL); // log2(e) * -1/2 + (void)Bind(&C.A2); + dc64(0x3FDEC709DC3A03FDULL); // log2(e) * 1/3 + (void)Bind(&C.A3); + dc64(0xBFD71547652B82FEULL); // log2(e) * -1/4 + (void)Bind(&C.A4); + dc64(0x3FD2776C50EF9BFEULL); // log2(e) * 1/5 + (void)Bind(&C.A5); + dc64(0xBFCEC709DC3A03FDULL); // log2(e) * -1/6 + (void)Bind(&C.A6); + dc64(0x3FCA61762A7ADED9ULL); // log2(e) * 1/7 + (void)Bind(&C.A7); + dc64(0xBFC71547652B82FEULL); // log2(e) * -1/8 + + Align(16); + (void)Bind(&C.Table); + dc64(0x3FEFC07F01FC07F0ULL); + dc64(0x3F86FE50B6EF0851ULL); // i= 0 + dc64(0x3FEF44659E4A4271ULL); + dc64(0x3FA11CD1D5133413ULL); // i= 1 + dc64(0x3FEECC07B301ECC0ULL); + dc64(0x3FAC4DFAB90AAB5FULL); // i= 2 + dc64(0x3FEE573AC901E574ULL); + dc64(0x3FB3AA2FDD27F1C3ULL); // i= 3 + dc64(0x3FEDE5D6E3F8868AULL); + dc64(0x3FB918A16E46335BULL); // i= 4 + dc64(0x3FED77B654B82C34ULL); + dc64(0x3FBE72EC117FA5B2ULL); // i= 5 + dc64(0x3FED0CB58F6EC074ULL); + dc64(0x3FC1DCD197552B7BULL); // i= 6 + dc64(0x3FECA4B3055EE191ULL); + dc64(0x3FC476A9F983F74DULL); // i= 7 + dc64(0x3FEC3F8F01C3F8F0ULL); + dc64(0x3FC70742D4EF027FULL); // i= 8 + dc64(0x3FEBDD2B899406F7ULL); + dc64(0x3FC98EDD077E70DFULL); // i= 9 + dc64(0x3FEB7D6C3DDA338BULL); + dc64(0x3FCC0DB6CDD94DEEULL); // i=10 + dc64(0x3FEB2036406C80D9ULL); + dc64(0x3FCE840BE74E6A4DULL); // i=11 + dc64(0x3FEAC5701AC5701BULL); + dc64(0x3FD0790ADBB03009ULL); // i=12 + dc64(0x3FEA6D01A6D01A6DULL); + dc64(0x3FD1AC05B291F070ULL); // i=13 + dc64(0x3FEA16D3F97A4B02ULL); + dc64(0x3FD2DB10FC4D9AAFULL); // i=14 + dc64(0x3FE9C2D14EE4A102ULL); + dc64(0x3FD406463B1B0449ULL); // i=15 + dc64(0x3FE970E4F80CB872ULL); + dc64(0x3FD52DBDFC4C96B3ULL); // i=16 + dc64(0x3FE920FB49D0E229ULL); + dc64(0x3FD6518FE4677BA7ULL); // i=17 + dc64(0x3FE8D3018D3018D3ULL); + dc64(0x3FD771D2BA7EFB3CULL); // i=18 + dc64(0x3FE886E5F0ABB04AULL); + dc64(0x3FD88E9C72E0B226ULL); // i=19 + dc64(0x3FE83C977AB2BEDDULL); + dc64(0x3FD9A802391E232FULL); // i=20 + dc64(0x3FE7F405FD017F40ULL); + dc64(0x3FDABE18797F1F49ULL); // i=21 + dc64(0x3FE7AD2208E0ECC3ULL); + dc64(0x3FDBD0F2E9E79031ULL); // i=22 + dc64(0x3FE767DCE434A9B1ULL); + dc64(0x3FDCE0A4923A587DULL); // i=23 + dc64(0x3FE724287F46DEBCULL); + dc64(0x3FDDED3FD442364CULL); // i=24 + dc64(0x3FE6E1F76B4337C7ULL); + dc64(0x3FDEF6D67328E220ULL); // i=25 + dc64(0x3FE6A13CD1537290ULL); + dc64(0x3FDFFD799A83FF9BULL); // i=26 + dc64(0x3FE661EC6A5122F9ULL); + dc64(0x3FE0809CF27F703DULL); // i=27 + dc64(0x3FE623FA77016240ULL); + dc64(0x3FE10113B153C8EAULL); // i=28 + dc64(0x3FE5E75BB8D015E7ULL); + dc64(0x3FE18028CF72976AULL); // i=29 + dc64(0x3FE5AC056B015AC0ULL); + dc64(0x3FE1FDE3D30E8126ULL); // i=30 + dc64(0x3FE571ED3C506B3AULL); + dc64(0x3FE27A4C0585CBF8ULL); // i=31 + dc64(0x3FE5390948F40FEBULL); + dc64(0x3FE2F56875EB3F26ULL); // i=32 + dc64(0x3FE5015015015015ULL); + dc64(0x3FE36F3FFB6D9162ULL); // i=33 + dc64(0x3FE4CAB88725AF6EULL); + dc64(0x3FE3E7D9379F7016ULL); // i=34 + dc64(0x3FE49539E3B2D067ULL); + dc64(0x3FE45F3A98A20739ULL); // i=35 + dc64(0x3FE460CBC7F5CF9AULL); + dc64(0x3FE4D56A5B33CEC4ULL); // i=36 + dc64(0x3FE42D6625D51F87ULL); + dc64(0x3FE54A6E8CA5438EULL); // i=37 + dc64(0x3FE3FB013FB013FBULL); + dc64(0x3FE5BE4D0CB51435ULL); // i=38 + dc64(0x3FE3C995A47BABE7ULL); + dc64(0x3FE6310B8F553048ULL); // i=39 + dc64(0x3FE3991C2C187F63ULL); + dc64(0x3FE6A2AF9E5A0F0AULL); // i=40 + dc64(0x3FE3698DF3DE0748ULL); + dc64(0x3FE7133E9B156C7CULL); // i=41 + dc64(0x3FE33AE45B57BCB2ULL); + dc64(0x3FE782BDBFDDA657ULL); // i=42 + dc64(0x3FE30D190130D190ULL); + dc64(0x3FE7F1322182CF16ULL); // i=43 + dc64(0x3FE2E025C04B8097ULL); + dc64(0x3FE85EA0B0B27B26ULL); // i=44 + dc64(0x3FE2B404AD012B40ULL); + dc64(0x3FE8CB0E3B4B3BBEULL); // i=45 + dc64(0x3FE288B01288B013ULL); + dc64(0x3FE9367F6DA0AB2FULL); // i=46 + dc64(0x3FE25E22708092F1ULL); + dc64(0x3FE9A0F8D3B0E050ULL); // i=47 + dc64(0x3FE23456789ABCDFULL); + dc64(0x3FEA0A7EDA4C112DULL); // i=48 + dc64(0x3FE20B470C67C0D9ULL); + dc64(0x3FEA7315D02F20C8ULL); // i=49 + dc64(0x3FE1E2EF3B3FB874ULL); + dc64(0x3FEADAC1E711C833ULL); // i=50 + dc64(0x3FE1BB4A4046ED29ULL); + dc64(0x3FEB418734A9008CULL); // i=51 + dc64(0x3FE19453808CA29CULL); + dc64(0x3FEBA769B39E4964ULL); // i=52 + dc64(0x3FE16E0689427379ULL); + dc64(0x3FEC0C6D447C5DD3ULL); // i=53 + dc64(0x3FE1485F0E0ACD3BULL); + dc64(0x3FEC7095AE91E1C7ULL); // i=54 + dc64(0x3FE12358E75D3033ULL); + dc64(0x3FECD3E6A0CA8907ULL); // i=55 + dc64(0x3FE0FEF010FEF011ULL); + dc64(0x3FED3663B27F31D5ULL); // i=56 + dc64(0x3FE0DB20A88F4696ULL); + dc64(0x3FED9810643D6615ULL); // i=57 + dc64(0x3FE0B7E6EC259DC8ULL); + dc64(0x3FEDF8F02086AF2CULL); // i=58 + dc64(0x3FE0953F39010954ULL); + dc64(0x3FEE59063C8822CEULL); // i=59 + dc64(0x3FE073260A47F7C6ULL); + dc64(0x3FEEB855F8CA88FBULL); // i=60 + dc64(0x3FE05197F7D73404ULL); + dc64(0x3FEF16E281DB7630ULL); // i=61 + dc64(0x3FE03091B51F5E1AULL); + dc64(0x3FEF74AEF0EFAFAEULL); // i=62 + dc64(0x3FE0101010101010ULL); + dc64(0x3FEFD1BE4C7F2AF9ULL); // i=63 } void Dispatcher::EmitF64FPREM() { @@ -2407,6 +2634,7 @@ void Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState* Thread) Ptrs.F64ScaleHandler = F64ScaleHandlerAddress; Ptrs.F64AtanHandler = F64AtanHandlerAddress; Ptrs.F64FYL2XHandler = F64FYL2XHandlerAddress; + Ptrs.F64FYL2XP1Handler = F64FYL2XP1HandlerAddress; Ptrs.F64FPREMHandler = F64FPREMHandlerAddress; Ptrs.F64FPREM1Handler = F64FPREM1HandlerAddress; diff --git a/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.h b/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.h index f1d7686b2..b0f3d7a47 100644 --- a/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.h +++ b/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.h @@ -107,6 +107,7 @@ private: uint64_t F64ScaleHandlerAddress {}; uint64_t F64AtanHandlerAddress {}; uint64_t F64FYL2XHandlerAddress {}; + uint64_t F64FYL2XP1HandlerAddress {}; uint64_t F64FPREMHandlerAddress {}; uint64_t F64FPREM1HandlerAddress {}; @@ -120,13 +121,23 @@ private: void EmitF32ToExtF80(); void EmitF64ToExtF80(); + // Shared label set for the LUT-based F64 log2 path used by both FYL2X and + // FYL2XP1. The pool is emitted once via EmitF64Log2Constants. + struct F64Log2Constants { + ARMEmitter::ForwardLabel One; + ARMEmitter::ForwardLabel A0, A1, A2, A3, A4, A5, A6, A7; + ARMEmitter::ForwardLabel Table; + }; + void EmitF64Sin(); void EmitF64Cos(); void EmitF64Tan(); void EmitF64F2XM1(); void EmitF64Scale(); void EmitF64Atan(); - void EmitF64FYL2X(); + void EmitF64FYL2X(F64Log2Constants& C); + void EmitF64FYL2XP1(F64Log2Constants& C); + void EmitF64Log2Constants(F64Log2Constants& C); void EmitF64FPREM(); void EmitF64FPREM1(); diff --git a/FEXCore/Source/Interface/Core/Interpreter/Fallbacks/F80Fallbacks.h b/FEXCore/Source/Interface/Core/Interpreter/Fallbacks/F80Fallbacks.h index e37e11661..5c0e656c4 100644 --- a/FEXCore/Source/Interface/Core/Interpreter/Fallbacks/F80Fallbacks.h +++ b/FEXCore/Source/Interface/Core/Interpreter/Fallbacks/F80Fallbacks.h @@ -302,6 +302,16 @@ struct OpHandlers { } }; +template<> +struct OpHandlers { + FEXCORE_PRESERVE_ALL_ATTR static VectorRegType handle(uint16_t FCW, VectorRegType Src1, VectorRegType Src2, FEXCore::Core::CpuStateFrame* Frame) { + FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1); + ScopedSoftFloatState State {FCW, Frame, true}; + const X80SoftFloat One {&State.State, 1.0}; + return X80SoftFloat::FYL2X(&State.State, X80SoftFloat::FADD(&State.State, Src1, One), Src2); + } +}; + template<> struct OpHandlers { FEXCORE_PRESERVE_ALL_ATTR static VectorRegType handle(uint16_t FCW, VectorRegType Src1, VectorRegType Src2, FEXCore::Core::CpuStateFrame* Frame) { @@ -417,6 +427,14 @@ struct OpHandlers { } }; +template<> +struct OpHandlers { + FEXCORE_PRESERVE_ALL_ATTR static double handle(double src1, double src2, FEXCore::Core::CpuStateFrame* Frame) { + FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1); + return src2 * log2(1.0 + src1); + } +}; + template<> struct OpHandlers { FEXCORE_PRESERVE_ALL_ATTR static double handle(double src1, double src2, FEXCore::Core::CpuStateFrame* Frame) { diff --git a/FEXCore/Source/Interface/Core/Interpreter/Fallbacks/InterpreterFallbacks.cpp b/FEXCore/Source/Interface/Core/Interpreter/Fallbacks/InterpreterFallbacks.cpp index 598ee2e1b..d4a14a150 100644 --- a/FEXCore/Source/Interface/Core/Interpreter/Fallbacks/InterpreterFallbacks.cpp +++ b/FEXCore/Source/Interface/Core/Interpreter/Fallbacks/InterpreterFallbacks.cpp @@ -72,6 +72,8 @@ void InterpreterOps::FillFallbackIndexPointers(Core::FallbackABIInfo* Info, uint reinterpret_cast(&FEXCore::CPU::OpHandlers::handle)}; Info[Core::OPINDEX_F80FYL2X] = {ABIHandlers[FABI_F80_I16_F80_F80_PTR], reinterpret_cast(&FEXCore::CPU::OpHandlers::handle)}; + Info[Core::OPINDEX_F80FYL2XP1] = {ABIHandlers[FABI_F80_I16_F80_F80_PTR], + reinterpret_cast(&FEXCore::CPU::OpHandlers::handle)}; Info[Core::OPINDEX_F80ATAN] = {ABIHandlers[FABI_F80_I16_F80_F80_PTR], reinterpret_cast(&FEXCore::CPU::OpHandlers::handle)}; Info[Core::OPINDEX_F80FPREM1] = {ABIHandlers[FABI_F80_I16_F80_F80_PTR], @@ -97,6 +99,8 @@ void InterpreterOps::FillFallbackIndexPointers(Core::FallbackABIInfo* Info, uint reinterpret_cast(&FEXCore::CPU::OpHandlers::handle)}; Info[Core::OPINDEX_F64FYL2X] = {ABIHandlers[FABI_F64_F64_F64_PTR], reinterpret_cast(&FEXCore::CPU::OpHandlers::handle)}; + Info[Core::OPINDEX_F64FYL2XP1] = {ABIHandlers[FABI_F64_F64_F64_PTR], + reinterpret_cast(&FEXCore::CPU::OpHandlers::handle)}; Info[Core::OPINDEX_F64SCALE] = {ABIHandlers[FABI_F64_F64_F64_PTR], reinterpret_cast(&FEXCore::CPU::OpHandlers::handle)}; @@ -254,6 +258,7 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf COMMON_BINARY_X87_OP(MUL) COMMON_BINARY_X87_OP(DIV) COMMON_BINARY_X87_OP(FYL2X) + COMMON_BINARY_X87_OP(FYL2XP1) COMMON_BINARY_X87_OP(ATAN) COMMON_BINARY_X87_OP(FPREM1) COMMON_BINARY_X87_OP(FPREM) @@ -268,6 +273,7 @@ bool InterpreterOps::GetFallbackHandler(const IR::IROp_Header* IROp, FallbackInf // Double Precision Binary COMMON_BINARY_F64_OP(FYL2X) + COMMON_BINARY_F64_OP(FYL2XP1) COMMON_BINARY_F64_OP(ATAN) COMMON_BINARY_F64_OP(FPREM1) COMMON_BINARY_F64_OP(FPREM) diff --git a/FEXCore/Source/Interface/Core/JIT/VectorOps.cpp b/FEXCore/Source/Interface/Core/JIT/VectorOps.cpp index a0812a9ac..97db29119 100644 --- a/FEXCore/Source/Interface/Core/JIT/VectorOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/VectorOps.cpp @@ -4720,6 +4720,22 @@ DEF_OP(F64FYL2X) { fmov(Dst.D(), VTMP1.D()); } +// Src=x(ST0), Src2=y(ST1). Marshal into VTMP1/VTMP2 and dispatch the shared handler. +DEF_OP(F64FYL2XP1) { + const auto Op = IROp->C(); + const auto Src = GetVReg(Op->Src); + const auto Src2 = GetVReg(Op->Src2); + const auto Dst = GetVReg(Node); + + fmov(VTMP1.D(), Src.D()); + fmov(VTMP2.D(), Src2.D()); + ldr(TMP1, STATE_PTR(CpuStateFrame, Pointers.F64FYL2XP1Handler)); + str(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, -16); + blr(TMP1); + ldr(ARMEmitter::XReg::lr, ARMEmitter::Reg::rsp, 16); + fmov(Dst.D(), VTMP1.D()); +} + DEF_OP(F64SCALE) { const auto Op = IROp->C(); const auto Src1 = GetVReg(Op->Src1); diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp index d7fb5d5fc..ca9cb9ff8 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher/X87.cpp @@ -624,13 +624,10 @@ void OpDispatchBuilder::FXCH(OpcodeArgs) { void OpDispatchBuilder::X87FYL2X(OpcodeArgs, bool IsFYL2XP1) { if (IsFYL2XP1) { - // create an add between top of stack and 1. - Ref One = ReducedPrecisionMode ? _VCastFromGPR(OpSize::i64Bit, OpSize::i64Bit, Constant(0x3FF0000000000000)) : - LoadAndCacheNamedVectorConstant(OpSize::i128Bit, NamedVectorConstant::NAMED_VECTOR_X87_ONE); - _F80AddValue(0, One); + _F80FYL2XP1Stack(); + } else { + _F80FYL2XStack(); } - - _F80FYL2XStack(); } void OpDispatchBuilder::FCOMI(OpcodeArgs, IR::OpSize Width, bool Integer, OpDispatchBuilder::FCOMIFlags WhichFlags, bool PopTwice) { diff --git a/FEXCore/Source/Interface/IR/IR.json b/FEXCore/Source/Interface/IR/IR.json index 586be2c44..115a0d3e7 100644 --- a/FEXCore/Source/Interface/IR/IR.json +++ b/FEXCore/Source/Interface/IR/IR.json @@ -2780,6 +2780,10 @@ "DestSize": "OpSize::i64Bit", "JITDispatch": true }, + "FPR = F64FYL2XP1 FPR:$Src, FPR:$Src2": { + "DestSize": "OpSize::i64Bit", + "JITDispatch": true + }, "FPR = F64TAN FPR:$Src": { "DestSize": "OpSize::i64Bit", "JITDispatch": true @@ -3208,6 +3212,20 @@ "DestSize": "OpSize::i128Bit", "JITDispatch": false }, + "FPR = F80FYL2XP1Stack": { + "Desc": [ + "Computes ST1 * log2(1 + ST0)", + "Stores the result in ST1, and pops the top of the stack.", + "Returns the new value at the top of the stack, i.e. the result of the operation." + ], + "HasSideEffects": true, + "DestSize": "OpSize::i128Bit", + "X87": true + }, + "FPR = F80FYL2XP1 FPR:$X80Src1, FPR:$X80Src2": { + "DestSize": "OpSize::i128Bit", + "JITDispatch": false + }, "F80VBSLStack OpSize:#RegisterSize, FPR:$VectorMask, u8:$SrcStack1, u8:$SrcStack2": { "Desc": [ "Does a vector bitwise select.", diff --git a/FEXCore/Source/Interface/IR/Passes/x87StackOptimizationPass.cpp b/FEXCore/Source/Interface/IR/Passes/x87StackOptimizationPass.cpp index 3145aae34..226e168dd 100644 --- a/FEXCore/Source/Interface/IR/Passes/x87StackOptimizationPass.cpp +++ b/FEXCore/Source/Interface/IR/Passes/x87StackOptimizationPass.cpp @@ -785,6 +785,12 @@ void X87StackOptimization::Run(IREmitter* Emit) { break; } + case OP_F80FYL2XP1STACK: { + HandleBinopStack(OP_F64FYL2XP1, false, OP_F80FYL2XP1, 1, 0, 1); + StackPop(); + break; + } + case OP_F80ATANSTACK: { HandleBinopStack(OP_F64ATAN, false, OP_F80ATAN, 1, 1, 0); StackPop(); diff --git a/FEXCore/include/FEXCore/Core/CoreState.h b/FEXCore/include/FEXCore/Core/CoreState.h index 37aa79672..7c64dd83c 100644 --- a/FEXCore/include/FEXCore/Core/CoreState.h +++ b/FEXCore/include/FEXCore/Core/CoreState.h @@ -302,6 +302,7 @@ enum FallbackHandlerIndex { OPINDEX_F80MUL, OPINDEX_F80DIV, OPINDEX_F80FYL2X, + OPINDEX_F80FYL2XP1, OPINDEX_F80ATAN, OPINDEX_F80FPREM1, OPINDEX_F80FPREM, @@ -315,6 +316,7 @@ enum FallbackHandlerIndex { OPINDEX_F64ATAN, OPINDEX_F64F2XM1, OPINDEX_F64FYL2X, + OPINDEX_F64FYL2XP1, OPINDEX_F64FPREM, OPINDEX_F64FPREM1, OPINDEX_F64SCALE, @@ -383,6 +385,7 @@ struct JITPointers { uint64_t F64ScaleHandler {}; uint64_t F64AtanHandler {}; uint64_t F64FYL2XHandler {}; + uint64_t F64FYL2XP1Handler {}; uint64_t F64FPREMHandler {}; uint64_t F64FPREM1Handler {}; /** @} */