Skip to content

Commit 8343788

Browse files
robehnpull[bot]
authored andcommitted
8316645: RISC-V: Remove dependency on libatomic by adding cmpxchg 1b
Reviewed-by: ihse, fyang, luhenry, mli
1 parent e7d3a62 commit 8343788

File tree

3 files changed

+118
-6
lines changed

3 files changed

+118
-6
lines changed

make/autoconf/libraries.m4

-6
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,6 @@ AC_DEFUN([LIB_SETUP_JVM_LIBS],
108108
BASIC_JVM_LIBS_$1="$BASIC_JVM_LIBS_$1 -latomic"
109109
fi
110110
fi
111-
112-
# Because RISC-V only has word-sized atomics, it requires libatomic where
113-
# other common architectures do not, so link libatomic by default.
114-
if test "x$OPENJDK_$1_OS" = xlinux && test "x$OPENJDK_$1_CPU" = xriscv64; then
115-
BASIC_JVM_LIBS_$1="$BASIC_JVM_LIBS_$1 -latomic"
116-
fi
117111
])
118112

119113
################################################################################

src/hotspot/os_cpu/linux_riscv/atomic_linux_riscv.hpp

+73
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,23 @@
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+
3642
template<size_t byte_size>
3743
struct 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+
58117
template<size_t byte_size>
59118
template<typename T>
60119
inline 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

test/hotspot/gtest/runtime/test_atomic.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,51 @@ TEST(AtomicCmpxchgTest, int64) {
146146
Support().test();
147147
}
148148

149+
struct AtomicCmpxchg1ByteStressSupport {
150+
char _default_val;
151+
int _base;
152+
char _array[7+32+7];
153+
154+
AtomicCmpxchg1ByteStressSupport() : _default_val(0xaa), _base(7), _array{} {}
155+
156+
void validate(char val, char val2, int index) {
157+
for (int i = 0; i < 7; i++) {
158+
EXPECT_EQ(_array[i], _default_val);
159+
}
160+
for (int i = 7; i < (7+32); i++) {
161+
if (i == index) {
162+
EXPECT_EQ(_array[i], val2);
163+
} else {
164+
EXPECT_EQ(_array[i], val);
165+
}
166+
}
167+
for (int i = 0; i < 7; i++) {
168+
EXPECT_EQ(_array[i], _default_val);
169+
}
170+
}
171+
172+
void test_index(int index) {
173+
char one = 1;
174+
Atomic::cmpxchg(&_array[index], _default_val, one);
175+
validate(_default_val, one, index);
176+
177+
Atomic::cmpxchg(&_array[index], one, _default_val);
178+
validate(_default_val, _default_val, index);
179+
}
180+
181+
void test() {
182+
memset(_array, _default_val, sizeof(_array));
183+
for (int i = _base; i < (_base+32); i++) {
184+
test_index(i);
185+
}
186+
}
187+
};
188+
189+
TEST(AtomicCmpxchg1Byte, stress) {
190+
AtomicCmpxchg1ByteStressSupport support;
191+
support.test();
192+
}
193+
149194
template<typename T>
150195
struct AtomicEnumTestSupport {
151196
volatile T _test_value;

0 commit comments

Comments
 (0)