8144847: PPC64: Update Transactional Memory and Atomic::cmpxchg code
authormdoerr
Thu, 10 Dec 2015 15:27:16 +0100
changeset 35063 cb24277be2e7
parent 35062 f3e0dee91918
child 35064 f1770c7f39bf
child 35065 b4ff0249c092
8144847: PPC64: Update Transactional Memory and Atomic::cmpxchg code Reviewed-by: stuefe, goetz
hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp
hotspot/src/cpu/ppc/vm/metaspaceShared_ppc.cpp
hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp
hotspot/src/os/aix/vm/libodm_aix.cpp
hotspot/src/os/aix/vm/libodm_aix.hpp
hotspot/src/os/aix/vm/os_aix.cpp
hotspot/src/os/aix/vm/os_aix.hpp
hotspot/src/os_cpu/aix_ppc/vm/atomic_aix_ppc.inline.hpp
hotspot/src/os_cpu/linux_ppc/vm/atomic_linux_ppc.inline.hpp
--- a/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp	Fri Dec 11 09:08:08 2015 +0100
+++ b/hotspot/src/cpu/ppc/vm/globalDefinitions_ppc.hpp	Thu Dec 10 15:27:16 2015 +0100
@@ -36,4 +36,9 @@
 // The PPC CPUs are NOT multiple-copy-atomic.
 #define CPU_NOT_MULTIPLE_COPY_ATOMIC
 
+#if defined(COMPILER2) && defined(AIX)
+// Include Transactional Memory lock eliding optimization
+#define INCLUDE_RTM_OPT 1
+#endif
+
 #endif // CPU_PPC_VM_GLOBALDEFINITIONS_PPC_HPP
--- a/hotspot/src/cpu/ppc/vm/metaspaceShared_ppc.cpp	Fri Dec 11 09:08:08 2015 +0100
+++ b/hotspot/src/cpu/ppc/vm/metaspaceShared_ppc.cpp	Thu Dec 10 15:27:16 2015 +0100
@@ -50,12 +50,29 @@
 // to be 'vtbl_list_size' instances of the vtable in order to
 // differentiate between the 'vtable_list_size' original Klass objects.
 
+#define __ masm->
+
 void MetaspaceShared::generate_vtable_methods(void** vtbl_list,
                                               void** vtable,
                                               char** md_top,
                                               char* md_end,
                                               char** mc_top,
                                               char* mc_end) {
-  Unimplemented();
+  intptr_t vtable_bytes = (num_virtuals * vtbl_list_size) * sizeof(void*);
+  *(intptr_t *)(*md_top) = vtable_bytes;
+  *md_top += sizeof(intptr_t);
+  void** dummy_vtable = (void**)*md_top;
+  *vtable = dummy_vtable;
+  *md_top += vtable_bytes;
+
+  // Get ready to generate dummy methods.
+
+  CodeBuffer cb((unsigned char*)*mc_top, mc_end - *mc_top);
+  MacroAssembler* masm = new MacroAssembler(&cb);
+
+  // There are more general problems with CDS on ppc, so I can not
+  // really test this. But having this instead of Unimplementd() allows
+  // us to pass TestOptionsWithRanges.java.
+  __ unimplemented();
 }
 
--- a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp	Fri Dec 11 09:08:08 2015 +0100
+++ b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp	Thu Dec 10 15:27:16 2015 +0100
@@ -210,12 +210,27 @@
   }
 
   // Adjust RTM (Restricted Transactional Memory) flags.
-  if (!has_tcheck() && UseRTMLocking) {
+  if (UseRTMLocking) {
+    // If CPU or OS are too old:
     // Can't continue because UseRTMLocking affects UseBiasedLocking flag
     // setting during arguments processing. See use_biased_locking().
     // VM_Version_init() is executed after UseBiasedLocking is used
     // in Thread::allocate().
-    vm_exit_during_initialization("RTM instructions are not available on this CPU");
+    if (!has_tcheck()) {
+      vm_exit_during_initialization("RTM instructions are not available on this CPU");
+    }
+    bool os_too_old = true;
+#ifdef AIX
+    if (os::Aix::os_version() >= 0x0701031e) { // at least AIX 7.1.3.30
+      os_too_old = false;
+    }
+#endif
+#ifdef linux
+    // TODO: check kernel version (we currently have too old versions only)
+#endif
+    if (os_too_old) {
+      vm_exit_during_initialization("RTM is not supported on this OS version.");
+    }
   }
 
   if (UseRTMLocking) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/os/aix/vm/libodm_aix.cpp	Thu Dec 10 15:27:16 2015 +0100
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, 2015 SAP AG. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "libodm_aix.hpp"
+#include "misc_aix.hpp"
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <string.h>
+#include "runtime/arguments.hpp"
+
+
+dynamicOdm::dynamicOdm() {
+  const char *libodmname = "/usr/lib/libodm.a(shr_64.o)";
+  _libhandle = dlopen(libodmname, RTLD_MEMBER | RTLD_NOW);
+  if (!_libhandle) {
+    trcVerbose("Couldn't open %s", libodmname);
+    return;
+  }
+  _odm_initialize  = (fun_odm_initialize )dlsym(_libhandle, "odm_initialize" );
+  _odm_set_path    = (fun_odm_set_path   )dlsym(_libhandle, "odm_set_path"   );
+  _odm_mount_class = (fun_odm_mount_class)dlsym(_libhandle, "odm_mount_class");
+  _odm_get_obj     = (fun_odm_get_obj    )dlsym(_libhandle, "odm_get_obj"    );
+  _odm_terminate   = (fun_odm_terminate  )dlsym(_libhandle, "odm_terminate"  );
+  if (!_odm_initialize || !_odm_set_path || !_odm_mount_class || !_odm_get_obj || !_odm_terminate) {
+    trcVerbose("Couldn't find all required odm symbols from %s", libodmname);
+    dlclose(_libhandle);
+    _libhandle = NULL;
+    return;
+  }
+}
+
+dynamicOdm::~dynamicOdm() {
+  if (_libhandle) { dlclose(_libhandle); }
+}
+
+
+void odmWrapper::clean_data() { if (_data) { free(_data); _data = NULL; } }
+
+
+int odmWrapper::class_offset(char *field, bool is_aix_5)
+{
+  assert(has_class(), "initialization");
+  for (int i = 0; i < odm_class()->nelem; i++) {
+    if (strcmp(odm_class()->elem[i].elemname, field) == 0) {
+      int offset = odm_class()->elem[i].offset;
+      if (is_aix_5) { offset += LINK_VAL_OFFSET; }
+      return offset;
+    }
+  }
+  return -1;
+}
+
+
+void odmWrapper::determine_os_kernel_version(uint32_t* p_ver) {
+  int major_aix_version = ((*p_ver) >> 24) & 0xFF,
+      minor_aix_version = ((*p_ver) >> 16) & 0xFF;
+  assert(*p_ver, "must be initialized");
+
+  odmWrapper odm("product", "/usr/lib/objrepos"); // could also use "lpp"
+  if (!odm.has_class()) {
+    trcVerbose("try_determine_os_kernel_version: odm init problem");
+    return;
+  }
+  int voff, roff, moff, foff;
+  bool is_aix_5 = (major_aix_version == 5);
+  voff = odm.class_offset("ver", is_aix_5);
+  roff = odm.class_offset("rel", is_aix_5);
+  moff = odm.class_offset("mod", is_aix_5);
+  foff = odm.class_offset("fix", is_aix_5);
+  if (voff == -1 || roff == -1 || moff == -1 || foff == -1) {
+    trcVerbose("try_determine_os_kernel_version: could not get offsets");
+    return;
+  }
+  if (!odm.retrieve_obj("name='bos.mp64'")) {
+    trcVerbose("try_determine_os_kernel_version: odm_get_obj failed");
+    return;
+  }
+  int version, release, modification, fix_level;
+  do {
+    version      = odm.read_short(voff);
+    release      = odm.read_short(roff);
+    modification = odm.read_short(moff);
+    fix_level    = odm.read_short(foff);
+    trcVerbose("odm found version: %d.%d.%d.%d", version, release, modification, fix_level);
+    if (version >> 8 != 0 || release >> 8 != 0 || modification >> 8 != 0 || fix_level >> 8 != 0) {
+      trcVerbose("8 bit numbers expected");
+      return;
+    }
+  } while (odm.retrieve_obj());
+
+  if (version != major_aix_version || release != minor_aix_version) {
+    trcVerbose("version determined by odm does not match uname");
+    return;
+  }
+  *p_ver = version << 24 | release << 16 | modification << 8 | fix_level;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/os/aix/vm/libodm_aix.hpp	Thu Dec 10 15:27:16 2015 +0100
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, 2015 SAP AG. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+// Encapsulates the libodm library and provides more convenient interfaces.
+
+#ifndef OS_AIX_VM_LIBODM_AIX_HPP
+#define OS_AIX_VM_LIBODM_AIX_HPP
+
+#include <odmi.h>
+
+
+// The purpose of this code is to dynamically load the libodm library
+// instead of statically linking against it. The library is AIX-specific.
+// It only exists on AIX, not on PASE. In order to share binaries
+// between AIX and PASE, we can't directly link against it.
+
+typedef int          (*fun_odm_initialize )(void);
+typedef char*        (*fun_odm_set_path   )(char*);
+typedef CLASS_SYMBOL (*fun_odm_mount_class)(char*);
+typedef void*        (*fun_odm_get_obj    )(CLASS_SYMBOL, char*, void*, int);
+typedef int          (*fun_odm_terminate  )(void);
+
+class dynamicOdm {
+  void *_libhandle;
+ protected:
+  fun_odm_initialize  _odm_initialize;
+  fun_odm_set_path    _odm_set_path;
+  fun_odm_mount_class _odm_mount_class;
+  fun_odm_get_obj     _odm_get_obj;
+  fun_odm_terminate   _odm_terminate;
+ public:
+  dynamicOdm();
+  ~dynamicOdm();
+  bool odm_loaded() {return _libhandle != NULL; }
+};
+
+
+// We provide a more convenient interface for odm access and
+// especially to determine the exact AIX kernel version.
+
+class odmWrapper : private dynamicOdm {
+  CLASS_SYMBOL _odm_class;
+  char *_data;
+  bool _initialized;
+  void clean_data();
+
+ public:
+  // Make sure everything gets initialized and cleaned up properly.
+  explicit odmWrapper(char* odm_class_name, char* odm_path = NULL) : _odm_class((CLASS_SYMBOL)-1),
+                                                                     _data(NULL), _initialized(false) {
+    if (!odm_loaded()) { return; }
+    _initialized = ((*_odm_initialize)() != -1);
+    if (_initialized) {
+      if (odm_path) { (*_odm_set_path)(odm_path); }
+      _odm_class = (*_odm_mount_class)(odm_class_name);
+    }
+  }
+  ~odmWrapper() {
+    if (_initialized) { (*_odm_terminate)(); clean_data(); }
+  }
+
+  CLASS_SYMBOL odm_class() { return _odm_class; }
+  bool has_class() { return odm_class() != (CLASS_SYMBOL)-1; }
+  int class_offset(char *field, bool is_aix_5);
+  char* data() { return _data; }
+
+  char* retrieve_obj(char* name = NULL) {
+    clean_data();
+    char *cnp = (char*)(void*)(*_odm_get_obj)(odm_class(), name, NULL, (name == NULL) ? ODM_NEXT : ODM_FIRST);
+    if (cnp != (char*)-1) { _data = cnp; }
+    return data();
+  }
+
+  int read_short(int offs) {
+    short *addr = (short*)(data() + offs);
+    return *addr;
+  }
+
+  // Determine the exact AIX kernel version as 4 byte value.
+  // The high order 2 bytes must be initialized already. They can be determined by uname.
+  static void determine_os_kernel_version(uint32_t* p_ver);
+};
+
+#endif // OS_AIX_VM_LIBODM_AIX_HPP
--- a/hotspot/src/os/aix/vm/os_aix.cpp	Fri Dec 11 09:08:08 2015 +0100
+++ b/hotspot/src/os/aix/vm/os_aix.cpp	Thu Dec 10 15:27:16 2015 +0100
@@ -38,6 +38,7 @@
 #include "jvm_aix.h"
 #include "libo4.hpp"
 #include "libperfstat_aix.hpp"
+#include "libodm_aix.hpp"
 #include "loadlib_aix.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/filemap.hpp"
@@ -197,9 +198,13 @@
 // -1 = uninitialized, 0 if AIX, 1 if OS/400 pase
 int       os::Aix::_on_pase = -1;
 
-// -1 = uninitialized, otherwise os version in the form 0xMMmm - MM:major, mm:minor
-//  E.g. 0x0601 for  AIX 6.1 or 0x0504 for OS/400 V5R4
-int       os::Aix::_os_version = -1;
+// 0 = uninitialized, otherwise 32 bit number:
+//  0xVVRRTTSS
+//  VV - major version
+//  RR - minor version
+//  TT - tech level, if known, 0 otherwise
+//  SS - service pack, if known, 0 otherwise
+uint32_t  os::Aix::_os_version = 0;
 
 int       os::Aix::_stack_page_size = -1;
 
@@ -358,7 +363,7 @@
 
 // Wrap the function "vmgetinfo" which is not available on older OS releases.
 static int checked_vmgetinfo(void *out, int command, int arg) {
-  if (os::Aix::on_pase() && os::Aix::os_version() < 0x0601) {
+  if (os::Aix::on_pase() && os::Aix::os_version_short() < 0x0601) {
     guarantee(false, "cannot call vmgetinfo on AS/400 older than V6R1");
   }
   return ::vmgetinfo(out, command, arg);
@@ -367,7 +372,7 @@
 // Given an address, returns the size of the page backing that address.
 size_t os::Aix::query_pagesize(void* addr) {
 
-  if (os::Aix::on_pase() && os::Aix::os_version() < 0x0601) {
+  if (os::Aix::on_pase() && os::Aix::os_version_short() < 0x0601) {
     // AS/400 older than V6R1: no vmgetinfo here, default to 4K
     return SIZE_4K;
   }
@@ -1491,6 +1496,10 @@
   st->print(name.machine);
   st->cr();
 
+  uint32_t ver = os::Aix::os_version();
+  st->print_cr("AIX kernel version %u.%u.%u.%u",
+               (ver >> 24) & 0xFF, (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
+
   // rlimit
   st->print("rlimit:");
   struct rlimit rlim;
@@ -4255,7 +4264,7 @@
 // one of Aix::on_pase(), Aix::os_version() static
 void os::Aix::initialize_os_info() {
 
-  assert(_on_pase == -1 && _os_version == -1, "already called.");
+  assert(_on_pase == -1 && _os_version == 0, "already called.");
 
   struct utsname uts;
   memset(&uts, 0, sizeof(uts));
@@ -4271,28 +4280,34 @@
     assert(major > 0, "invalid OS version");
     const int minor = atoi(uts.release);
     assert(minor > 0, "invalid OS release");
-    _os_version = (major << 8) | minor;
+    _os_version = (major << 24) | (minor << 16);
+    char ver_str[20] = {0};
+    char *name_str = "unknown OS";
     if (strcmp(uts.sysname, "OS400") == 0) {
       // We run on AS/400 PASE. We do not support versions older than V5R4M0.
       _on_pase = 1;
-      if (_os_version < 0x0504) {
+      if (os_version_short() < 0x0504) {
         trcVerbose("OS/400 releases older than V5R4M0 not supported.");
         assert(false, "OS/400 release too old.");
-      } else {
-        trcVerbose("We run on OS/400 (pase) V%dR%d", major, minor);
       }
+      name_str = "OS/400 (pase)";
+      jio_snprintf(ver_str, sizeof(ver_str), "%u.%u", major, minor);
     } else if (strcmp(uts.sysname, "AIX") == 0) {
       // We run on AIX. We do not support versions older than AIX 5.3.
       _on_pase = 0;
-      if (_os_version < 0x0503) {
+      // Determine detailed AIX version: Version, Release, Modification, Fix Level.
+      odmWrapper::determine_os_kernel_version(&_os_version);
+      if (os_version_short() < 0x0503) {
         trcVerbose("AIX release older than AIX 5.3 not supported.");
         assert(false, "AIX release too old.");
-      } else {
-        trcVerbose("We run on AIX %d.%d", major, minor);
       }
+      name_str = "AIX";
+      jio_snprintf(ver_str, sizeof(ver_str), "%u.%u.%u.%u",
+                   major, minor, (_os_version >> 8) & 0xFF, _os_version & 0xFF);
     } else {
-      assert(false, "unknown OS");
+      assert(false, name_str);
     }
+    trcVerbose("We run on %s %s", name_str, ver_str);
   }
 
   guarantee(_on_pase != -1 && _os_version, "Could not determine AIX/OS400 release");
@@ -4357,7 +4372,7 @@
 
   p = ::getenv("LDR_CNTRL");
   trcVerbose("LDR_CNTRL=%s.", p ? p : "<unset>");
-  if (os::Aix::on_pase() && os::Aix::os_version() == 0x0701) {
+  if (os::Aix::on_pase() && os::Aix::os_version_short() == 0x0701) {
     if (p && ::strstr(p, "TEXTPSIZE")) {
       trcVerbose("*** WARNING - LDR_CNTRL contains TEXTPSIZE. "
         "you may experience hangs or crashes on OS/400 V7R1.");
@@ -5016,7 +5031,7 @@
 }
 #endif
 
-bool os::start_debugging(char *buf, int buflen) {
+bool os::start_debugging(char *buf, int buflen) {
   int len = (int)strlen(buf);
   char *p = &buf[len];
 
--- a/hotspot/src/os/aix/vm/os_aix.hpp	Fri Dec 11 09:08:08 2015 +0100
+++ b/hotspot/src/os/aix/vm/os_aix.hpp	Thu Dec 10 15:27:16 2015 +0100
@@ -55,15 +55,12 @@
   // -1 = uninitialized, 0 = AIX, 1 = OS/400 (PASE)
   static int _on_pase;
 
-  // -1 = uninitialized, otherwise 16 bit number:
+  // 0 = uninitialized, otherwise 16 bit number:
   //  lower 8 bit - minor version
   //  higher 8 bit - major version
   //  For AIX, e.g. 0x0601 for AIX 6.1
   //  for OS/400 e.g. 0x0504 for OS/400 V5R4
-  static int _os_version;
-
-  // 4 Byte kernel version: Version, Release, Tech Level, Service Pack.
-  static unsigned int _os_kernel_version;
+  static uint32_t _os_version;
 
   // -1 = uninitialized,
   //  0 - SPEC1170 not requested (XPG_SUS_ENV is OFF or not set)
@@ -175,32 +172,31 @@
     return _on_pase ? false : true;
   }
 
-  // -1 = uninitialized, otherwise 16 bit number:
+  // Get 4 byte AIX kernel version number:
+  // highest 2 bytes: Version, Release
+  // if available: lowest 2 bytes: Tech Level, Service Pack.
+  static uint32_t os_version() {
+    assert(_os_version != 0, "not initialized");
+    return _os_version;
+  }
+
+  // 0 = uninitialized, otherwise 16 bit number:
   // lower 8 bit - minor version
   // higher 8 bit - major version
   // For AIX, e.g. 0x0601 for AIX 6.1
   // for OS/400 e.g. 0x0504 for OS/400 V5R4
-  static int os_version () {
-    assert(_os_version != -1, "not initialized");
-    return _os_version;
-  }
-
-  // Get 4 byte AIX kernel version number:
-  // highest 2 bytes: Version, Release
-  // if available: lowest 2 bytes: Tech Level, Service Pack.
-  static unsigned int os_kernel_version() {
-    if (_os_kernel_version) return _os_kernel_version;
-    return os_version() << 16;
+  static int os_version_short() {
+    return os_version() >> 16;
   }
 
   // Convenience method: returns true if running on PASE V5R4 or older.
   static bool on_pase_V5R4_or_older() {
-    return on_pase() && os_version() <= 0x0504;
+    return on_pase() && os_version_short() <= 0x0504;
   }
 
   // Convenience method: returns true if running on AIX 5.3 or older.
   static bool on_aix_53_or_older() {
-    return on_aix() && os_version() <= 0x0503;
+    return on_aix() && os_version_short() <= 0x0503;
   }
 
   // Returns true if we run in SPEC1170 compliant mode (XPG_SUS_ENV=ON).
--- a/hotspot/src/os_cpu/aix_ppc/vm/atomic_aix_ppc.inline.hpp	Fri Dec 11 09:08:08 2015 +0100
+++ b/hotspot/src/os_cpu/aix_ppc/vm/atomic_aix_ppc.inline.hpp	Thu Dec 10 15:27:16 2015 +0100
@@ -291,6 +291,71 @@
   return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
 }
 
+#define VM_HAS_SPECIALIZED_CMPXCHG_BYTE
+inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
+
+  // Note that cmpxchg guarantees a two-way memory barrier across
+  // the cmpxchg, so it's really a a 'fence_cmpxchg_acquire'
+  // (see atomic.hpp).
+
+  // Using 32 bit internally.
+  volatile int *dest_base = (volatile int*)((uintptr_t)dest & ~3);
+
+#ifdef VM_LITTLE_ENDIAN
+  const unsigned int shift_amount        = ((uintptr_t)dest & 3) * 8;
+#else
+  const unsigned int shift_amount        = ((~(uintptr_t)dest) & 3) * 8;
+#endif
+  const unsigned int masked_compare_val  = ((unsigned int)(unsigned char)compare_value),
+                     masked_exchange_val = ((unsigned int)(unsigned char)exchange_value),
+                     xor_value           = (masked_compare_val ^ masked_exchange_val) << shift_amount;
+
+  unsigned int old_value, value32;
+
+  __asm__ __volatile__ (
+    /* fence */
+    strasm_sync
+    /* simple guard */
+    "   lbz     %[old_value], 0(%[dest])                  \n"
+    "   cmpw    %[masked_compare_val], %[old_value]       \n"
+    "   bne-    2f                                        \n"
+    /* atomic loop */
+    "1:                                                   \n"
+    "   lwarx   %[value32], 0, %[dest_base]               \n"
+    /* extract byte and compare */
+    "   srd     %[old_value], %[value32], %[shift_amount] \n"
+    "   clrldi  %[old_value], %[old_value], 56            \n"
+    "   cmpw    %[masked_compare_val], %[old_value]       \n"
+    "   bne-    2f                                        \n"
+    /* replace byte and try to store */
+    "   xor     %[value32], %[xor_value], %[value32]      \n"
+    "   stwcx.  %[value32], 0, %[dest_base]               \n"
+    "   bne-    1b                                        \n"
+    /* acquire */
+    strasm_sync
+    /* exit */
+    "2:                                                   \n"
+    /* out */
+    : [old_value]           "=&r"   (old_value),
+      [value32]             "=&r"   (value32),
+                            "=m"    (*dest),
+                            "=m"    (*dest_base)
+    /* in */
+    : [dest]                "b"     (dest),
+      [dest_base]           "b"     (dest_base),
+      [shift_amount]        "r"     (shift_amount),
+      [masked_compare_val]  "r"     (masked_compare_val),
+      [xor_value]           "r"     (xor_value),
+                            "m"     (*dest),
+                            "m"     (*dest_base)
+    /* clobber */
+    : "cc",
+      "memory"
+    );
+
+  return (jbyte)(unsigned char)old_value;
+}
+
 inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) {
 
   // Note that cmpxchg guarantees a two-way memory barrier across
--- a/hotspot/src/os_cpu/linux_ppc/vm/atomic_linux_ppc.inline.hpp	Fri Dec 11 09:08:08 2015 +0100
+++ b/hotspot/src/os_cpu/linux_ppc/vm/atomic_linux_ppc.inline.hpp	Thu Dec 10 15:27:16 2015 +0100
@@ -291,6 +291,71 @@
   return (void*)xchg_ptr((intptr_t)exchange_value, (volatile intptr_t*)dest);
 }
 
+#define VM_HAS_SPECIALIZED_CMPXCHG_BYTE
+inline jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
+
+  // Note that cmpxchg guarantees a two-way memory barrier across
+  // the cmpxchg, so it's really a a 'fence_cmpxchg_acquire'
+  // (see atomic.hpp).
+
+  // Using 32 bit internally.
+  volatile int *dest_base = (volatile int*)((uintptr_t)dest & ~3);
+
+#ifdef VM_LITTLE_ENDIAN
+  const unsigned int shift_amount        = ((uintptr_t)dest & 3) * 8;
+#else
+  const unsigned int shift_amount        = ((~(uintptr_t)dest) & 3) * 8;
+#endif
+  const unsigned int masked_compare_val  = ((unsigned int)(unsigned char)compare_value),
+                     masked_exchange_val = ((unsigned int)(unsigned char)exchange_value),
+                     xor_value           = (masked_compare_val ^ masked_exchange_val) << shift_amount;
+
+  unsigned int old_value, value32;
+
+  __asm__ __volatile__ (
+    /* fence */
+    strasm_sync
+    /* simple guard */
+    "   lbz     %[old_value], 0(%[dest])                  \n"
+    "   cmpw    %[masked_compare_val], %[old_value]       \n"
+    "   bne-    2f                                        \n"
+    /* atomic loop */
+    "1:                                                   \n"
+    "   lwarx   %[value32], 0, %[dest_base]               \n"
+    /* extract byte and compare */
+    "   srd     %[old_value], %[value32], %[shift_amount] \n"
+    "   clrldi  %[old_value], %[old_value], 56            \n"
+    "   cmpw    %[masked_compare_val], %[old_value]       \n"
+    "   bne-    2f                                        \n"
+    /* replace byte and try to store */
+    "   xor     %[value32], %[xor_value], %[value32]      \n"
+    "   stwcx.  %[value32], 0, %[dest_base]               \n"
+    "   bne-    1b                                        \n"
+    /* acquire */
+    strasm_sync
+    /* exit */
+    "2:                                                   \n"
+    /* out */
+    : [old_value]           "=&r"   (old_value),
+      [value32]             "=&r"   (value32),
+                            "=m"    (*dest),
+                            "=m"    (*dest_base)
+    /* in */
+    : [dest]                "b"     (dest),
+      [dest_base]           "b"     (dest_base),
+      [shift_amount]        "r"     (shift_amount),
+      [masked_compare_val]  "r"     (masked_compare_val),
+      [xor_value]           "r"     (xor_value),
+                            "m"     (*dest),
+                            "m"     (*dest_base)
+    /* clobber */
+    : "cc",
+      "memory"
+    );
+
+  return (jbyte)(unsigned char)old_value;
+}
+
 inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) {
 
   // Note that cmpxchg guarantees a two-way memory barrier across