Path: blob/master/src/hotspot/os_cpu/linux_s390/atomic_linux_s390.hpp
40951 views
/*1* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2016, 2019 SAP SE. All rights reserved.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*23*/2425#ifndef OS_CPU_LINUX_S390_ATOMIC_LINUX_S390_HPP26#define OS_CPU_LINUX_S390_ATOMIC_LINUX_S390_HPP2728#include "runtime/atomic.hpp"29#include "runtime/os.hpp"30#include "runtime/vm_version.hpp"3132// Note that the compare-and-swap instructions on System z perform33// a serialization function before the storage operand is fetched34// and again after the operation is completed.35//36// Used constraint modifiers:37// = write-only access: Value on entry to inline-assembler code irrelevant.38// + read/write access: Value on entry is used; on exit value is changed.39// read-only access: Value on entry is used and never changed.40// & early-clobber access: Might be modified before all read-only operands41// have been used.42// a address register operand (not GR0).43// d general register operand (including GR0)44// Q memory operand w/o index register.45// 0..9 operand reference (by operand position).46// Used for operands that fill multiple roles. One example would be a47// write-only operand receiving its initial value from a read-only operand.48// Refer to cmpxchg(..) operand #0 and variable cmp_val for a real-life example.49//5051// On System z, all store operations are atomic if the address where the data is stored into52// is an integer multiple of the data length. Furthermore, all stores are ordered:53// a store which occurs conceptually before another store becomes visible to other CPUs54// before the other store becomes visible.5556//------------57// Atomic::add58//------------59// These methods force the value in memory to be augmented by the passed increment.60// Both, memory value and increment, are treated as 32bit signed binary integers.61// No overflow exceptions are recognized, and the condition code does not hold62// information about the value in memory.63//64// The value in memory is updated by using a compare-and-swap instruction. The65// instruction is retried as often as required.66//67// The return value of the method is the value that was successfully stored. At the68// time the caller receives back control, the value in memory may have changed already.6970// New atomic operations only include specific-operand-serialization, not full71// memory barriers. We can use the Fast-BCR-Serialization Facility for them.72inline void z196_fast_sync() {73__asm__ __volatile__ ("bcr 14, 0" : : : "memory");74}7576template<size_t byte_size>77struct Atomic::PlatformAdd {78template<typename D, typename I>79D add_and_fetch(D volatile* dest, I add_value, atomic_memory_order order) const;8081template<typename D, typename I>82D fetch_and_add(D volatile* dest, I add_value, atomic_memory_order order) const {83return add_and_fetch(dest, add_value, order) - add_value;84}85};8687template<>88template<typename D, typename I>89inline D Atomic::PlatformAdd<4>::add_and_fetch(D volatile* dest, I inc,90atomic_memory_order order) const {91STATIC_ASSERT(4 == sizeof(I));92STATIC_ASSERT(4 == sizeof(D));9394D old, upd;9596if (VM_Version::has_LoadAndALUAtomicV1()) {97if (order == memory_order_conservative) { z196_fast_sync(); }98__asm__ __volatile__ (99" LGFR 0,%[inc] \n\t" // save increment100" LA 3,%[mem] \n\t" // force data address into ARG2101// " LAA %[upd],%[inc],%[mem] \n\t" // increment and get old value102// " LAA 2,0,0(3) \n\t" // actually coded instruction103" .byte 0xeb \n\t" // LAA main opcode104" .byte 0x20 \n\t" // R1,R3105" .byte 0x30 \n\t" // R2,disp1106" .byte 0x00 \n\t" // disp2,disp3107" .byte 0x00 \n\t" // disp4,disp5108" .byte 0xf8 \n\t" // LAA minor opcode109" AR 2,0 \n\t" // calc new value in register110" LR %[upd],2 \n\t" // move to result register111//---< outputs >---112: [upd] "=&d" (upd) // write-only, updated counter value113, [mem] "+Q" (*dest) // read/write, memory to be updated atomically114//---< inputs >---115: [inc] "a" (inc) // read-only.116//---< clobbered >---117: "cc", "r0", "r2", "r3", "memory"118);119if (order == memory_order_conservative) { z196_fast_sync(); }120} else {121__asm__ __volatile__ (122" LLGF %[old],%[mem] \n\t" // get old value123"0: LA %[upd],0(%[inc],%[old]) \n\t" // calc result124" CS %[old],%[upd],%[mem] \n\t" // try to xchg res with mem125" JNE 0b \n\t" // no success? -> retry126//---< outputs >---127: [old] "=&a" (old) // write-only, old counter value128, [upd] "=&d" (upd) // write-only, updated counter value129, [mem] "+Q" (*dest) // read/write, memory to be updated atomically130//---< inputs >---131: [inc] "a" (inc) // read-only.132//---< clobbered >---133: "cc", "memory"134);135}136137return upd;138}139140141template<>142template<typename D, typename I>143inline D Atomic::PlatformAdd<8>::add_and_fetch(D volatile* dest, I inc,144atomic_memory_order order) const {145STATIC_ASSERT(8 == sizeof(I));146STATIC_ASSERT(8 == sizeof(D));147148D old, upd;149150if (VM_Version::has_LoadAndALUAtomicV1()) {151if (order == memory_order_conservative) { z196_fast_sync(); }152__asm__ __volatile__ (153" LGR 0,%[inc] \n\t" // save increment154" LA 3,%[mem] \n\t" // force data address into ARG2155// " LAAG %[upd],%[inc],%[mem] \n\t" // increment and get old value156// " LAAG 2,0,0(3) \n\t" // actually coded instruction157" .byte 0xeb \n\t" // LAA main opcode158" .byte 0x20 \n\t" // R1,R3159" .byte 0x30 \n\t" // R2,disp1160" .byte 0x00 \n\t" // disp2,disp3161" .byte 0x00 \n\t" // disp4,disp5162" .byte 0xe8 \n\t" // LAA minor opcode163" AGR 2,0 \n\t" // calc new value in register164" LGR %[upd],2 \n\t" // move to result register165//---< outputs >---166: [upd] "=&d" (upd) // write-only, updated counter value167, [mem] "+Q" (*dest) // read/write, memory to be updated atomically168//---< inputs >---169: [inc] "a" (inc) // read-only.170//---< clobbered >---171: "cc", "r0", "r2", "r3", "memory"172);173if (order == memory_order_conservative) { z196_fast_sync(); }174} else {175__asm__ __volatile__ (176" LG %[old],%[mem] \n\t" // get old value177"0: LA %[upd],0(%[inc],%[old]) \n\t" // calc result178" CSG %[old],%[upd],%[mem] \n\t" // try to xchg res with mem179" JNE 0b \n\t" // no success? -> retry180//---< outputs >---181: [old] "=&a" (old) // write-only, old counter value182, [upd] "=&d" (upd) // write-only, updated counter value183, [mem] "+Q" (*dest) // read/write, memory to be updated atomically184//---< inputs >---185: [inc] "a" (inc) // read-only.186//---< clobbered >---187: "cc", "memory"188);189}190191return upd;192}193194195//-------------196// Atomic::xchg197//-------------198// These methods force the value in memory to be replaced by the new value passed199// in as argument.200//201// The value in memory is replaced by using a compare-and-swap instruction. The202// instruction is retried as often as required. This makes sure that the new203// value can be seen, at least for a very short period of time, by other CPUs.204//205// If we would use a normal "load(old value) store(new value)" sequence,206// the new value could be lost unnoticed, due to a store(new value) from207// another thread.208//209// The return value is the (unchanged) value from memory as it was when the210// replacement succeeded.211template<>212template<typename T>213inline T Atomic::PlatformXchg<4>::operator()(T volatile* dest,214T exchange_value,215atomic_memory_order unused) const {216STATIC_ASSERT(4 == sizeof(T));217T old;218219__asm__ __volatile__ (220" LLGF %[old],%[mem] \n\t" // get old value221"0: CS %[old],%[upd],%[mem] \n\t" // try to xchg upd with mem222" JNE 0b \n\t" // no success? -> retry223//---< outputs >---224: [old] "=&d" (old) // write-only, prev value irrelevant225, [mem] "+Q" (*dest) // read/write, memory to be updated atomically226//---< inputs >---227: [upd] "d" (exchange_value) // read-only, value to be written to memory228//---< clobbered >---229: "cc", "memory"230);231232return old;233}234235template<>236template<typename T>237inline T Atomic::PlatformXchg<8>::operator()(T volatile* dest,238T exchange_value,239atomic_memory_order unused) const {240STATIC_ASSERT(8 == sizeof(T));241T old;242243__asm__ __volatile__ (244" LG %[old],%[mem] \n\t" // get old value245"0: CSG %[old],%[upd],%[mem] \n\t" // try to xchg upd with mem246" JNE 0b \n\t" // no success? -> retry247//---< outputs >---248: [old] "=&d" (old) // write-only, init from memory249, [mem] "+Q" (*dest) // read/write, memory to be updated atomically250//---< inputs >---251: [upd] "d" (exchange_value) // read-only, value to be written to memory252//---< clobbered >---253: "cc", "memory"254);255256return old;257}258259//----------------260// Atomic::cmpxchg261//----------------262// These methods compare the value in memory with a given compare value.263// If both values compare equal, the value in memory is replaced with264// the exchange value.265//266// The value in memory is compared and replaced by using a compare-and-swap267// instruction. The instruction is NOT retried (one shot only).268//269// The return value is the (unchanged) value from memory as it was when the270// compare-and-swap instruction completed. A successful exchange operation271// is indicated by (return value == compare_value). If unsuccessful, a new272// exchange value can be calculated based on the return value which is the273// latest contents of the memory location.274//275// Inspecting the return value is the only way for the caller to determine276// if the compare-and-swap instruction was successful:277// - If return value and compare value compare equal, the compare-and-swap278// instruction was successful and the value in memory was replaced by the279// exchange value.280// - If return value and compare value compare unequal, the compare-and-swap281// instruction was not successful. The value in memory was left unchanged.282//283// The s390 processors always fence before and after the csg instructions.284// Thus we ignore the memory ordering argument. The docu says: "A serialization285// function is performed before the operand is fetched and again after the286// operation is completed."287288// No direct support for cmpxchg of bytes; emulate using int.289template<>290struct Atomic::PlatformCmpxchg<1> : Atomic::CmpxchgByteUsingInt {};291292template<>293template<typename T>294inline T Atomic::PlatformCmpxchg<4>::operator()(T volatile* dest,295T cmp_val,296T xchg_val,297atomic_memory_order unused) const {298STATIC_ASSERT(4 == sizeof(T));299T old;300301__asm__ __volatile__ (302" CS %[old],%[upd],%[mem] \n\t" // Try to xchg upd with mem.303// outputs304: [old] "=&d" (old) // Write-only, prev value irrelevant.305, [mem] "+Q" (*dest) // Read/write, memory to be updated atomically.306// inputs307: [upd] "d" (xchg_val)308, "0" (cmp_val) // Read-only, initial value for [old] (operand #0).309// clobbered310: "cc", "memory"311);312313return old;314}315316template<>317template<typename T>318inline T Atomic::PlatformCmpxchg<8>::operator()(T volatile* dest,319T cmp_val,320T xchg_val,321atomic_memory_order unused) const {322STATIC_ASSERT(8 == sizeof(T));323T old;324325__asm__ __volatile__ (326" CSG %[old],%[upd],%[mem] \n\t" // Try to xchg upd with mem.327// outputs328: [old] "=&d" (old) // Write-only, prev value irrelevant.329, [mem] "+Q" (*dest) // Read/write, memory to be updated atomically.330// inputs331: [upd] "d" (xchg_val)332, "0" (cmp_val) // Read-only, initial value for [old] (operand #0).333// clobbered334: "cc", "memory"335);336337return old;338}339340template<size_t byte_size>341struct Atomic::PlatformOrderedLoad<byte_size, X_ACQUIRE>342{343template <typename T>344T operator()(const volatile T* p) const { T t = *p; OrderAccess::acquire(); return t; }345};346347#endif // OS_CPU_LINUX_S390_ATOMIC_LINUX_S390_HPP348349350