3333// Note that memory_order_conservative requires a full barrier after atomic stores.
3434// See https://patchwork.kernel.org/patch/3575821/
3535
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+
3642template <size_t byte_size>
3743struct Atomic ::PlatformAdd {
3844 template <typename D, typename I>
3945 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+
4053 if (order != memory_order_relaxed) {
4154 FULL_MEM_BARRIER;
4255 }
@@ -55,12 +68,65 @@ struct Atomic::PlatformAdd {
5568 }
5669};
5770
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+
58117template <size_t byte_size>
59118template <typename T>
60119inline T Atomic::PlatformXchg<byte_size>::operator ()(T volatile * dest,
61120 T exchange_value,
62121 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+
63128 STATIC_ASSERT (byte_size == sizeof (T));
129+
64130 if (order != memory_order_relaxed) {
65131 FULL_MEM_BARRIER;
66132 }
@@ -80,6 +146,11 @@ inline T Atomic::PlatformCmpxchg<byte_size>::operator()(T volatile* dest __attri
80146 T compare_value,
81147 T exchange_value,
82148 atomic_memory_order order) const {
149+
150+ #ifndef FULL_COMPILER_ATOMIC_SUPPORT
151+ STATIC_ASSERT (byte_size >= 4 );
152+ #endif
153+
83154 STATIC_ASSERT (byte_size == sizeof (T));
84155 T value = compare_value;
85156 if (order != memory_order_relaxed) {
@@ -148,4 +219,6 @@ struct Atomic::PlatformOrderedStore<byte_size, RELEASE_X_FENCE>
148219 void operator ()(volatile T* p, T v) const { release_store (p, v); OrderAccess::fence (); }
149220};
150221
222+ #undef FULL_COMPILER_ATOMIC_SUPPORT
223+
151224#endif // OS_CPU_LINUX_RISCV_ATOMIC_LINUX_RISCV_HPP
0 commit comments