< prev index next >

src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/abi/Binding.java

Print this page
@@ -20,16 +20,36 @@
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  package jdk.internal.foreign.abi;
  
+ import jdk.incubator.foreign.MemoryAddress;
+ import jdk.incubator.foreign.MemoryHandles;
  import jdk.incubator.foreign.MemoryLayout;
+ import jdk.incubator.foreign.MemorySegment;
+ import jdk.incubator.foreign.NativeScope;
+ import jdk.internal.foreign.MemoryAddressImpl;
  
+ import java.lang.invoke.MethodHandle;
+ import java.lang.invoke.MethodHandles;
+ import java.lang.invoke.MethodType;
  import java.util.ArrayList;
+ import java.util.Deque;
  import java.util.List;
+ import java.util.Map;
  import java.util.Objects;
  
+ import java.lang.invoke.VarHandle;
+ import java.nio.ByteOrder;
+ import java.util.concurrent.ConcurrentHashMap;
+ 
+ import static java.lang.invoke.MethodHandles.collectArguments;
+ import static java.lang.invoke.MethodHandles.filterArguments;
+ import static java.lang.invoke.MethodHandles.insertArguments;
+ import static java.lang.invoke.MethodHandles.permuteArguments;
+ import static java.lang.invoke.MethodType.methodType;
+ 
  /**
   * The binding operators defined in the Binding class can be combined into argument and return value processing 'recipes'.
   *
   * The binding operators are interpreted using a stack-base interpreter. Operators can either consume operands from the
   * stack, or push them onto the stack.

@@ -180,10 +200,34 @@
   * none
   *
   * --------------------
   */
  public abstract class Binding {
+     private static final MethodHandle MH_UNBOX_ADDRESS;
+     private static final MethodHandle MH_BOX_ADDRESS;
+     private static final MethodHandle MH_BASE_ADDRESS;
+     private static final MethodHandle MH_COPY_BUFFER;
+     private static final MethodHandle MH_ALLOCATE_BUFFER;
+ 
+     static {
+         try {
+             MethodHandles.Lookup lookup = MethodHandles.lookup();
+             MH_UNBOX_ADDRESS = lookup.findVirtual(MemoryAddress.class, "toRawLongValue",
+                     methodType(long.class));
+             MH_BOX_ADDRESS = lookup.findStatic(MemoryAddress.class, "ofLong",
+                     methodType(MemoryAddress.class, long.class));
+             MH_BASE_ADDRESS = lookup.findVirtual(MemorySegment.class, "baseAddress",
+                     methodType(MemoryAddress.class));
+             MH_COPY_BUFFER = lookup.findStatic(Binding.class, "copyBuffer",
+                     methodType(MemorySegment.class, MemorySegment.class, long.class, long.class, NativeScope.class));
+             MH_ALLOCATE_BUFFER = lookup.findStatic(MemorySegment.class, "allocateNative",
+                     methodType(MemorySegment.class, long.class, long.class));
+         } catch (ReflectiveOperationException e) {
+             throw new RuntimeException(e);
+         }
+     }
+ 
      enum Tag {
          MOVE,
          DEREFERENCE,
          COPY_BUFFER,
          ALLOC_BUFFER,

@@ -200,15 +244,51 @@
  
      public Tag tag() {
          return tag;
      }
  
+     public abstract void verifyUnbox(Deque<Class<?>> stack);
+     public abstract void verifyBox(Deque<Class<?>> stack);
+ 
+     public abstract void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope);
+     public abstract void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc);
+ 
+     public abstract MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos);
+     public abstract MethodHandle specializeBox(MethodHandle returnFilter);
+ 
+     private static MethodHandle mergeArguments(MethodHandle mh, int sourceIndex, int destIndex) {
+         MethodType oldType = mh.type();
+         Class<?> sourceType = oldType.parameterType(sourceIndex);
+         Class<?> destType = oldType.parameterType(destIndex);
+         if (sourceType != destType) {
+             // TODO meet?
+             throw new IllegalArgumentException("Parameter types differ: " + sourceType + " != " + destType);
+         }
+         MethodType newType = oldType.dropParameterTypes(destIndex, destIndex + 1);
+         int[] reorder = new int[oldType.parameterCount()];
+         assert destIndex > sourceIndex;
+         for (int i = 0, index = 0; i < reorder.length; i++) {
+             if (i != destIndex) {
+                 reorder[i] = index++;
+             } else {
+                 reorder[i] = sourceIndex;
+             }
+         }
+         return permuteArguments(mh, newType, reorder);
+     }
+ 
      private static void checkType(Class<?> type) {
          if (!type.isPrimitive() || type == void.class || type == boolean.class)
              throw new IllegalArgumentException("Illegal type: " + type);
      }
  
+     private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, NativeScope allocator) {
+         MemorySegment copy = allocator.allocate(size, alignment).segment();
+         copy.copyFrom(operand.asSlice(0, size));
+         return copy;
+     }
+ 
      public static Move move(VMStorage storage, Class<?> type) {
          checkType(type);
          return new Move(storage, type);
      }
  

@@ -334,10 +414,42 @@
  
          @Override
          public int hashCode() {
              return Objects.hash(tag(), storage, type);
          }
+ 
+         @Override
+         public void verifyUnbox(Deque<Class<?>> stack) {
+             Class<?> actualType = stack.pop();
+             Class<?> expectedType = type;
+             SharedUtils.checkType(actualType, expectedType);
+         }
+ 
+         @Override
+         public void verifyBox(Deque<Class<?>> stack) {
+             stack.push(type);
+         }
+ 
+         @Override
+         public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
+             storeFunc.store(storage, type, stack.pop());
+         }
+ 
+         @Override
+         public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
+             stack.push(loadFunc.load(storage, type));
+         }
+ 
+         @Override
+         public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
+             return specializedHandle; // no-op
+         }
+ 
+         @Override
+         public MethodHandle specializeBox(MethodHandle returnFilter) {
+             return returnFilter; // no-op
+         }
      }
  
      /**
       * DEREFERENCE([offset into memory region], [type])
       *   When unboxing: pops a MemorySegment from the operand stack,

@@ -384,10 +496,65 @@
  
          @Override
          public int hashCode() {
              return Objects.hash(tag(), offset, type);
          }
+ 
+         @Override
+         public void verifyUnbox(Deque<Class<?>> stack) {
+             Class<?> actualType = stack.pop();
+             SharedUtils.checkType(actualType, MemorySegment.class);
+             Class<?> newType = type;
+             stack.push(newType);
+         }
+ 
+         @Override
+         public void verifyBox(Deque<Class<?>> stack) {
+             Class<?> storeType = stack.pop();
+             SharedUtils.checkType(storeType, type);
+             Class<?> segmentType = stack.pop();
+             SharedUtils.checkType(segmentType, MemorySegment.class);
+         }
+ 
+         @Override
+         public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
+             MemorySegment operand = (MemorySegment) stack.pop();
+             MemoryAddress baseAddress = operand.baseAddress();
+             MemoryAddress readAddress = baseAddress.addOffset(offset);
+             stack.push(SharedUtils.read(readAddress, type));
+         }
+ 
+         @Override
+         public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
+             Object value = stack.pop();
+             MemorySegment operand = (MemorySegment) stack.pop();
+             MemoryAddress baseAddress = operand.baseAddress();
+             MemoryAddress writeAddress = baseAddress.addOffset(offset);
+             SharedUtils.write(writeAddress, type, value);
+         }
+ 
+         private VarHandle varHandle() {
+             return MemoryHandles.withOffset(MemoryHandles.varHandle(type, ByteOrder.nativeOrder()), offset);
+         }
+ 
+         @Override
+         public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
+             MethodHandle filter = filterArguments(
+                 varHandle()
+                     .toMethodHandle(VarHandle.AccessMode.GET)
+                     .asType(methodType(type, MemoryAddress.class)), 0, MH_BASE_ADDRESS);
+             return filterArguments(specializedHandle, insertPos, filter);
+         }
+ 
+         @Override
+         public MethodHandle specializeBox(MethodHandle returnFilter) {
+             MethodHandle setter = varHandle().toMethodHandle(VarHandle.AccessMode.SET);
+             setter = filterArguments(
+                 setter.asType(methodType(void.class, MemoryAddress.class, type)),
+                 0, MH_BASE_ADDRESS);
+             return collectArguments(returnFilter, returnFilter.type().parameterCount(), setter);
+         }
      }
  
      /**
       * COPY([size], [alignment])
       *   Creates a new MemorySegment with the given [size] and [alignment],

@@ -432,10 +599,53 @@
  
          @Override
          public int hashCode() {
              return Objects.hash(tag(), size, alignment);
          }
+ 
+         @Override
+         public void verifyUnbox(Deque<Class<?>> stack) {
+             Class<?> actualType = stack.pop();
+             SharedUtils.checkType(actualType, MemorySegment.class);
+             stack.push(MemorySegment.class);
+         }
+ 
+         @Override
+         public void verifyBox(Deque<Class<?>> stack) {
+             Class<?> actualType = stack.pop();
+             SharedUtils.checkType(actualType, MemoryAddress.class);
+             stack.push(MemorySegment.class);
+         }
+ 
+         @Override
+         public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
+             MemorySegment operand = (MemorySegment) stack.pop();
+             MemorySegment copy = scope.allocate(size, alignment).segment();
+             copy.copyFrom(operand.asSlice(0, size));
+             stack.push(copy);
+         }
+ 
+         @Override
+         public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
+             MemoryAddress operand = (MemoryAddress) stack.pop();
+             operand = MemoryAddressImpl.ofLongUnchecked(operand.toRawLongValue(), size);
+             MemorySegment copy = MemorySegment.allocateNative(size, alignment);
+             copy.copyFrom(operand.segment().asSlice(0, size));
+             stack.push(copy); // leaked
+         }
+ 
+         @Override
+         public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
+             MethodHandle filter = insertArguments(MH_COPY_BUFFER, 1, size, alignment);
+             specializedHandle = collectArguments(specializedHandle, insertPos, filter);
+             return mergeArguments(specializedHandle, 0, insertPos + 1);
+         }
+ 
+         @Override
+         public MethodHandle specializeBox(MethodHandle returnFilter) {
+             throw new UnsupportedOperationException();
+         }
      }
  
      /**
       * ALLOCATE([size], [alignment])
       *   Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.

@@ -478,10 +688,40 @@
  
          @Override
          public int hashCode() {
              return Objects.hash(tag(), size, alignment);
          }
+ 
+         @Override
+         public void verifyUnbox(Deque<Class<?>> stack) {
+             throw new UnsupportedOperationException();
+         }
+ 
+         @Override
+         public void verifyBox(Deque<Class<?>> stack) {
+             stack.push(MemorySegment.class);
+         }
+ 
+         @Override
+         public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
+             throw new UnsupportedOperationException();
+         }
+ 
+         @Override
+         public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
+             stack.push(MemorySegment.allocateNative(size, alignment));
+         }
+ 
+         @Override
+         public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
+             throw new UnsupportedOperationException();
+         }
+ 
+         @Override
+         public MethodHandle specializeBox(MethodHandle returnFilter) {
+             return collectArguments(returnFilter, 0, insertArguments(MH_ALLOCATE_BUFFER, 0, size, alignment));
+         }
      }
  
      /**
       * CONVERT_ADDRESS()
       *   When unboxing: pops a 'MemoryAddress' from the operand stack, converts it to a 'long',

@@ -510,10 +750,44 @@
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              return o != null && getClass() == o.getClass();
          }
+ 
+         @Override
+         public void verifyUnbox(Deque<Class<?>> stack) {
+             Class<?> actualType = stack.pop();
+             SharedUtils.checkType(actualType, MemoryAddress.class);
+             stack.push(long.class);
+         }
+ 
+         @Override
+         public void verifyBox(Deque<Class<?>> stack) {
+             Class<?> actualType = stack.pop();
+             SharedUtils.checkType(actualType, long.class);
+             stack.push(MemoryAddress.class);
+         }
+ 
+         @Override
+         public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
+             stack.push(((MemoryAddress) stack.pop()).toRawLongValue());
+         }
+ 
+         @Override
+         public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
+             stack.push(MemoryAddress.ofLong((long) stack.pop()));
+         }
+ 
+         @Override
+         public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
+             return filterArguments(specializedHandle, insertPos, MH_UNBOX_ADDRESS);
+         }
+ 
+         @Override
+         public MethodHandle specializeBox(MethodHandle returnFilter) {
+             return filterArguments(returnFilter, 0, MH_BOX_ADDRESS);
+         }
      }
  
      /**
       * BASE_ADDRESS()
       *   Pops a MemorySegment from the operand stack, and takes the base address of the segment

@@ -540,10 +814,44 @@
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              return o != null && getClass() == o.getClass();
          }
+ 
+         @Override
+         public void verifyUnbox(Deque<Class<?>> stack) {
+             Class<?> actualType = stack.pop();
+             SharedUtils.checkType(actualType, MemorySegment.class);
+             stack.push(MemoryAddress.class);
+         }
+ 
+         @Override
+         public void verifyBox(Deque<Class<?>> stack) {
+             Class<?> actualType = stack.pop();
+             SharedUtils.checkType(actualType, MemorySegment.class);
+             stack.push(MemoryAddress.class);
+         }
+ 
+         @Override
+         public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
+             stack.push(((MemorySegment) stack.pop()).baseAddress());
+         }
+ 
+         @Override
+         public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
+             stack.push(((MemorySegment) stack.pop()).baseAddress());
+         }
+ 
+         @Override
+         public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
+             return filterArguments(specializedHandle, insertPos, MH_BASE_ADDRESS);
+         }
+ 
+         @Override
+         public MethodHandle specializeBox(MethodHandle returnFilter) {
+             throw new UnsupportedOperationException();
+         }
      }
  
      /**
       * DUP()
       *   Duplicates the value on the top of the operand stack (without popping it!),

@@ -570,7 +878,73 @@
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              return o != null && getClass() == o.getClass();
          }
+ 
+         @Override
+         public void verifyUnbox(Deque<Class<?>> stack) {
+             stack.push(stack.peekLast());
+         }
+ 
+         @Override
+         public void verifyBox(Deque<Class<?>> stack) {
+             stack.push(stack.peekLast());
+         }
+ 
+         @Override
+         public void unbox(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc, NativeScope scope) {
+             stack.push(stack.peekLast());
+         }
+ 
+         @Override
+         public void box(Deque<Object> stack, BindingInterpreter.LoadFunc loadFunc) {
+             stack.push(stack.peekLast());
+         }
+ 
+         /*
+          * Fixes up Y-shaped data graphs (produced by DEREFERENCE):
+          *
+          * 1. DUP()
+          * 2. DEREFERENCE(0, int.class)
+          * 3. MOVE  (ignored)
+          * 4. DEREFERENCE(4, int.class)
+          * 5. MOVE  (ignored)
+          *
+          * (specialized in reverse!)
+          *
+          * 5. (int, int) -> void                       insertPos = 1
+          * 4. (MemorySegment, int) -> void             insertPos = 1
+          * 3. (MemorySegment, int) -> void             insertPos = 0
+          * 2. (MemorySegment, MemorySegment) -> void   insertPos = 0
+          * 1. (MemorySegment) -> void                  insertPos = 0
+          *
+          */
+         @Override
+         public MethodHandle specializeUnbox(MethodHandle specializedHandle, int insertPos) {
+             return mergeArguments(specializedHandle, insertPos, insertPos + 1);
+         }
+ 
+         /*
+          * Fixes up Y-shaped data graphs (produced by DEREFERENCE):
+          *
+          * 1. ALLOCATE_BUFFER(4, 4)
+          * 2. DUP
+          * 3. MOVE  (ignored)
+          * 4. DEREFERNCE(0, int.class)
+          *
+          * (specialized in reverse!)
+          *
+          * input: (MemorySegment) -> MemorySegment (identity function of high-level return)
+          * 4. (MemorySegment, MemorySegment, int) -> MemorySegment
+          * 3. (MemorySegment, MemorySegment, int) -> MemorySegment
+          * 2. (MemorySegment, int) -> MemorySegment
+          * 1. (int) -> MemorySegment
+          *
+          */
+         @Override
+         public MethodHandle specializeBox(MethodHandle returnFilter) {
+             // assumes shape like: (MS, ..., MS, T) R
+             return mergeArguments(returnFilter, 0, returnFilter.type().parameterCount() - 2);
+         }
      }
  }
< prev index next >