/* * Copyright (c) 2017, Oracle and/or its affiliates. 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. * */ #ifndef SHARE_VM_RUNTIME_ACCESS_HPP #define SHARE_VM_RUNTIME_ACCESS_HPP #include "oops/oopsHierarchy.hpp" #include "utilities/traits/decay.hpp" // = GENERAL = // Access is an API for performing memory accesses to the heap. Each access can have a number of "decorators". // A decorator is an attribute or property that affects the way a memory access is performed in some way. // There are different groups of decorators. Some have to do with memory ordering, others to do with // e.g. strength of references, strength of GC barriers or whether compression should be applied or not. // Some decorators are set at buildtime, such as whether primitives require GC barriers or not, others // at callsites such as whether an access is in the heap or not, and others are resolved at runtime // such as GC-specific barriers and encoding/decoding compressed oops. // By pipelining handling of these decorators, the design of the Access API allows separation of concern // over the different orthogonal concerns of decorators, while providing a powerful unified way of // expressing these orthogonal properties in a unified way. // = THE PIPELINE = // The design of accesses going through the Access interface consists of the following stages: // 1. The interface connecting inferred types into AccessInternal. // It uses proxy classes to infer return types when necessary. // 2. Type decaying. This stage gets rid of CV qualifiers and reference types. // It additionally sets volatile decorators for volatile accesses. // 3. Type reduction. This stage merges the type of the address with the type of the value // This might entail infering compressed oops need to be converted. // 4. Decorator rule inferring. This stage adds decorators according to well defined rules. // 5. Add buildtime decorators with buildtime capabilities // 6. Figure out if a dynamic dispatch is necessary, and potentially hardwire basic and raw accesses // 7. If not hardwired, make a dynamic dispatch through a function pointer that resolves itself at runtime // The resolved function pointer is looked up in accessConfigure.inline.hpp // 8. GC barriers can override any not hardwired access calls in the resolved barriers // == OPERATIONS == // * load: Load a value from an address. // * load_at: Load a value from an internal pointer relative to a base object. // * store: Store a value at an address. // * store_at: Store a value in an internal pointer relative to a base object. // * cas: Atomically Compare-And-Swap a new value at an address if previous value matched the compared value. // * cas_at: Atomically Compare-And-Swap a new value at an internal pointer address if previous value matched the compared value. // * swap: Atomically swap a new value at an address if previous value matched the compared value. // * swap_at: Atomically swap a new value at an internal pointer address if previous value matched the compared value. // == MEMORY ORDERING == // The memory ordering decorators can be described in the following way: // === Decorator Rules === // The different types of memory ordering guarantees have a strict order of strength. // Explicitly specifying the stronger ordering implies that the guarantees of the weaker // property holds too. // The following rules are applied to enforce this: // * MO_SEQ_CST -> MO_ACQUIRE // * MO_SEQ_CST -> MO_RELEASE // * MO_ACQUIRE -> MO_ATOMIC // * MO_RELEASE -> MO_ATOMIC // * MO_ATOMIC -> MO_VOLATILE // * !MO_VOLATILE -> MO_RELAXED // === Stores === // * MO_SEQ_CST: Sequentially consistent stores. // - The stores are observed in the same order by MO_SEQ_CST loads on other processors // - Preceding loads and stores in program order are not reordered with subsequent loads and stores in program order. // - Guarantees from releasing stores hold. // * MO_RELEASE: Releasing stores. // - The releasing store will make its preceding memory accesses observable to memory accesses // subsequent to an acquiring load observing this releasing store. // - Guarantees from relaxed stores hold. // * MO_ATOMIC: Relaxed atomic stores. // - The stores are atomic. // - Guarantees from volatile stores hold. // * MO_VOLATILE: Volatile stores. // - The stores are not reordered by the compiler (but possibly the HW) w.r.t. other // volatile accesses in program order (but possibly non-volatile accesses). // * MO_RELAXED (Default): No guarantees // - The compiler and hardware are free to reorder aggressively. And they will. // === Loads === // * MO_SEQ_CST: Sequentially consistent loads. // - These loads observe MO_SEQ_CST stores in the same order on other processors // - Preceding loads and stores in program order are not reordered with subsequent loads and stores in program order. // - Guarantees from acquiring loads hold. // * MO_ACQUIRE: Acquiring loads. // - An acquiring load will make subsequent memory accesses observe the memory accesses // preceding the releasing store that the acquiring load observed. // - Guarantees from relaxed stores hold. // * MO_ATOMIC: Relaxed atomic loads. // - The stores are atomic. // - Guarantees from volatile stores hold. // * MO_VOLATILE: Volatile loads. // - The loads are not reordered by the compiler (but possibly the HW) w.r.t. other // volatile accesses in program order (but possibly non-volatile accesses). // * MO_RELAXED (Default): No guarantees // - The compiler and hardware are free to reorder aggressively. And they will. // === CAS === // * MO_SEQ_CST: Sequentially consistent CAS. // - Guarantees from MO_SEQ_CST loads and MO_SEQ_CST stores hold unconditionally. // * MO_ATOMIC: Atomic but relaxed CAS. // - Guarantees from MO_ATOMIC loads and MO_ATOMIC stores hold unconditionally. // === Swap === // * MO_SEQ_CST: Sequentially consistent swap. // - Guarantees from MO_SEQ_CST loads and MO_SEQ_CST stores hold. // * MO_ATOMIC: Atomic but relaxed CAS. // - Guarantees from MO_ATOMIC loads and MO_ATOMIC stores hold. // // == GC DECORATORS == // These decorators describe GC-related properties. // === Access Location === // Accesses can take place on in e.g. the heap, old or young generation and different native roots. // The location is important to the GC as it may imply different actions. The following decorators are used: // * ACCESS_ON_HEAP: The access is performed in the heap. Many barriers such as card marking will // be omitted if this decorator is not set. // * ACCESS_ON_NMETHOD: The access is performed on an nmethod. This decorator may be recognized by GCs // that track references from nmethods to the heap in special ways. // * ACCESS_ON_YOUNG: The access is guaranteed to be performed on an object in the young generation // (if the GC is generational). Recognizing this decorator potentially allows eliding unnecessary GC // barriers on such young objects. // Default: EMPTY_DECORATOR // === Heap Access Type === // * ACCESS_ON_ARRAY: The access is performed on a heap allocated array. This is sometimes a special case // for some GCs. // === Reference Strength === // These decorators only apply to accesses on oop-like types (oop/narrowOop). // * GC_ACCESS_ON_STRONG: Memory access is performed on a strongly reachable reference. // * GC_ACCESS_ON_WEAK: The memory access is performed on a weakly reachable reference. // * GC_ACCESS_ON_PHANTOM: The memory access is performed on a phantomly reachable reference. // * Default (no explicit reference strength specified): GC_ACCESS_ON_STRONG // === Barrier Strength === // * ACCESS_RAW: The access will translate into a raw memory access, hence ignoring all concerns // except memory ordering. This will bypass all runtime checks in the pipeline. // * ACCESS_BASIC: The accesses will ignore all GC-specific concerns except compressed oops, hence // bypassing as much as possible in the normal pipeline in the most efficient way possible. // - Accesses on oop* translate to raw memory accesses without runtime checks // - Accesses on narrowOop* translate to encoded/decoded memory accesses without runtime checks // - Accesses on HeapWord* translate to a runtime check choosing one of the above // - Accesses on other types translate to raw memory accesses without runtime checks // * ACCESS_WEAK: The barrier is weak, meaning that it will not perform GC bookkeeping such as keeping references // alive, regardless of the type of reference being accessed. It will however perform the memory access // in a consistent way w.r.t. e.g. concurrent compaction if applicable, so that the right field is being // accessed. // Default: Normal accesses do not specify any of the decorators above. They will be resolved at runtime // according to the needs of the GC. If a GC requiring barriers on primitives is used, even primitive // types will have their accessors resolved at runtime. // Use of this decorator is for GC-specific code where hardwiring is possible, but should not be used in runtime code. // == VALUE DECORATORS == // * VALUE_NOT_NULL: This property can make certain barriers faster such as compressing oops. // * DEST_NOT_INITIALIZED: This property can be important to e.g. SATB barriers by marking that the previous value // uninitialized nonsense rather than a real value. typedef uint64_t DecoratorSet; enum Decorator { EMPTY_DECORATOR = UCONST64(0), DECORATOR_DEFAULT = EMPTY_DECORATOR, BT_BARRIER_ON_PRIMITIVES = UCONST64(1) << 0, BT_HAS_BUILDTIME_DECORATOR = UCONST64(1) << 1, // Runtime decorators RT_USE_COMPRESSED_OOPS = UCONST64(1) << 2, RT_HAS_RUNTIME_DECORATOR = UCONST64(1) << 3, // Memory ordering decorators MO_RELAXED = UCONST64(1) << 4, MO_VOLATILE = UCONST64(1) << 5, MO_ATOMIC = UCONST64(1) << 6, MO_ACQUIRE = UCONST64(1) << 7, MO_RELEASE = UCONST64(1) << 8, MO_SEQ_CST = UCONST64(1) << 9, // Access decorators ACCESS_RAW = UCONST64(1) << 10, // Raw accesses with specified memory semantics ACCESS_BASIC = UCONST64(1) << 11, // Includes compressed oops ACCESS_WEAK = UCONST64(1) << 12, // Do not keep alive // Actor decorators ACCESS_BY_MUTATOR = UCONST64(1) << 13, ACCESS_BY_STW_GC = UCONST64(1) << 14, ACCESS_BY_CONCURRENT_GC = UCONST64(1) << 15, // GC decorators GC_ACCESS_ON_STRONG = UCONST64(1) << 16, GC_ACCESS_ON_WEAK = UCONST64(1) << 17, GC_ACCESS_ON_PHANTOM = UCONST64(1) << 18, ACCESS_ON_HEAP = UCONST64(1) << 19, ACCESS_ON_ROOT = UCONST64(1) << 20, ACCESS_ON_YOUNG = UCONST64(1) << 21, ACCESS_ON_ARRAY = UCONST64(1) << 22, ACCESS_ON_ANONYMOUS = UCONST64(1) << 23, ACCESS_ON_NMETHOD = UCONST64(1) << 24, ACCESS_ON_KLASS = UCONST64(1) << 25, // Barriers for compressed oops GC_CONVERT_COMPRESSED_OOP = UCONST64(1) << 26, // Value decorators VALUE_NOT_NULL = UCONST64(1) << 27, VALUE_IS_OOP = UCONST64(1) << 28, // Destination address decorators DEST_NOT_INITIALIZED = UCONST64(1) << 29, DEST_COVARIANT = UCONST64(1) << 30, DEST_CONTRAVARIANT = UCONST64(1) << 31, DEST_CONJOINT = UCONST64(1) << 32, DEST_DISJOINT = UCONST64(1) << 33, COPY_ARRAYOF = UCONST64(1) << 34, ACCESS_ARRAYCOPY = UCONST64(1) << 35, ACCESS_ATOMIC = UCONST64(1) << 36, ACCESS_ALIGNED = UCONST64(1) << 37, DECORATOR_END = ACCESS_ALIGNED }; template struct HasDecorator { enum { value = (decorators & (DecoratorSet)decorator) != 0 }; }; template struct DecoratorIntersection { enum { value = set1 & set2 }; }; template struct DecoratorTest { enum { HAS_BUILDTIME_DECORATOR = HasDecorator::value, HAS_RUNTIME_DECORATOR = HasDecorator::value, HAS_BARRIER_ON_PRIMITIVES = HasDecorator::value, HAS_USE_COMPRESSED_OOPS = HasDecorator::value, HAS_ACCESS_RAW = HasDecorator::value, HAS_ACCESS_BASIC = HasDecorator::value, HAS_ACCESS_WEAK = HasDecorator::value, HAS_ACCESS_ON_HEAP = HasDecorator::value, HAS_ACCESS_ON_ARRAY = HasDecorator::value, HAS_ACCESS_ON_NMETHOD = HasDecorator::value, HAS_ACCESS_ON_KLASS = HasDecorator::value, HAS_ACCESS_ON_ROOT = HasDecorator::value, HAS_ACCESS_ON_ANONYMOUS = HasDecorator::value, HAS_ACCESS_ON_YOUNG = HasDecorator::value, HAS_MO_RELAXED = HasDecorator::value, HAS_MO_VOLATILE = HasDecorator::value, HAS_MO_ATOMIC = HasDecorator::value, HAS_MO_ACQUIRE = HasDecorator::value, HAS_MO_RELEASE = HasDecorator::value, HAS_MO_SEQ_CST = HasDecorator::value, HAS_GC_CONVERT_COMPRESSED_OOP = HasDecorator::value, NEEDS_OOP_COMPRESS = DecoratorTest::HAS_GC_CONVERT_COMPRESSED_OOP && DecoratorTest::HAS_USE_COMPRESSED_OOPS, HAS_GC_ACCESS_ON_STRONG = HasDecorator::value, HAS_GC_ACCESS_ON_WEAK = HasDecorator::value, HAS_GC_ACCESS_ON_PHANTOM = HasDecorator::value, IS_ON_STRONG = HAS_GC_ACCESS_ON_STRONG, IS_ON_WEAK = HAS_GC_ACCESS_ON_WEAK, IS_ON_PHANTOM = HAS_GC_ACCESS_ON_PHANTOM, IS_ON_REFERENCE = HAS_GC_ACCESS_ON_WEAK || HAS_GC_ACCESS_ON_PHANTOM, IS_WEAK_ACCESS = HAS_ACCESS_WEAK, HAS_DEST_CONJOINT = HasDecorator::value, HAS_DEST_DISJOINT = HasDecorator::value, HAS_DEST_COVARIANT = HasDecorator::value, HAS_DEST_CONTRAVARIANT = HasDecorator::value, HAS_DEST_NOT_INITIALIZED = HasDecorator::value, HAS_COPY_ARRAYOF = HasDecorator::value, HAS_ACCESS_ATOMIC = HasDecorator::value, HAS_VALUE_NOT_NULL = HasDecorator::value, HAS_VALUE_IS_OOP = HasDecorator::value }; }; namespace AccessInternal { template struct OopOrNarrowOopInternal { typedef oop type; }; template <> struct OopOrNarrowOopInternal { typedef narrowOop type; }; template struct OopOrNarrowOop { typedef typename OopOrNarrowOopInternal::type>::type type; }; inline void* field_addr(void* base, ptrdiff_t byte_offset) { return (void*)(intptr_t(base) + byte_offset); } template void store(P* addr, T value); template void store_at(P base, ptrdiff_t offset, T value); template T load(P* addr); template T load_at(P base, ptrdiff_t offset); template T cas(T new_value, P* addr, T compare_value); template T cas_at(T new_value, P base, ptrdiff_t offset, T compare_value); template T swap(T new_value, P* addr); template T swap_at(T new_value, P base, ptrdiff_t offset); template bool copy(arrayOop src_obj, arrayOop dst_obj, T *src, T *dst, size_t length); template void clone(oop src, oop dst, size_t size); template class LoadProxy { private: P *const _addr; public: LoadProxy(P* addr) : _addr(addr) {} template inline operator T() { return load(_addr); } inline operator P() { return load(_addr); } }; template class LoadAtProxy { private: const P _base; const ptrdiff_t _offset; public: LoadAtProxy(P base, ptrdiff_t offset) : _base(base), _offset(offset) {} template inline operator T() { return load_at(_base, _offset); } }; } template class Access: public AllStatic { public: template static inline void store(P* addr, T value) { AccessInternal::store(addr, value); } template static inline void oop_store(P* addr, T value) { AccessInternal::store(addr, value); } template static inline void store_at(P base, ptrdiff_t offset, T value) { AccessInternal::store_at(base, offset, value); } template static inline void oop_store_at(P base, ptrdiff_t offset, T value) { AccessInternal::store_at(base, offset, value); } template static inline AccessInternal::LoadProxy load(P* addr) { return AccessInternal::LoadProxy(addr); } template static inline AccessInternal::LoadProxy oop_load(P* addr) { return AccessInternal::LoadProxy(addr); } template static inline AccessInternal::LoadAtProxy load_at(P base, ptrdiff_t offset) { return AccessInternal::LoadAtProxy(base, offset); } template static inline AccessInternal::LoadAtProxy oop_load_at(P base, ptrdiff_t offset) { return AccessInternal::LoadAtProxy(base, offset); } template static inline T cas(T new_value, P* addr, T compare_value) { return AccessInternal::cas(new_value, addr, compare_value); } template static inline T oop_cas(T new_value, P* addr, T compare_value) { return AccessInternal::cas(new_value, addr, compare_value); } template static inline T cas_at(T new_value, P base, ptrdiff_t offset, T compare_value) { return AccessInternal::cas_at(new_value, base, offset, compare_value); } template static inline T oop_cas_at(T new_value, P base, ptrdiff_t offset, T compare_value) { return AccessInternal::cas_at(new_value, base, offset, compare_value); } template static inline T swap(T new_value, P* addr) { return AccessInternal::swap(new_value, addr); } template static inline T oop_swap(T new_value, P* addr) { return AccessInternal::swap(new_value, addr); } template static inline T swap_at(T new_value, P base, ptrdiff_t offset) { return AccessInternal::swap_at(new_value, base, offset); } template static inline T oop_swap_at(T new_value, P base, ptrdiff_t offset) { return AccessInternal::swap_at(new_value, base, offset); } template static inline bool copy(arrayOop src_obj, arrayOop dst_obj, T *src, T *dst, size_t length) { return AccessInternal::copy(src_obj, dst_obj, src, dst, length); } template static inline bool oop_copy(arrayOop src_obj, arrayOop dst_obj, T *src, T *dst, size_t length) { return AccessInternal::copy(src_obj, dst_obj, src, dst, length); } static inline void clone(oop src, oop dst, size_t size) { AccessInternal::clone(src, dst, size); } }; template class RawAccess: public Access {}; template class BasicAccess: public Access {}; template class HeapAccess: public Access { enum { CLASS_DECORATORS = ACCESS_ON_HEAP | decorators }; typedef Access ClassAccess; public: // Constrain base pointers to always be an oop for heap accesses template static inline void store_at(oop base, ptrdiff_t offset, T value) { ClassAccess::store_at(base, offset, value); } template static inline void oop_store_at(oop base, ptrdiff_t offset, T value) { ClassAccess::oop_store_at(base, offset, (typename AccessInternal::OopOrNarrowOop::type)value); } static inline AccessInternal::LoadAtProxy load_at(oop base, ptrdiff_t offset) { return ClassAccess::load_at(base, offset); } static inline AccessInternal::LoadAtProxy oop_load_at(oop base, ptrdiff_t offset) { return ClassAccess::oop_load_at(base, offset); } template static inline T cas_at(T new_value, oop base, ptrdiff_t offset, T compare_value) { return ClassAccess::cas_at(new_value, base, offset, compare_value); } template static inline T oop_cas_at(T new_value, oop base, ptrdiff_t offset, T compare_value) { return ClassAccess::oop_cas_at((typename AccessInternal::OopOrNarrowOop::type)new_value, base, offset, (typename AccessInternal::OopOrNarrowOop::type)compare_value); } template static inline T swap_at(T new_value, oop base, ptrdiff_t offset) { return ClassAccess::swap_at(new_value, base, offset); } template static inline T oop_swap_at(T new_value, oop base, ptrdiff_t offset) { return ClassAccess::oop_swap_at((typename AccessInternal::OopOrNarrowOop::type)new_value, base, offset); } }; template class RootAccess: public Access { }; template class NMethodAccess: private RootAccess { public: // Only supported access on nmethods for now static inline void oop_store_at(nmethod* base, ptrdiff_t offset, oop value) { AccessInternal::store_at(base, offset, value); } }; template class KlassAccess: private RootAccess { public: // Only supported access on Klass for now static inline void oop_store_at(Klass* base, ptrdiff_t offset, oop value) { AccessInternal::store_at(base, offset, value); } }; #endif // SHARE_VM_RUNTIME_ACCESS_HPP