1 /*
   2  *  Copyright (c) 2019, Oracle and/or its affiliates. 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 it
   6  *  under the terms of the GNU General Public License version 2 only, as
   7  *  published by the Free Software Foundation.  Oracle designates this
   8  *  particular file as subject to the "Classpath" exception as provided
   9  *  by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  *  This code is distributed in the hope that it will be useful, but WITHOUT
  12  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  *  version 2 for more details (a copy is included in the LICENSE file that
  15  *  accompanied this code).
  16  *
  17  *  You should have received a copy of the GNU General Public License version
  18  *  2 along with this work; if not, write to the Free Software Foundation,
  19  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  *   Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  *  or visit www.oracle.com if you need additional information or have any
  23  *  questions.
  24  *
  25  */
  26 package jdk.incubator.foreign;
  27 
  28 import jdk.internal.access.JavaLangInvokeAccess;
  29 import jdk.internal.access.SharedSecrets;
  30 import jdk.internal.foreign.Utils;
  31 import sun.invoke.util.Wrapper;
  32 
  33 import java.lang.invoke.VarHandle;
  34 import java.nio.ByteOrder;
  35 
  36 /**
  37  * This class defines several factory methods for constructing and combining memory access var handles.
  38  * To obtain a memory access var handle, clients must start from one of the <em>leaf</em> methods
  39  * (see {@link MemoryHandles#varHandle(Class, ByteOrder)},
  40  * {@link MemoryHandles#varHandle(Class, long, ByteOrder)}). This determines the variable type
  41  * (all primitive types but {@code void} and {@code boolean} are supported), as well as the alignment constraint and the
  42  * byte order associated to a memory access var handle. The resulting memory access var handle can then be combined in various ways
  43  * to emulate different addressing modes. The var handles created by this class feature a <em>mandatory</em> coordinate type
  44  * (of type {@link MemoryAddress}), and zero or more {@code long} coordinate types, which can be used to emulate
  45  * multi-dimensional array indexing.
  46  * <p>
  47  * As an example, consider the memory layout expressed by a {@link SequenceLayout} instance constructed as follows:
  48  * <blockquote><pre>{@code
  49 SequenceLayout seq = MemoryLayout.ofSequence(5,
  50     MemoryLayout.ofStruct(
  51         MemoryLayout.ofPaddingBits(32),
  52         MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
  53     ));
  54  * }</pre></blockquote>
  55  * To access the member layout named {@code value}, we can construct a memory access var handle as follows:
  56  * <blockquote><pre>{@code
  57 VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); //(MemoryAddress) -> int
  58 handle = MemoryHandles.withOffset(handle, 4); //(MemoryAddress) -> int
  59 handle = MemoryHandles.withStride(handle, 8); //(MemoryAddress, long) -> int
  60  * }</pre></blockquote>
  61  *
  62  * <h2>Addressing mode</h2>
  63  *
  64  * The final memory location accessed by a memory access var handle can be computed as follows:
  65  *
  66  * <blockquote><pre>{@code
  67 address = base + offset
  68  * }</pre></blockquote>
  69  *
  70  * where {@code base} denotes the address expressed by the {@link MemoryAddress} access coordinate, and {@code offset}
  71  * can be expressed in the following form:
  72  *
  73  * <blockquote><pre>{@code
  74 offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
  75  * }</pre></blockquote>
  76  *
  77  * where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as optional {@code long}
  78  * access coordinates, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
  79  * <em>static</em> constants which are can be acquired through the {@link MemoryHandles#withOffset(VarHandle, long)}
  80  * and the {@link MemoryHandles#withStride(VarHandle, long)} combinators, respectively.
  81  *
  82  * <h2><a id="memaccess-mode"></a>Alignment and access modes</h2>
  83  *
  84  * A memory access var handle is associated with an access size {@code S} and an alignment constraint {@code B}
  85  * (both expressed in bytes). We say that a memory access operation is <em>fully aligned</em> if it occurs
  86  * at a memory address {@code A} which is compatible with both alignment constraints {@code S} and {@code B}.
  87  * If access is fully aligned then following access modes are supported and are
  88  * guaranteed to support atomic access:
  89  * <ul>
  90  * <li>read write access modes for all {@code T}, with the exception of
  91  *     access modes {@code get} and {@code set} for {@code long} and
  92  *     {@code double} on 32-bit platforms.
  93  * <li>atomic update access modes for {@code int}, {@code long},
  94  *     {@code float} or {@code double}.
  95  *     (Future major platform releases of the JDK may support additional
  96  *     types for certain currently unsupported access modes.)
  97  * <li>numeric atomic update access modes for {@code int} and {@code long}.
  98  *     (Future major platform releases of the JDK may support additional
  99  *     numeric types for certain currently unsupported access modes.)
 100  * <li>bitwise atomic update access modes for {@code int} and {@code long}.
 101  *     (Future major platform releases of the JDK may support additional
 102  *     numeric types for certain currently unsupported access modes.)
 103  * </ul>
 104  *
 105  * If {@code T} is {@code float} or {@code double} then atomic
 106  * update access modes compare values using their bitwise representation
 107  * (see {@link Float#floatToRawIntBits} and
 108  * {@link Double#doubleToRawLongBits}, respectively).
 109  * <p>
 110  * Alternatively, a memory access operation is <em>partially aligned</em> if it occurs at a memory address {@code A}
 111  * which is only compatible with the alignment constraint {@code B}; in such cases, access for anything other than the
 112  * {@code get} and {@code set} access modes will result in an {@code IllegalStateException}. If access is partially aligned,
 113  * atomic access is only guaranteed with respect to the largest power of two that divides the GCD of {@code A} and {@code S}.
 114  * <p>
 115  * Finally, in all other cases, we say that a memory access operation is <em>misaligned</em>; in such cases an
 116  * {@code IllegalStateException} is thrown, irrespective of the access mode being used.
 117  */
 118 public final class MemoryHandles {
 119 
 120     private final static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
 121 
 122     private MemoryHandles() {
 123         //sorry, just the one!
 124     }
 125 
 126     /**
 127      * Creates a memory access var handle with the given carrier type and byte order.
 128      *
 129      * The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
 130      * and its variable type is set by the given carrier type.
 131      *
 132      * The alignment constraint for the resulting memory access var handle is the same as the in memory size of the
 133      * carrier type, and the accessed offset is set at zero.
 134      *
 135      * @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
 136      * which are common to all memory access var handles.
 137      *
 138      * @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int},
 139      * {@code float}, {@code long}, and {@code double}.
 140      * @param byteOrder the required byte order.
 141      * @return the new memory access var handle.
 142      * @throws IllegalArgumentException when an illegal carrier type is used
 143      */
 144     public static VarHandle varHandle(Class<?> carrier, ByteOrder byteOrder) {
 145         checkCarrier(carrier);
 146         return varHandle(carrier,
 147                 carrierSize(carrier),
 148                 byteOrder);
 149     }
 150 
 151     /**
 152      * Creates a memory access var handle with the given carrier type, alignment constraint, and byte order.
 153      *
 154      * The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
 155      * and its variable type is set by the given carrier type.
 156      *
 157      * The accessed offset is zero.
 158      *
 159      * @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
 160      * which are common to all memory access var handles.
 161      *
 162      * @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int},
 163      * {@code float}, {@code long}, and {@code double}.
 164      * @param alignmentBytes the alignment constraint (in bytes). Must be a power of two.
 165      * @param byteOrder the required byte order.
 166      * @return the new memory access var handle.
 167      * @throws IllegalArgumentException if an illegal carrier type is used, or if {@code alignmentBytes} is not a power of two.
 168      */
 169     public static VarHandle varHandle(Class<?> carrier, long alignmentBytes, ByteOrder byteOrder) {
 170         checkCarrier(carrier);
 171 
 172         if (alignmentBytes <= 0
 173                 || (alignmentBytes & (alignmentBytes - 1)) != 0) { // is power of 2?
 174             throw new IllegalArgumentException("Bad alignment: " + alignmentBytes);
 175         }
 176 
 177         return JLI.memoryAddressViewVarHandle(carrier, alignmentBytes - 1, byteOrder, 0, new long[]{});
 178     }
 179 
 180     /**
 181      * Creates a memory access var handle with a fixed offset added to the accessed offset. That is,
 182      * if the target memory access var handle accesses a memory location at offset <em>O</em>, the new memory access var
 183      * handle will access a memory location at offset <em>O' + O</em>.
 184      *
 185      * The resulting memory access var handle will feature the same access coordinates as the ones in the target memory access var handle.
 186      *
 187      * @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
 188      * which are common to all memory access var handles.
 189      *
 190      * @param target the target memory access handle to access after the offset adjustment.
 191      * @param bytesOffset the offset, in bytes. Must be positive or zero.
 192      * @return the new memory access var handle.
 193      * @throws IllegalArgumentException when the target var handle is not a memory access var handle,
 194      * or when {@code bytesOffset < 0}, or otherwise incompatible with the alignment constraint.
 195      */
 196     public static VarHandle withOffset(VarHandle target, long bytesOffset) {
 197         if (bytesOffset < 0) {
 198             throw new IllegalArgumentException("Illegal offset: " + bytesOffset);
 199         }
 200 
 201         long alignMask = JLI.memoryAddressAlignmentMask(target);
 202 
 203         if ((bytesOffset & alignMask) != 0) {
 204             throw new IllegalArgumentException("Offset " + bytesOffset + " does not conform to alignment " + (alignMask + 1));
 205         }
 206 
 207         return JLI.memoryAddressViewVarHandle(
 208                 JLI.memoryAddressCarrier(target),
 209                 alignMask,
 210                 JLI.memoryAddressByteOrder(target),
 211                 JLI.memoryAddressOffset(target) + bytesOffset,
 212                 JLI.memoryAddressStrides(target));
 213     }
 214 
 215     /**
 216      * Creates a memory access var handle with a <em>variable</em> offset added to the accessed offset.
 217      * That is, if the target memory access var handle accesses a memory location at offset <em>O</em>,
 218      * the new memory access var handle will access a memory location at offset <em>(S * X) + O</em>, where <em>S</em>
 219      * is a constant <em>stride</em>, whereas <em>X</em> is a dynamic value that will be provided as an additional access
 220      * coordinate (of type {@code long}). The new access coordinate will be <em>prepended</em> to the ones available
 221      * in the target memory access var handles (if any).
 222      *
 223      * @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
 224      * which are common to all memory access var handles.
 225      *
 226      * @param target the target memory access handle to access after the scale adjustment.
 227      * @param bytesStride the stride, in bytes, by which to multiply the coordinate value. Must be greater than zero.
 228      * @return the new memory access var handle.
 229      * @throws IllegalArgumentException when the target var handle is not a memory access var handle,
 230      * or if {@code bytesStride <= 0}, or otherwise incompatible with the alignment constraint.
 231      */
 232     public static VarHandle withStride(VarHandle target, long bytesStride) {
 233         if (bytesStride == 0) {
 234             throw new IllegalArgumentException("Stride must be positive: " + bytesStride);
 235         }
 236 
 237         long alignMask = JLI.memoryAddressAlignmentMask(target);
 238 
 239         if ((bytesStride & alignMask) != 0) {
 240             throw new IllegalArgumentException("Stride " + bytesStride + " does not conform to alignment " + (alignMask + 1));
 241         }
 242 
 243         long offset = JLI.memoryAddressOffset(target);
 244 
 245         long[] strides = JLI.memoryAddressStrides(target);
 246         long[] newStrides = new long[strides.length + 1];
 247         System.arraycopy(strides, 0, newStrides, 1, strides.length);
 248         newStrides[0] = bytesStride;
 249 
 250         return JLI.memoryAddressViewVarHandle(
 251                 JLI.memoryAddressCarrier(target),
 252                 alignMask,
 253                 JLI.memoryAddressByteOrder(target),
 254                 offset,
 255                 newStrides);
 256     }
 257 
 258     private static void checkCarrier(Class<?> carrier) {
 259         if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) {
 260             throw new IllegalArgumentException("Illegal carrier: " + carrier.getSimpleName());
 261         }
 262     }
 263 
 264     private static long carrierSize(Class<?> carrier) {
 265         long bitsAlignment = Math.max(8, Wrapper.forPrimitiveType(carrier).bitWidth());
 266         return Utils.bitsToBytesOrThrow(bitsAlignment, IllegalStateException::new);
 267     }
 268 }