diff --git a/FEXCore/Source/Interface/Core/DiskCache.h b/FEXCore/Source/Interface/Core/DiskCache.h index 35d51cd0a..3784e4768 100644 --- a/FEXCore/Source/Interface/Core/DiskCache.h +++ b/FEXCore/Source/Interface/Core/DiskCache.h @@ -244,7 +244,7 @@ namespace DiskCache { // The current version of the diskcache. // This must be changed any time codegen changes occur! // Be aware of the impact of changing this frequently! - static constexpr uint16_t FormatVersion = 25; + static constexpr uint16_t FormatVersion = 26; static constexpr uint32_t LOOKUP_KEY_MAX_BUCKET_DEPTH = 20; diff --git a/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp b/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp index a3e2ce1a7..62b5940fe 100644 --- a/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp +++ b/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp @@ -514,6 +514,42 @@ void Dispatcher::EmitDispatcher() { (void)b(&LoopTop); } + { + // All dynamic and static registers are spilled coming in to this handler. + // It's also the end of block and RIP might have changed, so we jump directly to the top of the loop. + ThreadDispatchSyscallHandler = GetCursorAddress(); + + // Store in the state that we are in a syscall + // 16bit LoadConstant to be a single instruction + // This gives the signal handler a value to check to see if we are in a syscall at all + LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, 0xFFFF); + str(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo)); + + ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.SyscallHandlerObj)); + ldr(ARMEmitter::XReg::x3, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.SyscallHandlerFunc)); + mov(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r1, STATE.R()); + + if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] { + GenerateIndirectRuntimeCall(ARMEmitter::Reg::r3); + } else { + blr(ARMEmitter::Reg::r3); + } + + // Fix the stack and any values that were stepped on + // Syscall result is in any static register that the frontend desired. + FillStaticRegs({ + .OptionalReg = ARMEmitter::Reg::r1, + .OptionalReg2 = ARMEmitter::Reg::r2, + }); + + // Now the registers we've spilled are back in their original host registers + // We can safely claim we are no longer in a syscall + str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo)); + + // Now go back to the regular dispatcher loop + (void)b(&LoopTop); + } + auto EmitLongALUOpHandler = [&](auto R, auto Offset) { auto Address = GetCursorAddress(); @@ -2627,6 +2663,7 @@ void Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState* Thread) Ptrs.ExitFunctionLinker = ExitFunctionLinkerAddress; Ptrs.ThreadStopHandlerSpillSRA = ThreadStopHandlerAddressSpillSRA; Ptrs.ThreadPauseHandlerSpillSRA = ThreadPauseHandlerAddressSpillSRA; + Ptrs.ThreadDispatchSyscallHandler = ThreadDispatchSyscallHandler; Ptrs.GuestSignal_SIGILL = GuestSignal_SIGILL; Ptrs.GuestSignal_SIGTRAP = GuestSignal_SIGTRAP; Ptrs.GuestSignal_SIGSEGV = GuestSignal_SIGSEGV; diff --git a/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.h b/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.h index b0f3d7a47..b2f979212 100644 --- a/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.h +++ b/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.h @@ -81,6 +81,7 @@ private: uint64_t AbsoluteLoopTopAddressEnterECFillSRA {}; uint64_t ThreadPauseHandlerAddress {}; uint64_t ThreadPauseHandlerAddressSpillSRA {}; + uint64_t ThreadDispatchSyscallHandler {}; uint64_t ExitFunctionLinkerAddress {}; uint64_t SignalHandlerReturnAddress {}; uint64_t SignalHandlerReturnAddressRT {}; diff --git a/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp b/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp index 5852d1e8f..2d90c7291 100644 --- a/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp @@ -283,51 +283,11 @@ DEF_OP(CondJump) { } DEF_OP(Syscall) { - // Arguments are passed as follows: - // X0: SyscallHandler - // X1: ThreadState + SpillStaticRegs(TMP1); - PushDynamicRegs(TMP1); - - uint32_t GPRSpillMask = ~0U; - uint32_t FPRSpillMask = ~0U; - - SpillStaticRegs(TMP1, { - .GPRSpillMask = GPRSpillMask, - .FPRSpillMask = FPRSpillMask, - }); - - // Now that we are spilled, store in the state that we are in a syscall - // Still without overwriting registers that matter - // 16bit LoadConstant to be a single instruction - // This gives the signal handler a value to check to see if we are in a syscall at all - LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, GPRSpillMask & 0xFFFF); - str(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo)); - - ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.SyscallHandlerObj)); - ldr(ARMEmitter::XReg::x3, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.SyscallHandlerFunc)); - mov(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r1, STATE.R()); - - if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] { - GenerateIndirectRuntimeCall(ARMEmitter::Reg::r3); - } else { - blr(ARMEmitter::Reg::r3); - } - - // Fix the stack and any values that were stepped on - // Syscall result is in any static register that the frontend desired. - FillStaticRegs({ - .OptionalReg = ARMEmitter::Reg::r1, - .OptionalReg2 = ARMEmitter::Reg::r2, - .GPRFillMask = GPRSpillMask, - .FPRFillMask = FPRSpillMask, - }); - - // Now the registers we've spilled are back in their original host registers - // We can safely claim we are no longer in a syscall - str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo)); - - PopDynamicRegs(); + // Jump to the syscall dispatch handler. We won't return after this. + ldr(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.ThreadDispatchSyscallHandler)); + br(TMP1); } DEF_OP(Thunk) { diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 5ee618981..8dc605ca7 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -59,12 +59,6 @@ void OpDispatchBuilder::SyscallOp(OpcodeArgs, bool IsSyscallInst) { FlushRegisterCache(); _Syscall(); - - if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_BLOCK_END) { - // RIP could have been updated after coming back from the Syscall. - NewRIP = _LoadContextGPR(GPRSize, offsetof(FEXCore::Core::CPUState, rip)); - ExitFunction(NewRIP); - } } void OpDispatchBuilder::ThunkOp(OpcodeArgs) { diff --git a/FEXCore/Source/Interface/IR/IREmitter.cpp b/FEXCore/Source/Interface/IR/IREmitter.cpp index a0b265166..ac187de31 100644 --- a/FEXCore/Source/Interface/IR/IREmitter.cpp +++ b/FEXCore/Source/Interface/IR/IREmitter.cpp @@ -19,6 +19,7 @@ namespace FEXCore::IR { static bool IsFragmentExit(FEXCore::IR::IROps Op) { switch (Op) { + case OP_SYSCALL: case OP_EXITFUNCTION: case OP_BREAK: return true; default: return false; diff --git a/FEXCore/include/FEXCore/Core/CoreState.h b/FEXCore/include/FEXCore/Core/CoreState.h index 0ac202eed..b62a46715 100644 --- a/FEXCore/include/FEXCore/Core/CoreState.h +++ b/FEXCore/include/FEXCore/Core/CoreState.h @@ -371,6 +371,7 @@ struct JITPointers { uint64_t ExitFunctionLinker {}; uint64_t ThreadStopHandlerSpillSRA {}; uint64_t ThreadPauseHandlerSpillSRA {}; + uint64_t ThreadDispatchSyscallHandler {}; uint64_t GuestSignal_SIGILL {}; uint64_t GuestSignal_SIGTRAP {}; uint64_t GuestSignal_SIGSEGV {};