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.internal.foreign; 27 28 import jdk.incubator.foreign.MemoryHandles; 29 import jdk.incubator.foreign.MemoryLayout; 30 import jdk.incubator.foreign.MemorySegment; 31 import jdk.internal.access.JavaLangInvokeAccess; 32 import jdk.internal.access.SharedSecrets; 33 import jdk.internal.access.foreign.MemorySegmentProxy; 34 35 import jdk.incubator.foreign.GroupLayout; 36 import jdk.incubator.foreign.SequenceLayout; 37 import jdk.incubator.foreign.ValueLayout; 38 import sun.invoke.util.Wrapper; 39 40 import java.lang.invoke.MethodHandle; 41 import java.lang.invoke.MethodHandles; 42 import java.lang.invoke.MethodType; 43 import java.lang.invoke.VarHandle; 44 import java.util.ArrayDeque; 45 import java.util.ArrayList; 46 import java.util.Deque; 47 import java.util.List; 48 import java.util.function.ToLongFunction; 49 import java.util.function.UnaryOperator; 50 51 /** 52 * This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout, ToLongFunction)}, 53 * a path can be constructed by selecting layout elements using the selector methods provided by this class 54 * (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}). 55 * Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected 56 * by the path (see {@link #offset}), or obtain a memory access var handle to access the selected layout element 57 * given an address pointing to a segment associated with the root layout (see {@link #dereferenceHandle(Class)}). 58 */ 59 public class LayoutPath { 60 61 private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess(); 62 63 private static final MethodHandle ADD_STRIDE; 64 private static final MethodHandle MH_ADD_SCALED_OFFSET; 65 66 private static final int UNSPECIFIED_ELEM_INDEX = -1; 67 68 static { 69 try { 70 MethodHandles.Lookup lookup = MethodHandles.lookup(); 71 ADD_STRIDE = lookup.findStatic(LayoutPath.class, "addStride", 72 MethodType.methodType(long.class, MemorySegment.class, long.class, long.class, long.class)); 73 MH_ADD_SCALED_OFFSET = lookup.findStatic(LayoutPath.class, "addScaledOffset", 74 MethodType.methodType(long.class, long.class, long.class, long.class)); 75 } catch (Throwable ex) { 76 throw new ExceptionInInitializerError(ex); 77 } 78 } 79 80 private final MemoryLayout layout; 81 private final long offset; 82 private final LayoutPath enclosing; 83 private final long[] strides; 84 private final long elementIndex; 85 private final ToLongFunction<MemoryLayout> sizeFunc; 86 LayoutPath(MemoryLayout layout, long offset, long[] strides, long elementIndex, LayoutPath enclosing, ToLongFunction<MemoryLayout> sizeFunc)87 private LayoutPath(MemoryLayout layout, long offset, long[] strides, long elementIndex, LayoutPath enclosing, ToLongFunction<MemoryLayout> sizeFunc) { 88 this.layout = layout; 89 this.offset = offset; 90 this.strides = strides; 91 this.enclosing = enclosing; 92 this.elementIndex = elementIndex; 93 this.sizeFunc = sizeFunc; 94 } 95 96 // Layout path selector methods 97 sequenceElement()98 public LayoutPath sequenceElement() { 99 check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout"); 100 SequenceLayout seq = (SequenceLayout)layout; 101 MemoryLayout elem = seq.elementLayout(); 102 return LayoutPath.nestedPath(elem, offset, addStride(sizeFunc.applyAsLong(elem)), UNSPECIFIED_ELEM_INDEX, this); 103 } 104 sequenceElement(long start, long step)105 public LayoutPath sequenceElement(long start, long step) { 106 check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout"); 107 SequenceLayout seq = (SequenceLayout)layout; 108 checkSequenceBounds(seq, start); 109 MemoryLayout elem = seq.elementLayout(); 110 long elemSize = sizeFunc.applyAsLong(elem); 111 return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step), 112 UNSPECIFIED_ELEM_INDEX, this); 113 } 114 sequenceElement(long index)115 public LayoutPath sequenceElement(long index) { 116 check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout"); 117 SequenceLayout seq = (SequenceLayout)layout; 118 checkSequenceBounds(seq, index); 119 long elemOffset = 0; 120 if (index > 0) { 121 //if index == 0, we do not depend on sequence element size, so skip 122 long elemSize = sizeFunc.applyAsLong(seq.elementLayout()); 123 elemOffset = elemSize * index; 124 } 125 return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, index, this); 126 } 127 groupElement(String name)128 public LayoutPath groupElement(String name) { 129 check(GroupLayout.class, "attempting to select a group element from a non-group layout"); 130 GroupLayout g = (GroupLayout)layout; 131 long offset = 0; 132 MemoryLayout elem = null; 133 int index = -1; 134 for (int i = 0; i < g.memberLayouts().size(); i++) { 135 MemoryLayout l = g.memberLayouts().get(i); 136 if (l.name().isPresent() && 137 l.name().get().equals(name)) { 138 elem = l; 139 index = i; 140 break; 141 } else if (g.isStruct()) { 142 offset += sizeFunc.applyAsLong(l); 143 } 144 } 145 if (elem == null) { 146 throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout); 147 } 148 return LayoutPath.nestedPath(elem, this.offset + offset, strides, index, this); 149 } 150 151 // Layout path projections 152 offset()153 public long offset() { 154 return offset; 155 } 156 dereferenceHandle(Class<?> carrier)157 public VarHandle dereferenceHandle(Class<?> carrier) { 158 Utils.checkPrimitiveCarrierCompat(carrier, layout); 159 checkAlignment(this); 160 161 List<Class<?>> expectedCoordinates = new ArrayList<>(); 162 Deque<Integer> perms = new ArrayDeque<>(); 163 perms.addFirst(0); 164 expectedCoordinates.add(MemorySegment.class); 165 166 VarHandle handle = Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, true, layout.byteAlignment() - 1, 167 ((ValueLayout)layout).order())); 168 169 for (int i = 0 ; i < strides.length ; i++) { 170 expectedCoordinates.add(long.class); 171 perms.addFirst(0); 172 perms.addLast(i + 1); 173 //add stride 174 handle = MemoryHandles.collectCoordinates(handle, 1 + i, 175 MethodHandles.insertArguments(ADD_STRIDE, 1, Utils.bitsToBytesOrThrow(strides[strides.length - 1 - i], IllegalStateException::new))); // MS, long, MS_n, long_n, long 176 } 177 //add offset 178 handle = MemoryHandles.insertCoordinates(handle, 1 + strides.length, Utils.bitsToBytesOrThrow(offset, IllegalStateException::new)); 179 180 if (strides.length > 0) { 181 // remove duplicate MS args 182 handle = MemoryHandles.permuteCoordinates(handle, expectedCoordinates, perms.stream().mapToInt(i -> i).toArray()); 183 } 184 return handle; 185 } 186 addScaledOffset(long base, long index, long stride)187 private static long addScaledOffset(long base, long index, long stride) { 188 return base + (stride * index); 189 } 190 offsetHandle()191 public MethodHandle offsetHandle() { 192 MethodHandle mh = MethodHandles.identity(long.class); 193 for (int i = strides.length - 1; i >=0; i--) { 194 MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2, strides[i]); 195 // (J, ...) -> J to (J, J, ...) -> J 196 // i.e. new coord is prefixed. Last coord will correspond to innermost layout 197 mh = MethodHandles.collectArguments(mh, 0, collector); 198 } 199 mh = MethodHandles.insertArguments(mh, 0, offset); 200 return mh; 201 } 202 layout()203 public MemoryLayout layout() { 204 return layout; 205 } 206 map(UnaryOperator<MemoryLayout> op)207 public MemoryLayout map(UnaryOperator<MemoryLayout> op) { 208 MemoryLayout newLayout = op.apply(layout); 209 if (enclosing == null) { 210 return newLayout; 211 } else if (enclosing.layout instanceof SequenceLayout) { 212 SequenceLayout seq = (SequenceLayout)enclosing.layout; 213 if (seq.elementCount().isPresent()) { 214 return enclosing.map(l -> dup(l, MemoryLayout.ofSequence(seq.elementCount().getAsLong(), newLayout))); 215 } else { 216 return enclosing.map(l -> dup(l, MemoryLayout.ofSequence(newLayout))); 217 } 218 } else if (enclosing.layout instanceof GroupLayout) { 219 GroupLayout g = (GroupLayout)enclosing.layout; 220 List<MemoryLayout> newElements = new ArrayList<>(g.memberLayouts()); 221 //if we selected a layout in a group we must have a valid index 222 newElements.set((int)elementIndex, newLayout); 223 if (g.isUnion()) { 224 return enclosing.map(l -> dup(l, MemoryLayout.ofUnion(newElements.toArray(new MemoryLayout[0])))); 225 } else { 226 return enclosing.map(l -> dup(l, MemoryLayout.ofStruct(newElements.toArray(new MemoryLayout[0])))); 227 } 228 } else { 229 return newLayout; 230 } 231 } 232 dup(MemoryLayout oldLayout, MemoryLayout newLayout)233 private MemoryLayout dup(MemoryLayout oldLayout, MemoryLayout newLayout) { 234 newLayout = newLayout.withBitAlignment(oldLayout.bitAlignment()); 235 if (oldLayout.name().isPresent()) { 236 newLayout.withName(oldLayout.name().get()); 237 } 238 return newLayout; 239 } 240 241 // Layout path construction 242 rootPath(MemoryLayout layout, ToLongFunction<MemoryLayout> sizeFunc)243 public static LayoutPath rootPath(MemoryLayout layout, ToLongFunction<MemoryLayout> sizeFunc) { 244 return new LayoutPath(layout, 0L, EMPTY_STRIDES, -1, null, sizeFunc); 245 } 246 nestedPath(MemoryLayout layout, long offset, long[] strides, long elementIndex, LayoutPath encl)247 private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long elementIndex, LayoutPath encl) { 248 return new LayoutPath(layout, offset, strides, elementIndex, encl, encl.sizeFunc); 249 } 250 251 // Helper methods 252 check(Class<?> layoutClass, String msg)253 private void check(Class<?> layoutClass, String msg) { 254 if (!layoutClass.isAssignableFrom(layout.getClass())) { 255 throw badLayoutPath(msg); 256 } 257 } 258 checkSequenceBounds(SequenceLayout seq, long index)259 private void checkSequenceBounds(SequenceLayout seq, long index) { 260 if (seq.elementCount().isPresent() && index >= seq.elementCount().getAsLong()) { 261 throw badLayoutPath(String.format("Sequence index out of bound; found: %d, size: %d", index, seq.elementCount().getAsLong())); 262 } 263 } 264 badLayoutPath(String cause)265 private static IllegalArgumentException badLayoutPath(String cause) { 266 return new IllegalArgumentException("Bad layout path: " + cause); 267 } 268 checkAlignment(LayoutPath path)269 private static void checkAlignment(LayoutPath path) { 270 MemoryLayout layout = path.layout; 271 long alignment = layout.bitAlignment(); 272 if (path.offset % alignment != 0) { 273 throw new UnsupportedOperationException("Invalid alignment requirements for layout " + layout); 274 } 275 for (long stride : path.strides) { 276 if (stride % alignment != 0) { 277 throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match stride " + stride); 278 } 279 } 280 LayoutPath encl = path.enclosing; 281 if (encl != null) { 282 if (encl.layout.bitAlignment() < alignment) { 283 throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match those for enclosing layout " + encl.layout); 284 } 285 checkAlignment(encl); 286 } 287 } 288 addStride(long stride)289 private long[] addStride(long stride) { 290 long[] newStrides = new long[strides.length + 1]; 291 System.arraycopy(strides, 0, newStrides, 0, strides.length); 292 newStrides[strides.length] = stride; 293 return newStrides; 294 } 295 296 private static final long[] EMPTY_STRIDES = new long[0]; 297 298 /** 299 * This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation 300 * is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class. 301 */ 302 public static class PathElementImpl implements MemoryLayout.PathElement, UnaryOperator<LayoutPath> { 303 304 public enum PathKind { 305 SEQUENCE_ELEMENT("unbound sequence element"), 306 SEQUENCE_ELEMENT_INDEX("bound sequence element"), 307 SEQUENCE_RANGE("sequence range"), 308 GROUP_ELEMENT("group element"); 309 310 final String description; 311 PathKind(String description)312 PathKind(String description) { 313 this.description = description; 314 } 315 description()316 public String description() { 317 return description; 318 } 319 } 320 321 final PathKind kind; 322 final UnaryOperator<LayoutPath> pathOp; 323 PathElementImpl(PathKind kind, UnaryOperator<LayoutPath> pathOp)324 public PathElementImpl(PathKind kind, UnaryOperator<LayoutPath> pathOp) { 325 this.kind = kind; 326 this.pathOp = pathOp; 327 } 328 329 @Override apply(LayoutPath layoutPath)330 public LayoutPath apply(LayoutPath layoutPath) { 331 return pathOp.apply(layoutPath); 332 } 333 kind()334 public PathKind kind() { 335 return kind; 336 } 337 } 338 addStride(MemorySegment segment, long stride, long base, long index)339 private static long addStride(MemorySegment segment, long stride, long base, long index) { 340 return MemorySegmentProxy.addOffsets(base, 341 MemorySegmentProxy.multiplyOffsets(stride, index, ((MemorySegmentProxy)segment)), (MemorySegmentProxy)segment); 342 } 343 } 344