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 * Memory access may be <em>aligned</em> or <em>misaligned</em> for the given carrier {@code T}, 85 * with respect to the underlying memory address, {@code A} say, which is the final address (computed by the formula described above) 86 * at which the memory dereference operation occurs. 87 * 88 * If access is misaligned then access for anything other than the {@code get} and {@code set} access modes will result 89 * in an {@code IllegalStateException}. In such cases atomic access is only guaranteed with respect to the largest power 90 * of two that divides the GCD of {@code A} and the size (in bytes) of {@code T}. 91 * If access is aligned then following access modes are supported and are 92 * guaranteed to support atomic access: 93 * <ul> 94 * <li>read write access modes for all {@code T}, with the exception of 95 * access modes {@code get} and {@code set} for {@code long} and 96 * {@code double} on 32-bit platforms. 97 * <li>atomic update access modes for {@code int}, {@code long}, 98 * {@code float} or {@code double}. 99 * (Future major platform releases of the JDK may support additional 100 * types for certain currently unsupported access modes.) 101 * <li>numeric atomic update access modes for {@code int} and {@code long}. 102 * (Future major platform releases of the JDK may support additional 103 * numeric types for certain currently unsupported access modes.) 104 * <li>bitwise atomic update access modes for {@code int} and {@code long}. 105 * (Future major platform releases of the JDK may support additional 106 * numeric types for certain currently unsupported access modes.) 107 * </ul> 108 * <p> 109 * If {@code T} is {@code float} or {@code double} then atomic 110 * update access modes compare values using their bitwise representation 111 * (see {@link Float#floatToRawIntBits} and 112 * {@link Double#doubleToRawLongBits}, respectively). 113 */ 114 public final class MemoryHandles { 115 116 private final static JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess(); 117 118 private MemoryHandles() { 119 //sorry, just the one! 120 } 121 122 /** 123 * Creates a memory access var handle with the given carrier type and byte order. 124 * 125 * The resulting memory access var handle features a single {@link MemoryAddress} access coordinate, 126 * and its variable type is set by the given carrier type. 127 * 128 * The alignment constraint for the resulting memory access var handle is the same as the in memory size of the 129 * carrier type, and the accessed offset is set at zero. 130 * 131 * @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>, 132 * which are common to all memory access var handles. 133 * 134 * @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int}, 135 * {@code float}, {@code long}, and {@code double}. 136 * @param byteOrder the required byte order. 137 * @return the new memory access var handle. 138 * @throws IllegalArgumentException when an illegal carrier type is used 139 */ 140 public static VarHandle varHandle(Class<?> carrier, ByteOrder byteOrder) { 141 checkCarrier(carrier); 142 return varHandle(carrier, 143 carrierSize(carrier), 144 byteOrder); 145 } 146 147 /** 148 * Creates a memory access var handle with the given carrier type, alignment constraint, and byte order. 149 * 150 * The resulting memory access var handle features a single {@link MemoryAddress} access coordinate, 151 * and its variable type is set by the given carrier type. 152 * 153 * The accessed offset is zero. 154 * 155 * @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>, 156 * which are common to all memory access var handles. 157 * 158 * @param carrier the carrier type. Valid carriers are {@code byte}, {@code short}, {@code char}, {@code int}, 159 * {@code float}, {@code long}, and {@code double}. 160 * @param alignmentBytes the alignment constraint (in bytes). Must be a power of two. 161 * @param byteOrder the required byte order. 162 * @return the new memory access var handle. 163 * @throws IllegalArgumentException if an illegal carrier type is used, or if {@code alignmentBytes} is not a power of two. 164 */ 165 public static VarHandle varHandle(Class<?> carrier, long alignmentBytes, ByteOrder byteOrder) { 166 checkCarrier(carrier); 167 168 if (alignmentBytes <= 0 169 || (alignmentBytes & (alignmentBytes - 1)) != 0) { // is power of 2? 170 throw new IllegalArgumentException("Bad alignment: " + alignmentBytes); 171 } 172 173 return JLI.memoryAddressViewVarHandle(carrier, alignmentBytes, byteOrder, 0, new long[]{}); 174 } 175 176 /** 177 * Creates a memory access var handle with a fixed offset added to the accessed offset. That is, 178 * if the target memory access var handle accesses a memory location at offset <em>O</em>, the new memory access var 179 * handle will access a memory location at offset <em>O' + O</em>. 180 * 181 * The resulting memory access var handle will feature the same access coordinates as the ones in the target memory access var handle. 182 * 183 * @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>, 184 * which are common to all memory access var handles. 185 * 186 * @param target the target memory access handle to access after the offset adjustment. 187 * @param bytesOffset the offset, in bytes. Must be positive or zero. 188 * @return the new memory access var handle. 189 * @throws IllegalArgumentException when the target var handle is not a memory access var handle, 190 * or when {@code bytesOffset < 0}, or otherwise incompatible with the alignment constraint. 191 */ 192 public static VarHandle withOffset(VarHandle target, long bytesOffset) { 193 if (bytesOffset < 0) { 194 throw new IllegalArgumentException("Illegal offset: " + bytesOffset); 195 } 196 197 long align = JLI.memoryAddressAlignment(target); 198 199 if (bytesOffset % align != 0) { 200 throw new IllegalArgumentException("Offset " + bytesOffset + " does not conform to alignment " + align); 201 } 202 203 return JLI.memoryAddressViewVarHandle( 204 JLI.memoryAddressCarrier(target), 205 align, 206 JLI.memoryAddressByteOrder(target), 207 JLI.memoryAddressOffset(target) + bytesOffset, 208 JLI.memoryAddressStrides(target)); 209 } 210 211 /** 212 * Creates a memory access var handle with a <em>variable</em> offset added to the accessed offset. 213 * That is, if the target memory access var handle accesses a memory location at offset <em>O</em>, 214 * the new memory access var handle will access a memory location at offset <em>(S * X) + O</em>, where <em>S</em> 215 * is a constant <em>stride</em>, whereas <em>X</em> is a dynamic value that will be provided as an additional access 216 * coordinate (of type {@code long}). The new access coordinate will be <em>prepended</em> to the ones available 217 * in the target memory access var handles (if any). 218 * 219 * @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>, 220 * which are common to all memory access var handles. 221 * 222 * @param target the target memory access handle to access after the scale adjustment. 223 * @param bytesStride the stride, in bytes, by which to multiply the coordinate value. Must be greater than zero. 224 * @return the new memory access var handle. 225 * @throws IllegalArgumentException when the target var handle is not a memory access var handle, 226 * or if {@code bytesStride <= 0}, or otherwise incompatible with the alignment constraint. 227 */ 228 public static VarHandle withStride(VarHandle target, long bytesStride) { 229 if (bytesStride == 0) { 230 throw new IllegalArgumentException("Stride must be positive: " + bytesStride); 231 } 232 233 long align = JLI.memoryAddressAlignment(target); 234 235 if (bytesStride % align != 0) { 236 throw new IllegalArgumentException("Stride " + bytesStride + " does not conform to alignment " + align); 237 } 238 239 long offset = JLI.memoryAddressOffset(target); 240 Class<?> carrier = JLI.memoryAddressCarrier(target); 241 242 long[] strides = JLI.memoryAddressStrides(target); 243 long[] newStrides = new long[strides.length + 1]; 244 System.arraycopy(strides, 0, newStrides, 1, strides.length); 245 newStrides[0] = bytesStride; 246 247 return JLI.memoryAddressViewVarHandle( 248 JLI.memoryAddressCarrier(target), 249 align, 250 JLI.memoryAddressByteOrder(target), 251 offset, 252 newStrides); 253 } 254 255 private static void checkCarrier(Class<?> carrier) { 256 if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) { 257 throw new IllegalArgumentException("Illegal carrier: " + carrier.getSimpleName()); 258 } 259 } 260 261 private static long carrierSize(Class<?> carrier) { 262 long bitsAlignment = Math.max(8, Wrapper.forPrimitiveType(carrier).bitWidth()); 263 return Utils.bitsToBytesOrThrow(bitsAlignment, IllegalStateException::new); 264 } 265 }