1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright 2012, 2013 SAP AG. 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_INLINE_HPP
  27 #define OS_CPU_LINUX_PPC_VM_ATOMIC_LINUX_PPC_INLINE_HPP
  28 
  29 #include "orderAccess_linux_ppc.inline.hpp"
  30 #include "runtime/atomic.hpp"
  31 #include "runtime/os.hpp"
  32 #include "vm_version_ppc.hpp"
  33 
  34 #ifndef PPC64
  35 #error "Atomic currently only implemented for PPC64"
  36 #endif
  37 
  38 // Implementation of class atomic
  39 
  40 inline void Atomic::store    (jbyte    store_value, jbyte*    dest) { *dest = store_value; }
  41 inline void Atomic::store    (jshort   store_value, jshort*   dest) { *dest = store_value; }
  42 inline void Atomic::store    (jint     store_value, jint*     dest) { *dest = store_value; }
  43 inline void Atomic::store    (jlong    store_value, jlong*    dest) { *dest = store_value; }
  44 inline void Atomic::store_ptr(intptr_t store_value, intptr_t* dest) { *dest = store_value; }
  45 inline void Atomic::store_ptr(void*    store_value, void*     dest) { *(void**)dest = store_value; }
  46 
  47 inline void Atomic::store    (jbyte    store_value, volatile jbyte*    dest) { *dest = store_value; }
  48 inline void Atomic::store    (jshort   store_value, volatile jshort*   dest) { *dest = store_value; }
  49 inline void Atomic::store    (jint     store_value, volatile jint*     dest) { *dest = store_value; }
  50 inline void Atomic::store    (jlong    store_value, volatile jlong*    dest) { *dest = store_value; }
  51 inline void Atomic::store_ptr(intptr_t store_value, volatile intptr_t* dest) { *dest = store_value; }
  52 inline void Atomic::store_ptr(void*    store_value, volatile void*     dest) { *(void* volatile *)dest = store_value; }
  53 
  54 inline jlong Atomic::load(volatile jlong* src) { return *src; }
  55 
  56 /*
  57   machine barrier instructions:
  58 
  59   - sync            two-way memory barrier, aka fence
  60   - lwsync          orders  Store|Store,
  61                                  Load|Store,
  62                                  Load|Load,
  63                         but not Store|Load
  64   - eieio           orders memory accesses for device memory (only)
  65   - isync           invalidates speculatively executed instructions
  66                         From the POWER ISA 2.06 documentation:
  67                          "[...] an isync instruction prevents the execution of
  68                         instructions following the isync until instructions
  69                         preceding the isync have completed, [...]"
  70                         From IBM's AIX assembler reference:
  71                          "The isync [...] instructions causes the processor to
  72                         refetch any instructions that might have been fetched
  73                         prior to the isync instruction. The instruction isync
  74                         causes the processor to wait for all previous instructions
  75                         to complete. Then any instructions already fetched are
  76                         discarded and instruction processing continues in the
  77                         environment established by the previous instructions."
  78 
  79   semantic barrier instructions:
  80   (as defined in orderAccess.hpp)
  81 
  82   - release         orders Store|Store,       (maps to lwsync)
  83                                 Load|Store
  84   - acquire         orders  Load|Store,       (maps to lwsync)
  85                                 Load|Load
  86   - fence           orders Store|Store,       (maps to sync)
  87                                 Load|Store,
  88                                 Load|Load,
  89                                Store|Load
  90 */
  91 
  92 #define strasm_sync                       "\n  sync    \n"
  93 #define strasm_lwsync                     "\n  lwsync  \n"
  94 #define strasm_isync                      "\n  isync   \n"
  95 #define strasm_release                    strasm_lwsync
  96 #define strasm_acquire                    strasm_lwsync
  97 #define strasm_fence                      strasm_sync
  98 #define strasm_nobarrier                  ""
  99 #define strasm_nobarrier_clobber_memory   ""
 100 
 101 inline jint     Atomic::add    (jint     add_value, volatile jint*     dest) {
 102 
 103   unsigned int result;
 104 
 105   __asm__ __volatile__ (
 106     strasm_lwsync
 107     "1: lwarx   %0,  0, %2    \n"
 108     "   add     %0, %0, %1    \n"
 109     "   stwcx.  %0,  0, %2    \n"
 110     "   bne-    1b            \n"
 111     strasm_isync
 112     : /*%0*/"=&r" (result)
 113     : /*%1*/"r" (add_value), /*%2*/"r" (dest)
 114     : "cc", "memory" );
 115 
 116   return (jint) result;
 117 }
 118 
 119 
 120 inline intptr_t Atomic::add_ptr(intptr_t add_value, volatile intptr_t* dest) {
 121 
 122   long result;
 123 
 124   __asm__ __volatile__ (
 125     strasm_lwsync
 126     "1: ldarx   %0,  0, %2    \n"
 127     "   add     %0, %0, %1    \n"
 128     "   stdcx.  %0,  0, %2    \n"
 129     "   bne-    1b            \n"
 130     strasm_isync
 131     : /*%0*/"=&r" (result)
 132     : /*%1*/"r" (add_value), /*%2*/"r" (dest)
 133     : "cc", "memory" );
 134 
 135   return (intptr_t) result;
 136 }
 137 
 138 inline void*    Atomic::add_ptr(intptr_t add_value, volatile void*     dest) {
 139   return (void*)add_ptr(add_value, (volatile intptr_t*)dest);
 140 }
 141 
 142 
 143 inline void Atomic::inc    (volatile jint*     dest) {
 144 
 145   unsigned int temp;
 146 
 147   __asm__ __volatile__ (
 148     strasm_nobarrier
 149     "1: lwarx   %0,  0, %2    \n"
 150     "   addic   %0, %0,  1    \n"
 151     "   stwcx.  %0,  0, %2    \n"
 152     "   bne-    1b            \n"
 153     strasm_nobarrier
 154     : /*%0*/"=&r" (temp), "=m" (*dest)
 155     : /*%2*/"r" (dest), "m" (*dest)
 156     : "cc" strasm_nobarrier_clobber_memory);
 157 
 158 }
 159 
 160 inline void Atomic::inc_ptr(volatile intptr_t* dest) {
 161 
 162   long temp;
 163 
 164   __asm__ __volatile__ (
 165     strasm_nobarrier
 166     "1: ldarx   %0,  0, %2    \n"
 167     "   addic   %0, %0,  1    \n"
 168     "   stdcx.  %0,  0, %2    \n"
 169     "   bne-    1b            \n"
 170     strasm_nobarrier
 171     : /*%0*/"=&r" (temp), "=m" (*dest)
 172     : /*%2*/"r" (dest), "m" (*dest)
 173     : "cc" strasm_nobarrier_clobber_memory);
 174 
 175 }
 176 
 177 inline void Atomic::inc_ptr(volatile void*     dest) {
 178   inc_ptr((volatile intptr_t*)dest);
 179 }
 180 
 181 
 182 inline void Atomic::dec    (volatile jint*     dest) {
 183 
 184   unsigned int temp;
 185 
 186   __asm__ __volatile__ (
 187     strasm_nobarrier
 188     "1: lwarx   %0,  0, %2    \n"
 189     "   addic   %0, %0, -1    \n"
 190     "   stwcx.  %0,  0, %2    \n"
 191     "   bne-    1b            \n"
 192     strasm_nobarrier
 193     : /*%0*/"=&r" (temp), "=m" (*dest)
 194     : /*%2*/"r" (dest), "m" (*dest)
 195     : "cc" strasm_nobarrier_clobber_memory);
 196 
 197 }
 198 
 199 inline void Atomic::dec_ptr(volatile intptr_t* dest) {
 200 
 201   long temp;
 202 
 203   __asm__ __volatile__ (
 204     strasm_nobarrier
 205     "1: ldarx   %0,  0, %2    \n"
 206     "   addic   %0, %0, -1    \n"
 207     "   stdcx.  %0,  0, %2    \n"
 208     "   bne-    1b            \n"
 209     strasm_nobarrier
 210     : /*%0*/"=&r" (temp), "=m" (*dest)
 211     : /*%2*/"r" (dest), "m" (*dest)
 212     : "cc" strasm_nobarrier_clobber_memory);
 213 
 214 }
 215 
 216 inline void Atomic::dec_ptr(volatile void*     dest) {
 217   dec_ptr((volatile intptr_t*)dest);
 218 }
 219 
 220 inline jint Atomic::xchg(jint exchange_value, volatile jint* dest) {
 221 
 222   // Note that xchg_ptr doesn't necessarily do an acquire
 223   // (see synchronizer.cpp).
 224 
 225   unsigned int old_value;
 226   const uint64_t zero = 0;
 227 
 228   __asm__ __volatile__ (
 229     /* lwsync */
 230     strasm_lwsync
 231     /* atomic loop */
 232     "1:                                                 \n"
 233     "   lwarx   %[old_value], %[dest], %[zero]          \n"
 234     "   stwcx.  %[exchange_value], %[dest], %[zero]     \n"
 235     "   bne-    1b                                      \n"
 236     /* isync */
 237     strasm_sync
 238     /* exit */
 239     "2:                                                 \n"
 240     /* out */
 241     : [old_value]       "=&r"   (old_value),
 242                         "=m"    (*dest)
 243     /* in */
 244     : [dest]            "b"     (dest),
 245       [zero]            "r"     (zero),
 246       [exchange_value]  "r"     (exchange_value),
 247                         "m"     (*dest)
 248     /* clobber */
 249     : "cc",
 250       "memory"
 251     );
 252 
 253   return (jint) old_value;
 254 }
 255 
 256 inline intptr_t Atomic::xchg_ptr(intptr_t exchange_value, volatile intptr_t* dest) {
 257 
 258   // Note that xchg_ptr doesn't necessarily do an acquire
 259   // (see synchronizer.cpp).
 260 
 261   long old_value;
 262   const uint64_t zero = 0;
 263 
 264   __asm__ __volatile__ (
 265     /* lwsync */
 266     strasm_lwsync
 267     /* atomic loop */
 268     "1:                                                 \n"
 269     "   ldarx   %[old_value], %[dest], %[zero]          \n"
 270     "   stdcx.  %[exchange_value], %[dest], %[zero]     \n"
 271     "   bne-    1b                                      \n"
 272     /* isync */
 273     strasm_sync
 274     /* exit */
 275     "2:                                                 \n"
 276     /* out */
 277     : [old_value]       "=&r"   (old_value),
 278                         "=m"    (*dest)
 279     /* in */
 280     : [dest]            "b"     (dest),
 281       [zero]            "r"     (zero),
 282       [exchange_value]  "r"     (exchange_value),
 283                         "m"     (*dest)
 284     /* clobber */
 285     : "cc",
 286       "memory"
 287     );
 288 
 289   return (intptr_t) old_value;
 290 }
 291 
 292 inline void* Atomic::xchg_ptr(void* exchange_value, volatile void* dest) {
 293   return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
 294 }
 295 
 296 inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) {
 297 
 298   // Note that cmpxchg guarantees a two-way memory barrier across
 299   // the cmpxchg, so it's really a a 'fence_cmpxchg_acquire'
 300   // (see atomic.hpp).
 301 
 302   unsigned int old_value;
 303   const uint64_t zero = 0;
 304 
 305   __asm__ __volatile__ (
 306     /* fence */
 307     strasm_sync
 308     /* simple guard */
 309     "   lwz     %[old_value], 0(%[dest])                \n"
 310     "   cmpw    %[compare_value], %[old_value]          \n"
 311     "   bne-    2f                                      \n"
 312     /* atomic loop */
 313     "1:                                                 \n"
 314     "   lwarx   %[old_value], %[dest], %[zero]          \n"
 315     "   cmpw    %[compare_value], %[old_value]          \n"
 316     "   bne-    2f                                      \n"
 317     "   stwcx.  %[exchange_value], %[dest], %[zero]     \n"
 318     "   bne-    1b                                      \n"
 319     /* acquire */
 320     strasm_sync
 321     /* exit */
 322     "2:                                                 \n"
 323     /* out */
 324     : [old_value]       "=&r"   (old_value),
 325                         "=m"    (*dest)
 326     /* in */
 327     : [dest]            "b"     (dest),
 328       [zero]            "r"     (zero),
 329       [compare_value]   "r"     (compare_value),
 330       [exchange_value]  "r"     (exchange_value),
 331                         "m"     (*dest)
 332     /* clobber */
 333     : "cc",
 334       "memory"
 335     );
 336 
 337   return (jint) old_value;
 338 }
 339 
 340 inline jlong Atomic::cmpxchg(jlong exchange_value, volatile jlong* dest, jlong compare_value) {
 341 
 342   // Note that cmpxchg guarantees a two-way memory barrier across
 343   // the cmpxchg, so it's really a a 'fence_cmpxchg_acquire'
 344   // (see atomic.hpp).
 345 
 346   long old_value;
 347   const uint64_t zero = 0;
 348 
 349   __asm__ __volatile__ (
 350     /* fence */
 351     strasm_sync
 352     /* simple guard */
 353     "   ld      %[old_value], 0(%[dest])                \n"
 354     "   cmpd    %[compare_value], %[old_value]          \n"
 355     "   bne-    2f                                      \n"
 356     /* atomic loop */
 357     "1:                                                 \n"
 358     "   ldarx   %[old_value], %[dest], %[zero]          \n"
 359     "   cmpd    %[compare_value], %[old_value]          \n"
 360     "   bne-    2f                                      \n"
 361     "   stdcx.  %[exchange_value], %[dest], %[zero]     \n"
 362     "   bne-    1b                                      \n"
 363     /* acquire */
 364     strasm_sync
 365     /* exit */
 366     "2:                                                 \n"
 367     /* out */
 368     : [old_value]       "=&r"   (old_value),
 369                         "=m"    (*dest)
 370     /* in */
 371     : [dest]            "b"     (dest),
 372       [zero]            "r"     (zero),
 373       [compare_value]   "r"     (compare_value),
 374       [exchange_value]  "r"     (exchange_value),
 375                         "m"     (*dest)
 376     /* clobber */
 377     : "cc",
 378       "memory"
 379     );
 380 
 381   return (jlong) old_value;
 382 }
 383 
 384 inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) {
 385   return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value);
 386 }
 387 
 388 inline void* Atomic::cmpxchg_ptr(void* exchange_value, volatile void* dest, void* compare_value) {
 389   return (void*)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value);
 390 }
 391 
 392 #undef strasm_sync
 393 #undef strasm_lwsync
 394 #undef strasm_isync
 395 #undef strasm_release
 396 #undef strasm_acquire
 397 #undef strasm_fence
 398 #undef strasm_nobarrier
 399 #undef strasm_nobarrier_clobber_memory
 400 
 401 #endif // OS_CPU_LINUX_PPC_VM_ATOMIC_LINUX_PPC_INLINE_HPP