atomic_ops.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include "common/common_types.h"
  5. #if _MSC_VER
  6. #include <intrin.h>
  7. #else
  8. #include <cstring>
  9. #endif
  10. namespace Common {
  11. #if _MSC_VER
  12. template <typename T>
  13. [[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected);
  14. template <typename T>
  15. [[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected, T& actual);
  16. template <>
  17. [[nodiscard]] inline bool AtomicCompareAndSwap<u8>(u8* pointer, u8 value, u8 expected) {
  18. const u8 result =
  19. _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
  20. return result == expected;
  21. }
  22. template <>
  23. [[nodiscard]] inline bool AtomicCompareAndSwap<u16>(u16* pointer, u16 value, u16 expected) {
  24. const u16 result =
  25. _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
  26. return result == expected;
  27. }
  28. template <>
  29. [[nodiscard]] inline bool AtomicCompareAndSwap<u32>(u32* pointer, u32 value, u32 expected) {
  30. const u32 result =
  31. _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
  32. return result == expected;
  33. }
  34. template <>
  35. [[nodiscard]] inline bool AtomicCompareAndSwap<u64>(u64* pointer, u64 value, u64 expected) {
  36. const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
  37. value, expected);
  38. return result == expected;
  39. }
  40. [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
  41. return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
  42. value[0],
  43. reinterpret_cast<__int64*>(expected.data())) != 0;
  44. }
  45. template <>
  46. [[nodiscard]] inline bool AtomicCompareAndSwap<u8>(u8* pointer, u8 value, u8 expected, u8& actual) {
  47. actual =
  48. _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
  49. return actual == expected;
  50. }
  51. template <>
  52. [[nodiscard]] inline bool AtomicCompareAndSwap<u16>(u16* pointer, u16 value, u16 expected,
  53. u16& actual) {
  54. actual =
  55. _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
  56. return actual == expected;
  57. }
  58. template <>
  59. [[nodiscard]] inline bool AtomicCompareAndSwap<u32>(u32* pointer, u32 value, u32 expected,
  60. u32& actual) {
  61. actual =
  62. _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
  63. return actual == expected;
  64. }
  65. template <>
  66. [[nodiscard]] inline bool AtomicCompareAndSwap<u64>(u64* pointer, u64 value, u64 expected,
  67. u64& actual) {
  68. actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
  69. expected);
  70. return actual == expected;
  71. }
  72. [[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
  73. u128& actual) {
  74. const bool result =
  75. _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
  76. value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
  77. actual = expected;
  78. return result;
  79. }
  80. [[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
  81. u128 result{};
  82. _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
  83. result[0], reinterpret_cast<__int64*>(result.data()));
  84. return result;
  85. }
  86. #else
  87. template <typename T>
  88. [[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected) {
  89. return __sync_bool_compare_and_swap(pointer, expected, value);
  90. }
  91. [[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected) {
  92. unsigned __int128 value_a;
  93. unsigned __int128 expected_a;
  94. std::memcpy(&value_a, value.data(), sizeof(u128));
  95. std::memcpy(&expected_a, expected.data(), sizeof(u128));
  96. return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
  97. }
  98. template <typename T>
  99. [[nodiscard]] inline bool AtomicCompareAndSwap(T* pointer, T value, T expected, T& actual) {
  100. actual = __sync_val_compare_and_swap(pointer, expected, value);
  101. return actual == expected;
  102. }
  103. [[nodiscard]] inline bool AtomicCompareAndSwap(u64* pointer, u128 value, u128 expected,
  104. u128& actual) {
  105. unsigned __int128 value_a;
  106. unsigned __int128 expected_a;
  107. unsigned __int128 actual_a;
  108. std::memcpy(&value_a, value.data(), sizeof(u128));
  109. std::memcpy(&expected_a, expected.data(), sizeof(u128));
  110. actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
  111. std::memcpy(actual.data(), &actual_a, sizeof(u128));
  112. return actual_a == expected_a;
  113. }
  114. [[nodiscard]] inline u128 AtomicLoad128(u64* pointer) {
  115. unsigned __int128 zeros_a = 0;
  116. unsigned __int128 result_a =
  117. __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
  118. u128 result;
  119. std::memcpy(result.data(), &result_a, sizeof(u128));
  120. return result;
  121. }
  122. #endif
  123. } // namespace Common