Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8278151: Heap segments should handle alignment constraints in a deterministic fashion #622

Closed
Closed
Original file line number Diff line number Diff line change
@@ -155,19 +155,35 @@ boolean isAligned(MemorySegment segment, long offset, MemoryLayout layout) {
* If, however, the segment being dereferenced is a heap segment, the above function will not work: a heap
* segment's base address is <em>virtualized</em> and, as such, cannot be used to construct an alignment check. Instead,
* heap segments are assumed to produce addresses which are never more aligned than the element size of the Java array from which
* they have originated from. Let {@code H} be a heap segment, and {@code AT} be the type of the Java array
* backing {@code H} and {@code A} be the alignment of the addresses produced by {@code H}; then:
* they have originated from, as shown in the following table:
*
* <ul>
* <li>if {@code AT = boolean[]}, then {@code A = 1}
* <li>if {@code AT = byte[]}, then {@code A = 1}
* <li>if {@code AT = short[]}, then {@code A = 2}
* <li>if {@code AT = char[]}, then {@code A = 2}
* <li>if {@code AT = int[]}, then {@code A = 4}
* <li>if {@code AT = float[]}, then {@code A = 4}
* <li>if {@code AT = long[]}, then {@code A = 8}
* <li>if {@code AT = double[]}, then {@code A = 8}
* </ul>
* <blockquote><table class="plain">
* <caption style="display:none">Array type of an array backing a segment and its address alignment</caption>
* <thead>
* <tr>
* <th scope="col">Array type</th>
* <th scope="col">Alignment</th>
* </tr>
* </thead>
* <tbody>
* <tr><th scope="row" style="font-weight:normal">{@code boolean[]}</th>
* <td style="text-align:center;">{@code 1}</td></tr>
* <tr><th scope="row" style="font-weight:normal">{@code byte[]}</th>
* <td style="text-align:center;">{@code 1}</td></tr>
* <tr><th scope="row" style="font-weight:normal">{@code char[]}</th>
* <td style="text-align:center;">{@code 2}</td></tr>
* <tr><th scope="row" style="font-weight:normal">{@code short[]}</th>
* <td style="text-align:center;">{@code 2}</td></tr>
* <tr><th scope="row" style="font-weight:normal">{@code int[]}</th>
* <td style="text-align:center;">{@code 4}</td></tr>
* <tr><th scope="row" style="font-weight:normal">{@code float[]}</th>
* <td style="text-align:center;">{@code 4}</td></tr>
* <tr><th scope="row" style="font-weight:normal">{@code long[]}</th>
* <td style="text-align:center;">{@code 8}</td></tr>
* <tr><th scope="row" style="font-weight:normal">{@code double[]}</th>
* <td style="text-align:center;">{@code 8}</td></tr>
* </tbody>
* </table></blockquote>
*
* Note that the above definition is conservative: it might be possible, for instance, that a heap segment
* constructed from a {@code byte[]} might have a subset of addresses {@code S} which happen to be 8-byte aligned. But determining
@@ -1042,12 +1058,8 @@ static void copy(MemorySegment srcSegment, ValueLayout srcElementLayout, long sr
if (srcElementLayout.byteSize() != dstElementLayout.byteSize()) {
throw new IllegalArgumentException("Source and destination layouts must have same size");
}
if (srcElementLayout.byteAlignment() > srcElementLayout.byteSize()) {
throw new IllegalArgumentException("Source layout alignment greater than its size");
}
if (dstElementLayout.byteAlignment() > dstElementLayout.byteSize()) {
throw new IllegalArgumentException("Destination layout alignment greater than its size");
}
Utils.checkNotHyperAligned(srcElementLayout, "Source layout alignment greater than its size");
Utils.checkNotHyperAligned(dstElementLayout, "Destination layout alignment greater than its size");
if (!srcImpl.isAlignedForElement(srcOffset, srcElementLayout)) {
throw new IllegalArgumentException("Source segment incompatible with alignment constraints");
}
@@ -1399,6 +1411,7 @@ default void set(ValueLayout.OfAddress layout, long offset, Addressable value) {
*/
@ForceInline
default char getAtIndex(ValueLayout.OfChar layout, long index) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return (char)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize()));
}

@@ -1415,6 +1428,7 @@ default char getAtIndex(ValueLayout.OfChar layout, long index) {
*/
@ForceInline
default void setAtIndex(ValueLayout.OfChar layout, long index, char value) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value);
}

@@ -1435,6 +1449,7 @@ default void setAtIndex(ValueLayout.OfChar layout, long index, char value) {
*/
@ForceInline
default short getAtIndex(ValueLayout.OfShort layout, long index) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return (short)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize()));
}

@@ -1451,6 +1466,7 @@ default short getAtIndex(ValueLayout.OfShort layout, long index) {
*/
@ForceInline
default void setAtIndex(ValueLayout.OfShort layout, long index, short value) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value);
}

@@ -1470,6 +1486,7 @@ default void setAtIndex(ValueLayout.OfShort layout, long index, short value) {
*/
@ForceInline
default int getAtIndex(ValueLayout.OfInt layout, long index) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return (int)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize()));
}

@@ -1486,6 +1503,7 @@ default int getAtIndex(ValueLayout.OfInt layout, long index) {
*/
@ForceInline
default void setAtIndex(ValueLayout.OfInt layout, long index, int value) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value);
}

@@ -1505,6 +1523,7 @@ default void setAtIndex(ValueLayout.OfInt layout, long index, int value) {
*/
@ForceInline
default float getAtIndex(ValueLayout.OfFloat layout, long index) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return (float)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize()));
}

@@ -1521,6 +1540,7 @@ default float getAtIndex(ValueLayout.OfFloat layout, long index) {
*/
@ForceInline
default void setAtIndex(ValueLayout.OfFloat layout, long index, float value) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value);
}

@@ -1540,6 +1560,7 @@ default void setAtIndex(ValueLayout.OfFloat layout, long index, float value) {
*/
@ForceInline
default long getAtIndex(ValueLayout.OfLong layout, long index) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return (long)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize()));
}

@@ -1556,6 +1577,7 @@ default long getAtIndex(ValueLayout.OfLong layout, long index) {
*/
@ForceInline
default void setAtIndex(ValueLayout.OfLong layout, long index, long value) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value);
}

@@ -1575,6 +1597,7 @@ default void setAtIndex(ValueLayout.OfLong layout, long index, long value) {
*/
@ForceInline
default double getAtIndex(ValueLayout.OfDouble layout, long index) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return (double)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize()));
}

@@ -1591,6 +1614,7 @@ default double getAtIndex(ValueLayout.OfDouble layout, long index) {
*/
@ForceInline
default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value);
}

@@ -1610,6 +1634,7 @@ default void setAtIndex(ValueLayout.OfDouble layout, long index, double value) {
*/
@ForceInline
default MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return (MemoryAddress)layout.accessHandle().get(this, Utils.scaleOffset(this, index, layout.byteSize()));
}

@@ -1626,6 +1651,7 @@ default MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) {
*/
@ForceInline
default void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable value) {
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
layout.accessHandle().set(this, Utils.scaleOffset(this, index, layout.byteSize()), value.address());
}

@@ -1660,9 +1686,7 @@ static void copy(
int dstBase = (int)baseAndScale;
int dstWidth = (int)(baseAndScale >> 32);
AbstractMemorySegmentImpl srcImpl = (AbstractMemorySegmentImpl)srcSegment;
if (srcLayout.byteAlignment() > srcLayout.byteSize()) {
throw new IllegalArgumentException("Source layout alignment greater than its size");
}
Utils.checkNotHyperAligned(srcLayout, "Source layout alignment greater than its size");
if (!srcImpl.isAlignedForElement(srcOffset, srcLayout)) {
throw new IllegalArgumentException("Source segment incompatible with alignment constraints");
}
@@ -1710,9 +1734,7 @@ static void copy(
int srcWidth = (int)(baseAndScale >> 32);
Objects.checkFromIndexSize(srcIndex, elementCount, Array.getLength(srcArray));
AbstractMemorySegmentImpl destImpl = (AbstractMemorySegmentImpl)dstSegment;
if (dstLayout.byteAlignment() > dstLayout.byteSize()) {
throw new IllegalArgumentException("Destination layout alignment greater than its size");
}
Utils.checkNotHyperAligned(dstLayout, "Source layout alignment greater than its size");
if (!destImpl.isAlignedForElement(dstOffset, dstLayout)) {
throw new IllegalArgumentException("Target segment incompatible with alignment constraints");
}
Original file line number Diff line number Diff line change
@@ -124,9 +124,7 @@ public Spliterator<MemorySegment> spliterator(MemoryLayout elementLayout) {
if (elementLayout.byteSize() == 0) {
throw new IllegalArgumentException("Element layout size cannot be zero");
}
if (elementLayout.byteAlignment() > elementLayout.byteSize()) {
throw new IllegalArgumentException("Element layout alignment greater than its size");
}
Utils.checkNotHyperAligned(elementLayout, "Element layout alignment greater than its size");
if (!isAlignedForElement(0, elementLayout)) {
throw new IllegalArgumentException("Incompatible alignment constraints");
}
Original file line number Diff line number Diff line change
@@ -267,6 +267,7 @@ public void set(ValueLayout.OfAddress layout, long offset, Addressable value) {
@CallerSensitive
public char getAtIndex(ValueLayout.OfChar layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}

@@ -275,6 +276,7 @@ public char getAtIndex(ValueLayout.OfChar layout, long index) {
@CallerSensitive
public void setAtIndex(ValueLayout.OfChar layout, long index, char value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}

@@ -283,6 +285,7 @@ public void setAtIndex(ValueLayout.OfChar layout, long index, char value) {
@CallerSensitive
public short getAtIndex(ValueLayout.OfShort layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}

@@ -291,6 +294,7 @@ public short getAtIndex(ValueLayout.OfShort layout, long index) {
@CallerSensitive
public void setAtIndex(ValueLayout.OfShort layout, long index, short value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}

@@ -299,6 +303,7 @@ public void setAtIndex(ValueLayout.OfShort layout, long index, short value) {
@CallerSensitive
public int getAtIndex(ValueLayout.OfInt layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}

@@ -307,6 +312,7 @@ public int getAtIndex(ValueLayout.OfInt layout, long index) {
@CallerSensitive
public void setAtIndex(ValueLayout.OfInt layout, long index, int value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}

@@ -315,6 +321,7 @@ public void setAtIndex(ValueLayout.OfInt layout, long index, int value) {
@CallerSensitive
public float getAtIndex(ValueLayout.OfFloat layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}

@@ -323,6 +330,7 @@ public float getAtIndex(ValueLayout.OfFloat layout, long index) {
@CallerSensitive
public void setAtIndex(ValueLayout.OfFloat layout, long index, float value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}

@@ -331,6 +339,7 @@ public void setAtIndex(ValueLayout.OfFloat layout, long index, float value) {
@CallerSensitive
public long getAtIndex(ValueLayout.OfLong layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}

@@ -339,6 +348,7 @@ public long getAtIndex(ValueLayout.OfLong layout, long index) {
@CallerSensitive
public void setAtIndex(ValueLayout.OfLong layout, long index, long value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}

@@ -347,6 +357,7 @@ public void setAtIndex(ValueLayout.OfLong layout, long index, long value) {
@CallerSensitive
public double getAtIndex(ValueLayout.OfDouble layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}

@@ -355,6 +366,7 @@ public double getAtIndex(ValueLayout.OfDouble layout, long index) {
@CallerSensitive
public void setAtIndex(ValueLayout.OfDouble layout, long index, double value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value);
}

@@ -363,6 +375,7 @@ public void setAtIndex(ValueLayout.OfDouble layout, long index, double value) {
@CallerSensitive
public MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
return NativeMemorySegmentImpl.EVERYTHING.get(layout, toRawLongValue() + (index * layout.byteSize()));
}

@@ -371,6 +384,7 @@ public MemoryAddress getAtIndex(ValueLayout.OfAddress layout, long index) {
@CallerSensitive
public void setAtIndex(ValueLayout.OfAddress layout, long index, Addressable value) {
Reflection.ensureNativeAccess(Reflection.getCallerClass());
Utils.checkNotHyperAligned(layout, "Layout alignment greater than its size");
NativeMemorySegmentImpl.EVERYTHING.set(layout, toRawLongValue() + (index * layout.byteSize()), value.address());
}
}
Original file line number Diff line number Diff line change
@@ -178,4 +178,11 @@ public static long scaleOffset(MemorySegment segment, long index, long size) {
public static boolean isAligned(long offset, long align) {
return (offset & (align - 1)) == 0;
}

@ForceInline
public static void checkNotHyperAligned(MemoryLayout layout, String msg) {
if (layout.byteAlignment() > layout.byteSize()) {
throw new IllegalArgumentException(msg);
}
}
}
12 changes: 12 additions & 0 deletions test/jdk/java/foreign/TestArrayCopy.java
Original file line number Diff line number Diff line change
@@ -253,6 +253,18 @@ public void testCarrierMismatchDst() {
MemorySegment.copy(new byte[] { 1, 2, 3, 4 }, 0, segment, JAVA_INT, 0, 4);
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testHyperAlignedSrc() {
MemorySegment segment = MemorySegment.ofArray(new byte[] {1, 2, 3, 4});
MemorySegment.copy(new byte[] { 1, 2, 3, 4 }, 0, segment, JAVA_BYTE.withBitAlignment(16), 0, 4);
}

@Test(expectedExceptions = IllegalArgumentException.class)
public void testHyperAlignedDst() {
MemorySegment segment = MemorySegment.ofArray(new byte[] {1, 2, 3, 4});
MemorySegment.copy(segment, JAVA_BYTE.withBitAlignment(16), 0, new byte[] { 1, 2, 3, 4 }, 0, 4);
}

/***** Utilities *****/

public static MemorySegment srcSegment(int bytesLength) {
Loading