Skip to content
Permalink
Browse files

8247937: Specialize downcall binding recipes using MethodHandle combi…

…nators

Reviewed-by: mcimadamore
  • Loading branch information
JornVernee committed Jun 23, 2020
1 parent 54e3c3e commit 5db208ab701cace32a3ff9511911053acee9f72d

Large diffs are not rendered by default.

@@ -22,168 +22,35 @@
*/
package jdk.internal.foreign.abi;

import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.foreign.Utils;
import jdk.incubator.foreign.NativeScope;

import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.function.Function;

public class BindingInterpreter {
private static final VarHandle VH_BYTE = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
private static final VarHandle VH_CHAR = MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder());
private static final VarHandle VH_SHORT = MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder());
private static final VarHandle VH_INT = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
private static final VarHandle VH_LONG = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
private static final VarHandle VH_FLOAT = MemoryHandles.varHandle(float.class, ByteOrder.nativeOrder());
private static final VarHandle VH_DOUBLE = MemoryHandles.varHandle(double.class, ByteOrder.nativeOrder());

static void unbox(Object arg, List<Binding> bindings, Function<VMStorage,
MemoryAddress> ptrFunction, List<? super MemorySegment> buffers) {
static void unbox(Object arg, List<Binding> bindings, StoreFunc storeFunc, NativeScope scope) {
Deque<Object> stack = new ArrayDeque<>();
stack.push(arg);
for (Binding b : bindings) {
switch (b.tag()) {
case MOVE -> {
Binding.Move binding = (Binding.Move) b;
MemoryAddress ptr = ptrFunction.apply(binding.storage());
writeOverSized(ptr, binding.type(), stack.pop());
}
case DEREFERENCE -> {
Binding.Dereference deref = (Binding.Dereference) b;
MemorySegment operand = (MemorySegment) stack.pop();
MemoryAddress baseAddress = operand.baseAddress();
MemoryAddress readAddress = baseAddress.addOffset(deref.offset());
stack.push(read(readAddress, deref.type()));
}
case COPY_BUFFER -> {
Binding.Copy binding = (Binding.Copy) b;
MemorySegment operand = (MemorySegment) stack.pop();
assert operand.byteSize() == binding.size() : "operand size mismatch";
MemorySegment copy = MemorySegment.allocateNative(binding.size(), binding.alignment());
copy.copyFrom(operand.asSlice(0, binding.size()));
buffers.add(copy);
stack.push(copy);
}
case ALLOC_BUFFER ->
throw new UnsupportedOperationException();
case CONVERT_ADDRESS ->
stack.push(((MemoryAddress) stack.pop()).toRawLongValue());
case BASE_ADDRESS ->
stack.push(((MemorySegment) stack.pop()).baseAddress());
case DUP ->
stack.push(stack.peekLast());
default -> throw new IllegalArgumentException("Unsupported tag: " + b);
}
b.unbox(stack, storeFunc, scope);
}
}

static Object box(List<Binding> bindings, Function<VMStorage, MemoryAddress> ptrFunction) {
static Object box(List<Binding> bindings, LoadFunc loadFunc) {
Deque<Object> stack = new ArrayDeque<>();
for (Binding b : bindings) {
switch (b.tag()) {
case MOVE -> {
Binding.Move binding = (Binding.Move) b;
MemoryAddress ptr = ptrFunction.apply(binding.storage());
stack.push(read(ptr, binding.type()));
}
case DEREFERENCE -> {
Binding.Dereference binding = (Binding.Dereference) b;
Object value = stack.pop();
MemorySegment operand = (MemorySegment) stack.pop();
MemoryAddress baseAddress = operand.baseAddress();
MemoryAddress writeAddress = baseAddress.addOffset(binding.offset());
write(writeAddress, binding.type(), value);
}
case COPY_BUFFER -> {
Binding.Copy binding = (Binding.Copy) b;
MemoryAddress operand = (MemoryAddress) stack.pop();
operand = MemoryAddressImpl.ofLongUnchecked(operand.toRawLongValue(), binding.size());
MemorySegment copy = MemorySegment.allocateNative(binding.size(), binding.alignment());
copy.copyFrom(operand.segment().asSlice(0, binding.size()));
stack.push(copy); // leaked
}
case ALLOC_BUFFER -> {
Binding.Allocate binding = (Binding.Allocate) b;
stack.push(MemorySegment.allocateNative(binding.size(), binding.alignment()));
}
case CONVERT_ADDRESS ->
stack.push(MemoryAddress.ofLong((long) stack.pop()));
case BASE_ADDRESS ->
stack.push(((MemorySegment) stack.pop()).baseAddress());
case DUP ->
stack.push(stack.peekLast());
default -> throw new IllegalArgumentException("Unsupported tag: " + b);
}
b.box(stack, loadFunc);
}

return stack.pop();
}

private static void writeOverSized(MemoryAddress ptr, Class<?> type, Object o) {
// use VH_LONG for integers to zero out the whole register in the process
if (type == long.class) {
VH_LONG.set(ptr, (long) o);
} else if (type == int.class) {
VH_LONG.set(ptr, (long) (int) o);
} else if (type == short.class) {
VH_LONG.set(ptr, (long) (short) o);
} else if (type == char.class) {
VH_LONG.set(ptr, (long) (char) o);
} else if (type == byte.class) {
VH_LONG.set(ptr, (long) (byte) o);
} else if (type == float.class) {
VH_FLOAT.set(ptr, (float) o);
} else if (type == double.class) {
VH_DOUBLE.set(ptr, (double) o);
} else {
throw new IllegalArgumentException("Unsupported carrier: " + type);
}
}

private static void write(MemoryAddress ptr, Class<?> type, Object o) {
if (type == long.class) {
VH_LONG.set(ptr, (long) o);
} else if (type == int.class) {
VH_INT.set(ptr, (int) o);
} else if (type == short.class) {
VH_SHORT.set(ptr, (short) o);
} else if (type == char.class) {
VH_CHAR.set(ptr, (char) o);
} else if (type == byte.class) {
VH_BYTE.set(ptr, (byte) o);
} else if (type == float.class) {
VH_FLOAT.set(ptr, (float) o);
} else if (type == double.class) {
VH_DOUBLE.set(ptr, (double) o);
} else {
throw new IllegalArgumentException("Unsupported carrier: " + type);
}
interface StoreFunc {
void store(VMStorage storage, Class<?> type, Object o);
}

private static Object read(MemoryAddress ptr, Class<?> type) {
if (type == long.class) {
return (long) VH_LONG.get(ptr);
} else if (type == int.class) {
return (int) VH_INT.get(ptr);
} else if (type == short.class) {
return (short) VH_SHORT.get(ptr);
} else if (type == char.class) {
return (char) VH_CHAR.get(ptr);
} else if (type == byte.class) {
return (byte) VH_BYTE.get(ptr);
} else if (type == float.class) {
return (float) VH_FLOAT.get(ptr);
} else if (type == double.class) {
return (double) VH_DOUBLE.get(ptr);
} else {
throw new IllegalArgumentException("Unsupported carrier: " + type);
}
interface LoadFunc {
Object load(VMStorage storage, Class<?> type);
}
}
@@ -26,6 +26,7 @@

import java.lang.invoke.MethodType;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class CallingSequence {
@@ -43,13 +44,20 @@ public CallingSequence(MethodType mt, FunctionDescriptor desc,
this.argumentBindings = argumentBindings;
}

public Stream<Binding.Move> moveBindings() {
public Stream<Binding.Move> argMoveBindings() {
return argumentBindings.stream()
.flatMap(List::stream)
.filter(Binding.Move.class::isInstance)
.map(Binding.Move.class::cast);
}

public Stream<Binding.Move> retMoveBindings() {
return returnBindings()
.stream()
.filter(Binding.Move.class::isInstance)
.map(Binding.Move.class::cast);
}

public int argumentCount() {
return argumentBindings.size();
}
@@ -58,6 +66,10 @@ public int argumentCount() {
return argumentBindings.get(i);
}

public Stream<Binding> argumentBindings() {
return argumentBindings.stream().flatMap(List::stream);
}

public List<Binding> returnBindings() {
return returnBindings;
}
@@ -23,9 +23,7 @@
package jdk.internal.foreign.abi;

import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import sun.security.action.GetPropertyAction;

import java.lang.invoke.MethodType;
@@ -81,102 +79,31 @@ private void verifyBindings(boolean forArguments, Class<?> carrier, List<Binding
}
}

private static void checkType(Class<?> actualType, Class<?> expectedType) {
if (expectedType != actualType) {
throw new IllegalArgumentException(
String.format("Invalid operand type: %s. %s expected", actualType, expectedType));
}
}

private static void verifyUnboxBindings(Class<?> inType, List<Binding> bindings) {
Deque<Class<?>> stack = new ArrayDeque<>();
stack.push(inType);

for (Binding b : bindings) {
switch (b.tag()) {
case MOVE -> {
Class<?> actualType = stack.pop();
Class<?> expectedType = ((Binding.Move) b).type();
checkType(actualType, expectedType);
}
case DEREFERENCE -> {
Class<?> actualType = stack.pop();
checkType(actualType, MemorySegment.class);
Class<?> newType = ((Binding.Dereference) b).type();
stack.push(newType);
}
case BASE_ADDRESS -> {
Class<?> actualType = stack.pop();
checkType(actualType, MemorySegment.class);
stack.push(MemoryAddress.class);
}
case CONVERT_ADDRESS -> {
Class<?> actualType = stack.pop();
checkType(actualType, MemoryAddress.class);
stack.push(long.class);
}
case ALLOC_BUFFER ->
throw new UnsupportedOperationException();
case COPY_BUFFER -> {
Class<?> actualType = stack.pop();
checkType(actualType, MemorySegment.class);
stack.push(MemorySegment.class);
}
case DUP ->
stack.push(stack.peekLast());
default -> throw new IllegalArgumentException("Unknown binding: " + b);
}
b.verifyUnbox(stack);
}

if (!stack.isEmpty()) {
throw new IllegalArgumentException("Stack must be empty after recipe");
}
}

private static void verifyBoxBindings(Class<?> outType, List<Binding> bindings) {
private static void verifyBoxBindings(Class<?> expectedReturnType, List<Binding> bindings) {
Deque<Class<?>> stack = new ArrayDeque<>();

for (Binding b : bindings) {
switch (b.tag()) {
case MOVE -> {
Class<?> newType = ((Binding.Move) b).type();
stack.push(newType);
}
case DEREFERENCE -> {
Class<?> storeType = stack.pop();
checkType(storeType, ((Binding.Dereference) b).type());
Class<?> segmentType = stack.pop();
checkType(segmentType, MemorySegment.class);
}
case CONVERT_ADDRESS -> {
Class<?> actualType = stack.pop();
checkType(actualType, long.class);
stack.push(MemoryAddress.class);
}
case BASE_ADDRESS -> {
Class<?> actualType = stack.pop();
checkType(actualType, MemorySegment.class);
stack.push(MemoryAddress.class);
}
case ALLOC_BUFFER -> {
stack.push(MemorySegment.class);
}
case COPY_BUFFER -> {
Class<?> actualType = stack.pop();
checkType(actualType, MemoryAddress.class);
stack.push(MemorySegment.class);
}
case DUP ->
stack.push(stack.peekLast());
default -> throw new IllegalArgumentException("Unknown binding: " + b);
}
b.verifyBox(stack);
}

if (stack.size() != 1) {
throw new IllegalArgumentException("Stack must contain exactly 1 value");
}

Class<?> actualReturnType = stack.pop();
checkType(actualReturnType, outType);
SharedUtils.checkType(actualReturnType, expectedReturnType);
}
}

0 comments on commit 5db208a

Please sign in to comment.
You can’t perform that action at this time.