Skip to content

Commit

Permalink
8282953: Drop MemoryLayout::map
Browse files Browse the repository at this point in the history
Reviewed-by: psandoz
  • Loading branch information
mcimadamore committed Mar 11, 2022
1 parent b1020d1 commit f645d4f
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 227 deletions.
59 changes: 11 additions & 48 deletions src/java.base/share/classes/java/lang/foreign/MemoryLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@
* element layout (see {@link SequenceLayout}); a <em>group layout</em> denotes an aggregation of (typically) heterogeneous
* member layouts (see {@link GroupLayout}).
* <p>
* For instance, consider the following struct declaration in C:
* Layouts can be optionally associated with a <em>name</em>. A layout name can be referred to when
* constructing <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
* <p>
* Consider the following struct declaration in C:
*
* {@snippet lang=c :
* typedef struct {
Expand Down Expand Up @@ -120,8 +123,7 @@
* <p>
* Layout paths are for example useful in order to obtain {@linkplain MemoryLayout#bitOffset(PathElement...) offsets} of
* arbitrarily nested layouts inside another layout, to quickly obtain a {@linkplain #varHandle(PathElement...) memory access handle}
* corresponding to the selected layout, to {@linkplain #select(PathElement...) select} an arbitrarily nested layout inside
* another layout, or to {@link #map(UnaryOperator, PathElement...) transform} a nested layout element inside
* corresponding to the selected layout, or to {@linkplain #select(PathElement...) select} an arbitrarily nested layout inside
* another layout.
* <p>
* Such <em>layout paths</em> can be constructed programmatically using the methods in this class.
Expand All @@ -138,22 +140,6 @@
* PathElement.groupElement("value"));
* }
*
* And, we can also replace the layout named {@code value} with another layout, as follows:
* {@snippet lang=java :
* MemoryLayout taggedValuesWithHole = taggedValues.map(l -> MemoryLayout.paddingLayout(32),
* PathElement.sequenceElement(), PathElement.groupElement("value"));
* }
*
* That is, the above declaration is identical to the following, more verbose one:
* {@snippet lang=java :
* MemoryLayout taggedValuesWithHole = MemoryLayout.sequenceLayout(5,
* MemoryLayout.structLayout(
* ValueLayout.JAVA_BYTE.withName("kind"),
* MemoryLayout.paddingLayout(32),
* MemoryLayout.paddingLayout(32)
* ));
* }
*
* Layout paths can feature one or more <em>free dimensions</em>. For instance, a layout path traversing
* an unspecified sequence element (that is, where one of the path component was obtained with the
* {@link PathElement#sequenceElement()} method) features an additional free dimension, which will have to be bound at runtime.
Expand Down Expand Up @@ -183,11 +169,6 @@
* long offset2 = (long) offsetHandle.invokeExact(2L); // 16
* }
*
* <h2>Layout attributes</h2>
*
* Layouts can be optionally associated with a <em>name</em>. A layout name can be referred to when
* constructing <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
*
* @implSpec
* Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*
Expand Down Expand Up @@ -292,7 +273,7 @@ default long byteAlignment() {
* in {@code elements} is {@code null}.
*/
default long bitOffset(PathElement... elements) {
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::offset,
return computePathOp(LayoutPath.rootPath(this), LayoutPath::offset,
EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE), elements);
}

Expand Down Expand Up @@ -324,7 +305,7 @@ default long bitOffset(PathElement... elements) {
* multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
*/
default MethodHandle bitOffsetHandle(PathElement... elements) {
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::offsetHandle,
return computePathOp(LayoutPath.rootPath(this), LayoutPath::offsetHandle,
EnumSet.of(PathKind.SEQUENCE_RANGE), elements);
}

Expand Down Expand Up @@ -416,7 +397,7 @@ default MethodHandle byteOffsetHandle(PathElement... elements) {
* @see MethodHandles#memorySegmentViewVarHandle(ValueLayout)
*/
default VarHandle varHandle(PathElement... elements) {
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::dereferenceHandle,
return computePathOp(LayoutPath.rootPath(this), LayoutPath::dereferenceHandle,
Set.of(), elements);
}

Expand All @@ -441,7 +422,7 @@ default VarHandle arrayElementVarHandle(PathElement... elements) {
PathElement[] newElements = new PathElement[elements.length + 1];
newElements[0] = PathElement.sequenceElement();
System.arraycopy(elements, 0, newElements, 1, elements.length);
return computePathOp(LayoutPath.rootPath(MemoryLayout.sequenceLayout(Long.MAX_VALUE, this), MemoryLayout::bitSize),
return computePathOp(LayoutPath.rootPath(MemoryLayout.sequenceLayout(Long.MAX_VALUE, this)),
LayoutPath::dereferenceHandle, Set.of(), newElements);
}

Expand Down Expand Up @@ -484,7 +465,7 @@ default VarHandle arrayElementVarHandle(PathElement... elements) {
* @throws UnsupportedOperationException if the size of the selected layout in bits is not a multiple of 8.
*/
default MethodHandle sliceHandle(PathElement... elements) {
return computePathOp(LayoutPath.rootPath(this, MemoryLayout::bitSize), LayoutPath::sliceHandle,
return computePathOp(LayoutPath.rootPath(this), LayoutPath::sliceHandle,
Set.of(), elements);
}

Expand All @@ -498,25 +479,7 @@ default MethodHandle sliceHandle(PathElement... elements) {
* (see {@link PathElement#sequenceElement(long)} and {@link PathElement#sequenceElement(long, long)}).
*/
default MemoryLayout select(PathElement... elements) {
return computePathOp(LayoutPath.rootPath(this, l -> 0L), LayoutPath::layout,
EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE), elements);
}

/**
* Creates a transformed copy of this layout where a selected layout, from a path rooted in this layout,
* is replaced with the result of applying the given operation.
*
* @param op the unary operation to be applied to the selected layout.
* @param elements the layout path elements.
* @return a new layout where the layout selected by the layout path in {@code elements},
* has been replaced by the result of applying {@code op} to the selected layout.
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout,
* or if the layout path contains one or more path elements that select one or more sequence element indices
* (see {@link PathElement#sequenceElement(long)} and {@link PathElement#sequenceElement(long, long)}).
*/
default MemoryLayout map(UnaryOperator<MemoryLayout> op, PathElement... elements) {
Objects.requireNonNull(op);
return computePathOp(LayoutPath.rootPath(this, l -> 0L), path -> path.map(op),
return computePathOp(LayoutPath.rootPath(this), LayoutPath::layout,
EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE), elements);
}

Expand Down
103 changes: 21 additions & 82 deletions src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,10 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;

/**
* This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout, ToLongFunction)},
* This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout)},
* a path can be constructed by selecting layout elements using the selector methods provided by this class
* (see {@link #sequenceElement()}, {@link #sequenceElement(long)}, {@link #sequenceElement(long, long)}, {@link #groupElement(String)}).
* Once a path has been fully constructed, clients can ask for the offset associated with the layout element selected
Expand All @@ -51,17 +46,12 @@
*/
public class LayoutPath {

private static final MethodHandle ADD_STRIDE;
private static final MethodHandle MH_ADD_SCALED_OFFSET;
private static final MethodHandle MH_SLICE;

private static final int UNSPECIFIED_ELEM_INDEX = -1;

static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
ADD_STRIDE = lookup.findStatic(LayoutPath.class, "addStride",
MethodType.methodType(long.class, MemorySegment.class, long.class, long.class, long.class));
MH_ADD_SCALED_OFFSET = lookup.findStatic(LayoutPath.class, "addScaledOffset",
MethodType.methodType(long.class, long.class, long.class, long.class));
MH_SLICE = lookup.findVirtual(MemorySegment.class, "asSlice",
Expand All @@ -75,16 +65,12 @@ public class LayoutPath {
private final long offset;
private final LayoutPath enclosing;
private final long[] strides;
private final long elementIndex;
private final ToLongFunction<MemoryLayout> sizeFunc;

private LayoutPath(MemoryLayout layout, long offset, long[] strides, long elementIndex, LayoutPath enclosing, ToLongFunction<MemoryLayout> sizeFunc) {
private LayoutPath(MemoryLayout layout, long offset, long[] strides, LayoutPath enclosing) {
this.layout = layout;
this.offset = offset;
this.strides = strides;
this.enclosing = enclosing;
this.elementIndex = elementIndex;
this.sizeFunc = sizeFunc;
}

// Layout path selector methods
Expand All @@ -93,17 +79,16 @@ public LayoutPath sequenceElement() {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
MemoryLayout elem = seq.elementLayout();
return LayoutPath.nestedPath(elem, offset, addStride(sizeFunc.applyAsLong(elem)), UNSPECIFIED_ELEM_INDEX, this);
return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), this);
}

public LayoutPath sequenceElement(long start, long step) {
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
SequenceLayout seq = (SequenceLayout)layout;
checkSequenceBounds(seq, start);
MemoryLayout elem = seq.elementLayout();
long elemSize = sizeFunc.applyAsLong(elem);
return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step),
UNSPECIFIED_ELEM_INDEX, this);
long elemSize = elem.bitSize();
return LayoutPath.nestedPath(elem, offset + (start * elemSize), addStride(elemSize * step), this);
}

public LayoutPath sequenceElement(long index) {
Expand All @@ -113,33 +98,31 @@ public LayoutPath sequenceElement(long index) {
long elemOffset = 0;
if (index > 0) {
//if index == 0, we do not depend on sequence element size, so skip
long elemSize = sizeFunc.applyAsLong(seq.elementLayout());
long elemSize = seq.elementLayout().bitSize();
elemOffset = elemSize * index;
}
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, index, this);
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, this);
}

public LayoutPath groupElement(String name) {
check(GroupLayout.class, "attempting to select a group element from a non-group layout");
GroupLayout g = (GroupLayout)layout;
long offset = 0;
MemoryLayout elem = null;
int index = -1;
for (int i = 0; i < g.memberLayouts().size(); i++) {
MemoryLayout l = g.memberLayouts().get(i);
if (l.name().isPresent() &&
l.name().get().equals(name)) {
elem = l;
index = i;
break;
} else if (g.isStruct()) {
offset += sizeFunc.applyAsLong(l);
offset += l.bitSize();
}
}
if (elem == null) {
throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
}
return LayoutPath.nestedPath(elem, this.offset + offset, strides, index, this);
return LayoutPath.nestedPath(elem, this.offset + offset, strides, this);
}

// Layout path projections
Expand All @@ -154,28 +137,16 @@ public VarHandle dereferenceHandle() {
}
checkAlignment(this);

List<Class<?>> expectedCoordinates = new ArrayList<>();
Deque<Integer> perms = new ArrayDeque<>();
perms.addFirst(0);
expectedCoordinates.add(MemorySegment.class);

VarHandle handle = Utils.makeSegmentViewVarHandle(valueLayout);

for (int i = 0 ; i < strides.length ; i++) {
expectedCoordinates.add(long.class);
perms.addFirst(0);
perms.addLast(i + 1);
//add stride
handle = MethodHandles.collectCoordinates(handle, 1 + i,
MethodHandles.insertArguments(ADD_STRIDE, 1, Utils.bitsToBytesOrThrow(strides[strides.length - 1 - i], IllegalStateException::new))); // MS, long, MS_n, long_n, long
}
//add offset
handle = MethodHandles.insertCoordinates(handle, 1 + strides.length, Utils.bitsToBytesOrThrow(offset, IllegalStateException::new));

if (strides.length > 0) {
// remove duplicate MS args
handle = MethodHandles.permuteCoordinates(handle, expectedCoordinates, perms.stream().mapToInt(i -> i).toArray());
for (int i = strides.length - 1; i >= 0; i--) {
MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2,
Utils.bitsToBytesOrThrow(strides[i], IllegalArgumentException::new));
// (J, ...) -> J to (J, J, ...) -> J
// i.e. new coord is prefixed. Last coord will correspond to innermost layout
handle = MethodHandles.collectCoordinates(handle, 1, collector);
}
handle = MethodHandles.insertCoordinates(handle, 1,
Utils.bitsToBytesOrThrow(offset, IllegalArgumentException::new));
return handle;
}

Expand Down Expand Up @@ -215,42 +186,14 @@ public MemoryLayout layout() {
return layout;
}

public MemoryLayout map(UnaryOperator<MemoryLayout> op) {
MemoryLayout newLayout = op.apply(layout);
if (enclosing == null) {
return newLayout;
} else if (enclosing.layout instanceof SequenceLayout seq) {
return enclosing.map(l -> dup(l, MemoryLayout.sequenceLayout(seq.elementCount(), newLayout)));
} else if (enclosing.layout instanceof GroupLayout g) {
List<MemoryLayout> newElements = new ArrayList<>(g.memberLayouts());
//if we selected a layout in a group we must have a valid index
newElements.set((int)elementIndex, newLayout);
if (g.isUnion()) {
return enclosing.map(l -> dup(l, MemoryLayout.unionLayout(newElements.toArray(new MemoryLayout[0]))));
} else {
return enclosing.map(l -> dup(l, MemoryLayout.structLayout(newElements.toArray(new MemoryLayout[0]))));
}
} else {
return newLayout;
}
}

private MemoryLayout dup(MemoryLayout oldLayout, MemoryLayout newLayout) {
newLayout = newLayout.withBitAlignment(oldLayout.bitAlignment());
if (oldLayout.name().isPresent()) {
newLayout = newLayout.withName(oldLayout.name().get());
}
return newLayout;
}

// Layout path construction

public static LayoutPath rootPath(MemoryLayout layout, ToLongFunction<MemoryLayout> sizeFunc) {
return new LayoutPath(layout, 0L, EMPTY_STRIDES, -1, null, sizeFunc);
public static LayoutPath rootPath(MemoryLayout layout) {
return new LayoutPath(layout, 0L, EMPTY_STRIDES, null);
}

private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long elementIndex, LayoutPath encl) {
return new LayoutPath(layout, offset, strides, elementIndex, encl, encl.sizeFunc);
private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, LayoutPath encl) {
return new LayoutPath(layout, offset, strides, encl);
}

// Helper methods
Expand Down Expand Up @@ -340,8 +283,4 @@ public PathKind kind() {
return kind;
}
}

private static long addStride(MemorySegment segment, long stride, long base, long index) {
return base + (stride * index);
}
}
Loading

0 comments on commit f645d4f

Please sign in to comment.