8329997: Add provisions for checking memory segment alignment constraints

Reviewed-by: jvernee, mcimadamore
This commit is contained in:
Per Minborg 2024-04-18 11:22:47 +00:00
parent 60b65e6090
commit b648ed0a08
4 changed files with 133 additions and 6 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -44,7 +44,6 @@ import java.util.stream.Stream;
import jdk.internal.foreign.AbstractMemorySegmentImpl; import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.MemorySessionImpl; import jdk.internal.foreign.MemorySessionImpl;
import jdk.internal.foreign.SegmentFactories; import jdk.internal.foreign.SegmentFactories;
import jdk.internal.foreign.Utils;
import jdk.internal.javac.Restricted; import jdk.internal.javac.Restricted;
import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.CallerSensitive;
import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.ForceInline;
@ -95,6 +94,11 @@ import jdk.internal.vm.annotation.ForceInline;
* address of the region of memory which backs the segment.</li> * address of the region of memory which backs the segment.</li>
* </ul> * </ul>
* <p> * <p>
* Every memory segment has a {@linkplain #maxByteAlignment() maximum byte alignment},
* expressed as a {@code long} value. The maximum alignment is always a power of two,
* derived from the segment address, and the segment type, as explained in more detail
* <a href="#segment-alignment">below</a>.
* <p>
* Every memory segment has a {@linkplain #byteSize() size}. The size of a heap segment * Every memory segment has a {@linkplain #byteSize() size}. The size of a heap segment
* is derived from the Java array from which it is obtained. This size is predictable * is derived from the Java array from which it is obtained. This size is predictable
* across Java runtimes. The size of a native segment is either passed explicitly * across Java runtimes. The size of a native segment is either passed explicitly
@ -394,6 +398,14 @@ import jdk.internal.vm.annotation.ForceInline;
* byteSegment.get(ValueLayout.JAVA_INT_UNALIGNED, 0); // ok: ValueLayout.JAVA_INT_UNALIGNED.byteAlignment() == ValueLayout.JAVA_BYTE.byteAlignment() * byteSegment.get(ValueLayout.JAVA_INT_UNALIGNED, 0); // ok: ValueLayout.JAVA_INT_UNALIGNED.byteAlignment() == ValueLayout.JAVA_BYTE.byteAlignment()
* } * }
* *
* Clients can use the {@linkplain MemorySegment#maxByteAlignment()} method to check if
* a memory segment supports the alignment constraint of a memory layout, as follows:
* {@snippet lang=java:
* MemoryLayout layout = ...
* MemorySegment segment = ...
* boolean isAligned = segment.maxByteAlignment() >= layout.byteAlignment();
* }
*
* <h2 id="wrapping-addresses">Zero-length memory segments</h2> * <h2 id="wrapping-addresses">Zero-length memory segments</h2>
* *
* When interacting with <a href="package-summary.html#ffa">foreign functions</a>, it is * When interacting with <a href="package-summary.html#ffa">foreign functions</a>, it is
@ -589,6 +601,26 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
*/ */
long byteSize(); long byteSize();
/**
* {@return the <a href="#segment-alignment">maximum byte alignment</a>
* associated with this memory segment}
* <p>
* The returned alignment is always a power of two and is derived from
* the segment {@linkplain #address() address()} and, if it is a heap segment,
* the type of the {@linkplain #heapBase() backing heap storage}.
* <p>
* This method can be used to ensure that a segment is sufficiently aligned
* with a layout:
* {@snippet lang=java:
* MemoryLayout layout = ...
* MemorySegment segment = ...
* if (segment.maxByteAlignment() < layout.byteAlignment()) {
* // Take action (e.g. throw an Exception)
* }
* }
*/
long maxByteAlignment();
/** /**
* Returns a slice of this memory segment, at the given offset. The returned * Returns a slice of this memory segment, at the given offset. The returned
* segment's address is the address of this segment plus the given offset; * segment's address is the address of this segment plus the given offset;
@ -1424,6 +1456,9 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
/** /**
* A zero-length native segment modelling the {@code NULL} address. Equivalent to * A zero-length native segment modelling the {@code NULL} address. Equivalent to
* {@code MemorySegment.ofAddress(0L)}. * {@code MemorySegment.ofAddress(0L)}.
* <p>
* The {@linkplain MemorySegment#maxByteAlignment() maximum byte alignment} for
* the {@code NULL} segment is of 2<sup>62</sup>.
*/ */
MemorySegment NULL = MemorySegment.ofAddress(0L); MemorySegment NULL = MemorySegment.ofAddress(0L);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -77,6 +77,13 @@ abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
return offset; return offset;
} }
@Override
public final long maxByteAlignment() {
return address() == 0
? maxAlignMask()
: Math.min(maxAlignMask(), Long.lowestOneBit(address()));
}
@Override @Override
abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope); abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,6 @@
package jdk.internal.foreign; package jdk.internal.foreign;
import java.lang.foreign.MemorySegment;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Optional; import java.util.Optional;
@ -61,6 +60,12 @@ sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl permits M
return Optional.empty(); return Optional.empty();
} }
public final long maxByteAlignment() {
return address() == 0
? 1L << 62
: Long.lowestOneBit(address());
}
@ForceInline @ForceInline
@Override @Override
NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) { NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,11 +26,25 @@
* @run testng TestMemoryAlignment * @run testng TestMemoryAlignment
*/ */
import java.io.File;
import java.io.IOException;
import java.lang.foreign.*; import java.lang.foreign.*;
import java.lang.foreign.MemoryLayout.PathElement; import java.lang.foreign.MemoryLayout.PathElement;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.stream.LongStream; import java.util.stream.LongStream;
import java.util.stream.Stream;
import org.testng.annotations.*; import org.testng.annotations.*;
import static org.testng.Assert.*; import static org.testng.Assert.*;
@ -124,10 +138,76 @@ public class TestMemoryAlignment {
} }
} }
@Test(dataProvider = "alignments")
public void testActualByteAlignment(long align) {
if (align > (1L << 10)) {
return;
}
try (Arena arena = Arena.ofConfined()) {
var segment = arena.allocate(4, align);
assertTrue(segment.maxByteAlignment() >= align);
// Power of two?
assertEquals(Long.bitCount(segment.maxByteAlignment()), 1);
assertEquals(segment.asSlice(1).maxByteAlignment(), 1);
}
}
public void testActualByteAlignmentMappedSegment() throws IOException {
File tmp = File.createTempFile("tmp", "txt");
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE);
Arena arena = Arena.ofConfined()) {
var segment =channel.map(FileChannel.MapMode.READ_WRITE, 0L, 32L, arena);
// We do not know anything about mapping alignment other than it should
// be positive.
assertTrue(segment.maxByteAlignment() >= Byte.BYTES);
// Power of two?
assertEquals(Long.bitCount(segment.maxByteAlignment()), 1);
assertEquals(segment.asSlice(1).maxByteAlignment(), 1);
} finally {
tmp.delete();
}
}
@Test()
public void testActualByteAlignmentNull() {
long alignment = MemorySegment.NULL.maxByteAlignment();
assertEquals(1L << 62, alignment);
}
@Test(dataProvider = "heapSegments")
public void testActualByteAlignmentHeap(MemorySegment segment, int bytes) {
assertEquals(segment.maxByteAlignment(), bytes);
// A slice at offset 1 should always have an alignment of 1
var segmentSlice = segment.asSlice(1);
assertEquals(segmentSlice.maxByteAlignment(), 1);
}
@DataProvider(name = "alignments") @DataProvider(name = "alignments")
public Object[][] createAlignments() { public Object[][] createAlignments() {
return LongStream.range(1, 20) return LongStream.range(1, 20)
.mapToObj(v -> new Object[] { 1L << v }) .mapToObj(v -> new Object[] { 1L << v })
.toArray(Object[][]::new); .toArray(Object[][]::new);
} }
@DataProvider(name = "heapSegments")
public Object[][] heapSegments() {
return Stream.of(
new Object[]{MemorySegment.ofArray(new byte[]{1}), Byte.BYTES},
new Object[]{MemorySegment.ofArray(new short[]{1}), Short.BYTES},
new Object[]{MemorySegment.ofArray(new char[]{1}), Character.BYTES},
new Object[]{MemorySegment.ofArray(new int[]{1}), Integer.BYTES},
new Object[]{MemorySegment.ofArray(new long[]{1}), Long.BYTES},
new Object[]{MemorySegment.ofArray(new float[]{1}), Float.BYTES},
new Object[]{MemorySegment.ofArray(new double[]{1}), Double.BYTES},
new Object[]{MemorySegment.ofBuffer(ByteBuffer.allocate(8)), Byte.BYTES},
new Object[]{MemorySegment.ofBuffer(CharBuffer.allocate(8)), Character.BYTES},
new Object[]{MemorySegment.ofBuffer(ShortBuffer.allocate(8)), Short.BYTES},
new Object[]{MemorySegment.ofBuffer(IntBuffer.allocate(8)), Integer.BYTES},
new Object[]{MemorySegment.ofBuffer(LongBuffer.allocate(8)), Long.BYTES},
new Object[]{MemorySegment.ofBuffer(FloatBuffer.allocate(8)), Float.BYTES},
new Object[]{MemorySegment.ofBuffer(DoubleBuffer.allocate(8)), Double.BYTES}
)
.toArray(Object[][]::new);
}
} }