atomic_gcc.h 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // Copyright 2013 Dolphin Emulator Project
  2. // Licensed under GPLv2
  3. // Refer to the license.txt file included.
  4. #ifndef _ATOMIC_GCC_H_
  5. #define _ATOMIC_GCC_H_
  6. #include "common/common.h"
  7. // Atomic operations are performed in a single step by the CPU. It is
  8. // impossible for other threads to see the operation "half-done."
  9. //
  10. // Some atomic operations can be combined with different types of memory
  11. // barriers called "Acquire semantics" and "Release semantics", defined below.
  12. //
  13. // Acquire semantics: Future memory accesses cannot be relocated to before the
  14. // operation.
  15. //
  16. // Release semantics: Past memory accesses cannot be relocated to after the
  17. // operation.
  18. //
  19. // These barriers affect not only the compiler, but also the CPU.
  20. namespace Common
  21. {
  22. inline void AtomicAdd(volatile u32& target, u32 value) {
  23. __sync_add_and_fetch(&target, value);
  24. }
  25. inline void AtomicAnd(volatile u32& target, u32 value) {
  26. __sync_and_and_fetch(&target, value);
  27. }
  28. inline void AtomicDecrement(volatile u32& target) {
  29. __sync_add_and_fetch(&target, -1);
  30. }
  31. inline void AtomicIncrement(volatile u32& target) {
  32. __sync_add_and_fetch(&target, 1);
  33. }
  34. inline u32 AtomicLoad(volatile u32& src) {
  35. return src; // 32-bit reads are always atomic.
  36. }
  37. inline u32 AtomicLoadAcquire(volatile u32& src) {
  38. //keep the compiler from caching any memory references
  39. u32 result = src; // 32-bit reads are always atomic.
  40. //__sync_synchronize(); // TODO: May not be necessary.
  41. // Compiler instruction only. x86 loads always have acquire semantics.
  42. __asm__ __volatile__ ( "":::"memory" );
  43. return result;
  44. }
  45. inline void AtomicOr(volatile u32& target, u32 value) {
  46. __sync_or_and_fetch(&target, value);
  47. }
  48. inline void AtomicStore(volatile u32& dest, u32 value) {
  49. dest = value; // 32-bit writes are always atomic.
  50. }
  51. inline void AtomicStoreRelease(volatile u32& dest, u32 value) {
  52. __sync_lock_test_and_set(&dest, value); // TODO: Wrong! This function is has acquire semantics.
  53. }
  54. }
  55. // Old code kept here for reference in case we need the parts with __asm__ __volatile__.
  56. #if 0
  57. LONG SyncInterlockedIncrement(LONG *Dest)
  58. {
  59. #if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
  60. return __sync_add_and_fetch(Dest, 1);
  61. #else
  62. register int result;
  63. __asm__ __volatile__("lock; xadd %0,%1"
  64. : "=r" (result), "=m" (*Dest)
  65. : "0" (1), "m" (*Dest)
  66. : "memory");
  67. return result;
  68. #endif
  69. }
  70. LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val)
  71. {
  72. #if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
  73. return __sync_add_and_fetch(Dest, Val);
  74. #else
  75. register int result;
  76. __asm__ __volatile__("lock; xadd %0,%1"
  77. : "=r" (result), "=m" (*Dest)
  78. : "0" (Val), "m" (*Dest)
  79. : "memory");
  80. return result;
  81. #endif
  82. }
  83. LONG SyncInterlockedExchange(LONG *Dest, LONG Val)
  84. {
  85. #if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
  86. return __sync_lock_test_and_set(Dest, Val);
  87. #else
  88. register int result;
  89. __asm__ __volatile__("lock; xchg %0,%1"
  90. : "=r" (result), "=m" (*Dest)
  91. : "0" (Val), "m" (*Dest)
  92. : "memory");
  93. return result;
  94. #endif
  95. }
  96. #endif
  97. #endif