summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Morph2023-03-21 21:28:38 -0400
committerGravatar Morph2023-03-27 17:45:22 -0400
commit27c33ab73fd03d659654c49967a081214daf6ac2 (patch)
treece139e675dce91045a05fb3d5e44803af9931d0e /src
parentx64: cpu_detect: Add detection of waitpkg instructions (diff)
downloadyuzu-27c33ab73fd03d659654c49967a081214daf6ac2.tar.gz
yuzu-27c33ab73fd03d659654c49967a081214daf6ac2.tar.xz
yuzu-27c33ab73fd03d659654c49967a081214daf6ac2.zip
x64: Add MicroSleep
MicroSleep allows the processor to pause for a "short" amount of time (in the microsecond range). This is useful for spin-waiting that does not require nanosecond precision. This uses the new TPAUSE instruction introduced on Intel's newest processors as part of the waitpkg instructions. For CPUs that do not support waitpkg instructions, this is equivalent to yield(). Co-Authored-By: liamwhite <liamwhite@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/x64/cpu_wait.cpp72
-rw-r--r--src/common/x64/cpu_wait.h10
3 files changed, 84 insertions, 0 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index c1d2b24a1..13ed68b3f 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -160,6 +160,8 @@ if(ARCHITECTURE_x86_64)
160 PRIVATE 160 PRIVATE
161 x64/cpu_detect.cpp 161 x64/cpu_detect.cpp
162 x64/cpu_detect.h 162 x64/cpu_detect.h
163 x64/cpu_wait.cpp
164 x64/cpu_wait.h
163 x64/native_clock.cpp 165 x64/native_clock.cpp
164 x64/native_clock.h 166 x64/native_clock.h
165 x64/xbyak_abi.h 167 x64/xbyak_abi.h
diff --git a/src/common/x64/cpu_wait.cpp b/src/common/x64/cpu_wait.cpp
new file mode 100644
index 000000000..1fab0bfe8
--- /dev/null
+++ b/src/common/x64/cpu_wait.cpp
@@ -0,0 +1,72 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5
6#ifdef _MSC_VER
7#include <intrin.h>
8#endif
9
10#include "common/x64/cpu_detect.h"
11#include "common/x64/cpu_wait.h"
12
13namespace Common::X64 {
14
15#ifdef _MSC_VER
16__forceinline static u64 FencedRDTSC() {
17 _mm_lfence();
18 _ReadWriteBarrier();
19 const u64 result = __rdtsc();
20 _mm_lfence();
21 _ReadWriteBarrier();
22 return result;
23}
24
25__forceinline static void TPAUSE() {
26 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
27 // For reference:
28 // At 1 GHz, 100K cycles is 100us
29 // At 2 GHz, 100K cycles is 50us
30 // At 4 GHz, 100K cycles is 25us
31 static constexpr auto PauseCycles = 100'000;
32 _tpause(0, FencedRDTSC() + PauseCycles);
33}
34#else
35static u64 FencedRDTSC() {
36 u64 result;
37 asm volatile("lfence\n\t"
38 "rdtsc\n\t"
39 "shl $32, %%rdx\n\t"
40 "or %%rdx, %0\n\t"
41 "lfence"
42 : "=a"(result)
43 :
44 : "rdx", "memory", "cc");
45 return result;
46}
47
48static void TPAUSE() {
49 // 100,000 cycles is a reasonable amount of time to wait to save on CPU resources.
50 // For reference:
51 // At 1 GHz, 100K cycles is 100us
52 // At 2 GHz, 100K cycles is 50us
53 // At 4 GHz, 100K cycles is 25us
54 static constexpr auto PauseCycles = 100'000;
55 const auto tsc = FencedRDTSC() + PauseCycles;
56 const auto eax = static_cast<u32>(tsc & 0xFFFFFFFF);
57 const auto edx = static_cast<u32>(tsc >> 32);
58 asm volatile("tpause %0" : : "r"(0), "d"(edx), "a"(eax));
59}
60#endif
61
62void MicroSleep() {
63 static const bool has_waitpkg = GetCPUCaps().waitpkg;
64
65 if (has_waitpkg) {
66 TPAUSE();
67 } else {
68 std::this_thread::yield();
69 }
70}
71
72} // namespace Common::X64
diff --git a/src/common/x64/cpu_wait.h b/src/common/x64/cpu_wait.h
new file mode 100644
index 000000000..99d3757a7
--- /dev/null
+++ b/src/common/x64/cpu_wait.h
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace Common::X64 {
7
8void MicroSleep();
9
10} // namespace Common::X64