From 54c1b7ac730b1d5f97c1452d008cc8954273efe2 Mon Sep 17 00:00:00 2001 From: Lukas Stadler <lukas.stadler@oracle.com> Date: Wed, 2 Aug 2017 10:32:35 +0200 Subject: [PATCH] prototype handling of character vectors in NFI .C --- .../truffle/r/ffi/impl/nfi/TruffleNFI_C.java | 126 +++++++++++++++++- .../oracle/truffle/r/runtime/ffi/CRFFI.java | 11 +- 2 files changed, 129 insertions(+), 8 deletions(-) diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java index 2913e0e1b7..54e3f8b3a3 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java @@ -22,25 +22,147 @@ */ package com.oracle.truffle.r.ffi.impl.nfi; +import java.nio.charset.StandardCharsets; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.CanResolve; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.interop.MessageResolution; +import com.oracle.truffle.api.interop.Resolve; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter; import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_CFactory.TruffleNFI_InvokeCNodeGen; import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RStringVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractAtomicVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.ffi.CRFFI; import com.oracle.truffle.r.runtime.ffi.NativeCallInfo; +import sun.misc.Unsafe; + public class TruffleNFI_C implements CRFFI { + + @MessageResolution(receiverType = StringWrapper.class) + public static class StringWrapperMR { + + @Resolve(message = "IS_POINTER") + public abstract static class StringWrapperNativeIsPointerNode extends Node { + protected Object access(@SuppressWarnings("unused") StringWrapper receiver) { + return true; + } + } + + @Resolve(message = "AS_POINTER") + public abstract static class StringWrapperNativeAsPointerNode extends Node { + protected Object access(StringWrapper receiver) { + return receiver.asPointer(); + } + } + + @CanResolve + public abstract static class StringWrapperCheck extends Node { + + protected static boolean test(TruffleObject receiver) { + return receiver instanceof StringWrapper; + } + } + } + + public static final class StringWrapper implements TruffleObject { + + private final RAbstractStringVector vector; + private long address; + + public StringWrapper(RAbstractStringVector vector) { + this.vector = vector; + } + + @Override + public ForeignAccess getForeignAccess() { + return StringWrapperMRForeign.ACCESS; + } + + public long asPointer() { + if (address == 0) { + address = allocate(); + } + return address; + } + + @TruffleBoundary + private long allocate() { + int length = vector.getLength(); + int size = length * 8; + byte[][] bytes = new byte[length][]; + for (int i = 0; i < length; i++) { + String element = vector.getDataAt(i); + bytes[i] = element.getBytes(StandardCharsets.US_ASCII); + size += bytes[i].length + 1; + } + long memory = UnsafeAdapter.UNSAFE.allocateMemory(size); + long ptr = memory + length * 8; // start of the actual character data + for (int i = 0; i < length; i++) { + UnsafeAdapter.UNSAFE.putLong(memory + i * 8, ptr); + UnsafeAdapter.UNSAFE.copyMemory(bytes[i], Unsafe.ARRAY_BYTE_BASE_OFFSET, null, ptr, bytes[i].length); + ptr += bytes[i].length; + UnsafeAdapter.UNSAFE.putByte(ptr++, (byte) 0); + } + assert ptr == memory + size : "should have filled everything"; + return memory; + } + + public RAbstractStringVector copyBack(RAbstractStringVector original) { + if (address == 0) { + return original; + } else { + RStringVector result = original.materialize(); + String[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy(); + for (int i = 0; i < data.length; i++) { + long ptr = UnsafeAdapter.UNSAFE.getLong(address + i * 8); + int length = 0; + while (UnsafeAdapter.UNSAFE.getByte(ptr + length) != 0) { + length++; + } + byte[] bytes = new byte[length]; + UnsafeAdapter.UNSAFE.copyMemory(null, ptr, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, length); + data[i] = new String(bytes, StandardCharsets.US_ASCII); + } + UnsafeAdapter.UNSAFE.freeMemory(address); + return RDataFactory.createStringVector(data, true); + } + } + } + abstract static class TruffleNFI_InvokeCNode extends InvokeCNode { @Child private Node bindNode = Message.createInvoke(1).createNode(); + @Override + protected Object getNativeArgument(int index, ArgumentType type, RAbstractAtomicVector vector) { + if (type == ArgumentType.VECTOR_STRING) { + return new StringWrapper((RAbstractStringVector) vector); + } else { + return super.getNativeArgument(index, type, vector); + } + } + + @Override + protected Object postProcessArgument(ArgumentType type, RAbstractAtomicVector vector, Object nativeArgument) { + if (type == ArgumentType.VECTOR_STRING) { + return ((StringWrapper) nativeArgument).copyBack((RAbstractStringVector) vector); + } else { + return super.postProcessArgument(type, vector, nativeArgument); + } + } + @Specialization(guards = "args.length == 0") protected void invokeCall0(NativeCallInfo nativeCallInfo, @SuppressWarnings("unused") Object[] args, @SuppressWarnings("unused") boolean hasStrings, @Cached("createExecute(args.length)") Node executeNode) { @@ -84,8 +206,8 @@ public class TruffleNFI_C implements CRFFI { sb.append("[sint32]"); } else if (arg instanceof double[]) { sb.append("[double]"); - } else if (arg instanceof byte[][]) { - sb.append("[pointer]"); + } else if (arg instanceof StringWrapper) { + sb.append("pointer"); } else { throw RInternalError.unimplemented(".C type: " + arg.getClass().getSimpleName()); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java index 5143ea58df..80d1e70c87 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java @@ -40,7 +40,6 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector; import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractVector; import com.oracle.truffle.r.runtime.nodes.RBaseNode; /** @@ -50,7 +49,7 @@ public interface CRFFI { public static abstract class InvokeCNode extends RBaseNode { - enum ArgumentType { + public enum ArgumentType { VECTOR_DOUBLE, VECTOR_INT, VECTOR_LOGICAL, @@ -71,7 +70,7 @@ public interface CRFFI { protected abstract void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings); @TruffleBoundary - private Object getNativeArgument(int index, ArgumentType type, RAbstractAtomicVector vector) { + protected Object getNativeArgument(int index, ArgumentType type, RAbstractAtomicVector vector) { CompilerAsserts.neverPartOfCompilation(); switch (type) { case VECTOR_DOUBLE: { @@ -121,7 +120,7 @@ public interface CRFFI { } } - protected Object[] getNativeArguments(Object[] array, ArgumentType[] argTypes) { + private Object[] getNativeArguments(Object[] array, ArgumentType[] argTypes) { Object[] nativeArgs = new Object[array.length]; for (int i = 0; i < array.length; i++) { nativeArgs[i] = getNativeArgument(i, argTypes[i], (RAbstractAtomicVector) array[i]); @@ -130,7 +129,7 @@ public interface CRFFI { } @TruffleBoundary - private static Object postProcessArgument(ArgumentType type, RAbstractAtomicVector vector, Object nativeArgument) { + protected Object postProcessArgument(ArgumentType type, RAbstractAtomicVector vector, Object nativeArgument) { switch (type) { case VECTOR_STRING: return ((RAbstractStringVector) vector).materialize().copyResetData(decodeStrings((byte[][]) nativeArgument)); @@ -151,7 +150,7 @@ public interface CRFFI { } } - protected Object[] postProcessArguments(Object[] array, ArgumentType[] argTypes, Object[] nativeArgs) { + private Object[] postProcessArguments(Object[] array, ArgumentType[] argTypes, Object[] nativeArgs) { Object[] results = new Object[array.length]; for (int i = 0; i < array.length; i++) { results[i] = postProcessArgument(argTypes[i], (RAbstractAtomicVector) array[i], nativeArgs[i]); -- GitLab