| |
| |
|
|
| #include <thread> |
|
|
| #ifdef _MSC_VER |
| #include <intrin.h> |
| #endif |
|
|
| #include "common/x64/cpu_detect.h" |
| #include "common/x64/cpu_wait.h" |
| #include "common/x64/rdtsc.h" |
|
|
| namespace Common::X64 { |
|
|
| namespace { |
|
|
| |
| |
| |
| |
| |
| constexpr auto PauseCycles = 100'000U; |
|
|
| } |
|
|
| #ifdef _MSC_VER |
| __forceinline static void TPAUSE() { |
| static constexpr auto RequestC02State = 0U; |
| _tpause(RequestC02State, FencedRDTSC() + PauseCycles); |
| } |
|
|
| __forceinline static void MWAITX() { |
| static constexpr auto EnableWaitTimeFlag = 1U << 1; |
| static constexpr auto RequestC1State = 0U; |
|
|
| |
| alignas(64) u64 monitor_var{}; |
| _mm_monitorx(&monitor_var, 0, 0); |
| _mm_mwaitx(EnableWaitTimeFlag, RequestC1State, PauseCycles); |
| } |
| #else |
| static void TPAUSE() { |
| static constexpr auto RequestC02State = 0U; |
| const auto tsc = FencedRDTSC() + PauseCycles; |
| const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF); |
| const auto edx = static_cast<u32>(tsc >> 32); |
| asm volatile("tpause %0" : : "r"(RequestC02State), "d"(edx), "a"(eax)); |
| } |
|
|
| static void MWAITX() { |
| static constexpr auto EnableWaitTimeFlag = 1U << 1; |
| static constexpr auto RequestC1State = 0U; |
|
|
| |
| alignas(64) u64 monitor_var{}; |
| asm volatile("monitorx" : : "a"(&monitor_var), "c"(0), "d"(0)); |
| asm volatile("mwaitx" : : "a"(RequestC1State), "b"(PauseCycles), "c"(EnableWaitTimeFlag)); |
| } |
| #endif |
|
|
| void MicroSleep() { |
| static const bool has_waitpkg = GetCPUCaps().waitpkg; |
| static const bool has_monitorx = GetCPUCaps().monitorx; |
|
|
| if (has_waitpkg) { |
| TPAUSE(); |
| } else if (has_monitorx) { |
| MWAITX(); |
| } else { |
| std::this_thread::yield(); |
| } |
| } |
|
|
| } |
|
|