mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8289148: j.l.foreign.VaList::nextVarg call could throw IndexOutOfBoundsException or even crash the VM
8289333: Specification of method j.l.foreign.VaList::skip deserves clarification 8289156: j.l.foreign.VaList::skip call could throw java.lang.IndexOutOfBoundsException: Out of bound access on segment Reviewed-by: mcimadamore
This commit is contained in:
parent
c3806b93c4
commit
3164c98f4c
7 changed files with 378 additions and 160 deletions
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
package java.lang.foreign;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -50,7 +51,16 @@ import jdk.internal.reflect.Reflection;
|
|||
* <p>
|
||||
* As such, this interface only supports reading {@code int}, {@code double},
|
||||
* and any other type that fits into a {@code long}.
|
||||
*
|
||||
* <h2 id="safety">Safety considerations</h2>
|
||||
* It is possible for clients to access elements outside the spatial bounds of a variable argument list.
|
||||
* Variable argument list implementations will try to detect out-of-bounds reads on a best-effort basis.
|
||||
* <p>
|
||||
* Whether this detection succeeds depends on the factory method used to create the variable argument list:
|
||||
* <ul>
|
||||
* <li>Variable argument lists created <em>safely</em>, using {@link #make(Consumer, MemorySession)} are capable of detecting out-of-bounds reads;</li>
|
||||
* <li>Variable argument lists created <em>unsafely</em>, using {@link #ofAddress(MemoryAddress, MemorySession)} are not capable of detecting out-of-bounds reads</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This class is not thread safe, and all accesses should occur within a single thread
|
||||
* (regardless of the memory session associated with the variable arity list).
|
||||
*
|
||||
|
@ -74,6 +84,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
|||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
int nextVarg(ValueLayout.OfInt layout);
|
||||
|
||||
|
@ -87,6 +98,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
|||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
long nextVarg(ValueLayout.OfLong layout);
|
||||
|
||||
|
@ -100,6 +112,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
|||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
double nextVarg(ValueLayout.OfDouble layout);
|
||||
|
||||
|
@ -113,6 +126,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
|||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
MemoryAddress nextVarg(ValueLayout.OfAddress layout);
|
||||
|
||||
|
@ -135,6 +149,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
|||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator);
|
||||
|
||||
|
@ -146,6 +161,7 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
|||
* {@linkplain MemorySession#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread other than the thread owning
|
||||
* the {@linkplain #session() session} associated with this variable argument list.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
void skip(MemoryLayout... layouts);
|
||||
|
||||
|
@ -185,6 +201,8 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
|||
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
|
||||
* restricted methods, and use safe and supported functionalities, where possible.
|
||||
*
|
||||
* @implNote variable argument lists created using this method can not detect <a href=VaList.html#safety>out-of-bounds</a> reads.
|
||||
*
|
||||
* @param address a memory address pointing to an existing variable argument list.
|
||||
* @param session the memory session to be associated with the returned variable argument list.
|
||||
* @return a new variable argument list backed by the memory region at {@code address}.
|
||||
|
@ -214,6 +232,8 @@ sealed public interface VaList extends Addressable permits WinVaList, SysVVaList
|
|||
* Note that when there are no elements added to the created va list,
|
||||
* this method will return the same as {@link #empty()}.
|
||||
*
|
||||
* @implNote variable argument lists created using this method can detect <a href=VaList.html#safety>out-of-bounds</a> reads.
|
||||
*
|
||||
* @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements
|
||||
* of the underlying variable argument list.
|
||||
* @param session the memory session to be associated with the new variable arity list.
|
||||
|
|
|
@ -56,6 +56,7 @@ import java.lang.ref.Reference;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -382,13 +383,15 @@ public class SharedUtils {
|
|||
return firstPos != -1 && argIndex >= firstPos;
|
||||
}
|
||||
|
||||
public static NoSuchElementException newVaListNSEE(MemoryLayout layout) {
|
||||
return new NoSuchElementException("No such element: " + layout);
|
||||
}
|
||||
|
||||
public static class SimpleVaArg {
|
||||
public final Class<?> carrier;
|
||||
public final MemoryLayout layout;
|
||||
public final Object value;
|
||||
|
||||
public SimpleVaArg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
this.carrier = carrier;
|
||||
public SimpleVaArg(MemoryLayout layout, Object value) {
|
||||
this.layout = layout;
|
||||
this.value = value;
|
||||
}
|
||||
|
|
|
@ -52,8 +52,6 @@ import static jdk.internal.foreign.abi.aarch64.CallArranger.MAX_REGISTER_ARGUMEN
|
|||
public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
|
||||
static final Class<?> CARRIER = MemoryAddress.class;
|
||||
|
||||
// See AAPCS Appendix B "Variable Argument Lists" for definition of
|
||||
// va_list on AArch64.
|
||||
//
|
||||
|
@ -73,6 +71,8 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
AArch64.C_INT.withName("__vr_offs")
|
||||
).withName("__va_list");
|
||||
|
||||
private static final long STACK_SLOT_SIZE = 8;
|
||||
|
||||
private static final MemoryLayout GP_REG
|
||||
= MemoryLayout.paddingLayout(64).withBitAlignment(64);
|
||||
private static final MemoryLayout FP_REG
|
||||
|
@ -101,22 +101,32 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
= new SharedUtils.EmptyVaList(emptyListAddress());
|
||||
|
||||
private final MemorySegment segment;
|
||||
private MemorySegment stack;
|
||||
private final MemorySegment gpRegsArea;
|
||||
private final long gpLimit;
|
||||
private final MemorySegment fpRegsArea;
|
||||
private final long fpLimit;
|
||||
|
||||
private LinuxAArch64VaList(MemorySegment segment, MemorySegment gpRegsArea, MemorySegment fpRegsArea) {
|
||||
private LinuxAArch64VaList(MemorySegment segment, MemorySegment stack,
|
||||
MemorySegment gpRegsArea, long gpLimit, MemorySegment fpRegsArea, long fpLimit) {
|
||||
this.segment = segment;
|
||||
this.stack = stack;
|
||||
this.gpRegsArea = gpRegsArea;
|
||||
this.gpLimit = gpLimit;
|
||||
this.fpRegsArea = fpRegsArea;
|
||||
this.fpLimit = fpLimit;
|
||||
}
|
||||
|
||||
private static LinuxAArch64VaList readFromSegment(MemorySegment segment) {
|
||||
MemorySegment stack = MemorySegment.ofAddress(stackPtr(segment),
|
||||
Long.MAX_VALUE, segment.session()); // size unknown
|
||||
|
||||
MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).addOffset(-MAX_GP_OFFSET),
|
||||
MAX_GP_OFFSET, segment.session());
|
||||
|
||||
MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).addOffset(-MAX_FP_OFFSET),
|
||||
MAX_FP_OFFSET, segment.session());
|
||||
return new LinuxAArch64VaList(segment, gpRegsArea, fpRegsArea);
|
||||
return new LinuxAArch64VaList(segment, stack, gpRegsArea, MAX_GP_OFFSET, fpRegsArea, MAX_FP_OFFSET);
|
||||
}
|
||||
|
||||
private static MemoryAddress emptyListAddress() {
|
||||
|
@ -165,12 +175,17 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
return offs;
|
||||
}
|
||||
|
||||
private MemoryAddress stackPtr() {
|
||||
private static MemoryAddress stackPtr(MemorySegment segment) {
|
||||
return (MemoryAddress) VH_stack.get(segment);
|
||||
}
|
||||
|
||||
private void stackPtr(MemoryAddress ptr) {
|
||||
VH_stack.set(segment, ptr);
|
||||
private MemoryAddress stackPtr() {
|
||||
return stackPtr(segment);
|
||||
}
|
||||
|
||||
private void setStack(MemorySegment newStack) {
|
||||
stack = newStack;
|
||||
VH_stack.set(segment, stack.address());
|
||||
}
|
||||
|
||||
private void consumeGPSlots(int num) {
|
||||
|
@ -199,54 +214,62 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
return fpRegsArea.byteSize() + vrOffs();
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
if (layout.byteAlignment() > 8) {
|
||||
stackPtr(Utils.alignUp(stackPtr(), 16));
|
||||
private long preAlignOffset(MemoryLayout layout) {
|
||||
long alignmentOffset = 0;
|
||||
if (layout.byteAlignment() > STACK_SLOT_SIZE) {
|
||||
long addr = stack.address().toRawLongValue();
|
||||
alignmentOffset = Utils.alignUp(addr, 16) - addr;
|
||||
}
|
||||
return alignmentOffset;
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
setStack(stack.asSlice(preAlignOffset(layout)));
|
||||
}
|
||||
|
||||
private void postAlignStack(MemoryLayout layout) {
|
||||
stackPtr(Utils.alignUp(stackPtr().addOffset(layout.byteSize()), 8));
|
||||
setStack(stack.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
return (MemoryAddress) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(MemorySegment.class, layout, allocator);
|
||||
return (MemorySegment) read( layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout) {
|
||||
return read(carrier, layout, THROWING_ALLOCATOR);
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
return switch (typeClass) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA, STRUCT_REFERENCE -> {
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), session());
|
||||
MemorySegment slice = stack.asSlice(0, layout.byteSize());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
postAlignStack(layout);
|
||||
|
@ -254,7 +277,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
}
|
||||
case POINTER, INTEGER, FLOAT -> {
|
||||
VarHandle reader = layout.varHandle();
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), session());
|
||||
MemorySegment slice = stack.asSlice(0, layout.byteSize());
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
|
@ -263,6 +286,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
} else {
|
||||
return switch (typeClass) {
|
||||
case STRUCT_REGISTER -> {
|
||||
checkGPElement(layout, numSlots(layout));
|
||||
// Struct is passed packed in integer registers.
|
||||
MemorySegment value = allocator.allocate(layout);
|
||||
long offset = 0;
|
||||
|
@ -275,6 +299,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
yield value;
|
||||
}
|
||||
case STRUCT_HFA -> {
|
||||
checkFPElement(layout, numSlots(layout));
|
||||
// Struct is passed with each element in a separate floating
|
||||
// point register.
|
||||
MemorySegment value = allocator.allocate(layout);
|
||||
|
@ -290,6 +315,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
yield value;
|
||||
}
|
||||
case STRUCT_REFERENCE -> {
|
||||
checkGPElement(layout, 1);
|
||||
// Struct is passed indirectly via a pointer in an integer register.
|
||||
VarHandle ptrReader = AArch64.C_POINTER.varHandle();
|
||||
MemoryAddress ptr = (MemoryAddress) ptrReader.get(
|
||||
|
@ -302,12 +328,14 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
yield seg;
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
checkGPElement(layout, 1);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(gpRegsArea.asSlice(currentGPOffset()));
|
||||
consumeGPSlots(1);
|
||||
yield res;
|
||||
}
|
||||
case FLOAT -> {
|
||||
checkFPElement(layout, 1);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(fpRegsArea.asSlice(currentFPOffset()));
|
||||
consumeFPSlots(1);
|
||||
|
@ -317,6 +345,24 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkGPElement(MemoryLayout layout, long slots) {
|
||||
if ((grOffs() + MAX_GP_OFFSET) + (slots * GP_SLOT_SIZE) > gpLimit) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFPElement(MemoryLayout layout, long slots) {
|
||||
if ((vrOffs() + MAX_FP_OFFSET) + (slots * FP_SLOT_SIZE) > fpLimit) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStackElement(MemoryLayout layout) {
|
||||
if (preAlignOffset(layout) + layout.byteSize() > stack.byteSize()) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
|
@ -325,14 +371,20 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
postAlignStack(layout);
|
||||
} else if (typeClass == TypeClass.FLOAT || typeClass == TypeClass.STRUCT_HFA) {
|
||||
consumeFPSlots(numSlots(layout));
|
||||
long slots = numSlots(layout);
|
||||
checkFPElement(layout, slots);
|
||||
consumeFPSlots((int) slots);
|
||||
} else if (typeClass == TypeClass.STRUCT_REFERENCE) {
|
||||
checkGPElement(layout, 1);
|
||||
consumeGPSlots(1);
|
||||
} else {
|
||||
consumeGPSlots(numSlots(layout));
|
||||
long slots = numSlots(layout);
|
||||
checkGPElement(layout, slots);
|
||||
consumeGPSlots((int) slots);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,7 +406,7 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
public VaList copy() {
|
||||
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session());
|
||||
copy.copyFrom(segment);
|
||||
return new LinuxAArch64VaList(copy, gpRegsArea, fpRegsArea);
|
||||
return new LinuxAArch64VaList(copy, stack, gpRegsArea, gpLimit, fpRegsArea, fpLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -362,8 +414,8 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
return segment.address();
|
||||
}
|
||||
|
||||
private static int numSlots(MemoryLayout layout) {
|
||||
return (int) Utils.alignUp(layout.byteSize(), 8) / 8;
|
||||
private static long numSlots(MemoryLayout layout) {
|
||||
return Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE) / STACK_SLOT_SIZE;
|
||||
}
|
||||
|
||||
private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset,
|
||||
|
@ -405,35 +457,35 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
return arg(layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) {
|
||||
stackArgs.add(new SimpleVaArg(carrier, layout, value));
|
||||
stackArgs.add(new SimpleVaArg(layout, value));
|
||||
} else {
|
||||
switch (typeClass) {
|
||||
case STRUCT_REGISTER -> {
|
||||
|
@ -495,30 +547,32 @@ public non-sealed class LinuxAArch64VaList implements VaList, Scoped {
|
|||
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
|
||||
MemorySegment vaListSegment = allocator.allocate(LAYOUT);
|
||||
MemoryAddress stackArgsPtr = MemoryAddress.NULL;
|
||||
MemorySegment stackArgsSegment;
|
||||
if (!stackArgs.isEmpty()) {
|
||||
long stackArgsSize = stackArgs.stream()
|
||||
.reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), 8), Long::sum);
|
||||
MemorySegment stackArgsSegment = allocator.allocate(stackArgsSize, 16);
|
||||
stackArgsPtr = stackArgsSegment.address();
|
||||
.reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
|
||||
stackArgsSegment = allocator.allocate(stackArgsSize, 16);
|
||||
MemorySegment writeCursor = stackArgsSegment;
|
||||
for (SimpleVaArg arg : stackArgs) {
|
||||
final long alignedSize = Utils.alignUp(arg.layout.byteSize(), 8);
|
||||
stackArgsSegment = Utils.alignUp(stackArgsSegment, alignedSize);
|
||||
final long alignedSize = Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE);
|
||||
writeCursor = Utils.alignUp(writeCursor, alignedSize);
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(stackArgsSegment, arg.value);
|
||||
stackArgsSegment = stackArgsSegment.asSlice(alignedSize);
|
||||
writer.set(writeCursor, arg.value);
|
||||
writeCursor = writeCursor.asSlice(alignedSize);
|
||||
}
|
||||
} else {
|
||||
stackArgsSegment = MemorySegment.ofAddress(MemoryAddress.NULL, 0, session);
|
||||
}
|
||||
|
||||
VH_gr_top.set(vaListSegment, gpRegs.asSlice(gpRegs.byteSize()).address());
|
||||
VH_vr_top.set(vaListSegment, fpRegs.asSlice(fpRegs.byteSize()).address());
|
||||
VH_stack.set(vaListSegment, stackArgsPtr);
|
||||
VH_stack.set(vaListSegment, stackArgsSegment.address());
|
||||
VH_gr_offs.set(vaListSegment, -MAX_GP_OFFSET);
|
||||
VH_vr_offs.set(vaListSegment, -MAX_FP_OFFSET);
|
||||
|
||||
assert gpRegs.session().ownerThread() == vaListSegment.session().ownerThread();
|
||||
assert fpRegs.session().ownerThread() == vaListSegment.session().ownerThread();
|
||||
return new LinuxAArch64VaList(vaListSegment, gpRegs, fpRegs);
|
||||
return new LinuxAArch64VaList(vaListSegment, stackArgsSegment, gpRegs, currentGPOffset, fpRegs, currentFPOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ import static jdk.internal.foreign.abi.SharedUtils.alignUp;
|
|||
* char* instead of the structure defined in the AAPCS.
|
||||
*/
|
||||
public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
||||
public static final Class<?> CARRIER = MemoryAddress.class;
|
||||
private static final long VA_SLOT_SIZE_BYTES = 8;
|
||||
private static final VarHandle VH_address = C_POINTER.varHandle();
|
||||
|
||||
|
@ -66,41 +65,42 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
|||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
return (MemoryAddress) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(MemorySegment.class, layout, allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout) {
|
||||
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
Object res;
|
||||
if (carrier == MemorySegment.class) {
|
||||
if (layout instanceof GroupLayout) {
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
MemoryAddress structAddr = (MemoryAddress) VH_address.get(segment);
|
||||
MemorySegment struct = MemorySegment.ofAddress(structAddr, layout.byteSize(), session());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
|
@ -109,14 +109,17 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
|||
yield seg;
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> {
|
||||
long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
checkElement(layout, size);
|
||||
MemorySegment struct = allocator.allocate(layout)
|
||||
.copyFrom(segment.asSlice(0, layout.byteSize()));
|
||||
segment = segment.asSlice(alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES));
|
||||
segment = segment.asSlice(size);
|
||||
yield struct;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
};
|
||||
} else {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
VarHandle reader = layout.varHandle();
|
||||
res = reader.get(segment);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
|
@ -124,6 +127,13 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
|||
return res;
|
||||
}
|
||||
|
||||
private static long sizeOf(MemoryLayout layout) {
|
||||
return switch (TypeClass.classifyLayout(layout)) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
default -> VA_SLOT_SIZE_BYTES;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
|
@ -131,10 +141,15 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
|||
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
segment = segment.asSlice(switch (TypeClass.classifyLayout(layout)) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
default -> VA_SLOT_SIZE_BYTES;
|
||||
});
|
||||
long size = sizeOf(layout);
|
||||
checkElement(layout, size);
|
||||
segment = segment.asSlice(size);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkElement(MemoryLayout layout, long size) {
|
||||
if (segment.byteSize() < size) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,36 +188,36 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
|||
this.session = session;
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
args.add(new SimpleVaArg(carrier, layout, value));
|
||||
args.add(new SimpleVaArg(layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
return arg(layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
|
@ -212,22 +227,18 @@ public non-sealed class MacOsAArch64VaList implements VaList, Scoped {
|
|||
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
|
||||
|
||||
// Each argument may occupy up to four slots
|
||||
MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size() * 4);
|
||||
|
||||
List<MemorySegment> attachedSegments = new ArrayList<>();
|
||||
attachedSegments.add(segment);
|
||||
long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum);
|
||||
MemorySegment segment = allocator.allocate(allocationSize);
|
||||
MemorySegment cursor = segment;
|
||||
|
||||
for (SimpleVaArg arg : args) {
|
||||
if (arg.carrier == MemorySegment.class) {
|
||||
if (arg.layout instanceof GroupLayout) {
|
||||
MemorySegment msArg = ((MemorySegment) arg.value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(arg.layout);
|
||||
switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment copy = allocator.allocate(arg.layout);
|
||||
copy.copyFrom(msArg); // by-value
|
||||
attachedSegments.add(copy);
|
||||
VH_address.set(cursor, copy.address());
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
|
|
|
@ -48,8 +48,6 @@ import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
|
|||
public non-sealed class SysVVaList implements VaList, Scoped {
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
|
||||
static final Class<?> CARRIER = MemoryAddress.class;
|
||||
|
||||
// struct typedef __va_list_tag __va_list_tag {
|
||||
// unsigned int gp_offset; /* 0 4 */
|
||||
// unsigned int fp_offset; /* 4 4 */
|
||||
|
@ -66,6 +64,8 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
SysV.C_POINTER.withName("reg_save_area")
|
||||
).withName("__va_list_tag");
|
||||
|
||||
private static final long STACK_SLOT_SIZE = 8;
|
||||
|
||||
private static final MemoryLayout GP_REG = MemoryLayout.paddingLayout(64).withBitAlignment(64);
|
||||
private static final MemoryLayout FP_REG = MemoryLayout.paddingLayout(128).withBitAlignment(128);
|
||||
|
||||
|
@ -112,16 +112,25 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress());
|
||||
|
||||
private final MemorySegment segment;
|
||||
private MemorySegment overflowArgArea;
|
||||
private final MemorySegment regSaveArea;
|
||||
private final long gpLimit;
|
||||
private final long fpLimit;
|
||||
|
||||
private SysVVaList(MemorySegment segment, MemorySegment regSaveArea) {
|
||||
private SysVVaList(MemorySegment segment,
|
||||
MemorySegment overflowArgArea,
|
||||
MemorySegment regSaveArea, long gpLimit, long fpLimit) {
|
||||
this.segment = segment;
|
||||
this.overflowArgArea = overflowArgArea;
|
||||
this.regSaveArea = regSaveArea;
|
||||
this.gpLimit = gpLimit;
|
||||
this.fpLimit = fpLimit;
|
||||
}
|
||||
|
||||
private static SysVVaList readFromSegment(MemorySegment segment) {
|
||||
MemorySegment regSaveArea = getRegSaveArea(segment);
|
||||
return new SysVVaList(segment, regSaveArea);
|
||||
MemorySegment overflowArgArea = getArgOverflowArea(segment);
|
||||
return new SysVVaList(segment, overflowArgArea, regSaveArea, MAX_GP_OFFSET, MAX_FP_OFFSET);
|
||||
}
|
||||
|
||||
private static MemoryAddress emptyListAddress() {
|
||||
|
@ -157,72 +166,78 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
VH_fp_offset.set(segment, i);
|
||||
}
|
||||
|
||||
private MemoryAddress stackPtr() {
|
||||
return (MemoryAddress) VH_overflow_arg_area.get(segment);
|
||||
}
|
||||
|
||||
private void stackPtr(MemoryAddress ptr) {
|
||||
VH_overflow_arg_area.set(segment, ptr);
|
||||
}
|
||||
|
||||
private MemorySegment regSaveArea() {
|
||||
return getRegSaveArea(segment);
|
||||
}
|
||||
|
||||
private static MemorySegment getRegSaveArea(MemorySegment segment) {
|
||||
return MemorySegment.ofAddress(((MemoryAddress)VH_reg_save_area.get(segment)),
|
||||
LAYOUT_REG_SAVE_AREA.byteSize(), segment.session());
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
if (layout.byteAlignment() > 8) {
|
||||
stackPtr(Utils.alignUp(stackPtr(), 16));
|
||||
private static MemorySegment getArgOverflowArea(MemorySegment segment) {
|
||||
return MemorySegment.ofAddress(((MemoryAddress)VH_overflow_arg_area.get(segment)),
|
||||
Long.MAX_VALUE, segment.session()); // size unknown
|
||||
}
|
||||
|
||||
private long preAlignOffset(MemoryLayout layout) {
|
||||
long alignmentOffset = 0;
|
||||
if (layout.byteAlignment() > STACK_SLOT_SIZE) {
|
||||
long addr = overflowArgArea.address().toRawLongValue();
|
||||
alignmentOffset = Utils.alignUp(addr, 16) - addr;
|
||||
}
|
||||
return alignmentOffset;
|
||||
}
|
||||
|
||||
private void setOverflowArgArea(MemorySegment newSegment) {
|
||||
overflowArgArea = newSegment;
|
||||
VH_overflow_arg_area.set(segment, overflowArgArea.address());
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
setOverflowArgArea(overflowArgArea.asSlice(preAlignOffset(layout)));
|
||||
}
|
||||
|
||||
private void postAlignStack(MemoryLayout layout) {
|
||||
stackPtr(Utils.alignUp(stackPtr().addOffset(layout.byteSize()), 8));
|
||||
setOverflowArgArea(overflowArgArea.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
return (MemoryAddress) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(MemorySegment.class, layout, allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout) {
|
||||
return read(carrier, layout, THROWING_ALLOCATOR);
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)
|
||||
|| typeClass.inMemory()) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
return switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), session());
|
||||
MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
postAlignStack(layout);
|
||||
|
@ -230,15 +245,14 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
}
|
||||
case POINTER, INTEGER, FLOAT -> {
|
||||
VarHandle reader = layout.varHandle();
|
||||
try (MemorySession localSession = MemorySession.openConfined()) {
|
||||
MemorySegment slice = MemorySegment.ofAddress(stackPtr(), layout.byteSize(), localSession);
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
}
|
||||
MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
checkRegSaveAreaElement(layout, typeClass);
|
||||
return switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
MemorySegment value = allocator.allocate(layout);
|
||||
|
@ -274,6 +288,22 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkRegSaveAreaElement(MemoryLayout layout, TypeClass typeClass) {
|
||||
long gpSize = typeClass.nIntegerRegs() * GP_SLOT_SIZE;
|
||||
long fpSize = typeClass.nVectorRegs() * FP_SLOT_SIZE;
|
||||
if (currentGPOffset() + gpSize > gpLimit
|
||||
|| currentFPOffset() + fpSize > fpLimit) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStackElement(MemoryLayout layout) {
|
||||
long offset = preAlignOffset(layout);
|
||||
if (offset + layout.byteSize() > overflowArgArea.byteSize()) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
|
@ -282,9 +312,11 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
postAlignStack(layout);
|
||||
} else {
|
||||
checkRegSaveAreaElement(layout, typeClass);
|
||||
currentGPOffset(currentGPOffset() + (((int) typeClass.nIntegerRegs()) * GP_SLOT_SIZE));
|
||||
currentFPOffset(currentFPOffset() + (((int) typeClass.nVectorRegs()) * FP_SLOT_SIZE));
|
||||
}
|
||||
|
@ -308,7 +340,7 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
public VaList copy() {
|
||||
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.session());
|
||||
copy.copyFrom(segment);
|
||||
return new SysVVaList(copy, regSaveArea);
|
||||
return new SysVVaList(copy, overflowArgArea, regSaveArea, gpLimit, fpLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -326,8 +358,8 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
return "SysVVaList{"
|
||||
+ "gp_offset=" + currentGPOffset()
|
||||
+ ", fp_offset=" + currentFPOffset()
|
||||
+ ", overflow_arg_area=" + stackPtr()
|
||||
+ ", reg_save_area=" + regSaveArea()
|
||||
+ ", overflow_arg_area=" + overflowArgArea
|
||||
+ ", reg_save_area=" + regSaveArea
|
||||
+ '}';
|
||||
}
|
||||
|
||||
|
@ -345,37 +377,37 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
return arg(layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass)
|
||||
|| typeClass.inMemory()) {
|
||||
// stack it!
|
||||
stackArgs.add(new SimpleVaArg(carrier, layout, value));
|
||||
stackArgs.add(new SimpleVaArg(layout, value));
|
||||
} else {
|
||||
switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
|
@ -421,31 +453,33 @@ public non-sealed class SysVVaList implements VaList, Scoped {
|
|||
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
|
||||
MemorySegment vaListSegment = allocator.allocate(LAYOUT);
|
||||
MemoryAddress stackArgsPtr = MemoryAddress.NULL;
|
||||
MemorySegment stackArgsSegment;
|
||||
if (!stackArgs.isEmpty()) {
|
||||
long stackArgsSize = stackArgs.stream().reduce(0L, (acc, e) -> acc + e.layout.byteSize(), Long::sum);
|
||||
MemorySegment stackArgsSegment = allocator.allocate(stackArgsSize, 16);
|
||||
MemorySegment maOverflowArgArea = stackArgsSegment;
|
||||
long stackArgsSize = stackArgs.stream().reduce(0L,
|
||||
(acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
|
||||
stackArgsSegment = allocator.allocate(stackArgsSize, 16);
|
||||
MemorySegment writeCursor = stackArgsSegment;
|
||||
for (SimpleVaArg arg : stackArgs) {
|
||||
if (arg.layout.byteSize() > 8) {
|
||||
maOverflowArgArea = Utils.alignUp(maOverflowArgArea, Math.min(16, arg.layout.byteSize()));
|
||||
writeCursor = Utils.alignUp(writeCursor, Math.min(16, arg.layout.byteSize()));
|
||||
}
|
||||
if (arg.value instanceof MemorySegment) {
|
||||
maOverflowArgArea.copyFrom((MemorySegment) arg.value);
|
||||
writeCursor.copyFrom((MemorySegment) arg.value);
|
||||
} else {
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(maOverflowArgArea, arg.value);
|
||||
writer.set(writeCursor, arg.value);
|
||||
}
|
||||
maOverflowArgArea = maOverflowArgArea.asSlice(arg.layout.byteSize());
|
||||
writeCursor = writeCursor.asSlice(Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE));
|
||||
}
|
||||
stackArgsPtr = stackArgsSegment.address();
|
||||
} else {
|
||||
stackArgsSegment = MemorySegment.ofAddress(MemoryAddress.NULL, 0, session);
|
||||
}
|
||||
|
||||
VH_fp_offset.set(vaListSegment, (int) FP_OFFSET);
|
||||
VH_overflow_arg_area.set(vaListSegment, stackArgsPtr);
|
||||
VH_overflow_arg_area.set(vaListSegment, stackArgsSegment.address());
|
||||
VH_reg_save_area.set(vaListSegment, reg_save_area.address());
|
||||
assert reg_save_area.session().ownerThread() == vaListSegment.session().ownerThread();
|
||||
return new SysVVaList(vaListSegment, reg_save_area);
|
||||
return new SysVVaList(vaListSegment, stackArgsSegment, reg_save_area, currentGPOffset, currentFPOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,6 @@ import static jdk.internal.foreign.PlatformLayouts.Win64.C_POINTER;
|
|||
// : *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
|
||||
//
|
||||
public non-sealed class WinVaList implements VaList, Scoped {
|
||||
public static final Class<?> CARRIER = MemoryAddress.class;
|
||||
private static final long VA_SLOT_SIZE_BYTES = 8;
|
||||
private static final VarHandle VH_address = C_POINTER.varHandle();
|
||||
|
||||
|
@ -77,38 +76,39 @@ public non-sealed class WinVaList implements VaList, Scoped {
|
|||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(int.class, layout);
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(long.class, layout);
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(double.class, layout);
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAddress nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemoryAddress) read(MemoryAddress.class, layout);
|
||||
return (MemoryAddress) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(MemorySegment.class, layout, allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout) {
|
||||
return read(carrier, layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(Class<?> carrier, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
Object res;
|
||||
if (carrier == MemorySegment.class) {
|
||||
checkElement(layout);
|
||||
if (layout instanceof GroupLayout) {
|
||||
TypeClass typeClass = TypeClass.typeClassFor(layout, false);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
|
@ -130,12 +130,21 @@ public non-sealed class WinVaList implements VaList, Scoped {
|
|||
return res;
|
||||
}
|
||||
|
||||
private void checkElement(MemoryLayout layout) {
|
||||
if (segment.byteSize() < VA_SLOT_SIZE_BYTES) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
sessionImpl().checkValidState();
|
||||
Stream.of(layouts).forEach(Objects::requireNonNull);
|
||||
segment = segment.asSlice(layouts.length * VA_SLOT_SIZE_BYTES);
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
checkElement(layout);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
static WinVaList ofAddress(MemoryAddress addr, MemorySession session) {
|
||||
|
@ -173,36 +182,36 @@ public non-sealed class WinVaList implements VaList, Scoped {
|
|||
this.session = session;
|
||||
}
|
||||
|
||||
private Builder arg(Class<?> carrier, MemoryLayout layout, Object value) {
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
args.add(new SimpleVaArg(carrier, layout, value));
|
||||
args.add(new SimpleVaArg(layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(int.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(long.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(double.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, Addressable value) {
|
||||
return arg(MemoryAddress.class, layout, value.address());
|
||||
return arg(layout, value.address());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(MemorySegment.class, layout, value);
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
|
@ -211,19 +220,16 @@ public non-sealed class WinVaList implements VaList, Scoped {
|
|||
}
|
||||
SegmentAllocator allocator = SegmentAllocator.newNativeArena(session);
|
||||
MemorySegment segment = allocator.allocate(VA_SLOT_SIZE_BYTES * args.size());
|
||||
List<MemorySegment> attachedSegments = new ArrayList<>();
|
||||
attachedSegments.add(segment);
|
||||
MemorySegment cursor = segment;
|
||||
|
||||
for (SimpleVaArg arg : args) {
|
||||
if (arg.carrier == MemorySegment.class) {
|
||||
if (arg.layout instanceof GroupLayout) {
|
||||
MemorySegment msArg = ((MemorySegment) arg.value);
|
||||
TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false);
|
||||
switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment copy = allocator.allocate(arg.layout);
|
||||
copy.copyFrom(msArg); // by-value
|
||||
attachedSegments.add(copy);
|
||||
VH_address.set(cursor, copy.address());
|
||||
}
|
||||
case STRUCT_REGISTER ->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue