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

Upstream 8252504: Add a method to MemoryLayout which returns a offset-computing method handle

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P4 P4
    • 16
    • core-libs
    • None
    • minimal
    • 2 new APIs are added, the implementation of existing APIs is not modified.
    • Java API
    • JDK

      Summary

      Add methods to MemoryLayout that can be used create method handles that lazily compute offsets of nested layouts, by specifying a set of element indices corresponding to one or more enclosing sequence layouts.

      Problem

      MemoryLayout::byteOffset is useful, but only works when the path elements passed to it are fully specified.

      There might be cases where the user has a multi-dimensional layout like:

      MemoryLayout.ofSequence(100,
          MemoryLayout.ofSequence(200,
               MemoryLayout.ofValueBits(32).withName("elem")
          )
      )

      Now, when we derive a VarHandle for this, we can get two free variables, for the two sequence indices - these values will be used to determine the final offset calculation.

      But there's no way to do this with offsets - e.g. there's no way to pass an open-ended layout path to MemoryLayout::byteOffset - because that method wants the layout to be fully specified.

      Solution

      To improve usability in this cases, we add two extra methods:

      MethodHandle MemoryLayout::bitOffsetHandle(PathElement...)
      MethodHandle MemoryLayout::byteOffsetHandle

      Which returns a MethodHandle whose signature is:

      (J...J)J

      where the long parameters correspond to one or more free dimensions in the layout path. This would add some symmetry to the API, since now both VarHandles and offsets can be computed in the same way.

      The patch also removes an incorrect apiNote in the javadoc of the bitOffset, and byteOffset methods. This states that free dimensions in the layout path will be interpreted as having an index of 0, but actually the implementation rejects these outright.

      Specification

      All spec changes are made to the jdk.incubator.foreign.MemoryLayout class.

      In the class' javadoc:

        * it follows that the memory access var handle {@code valueHandle} will feature an <em>additional</em> {@code long}
        * access coordinate.
        *
      + * <p>A layout path with free dimensions can also be used to create an offset computing method handle, using the
      + * {@link #bitOffset(PathElement...)} or {@link #byteOffsetHandle(PathElement...)} method. Again, free dimensions are
      + * translated into {@code long} parameters of the created method handle. The method handle can be used to compute the
      + * offsets of elements of a sequence at different indices, by supplying these indices when invoking the method handle.
      + * For instance:
      + *
      + * <blockquote><pre>{@code
      +MethodHandle offsetHandle = taggedValues.byteOffsetHandle(PathElement.sequenceElement(),
      +                                                          PathElement.groupElement("kind"));
      +long offset1 = (long) offsetHandle.invokeExact(1L); // 8
      +long offset2 = (long) offsetHandle.invokeExact(2L); // 16
      + * }</pre></blockquote>
      + *
        * <h2>Layout attributes</h2>
        *
        * Layouts can be optionally associated with one or more <em>attributes</em>. A layout attribute forms a <em>name/value</em>

      ...

            * Computes the offset, in bits, of the layout selected by a given layout path, where the path is considered rooted in this
            * layout.
            *
      -     * @apiNote if the layout path has one (or more) free dimensions,
      -     * the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
      -     *
            * @param elements the layout path elements.
            * @return The offset, in bits, of the layout selected by the layout path in {@code elements}.
            * @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 multiple sequence element indices
            * (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
            * @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
            */
           default long bitOffset(PathElement... elements) {

      ...

      +    /**
      +     * Creates a method handle that can be used to compute the offset, in bits, of the layout selected
      +     * by a given layout path, where the path is considered rooted in this layout.
      +     *
      +     * <p>The returned method handle has a return type of {@code long}, and features as many {@code long}
      +     * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()},
      +     * where the order of the parameters corresponds to the order of the path elements.
      +     * The returned method handle can be used to compute a layout offset similar to {@link #bitOffset(PathElement...)},
      +     * but where some sequence indices are specified only when invoking the method handle.
      +     *
      +     * <p>The final offset returned by the method handle is computed as follows:
      +     *
      +     * <blockquote><pre>{@code
      +    offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
      +     * }</pre></blockquote>
      +     *
      +     * where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as {@code long}
      +     * arguments, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
      +     * <em>static</em> stride constants which are derived from the layout path.
      +     *
      +     * @param elements the layout path elements.
      +     * @return a method handle that can be used to compute the bit offset of the layout element
      +     * specified by the given layout path elements, when supplied with the missing sequence element indices.
      +     * @throws IllegalArgumentException if the layout path contains one or more path elements that select
      +     * multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
      +     * @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
      +     */
      +    default MethodHandle bitOffsetHandle(PathElement... elements) {

      ...

           /**
            * Computes the offset, in bytes, of the layout selected by a given layout path, where the path is considered rooted in this
            * layout.
            *
      -     * @apiNote if the layout path has one (or more) free dimensions,
      -     * the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}.
      -     *
            * @param elements the layout path elements.
            * @return The offset, in bytes, of the layout selected by the layout path in {@code elements}.
            * @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 multiple sequence element indices
            * (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
            * @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size,
            * or if {@code bitOffset(elements)} is not a multiple of 8.
            */
           default long byteOffset(PathElement... elements) {
      

      ...

      +    /**
      +     * Creates a method handle that can be used to compute the offset, in bytes, of the layout selected
      +     * by a given layout path, where the path is considered rooted in this layout.
      +     *
      +     * <p>The returned method handle has a return type of {@code long}, and features as many {@code long}
      +     * parameter types as there are free dimensions in the provided layout path (see {@link PathElement#sequenceElement()},
      +     * where the order of the parameters corresponds to the order of the path elements.
      +     * The returned method handle can be used to compute a layout offset similar to {@link #byteOffset(PathElement...)},
      +     * but where some sequence indices are specified only when invoking the method handle.
      +     *
      +     * <p>The final offset returned by the method handle is computed as follows:
      +     *
      +     * <blockquote><pre>{@code
      +    bitOffset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
      +    offset = bitOffset / 8
      +     * }</pre></blockquote>
      +     *
      +     * where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as {@code long}
      +     * arguments, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
      +     * <em>static</em> stride constants which are derived from the layout path.
      +     *
      +     * <p>The method handle will throw an {@link UnsupportedOperationException} if the computed
      +     * offset in bits is not a multiple of 8.
      +     *
      +     * @param elements the layout path elements.
      +     * @return a method handle that can be used to compute the byte offset of the layout element
      +     * specified by the given layout path elements, when supplied with the missing sequence element indices.
      +     * @throws IllegalArgumentException if the layout path contains one or more path elements that select
      +     * multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
      +     * @throws UnsupportedOperationException if one of the layouts traversed by the layout path has unspecified size.
      +     */
      +    default MethodHandle byteOffsetHandle(PathElement... elements) {

            jvernee Jorn Vernee
            jvernee Jorn Vernee
            Chris Hegarty, Maurizio Cimadamore
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: