Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8326711

Dubious claim on long[]/double[] alignment in MemorySegment javadoc

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P4 P4
    • 23
    • core-libs
    • None
    • behavioral
    • minimal
    • Stronger alignment guarantees widen the set of operations that are possible, so there are no use cases that will stop working.
    • Java API
    • SE

      Summary

      Set the alignment of java.lang.foreign.ValueLayout.JAVA_LONG and java.lang.foreign.ValueLayout.JAVA_DOUBLE to 8 on all platforms

      Problem

      Currently the alignment of JAVA_LONG and JAVA_DOUBLE is set to the size of a native address, which makes it 4 on 32-bit platforms. This effectively disables atomic access modes when accessing a MemorySegment wrapping a long[] or double[] using a memory access var handle (as created through the MemoryLayout::varHandle method)

      Solution

      After further discussion, we came to the conclusion that it is desirable to specify 8-byte alignment for JAVA_LONG and JAVA_DOUBLE, even on 32-bit platforms, so that atomic access modes can be supported on those platforms as well. (This alignment also matches what the reference implementation uses for long and double fields and array elements in practice).

      This also matches the existing behavior of the var handles returned by MethodHandles::arrayElementVarHandle, which also supports atomic access modes, and does essentially the same thing, but with a different API.

      However, a user might expect a JAVA_LONG or JAVA_DOUBLE to have the same alignment as a C long long or double on a non-Windows 32-bit system, which is no longer the case. For instance, JAVA_LONG and JAVA_DOUBLE can not be used to link a native function (through the java.lang.foreign.Linker). Instead, users should use Linker::canonicalLayouts to obtain a native long long or double layout with the correct alignment, and link using these layouts.

      Specification

      diff --git a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java
      index faf28b01bf0a..a58595cb5416 100644
      --- a/src/java.base/share/classes/java/lang/foreign/MemorySegment.java
      +++ b/src/java.base/share/classes/java/lang/foreign/MemorySegment.java
      @@ -305,8 +305,7 @@
        * and/or garbage collection behavior).
        * <p>
        * In practice, the Java runtime lays out arrays in memory so that each n-byte element
      - * occurs at an n-byte aligned physical address (except for {@code long[]} and
      
      • * {@code double[]
      }, where alignment is platform-dependent, as explained below). The + * occurs at an n-byte aligned physical address. The * runtime preserves this invariant even if the array is relocated during garbage * collection. Access operations rely on this invariant to determine if the specified * offset in a heap segment refers to an aligned address in physical memory. @@ -325,26 +324,17 @@ * would correspond to physical address 1008 but offset 4 would correspond to * physical address 1010.</li> * <li>The starting physical address of a {@code long[]} array will be 8-byte aligned - * (e.g. 1000) on 64-bit platforms, so that successive long elements occur at - * 8-byte aligned addresses (e.g., 1000, 1008, 1016, 1024, etc.) On 64-bit platforms, - * a heap segment backed by a {@code long[]} array can be accessed at offsets - * 0, 8, 16, 24, etc under an 8-byte alignment constraint. In addition, the segment - * can be accessed at offsets 0, 4, 8, 12, etc under a 4-byte alignment constraint, - * because the target addresses (1000, 1004, 1008, 1012) are 4-byte aligned. And, - * the segment can be accessed at offsets 0, 2, 4, 6, etc under a 2-byte alignment - * constraint, because the target addresses (e.g. 1000, 1002, 1004, 1006) are - * 2-byte aligned.</li> - * <li>The starting physical address of a {@code long[]} array will be 4-byte aligned - * (e.g. 1004) on 32-bit platforms, so that successive long elements occur at 4-byte - * aligned addresses (e.g., 1004, 1008, 1012, 1016, etc.) On 32-bit platforms, a heap - * segment backed by a {@code long[]} array can be accessed at offsets - * 0, 4, 8, 12, etc under a 4-byte alignment constraint, because the target addresses - * (1004, 1008, 1012, 1016) are 4-byte aligned. And, the segment can be accessed at - * offsets 0, 2, 4, 6, etc under a 2-byte alignment constraint, because the target - * addresses (e.g. 1000, 1002, 1004, 1006) are 2-byte aligned.</li> + * (e.g. 1000), so that successive long elements occur at 8-byte aligned addresses + * (e.g., 1000, 1008, 1016, 1024, etc.) A heap segment backed by a {@code long[]} + * array can be accessed at offsets 0, 8, 16, 24, etc under an 8-byte alignment + * constraint. In addition, the segment can be accessed at offsets 0, 4, 8, 12, + * etc under a 4-byte alignment constraint, because the target addresses (1000, 1004, + * 1008, 1012) are 4-byte aligned. And, the segment can be accessed at offsets 0, 2, + * 4, 6, etc under a 2-byte alignment constraint, because the target addresses (e.g. + * 1000, 1002, 1004, 1006) are 2-byte aligned.</li> * </ul> * <p> - * In other words, heap segments feature a (platform-dependent) <em>maximum</em> + * In other words, heap segments feature a <em>maximum</em> * alignment which is derived from the size of the elements of the Java array backing the * segment, as shown in the following table: * @@ -389,10 +379,7 @@ * In such circumstances, clients have two options. They can use a heap segment backed * by a different array type (e.g. {@code long[]}), capable of supporting greater maximum * alignment. More specifically, the maximum alignment associated with {@code long[]} is - * set to {@code ValueLayout.JAVA_LONG.byteAlignment()} which is a platform-dependent - * value (set to {@code ValueLayout.ADDRESS.byteSize()}). That is, {@code long[]}) is - * guaranteed to provide at least 8-byte alignment in 64-bit platforms, but only 4-byte - * alignment in 32-bit platforms: + * set to {@code ValueLayout.JAVA_LONG.byteAlignment()}, which is 8 bytes: * * {@snippet lang=java : * MemorySegment longSegment = MemorySegment.ofArray(new long[10]); diff --git a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java index e8740be387c6..77bfe0e0209b 100644 --- a/src/java.base/share/classes/java/lang/foreign/ValueLayout.java +++ b/src/java.base/share/classes/java/lang/foreign/ValueLayout.java @@ -47,9 +47,6 @@ * For instance, the byte order of these constants is set to the * {@linkplain ByteOrder#nativeOrder() native byte order}, thus making it easy * to work with other APIs, such as arrays and {@link java.nio.ByteBuffer}. - * Moreover, the alignment constraint of {@link ValueLayout#JAVA_LONG} and - * {@link ValueLayout#JAVA_DOUBLE} is set to 8 bytes on 64-bit platforms, - * but only to 4 bytes on 32-bit platforms. * * @implSpec implementing classes and subclasses are immutable, thread-safe and * <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.

            jvernee Jorn Vernee
            shade Aleksey Shipilev
            Maurizio Cimadamore
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: