atomic_linux_ppc.hpp revision 13477:4d61110c6046
1/*
2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2012, 2014 SAP SE. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 *
24 */
25
26#ifndef OS_CPU_LINUX_PPC_VM_ATOMIC_LINUX_PPC_HPP
27#define OS_CPU_LINUX_PPC_VM_ATOMIC_LINUX_PPC_HPP
28
29#ifndef PPC64
30#error "Atomic currently only implemented for PPC64"
31#endif
32
33// Implementation of class atomic
34
35inline void Atomic::store    (jbyte    store_value, jbyte*    dest) { *dest = store_value; }
36inline void Atomic::store    (jshort   store_value, jshort*   dest) { *dest = store_value; }
37inline void Atomic::store    (jint     store_value, jint*     dest) { *dest = store_value; }
38inline void Atomic::store    (jlong    store_value, jlong*    dest) { *dest = store_value; }
39inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; }
40inline void Atomic::store_ptr(void*    store_value, void*     dest) { *(void**)dest = store_value; }
41
42inline void Atomic::store    (jbyte    store_value, volatile jbyte*    dest) { *dest = store_value; }
43inline void Atomic::store    (jshort   store_value, volatile jshort*   dest) { *dest = store_value; }
44inline void Atomic::store    (jint     store_value, volatile jint*     dest) { *dest = store_value; }
45inline void Atomic::store    (jlong    store_value, volatile jlong*    dest) { *dest = store_value; }
46inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; }
47inline void Atomic::store_ptr(void*    store_value, volatile void*     dest) { *(void* volatile *)dest = store_value; }
48
49inline jlong Atomic::load(const volatile jlong* src) { return *src; }
50
51//
52// machine barrier instructions:
53//
54// - sync            two-way memory barrier, aka fence
55// - lwsync          orders  Store|Store,
56//                            Load|Store,
57//                            Load|Load,
58//                   but not Store|Load
59// - eieio           orders memory accesses for device memory (only)
60// - isync           invalidates speculatively executed instructions
61//                   From the POWER ISA 2.06 documentation:
62//                    "[...] an isync instruction prevents the execution of
63//                   instructions following the isync until instructions
64//                   preceding the isync have completed, [...]"
65//                   From IBM's AIX assembler reference:
66//                    "The isync [...] instructions causes the processor to
67//                   refetch any instructions that might have been fetched
68//                   prior to the isync instruction. The instruction isync
69//                   causes the processor to wait for all previous instructions
70//                   to complete. Then any instructions already fetched are
71//                   discarded and instruction processing continues in the
72//                   environment established by the previous instructions."
73//
74// semantic barrier instructions:
75// (as defined in orderAccess.hpp)
76//
77// - release         orders Store|Store,       (maps to lwsync)
78//                           Load|Store
79// - acquire         orders  Load|Store,       (maps to lwsync)
80//                           Load|Load
81// - fence           orders Store|Store,       (maps to sync)
82//                           Load|Store,
83//                           Load|Load,
84//                          Store|Load
85//
86
87#define strasm_sync                       "\n  sync    \n"
88#define strasm_lwsync                     "\n  lwsync  \n"
89#define strasm_isync                      "\n  isync   \n"
90#define strasm_release                    strasm_lwsync
91#define strasm_acquire                    strasm_lwsync
92#define strasm_fence                      strasm_sync
93#define strasm_nobarrier                  ""
94#define strasm_nobarrier_clobber_memory   ""
95
96inline jint     Atomic::add    (jint     add_value, volatile jint*     dest) {
97
98  unsigned int result;
99
100  __asm__ __volatile__ (
101    strasm_lwsync
102    "1: lwarx   %0,  0, %2    \n"
103    "   add     %0, %0, %1    \n"
104    "   stwcx.  %0,  0, %2    \n"
105    "   bne-    1b            \n"
106    strasm_isync
107    : /*%0*/"=&r" (result)
108    : /*%1*/"r" (add_value), /*%2*/"r" (dest)
109    : "cc", "memory" );
110
111  return (jint) result;
112}
113
114
115inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) {
116
117  long result;
118
119  __asm__ __volatile__ (
120    strasm_lwsync
121    "1: ldarx   %0,  0, %2    \n"
122    "   add     %0, %0, %1    \n"
123    "   stdcx.  %0,  0, %2    \n"
124    "   bne-    1b            \n"
125    strasm_isync
126    : /*%0*/"=&r" (result)
127    : /*%1*/"r" (add_value), /*%2*/"r" (dest)
128    : "cc", "memory" );
129
130  return (intptr_t) result;
131}
132
133inline void*    Atomic::add_ptr(intptr_t add_value, volatile void*     dest) {
134  return (void*)add_ptr(add_value, (volatile intptr_t*)dest);
135}
136
137
138inline void Atomic::inc    (volatile jint*     dest) {
139
140  unsigned int temp;
141
142  __asm__ __volatile__ (
143    strasm_nobarrier
144    "1: lwarx   %0,  0, %2    \n"
145    "   addic   %0, %0,  1    \n"
146    "   stwcx.  %0,  0, %2    \n"
147    "   bne-    1b            \n"
148    strasm_nobarrier
149    : /*%0*/"=&r" (temp), "=m" (*dest)
150    : /*%2*/"r" (dest), "m" (*dest)
151    : "cc" strasm_nobarrier_clobber_memory);
152
153}
154
155inline void Atomic::inc_ptr(volatile intptr_t* dest) {
156
157  long temp;
158
159  __asm__ __volatile__ (
160    strasm_nobarrier
161    "1: ldarx   %0,  0, %2    \n"
162    "   addic   %0, %0,  1    \n"
163    "   stdcx.  %0,  0, %2    \n"
164    "   bne-    1b            \n"
165    strasm_nobarrier
166    : /*%0*/"=&r" (temp), "=m" (*dest)
167    : /*%2*/"r" (dest), "m" (*dest)
168    : "cc" strasm_nobarrier_clobber_memory);
169
170}
171
172inline void Atomic::inc_ptr(volatile void*     dest) {
173  inc_ptr((volatile intptr_t*)dest);
174}
175
176
177inline void Atomic::dec    (volatile jint*     dest) {
178
179  unsigned int temp;
180
181  __asm__ __volatile__ (
182    strasm_nobarrier
183    "1: lwarx   %0,  0, %2    \n"
184    "   addic   %0, %0, -1    \n"
185    "   stwcx.  %0,  0, %2    \n"
186    "   bne-    1b            \n"
187    strasm_nobarrier
188    : /*%0*/"=&r" (temp), "=m" (*dest)
189    : /*%2*/"r" (dest), "m" (*dest)
190    : "cc" strasm_nobarrier_clobber_memory);
191
192}
193
194inline void Atomic::dec_ptr(volatile intptr_t* dest) {
195
196  long temp;
197
198  __asm__ __volatile__ (
199    strasm_nobarrier
200    "1: ldarx   %0,  0, %2    \n"
201    "   addic   %0, %0, -1    \n"
202    "   stdcx.  %0,  0, %2    \n"
203    "   bne-    1b            \n"
204    strasm_nobarrier
205    : /*%0*/"=&r" (temp), "=m" (*dest)
206    : /*%2*/"r" (dest), "m" (*dest)
207    : "cc" strasm_nobarrier_clobber_memory);
208
209}
210
211inline void Atomic::dec_ptr(volatile void*     dest) {
212  dec_ptr((volatile intptr_t*)dest);
213}
214
215inline jint Atomic::xchg(jint exchange_value, volatile jint* dest) {
216
217  // Note that xchg_ptr doesn't necessarily do an acquire
218  // (see synchronizer.cpp).
219
220  unsigned int old_value;
221  const uint64_t zero = 0;
222
223  __asm__ __volatile__ (
224    /* lwsync */
225    strasm_lwsync
226    /* atomic loop */
227    "1:                                                 \n"
228    "   lwarx   %[old_value], %[dest], %[zero]          \n"
229    "   stwcx.  %[exchange_value], %[dest], %[zero]     \n"
230    "   bne-    1b                                      \n"
231    /* isync */
232    strasm_sync
233    /* exit */
234    "2:                                                 \n"
235    /* out */
236    : [old_value]       "=&r"   (old_value),
237                        "=m"    (*dest)
238    /* in */
239    : [dest]            "b"     (dest),
240      [zero]            "r"     (zero),
241      [exchange_value]  "r"     (exchange_value),
242                        "m"     (*dest)
243    /* clobber */
244    : "cc",
245      "memory"
246    );
247
248  return (jint) old_value;
249}
250
251inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) {
252
253  // Note that xchg_ptr doesn't necessarily do an acquire
254  // (see synchronizer.cpp).
255
256  long old_value;
257  const uint64_t zero = 0;
258
259  __asm__ __volatile__ (
260    /* lwsync */
261    strasm_lwsync
262    /* atomic loop */
263    "1:                                                 \n"
264    "   ldarx   %[old_value], %[dest], %[zero]          \n"
265    "   stdcx.  %[exchange_value], %[dest], %[zero]     \n"
266    "   bne-    1b                                      \n"
267    /* isync */
268    strasm_sync
269    /* exit */
270    "2:                                                 \n"
271    /* out */
272    : [old_value]       "=&r"   (old_value),
273                        "=m"    (*dest)
274    /* in */
275    : [dest]            "b"     (dest),
276      [zero]            "r"     (zero),
277      [exchange_value]  "r"     (exchange_value),
278                        "m"     (*dest)
279    /* clobber */
280    : "cc",
281      "memory"
282    );
283
284  return (intptr_t) old_value;
285}
286
287inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) {
288  return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
289}
290
291inline void cmpxchg_pre_membar(cmpxchg_memory_order order) {
292  if (order != memory_order_relaxed) {
293    __asm__ __volatile__ (
294      /* fence */
295      strasm_sync
296      );
297  }
298}
299
300inline void cmpxchg_post_membar(cmpxchg_memory_order order) {
301  if (order != memory_order_relaxed) {
302    __asm__ __volatile__ (
303      /* fence */
304      strasm_sync
305      );
306  }
307}
308
309template<>
310template<typename T>
311inline T Atomic::PlatformCmpxchg<1>::operator()(T exchange_value,
312                                                T volatile* dest,
313                                                T compare_value,
314                                                cmpxchg_memory_order order) const {
315  STATIC_ASSERT(1 == sizeof(T));
316
317  // Note that cmpxchg guarantees a two-way memory barrier across
318  // the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
319  // specified otherwise (see atomic.hpp).
320
321  // Using 32 bit internally.
322  volatile int *dest_base = (volatile int*)((uintptr_t)dest & ~3);
323
324#ifdef VM_LITTLE_ENDIAN
325  const unsigned int shift_amount        = ((uintptr_t)dest & 3) * 8;
326#else
327  const unsigned int shift_amount        = ((~(uintptr_t)dest) & 3) * 8;
328#endif
329  const unsigned int masked_compare_val  = ((unsigned int)(unsigned char)compare_value),
330                     masked_exchange_val = ((unsigned int)(unsigned char)exchange_value),
331                     xor_value           = (masked_compare_val ^ masked_exchange_val) << shift_amount;
332
333  unsigned int old_value, value32;
334
335  cmpxchg_pre_membar(order);
336
337  __asm__ __volatile__ (
338    /* simple guard */
339    "   lbz     %[old_value], 0(%[dest])                  \n"
340    "   cmpw    %[masked_compare_val], %[old_value]       \n"
341    "   bne-    2f                                        \n"
342    /* atomic loop */
343    "1:                                                   \n"
344    "   lwarx   %[value32], 0, %[dest_base]               \n"
345    /* extract byte and compare */
346    "   srd     %[old_value], %[value32], %[shift_amount] \n"
347    "   clrldi  %[old_value], %[old_value], 56            \n"
348    "   cmpw    %[masked_compare_val], %[old_value]       \n"
349    "   bne-    2f                                        \n"
350    /* replace byte and try to store */
351    "   xor     %[value32], %[xor_value], %[value32]      \n"
352    "   stwcx.  %[value32], 0, %[dest_base]               \n"
353    "   bne-    1b                                        \n"
354    /* exit */
355    "2:                                                   \n"
356    /* out */
357    : [old_value]           "=&r"   (old_value),
358      [value32]             "=&r"   (value32),
359                            "=m"    (*dest),
360                            "=m"    (*dest_base)
361    /* in */
362    : [dest]                "b"     (dest),
363      [dest_base]           "b"     (dest_base),
364      [shift_amount]        "r"     (shift_amount),
365      [masked_compare_val]  "r"     (masked_compare_val),
366      [xor_value]           "r"     (xor_value),
367                            "m"     (*dest),
368                            "m"     (*dest_base)
369    /* clobber */
370    : "cc",
371      "memory"
372    );
373
374  cmpxchg_post_membar(order);
375
376  return PrimitiveConversions::cast<T>((unsigned char)old_value);
377}
378
379template<>
380template<typename T>
381inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
382                                                T volatile* dest,
383                                                T compare_value,
384                                                cmpxchg_memory_order order) const {
385  STATIC_ASSERT(4 == sizeof(T));
386
387  // Note that cmpxchg guarantees a two-way memory barrier across
388  // the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
389  // specified otherwise (see atomic.hpp).
390
391  T old_value;
392  const uint64_t zero = 0;
393
394  cmpxchg_pre_membar(order);
395
396  __asm__ __volatile__ (
397    /* simple guard */
398    "   lwz     %[old_value], 0(%[dest])                \n"
399    "   cmpw    %[compare_value], %[old_value]          \n"
400    "   bne-    2f                                      \n"
401    /* atomic loop */
402    "1:                                                 \n"
403    "   lwarx   %[old_value], %[dest], %[zero]          \n"
404    "   cmpw    %[compare_value], %[old_value]          \n"
405    "   bne-    2f                                      \n"
406    "   stwcx.  %[exchange_value], %[dest], %[zero]     \n"
407    "   bne-    1b                                      \n"
408    /* exit */
409    "2:                                                 \n"
410    /* out */
411    : [old_value]       "=&r"   (old_value),
412                        "=m"    (*dest)
413    /* in */
414    : [dest]            "b"     (dest),
415      [zero]            "r"     (zero),
416      [compare_value]   "r"     (compare_value),
417      [exchange_value]  "r"     (exchange_value),
418                        "m"     (*dest)
419    /* clobber */
420    : "cc",
421      "memory"
422    );
423
424  cmpxchg_post_membar(order);
425
426  return old_value;
427}
428
429template<>
430template<typename T>
431inline T Atomic::PlatformCmpxchg<8>::operator()(T exchange_value,
432                                                T volatile* dest,
433                                                T compare_value,
434                                                cmpxchg_memory_order order) const {
435  STATIC_ASSERT(8 == sizeof(T));
436
437  // Note that cmpxchg guarantees a two-way memory barrier across
438  // the cmpxchg, so it's really a a 'fence_cmpxchg_fence' if not
439  // specified otherwise (see atomic.hpp).
440
441  T old_value;
442  const uint64_t zero = 0;
443
444  cmpxchg_pre_membar(order);
445
446  __asm__ __volatile__ (
447    /* simple guard */
448    "   ld      %[old_value], 0(%[dest])                \n"
449    "   cmpd    %[compare_value], %[old_value]          \n"
450    "   bne-    2f                                      \n"
451    /* atomic loop */
452    "1:                                                 \n"
453    "   ldarx   %[old_value], %[dest], %[zero]          \n"
454    "   cmpd    %[compare_value], %[old_value]          \n"
455    "   bne-    2f                                      \n"
456    "   stdcx.  %[exchange_value], %[dest], %[zero]     \n"
457    "   bne-    1b                                      \n"
458    /* exit */
459    "2:                                                 \n"
460    /* out */
461    : [old_value]       "=&r"   (old_value),
462                        "=m"    (*dest)
463    /* in */
464    : [dest]            "b"     (dest),
465      [zero]            "r"     (zero),
466      [compare_value]   "r"     (compare_value),
467      [exchange_value]  "r"     (exchange_value),
468                        "m"     (*dest)
469    /* clobber */
470    : "cc",
471      "memory"
472    );
473
474  cmpxchg_post_membar(order);
475
476  return old_value;
477}
478
479#undef strasm_sync
480#undef strasm_lwsync
481#undef strasm_isync
482#undef strasm_release
483#undef strasm_acquire
484#undef strasm_fence
485#undef strasm_nobarrier
486#undef strasm_nobarrier_clobber_memory
487
488#endif // OS_CPU_LINUX_PPC_VM_ATOMIC_LINUX_PPC_HPP
489