33
33
// Note that memory_order_conservative requires a full barrier after atomic stores.
34
34
// See https://patchwork.kernel.org/patch/3575821/
35
35
36
+ #if defined(__clang_major__)
37
+ #define FULL_COMPILER_ATOMIC_SUPPORT
38
+ #elif (__GNUC__ > 13) || ((__GNUC__ == 13) && (__GNUC_MINOR__ >= 2))
39
+ #define FULL_COMPILER_ATOMIC_SUPPORT
40
+ #endif
41
+
36
42
template <size_t byte_size>
37
43
struct Atomic ::PlatformAdd {
38
44
template <typename D, typename I>
39
45
D add_then_fetch (D volatile * dest, I add_value, atomic_memory_order order) const {
46
+
47
+ #ifndef FULL_COMPILER_ATOMIC_SUPPORT
48
+ // If we add add and fetch for sub word and are using older compiler
49
+ // it must be added here due to not using lib atomic.
50
+ STATIC_ASSERT (byte_size >= 4 );
51
+ #endif
52
+
40
53
if (order != memory_order_relaxed) {
41
54
FULL_MEM_BARRIER;
42
55
}
@@ -55,12 +68,65 @@ struct Atomic::PlatformAdd {
55
68
}
56
69
};
57
70
71
+ #ifndef FULL_COMPILER_ATOMIC_SUPPORT
72
+ template <>
73
+ template <typename T>
74
+ inline T Atomic::PlatformCmpxchg<1 >::operator ()(T volatile * dest __attribute__ ((unused)),
75
+ T compare_value,
76
+ T exchange_value,
77
+ atomic_memory_order order) const {
78
+ STATIC_ASSERT (1 == sizeof (T));
79
+
80
+ if (order != memory_order_relaxed) {
81
+ FULL_MEM_BARRIER;
82
+ }
83
+
84
+ uint32_t volatile * aligned_dst = (uint32_t volatile *)(((uintptr_t )dest) & (~((uintptr_t )0x3 )));
85
+ int shift = 8 * (((uintptr_t )dest) - ((uintptr_t )aligned_dst)); // 0, 8, 16, 24
86
+
87
+ uint64_t mask = 0xfful << shift; // 0x00000000..FF..
88
+ uint64_t remask = ~mask; // 0xFFFFFFFF..00..
89
+
90
+ uint64_t w_cv = ((uint64_t )(unsigned char )compare_value) << shift; // widen to 64-bit 0x00000000..CC..
91
+ uint64_t w_ev = ((uint64_t )(unsigned char )exchange_value) << shift; // widen to 64-bit 0x00000000..EE..
92
+
93
+ uint64_t old_value;
94
+ uint64_t rc_temp;
95
+
96
+ __asm__ __volatile__ (
97
+ " 1: lr.w %0, %2 \n\t "
98
+ " and %1, %0, %5 \n\t " // ignore unrelated bytes and widen to 64-bit 0x00000000..XX..
99
+ " bne %1, %3, 2f \n\t " // compare 64-bit w_cv
100
+ " and %1, %0, %6 \n\t " // remove old byte
101
+ " or %1, %1, %4 \n\t " // add new byte
102
+ " sc.w %1, %1, %2 \n\t " // store new word
103
+ " bnez %1, 1b \n\t "
104
+ " 2: \n\t "
105
+ : /* %0*/ " =&r" (old_value), /* %1*/ " =&r" (rc_temp), /* %2*/ " +A" (*aligned_dst)
106
+ : /* %3*/ " r" (w_cv), /* %4*/ " r" (w_ev), /* %5*/ " r" (mask), /* %6*/ " r" (remask)
107
+ : " memory" );
108
+
109
+ if (order != memory_order_relaxed) {
110
+ FULL_MEM_BARRIER;
111
+ }
112
+
113
+ return (T)((old_value & mask) >> shift);
114
+ }
115
+ #endif
116
+
58
117
template <size_t byte_size>
59
118
template <typename T>
60
119
inline T Atomic::PlatformXchg<byte_size>::operator ()(T volatile * dest,
61
120
T exchange_value,
62
121
atomic_memory_order order) const {
122
+ #ifndef FULL_COMPILER_ATOMIC_SUPPORT
123
+ // If we add xchg for sub word and are using older compiler
124
+ // it must be added here due to not using lib atomic.
125
+ STATIC_ASSERT (byte_size >= 4 );
126
+ #endif
127
+
63
128
STATIC_ASSERT (byte_size == sizeof (T));
129
+
64
130
if (order != memory_order_relaxed) {
65
131
FULL_MEM_BARRIER;
66
132
}
@@ -80,6 +146,11 @@ inline T Atomic::PlatformCmpxchg<byte_size>::operator()(T volatile* dest __attri
80
146
T compare_value,
81
147
T exchange_value,
82
148
atomic_memory_order order) const {
149
+
150
+ #ifndef FULL_COMPILER_ATOMIC_SUPPORT
151
+ STATIC_ASSERT (byte_size >= 4 );
152
+ #endif
153
+
83
154
STATIC_ASSERT (byte_size == sizeof (T));
84
155
T value = compare_value;
85
156
if (order != memory_order_relaxed) {
@@ -148,4 +219,6 @@ struct Atomic::PlatformOrderedStore<byte_size, RELEASE_X_FENCE>
148
219
void operator ()(volatile T* p, T v) const { release_store (p, v); OrderAccess::fence (); }
149
220
};
150
221
222
+ #undef FULL_COMPILER_ATOMIC_SUPPORT
223
+
151
224
#endif // OS_CPU_LINUX_RISCV_ATOMIC_LINUX_RISCV_HPP
0 commit comments