diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java index 5b0c8f546e17f2d739ebcdce3fad98451d759eae..4fe1bc302f4ab5e310006f81af0af294e7c4c3a0 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java @@ -41,7 +41,7 @@ class TruffleLLVM_C implements CRFFI { private int numArgs; @Override - public synchronized void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings) { + public synchronized void execute(NativeCallInfo nativeCallInfo, Object[] args) { Object[] wargs = wrap(args); try { if (messageNode == null) { 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 54e3f8b3a3f59a4ded7b4dcea1ab7dac7e913361..d74f6aec88b242439f430ab7687e79d592d3775e 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,122 +22,39 @@ */ package com.oracle.truffle.r.ffi.impl.nfi; -import java.nio.charset.StandardCharsets; - +import com.oracle.truffle.api.CompilerAsserts; 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; - } + private static final String[] SIGNATURES = new String[32]; - 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; + private static String getSignatureForArity(int arity) { + CompilerAsserts.neverPartOfCompilation(); + if (arity >= SIGNATURES.length || SIGNATURES[arity] == null) { + StringBuilder str = new StringBuilder().append('('); + for (int i = 0; i < arity; i++) { + str.append(i > 0 ? ", " : ""); + str.append("pointer"); } - 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); + String signature = str.append("): void").toString(); + if (arity < SIGNATURES.length) { + SIGNATURES[arity] = signature; } + return signature; + } else { + return SIGNATURES[arity]; } } @@ -145,31 +62,25 @@ public class TruffleNFI_C implements CRFFI { @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); + @TruffleBoundary + protected TruffleObject getFunction(TruffleObject address, int arity) { + // cache signatures + try { + return (TruffleObject) ForeignAccess.sendInvoke(bindNode, address, "bind", getSignatureForArity(arity)); + } catch (InteropException ex) { + throw RInternalError.shouldNotReachHere(ex); } } - @Specialization(guards = "args.length == 0") - protected void invokeCall0(NativeCallInfo nativeCallInfo, @SuppressWarnings("unused") Object[] args, @SuppressWarnings("unused") boolean hasStrings, - @Cached("createExecute(args.length)") Node executeNode) { + @Specialization(guards = {"args.length == cachedArgsLength", "nativeCallInfo.address.asTruffleObject() == cachedAddress"}) + protected void invokeCallCached(@SuppressWarnings("unused") NativeCallInfo nativeCallInfo, Object[] args, + @SuppressWarnings("unused") @Cached("args.length") int cachedArgsLength, + @Cached("createExecute(cachedArgsLength)") Node executeNode, + @SuppressWarnings("unused") @Cached("nativeCallInfo.address.asTruffleObject()") TruffleObject cachedAddress, + @Cached("getFunction(cachedAddress, cachedArgsLength)") TruffleObject cachedFunction) { synchronized (TruffleNFI_Call.class) { try { - TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode, nativeCallInfo.address.asTruffleObject(), "bind", "(): void"); - ForeignAccess.sendExecute(executeNode, callFunction); + ForeignAccess.sendExecute(executeNode, cachedFunction, args); } catch (InteropException ex) { throw RInternalError.shouldNotReachHere(ex); } @@ -177,14 +88,12 @@ public class TruffleNFI_C implements CRFFI { } @Specialization(limit = "99", guards = "args.length == cachedArgsLength") - protected void invokeCall1(NativeCallInfo nativeCallInfo, Object[] args, @SuppressWarnings("unused") boolean hasStrings, + protected void invokeCallCachedLength(NativeCallInfo nativeCallInfo, Object[] args, @Cached("args.length") int cachedArgsLength, @Cached("createExecute(cachedArgsLength)") Node executeNode) { synchronized (TruffleNFI_Call.class) { try { - Object[] nargs = new Object[cachedArgsLength]; - TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode, nativeCallInfo.address.asTruffleObject(), "bind", getSignature(args, nargs)); - ForeignAccess.sendExecute(executeNode, callFunction, nargs); + ForeignAccess.sendExecute(executeNode, getFunction(nativeCallInfo.address.asTruffleObject(), cachedArgsLength), args); } catch (InteropException ex) { throw RInternalError.shouldNotReachHere(ex); } @@ -196,30 +105,6 @@ public class TruffleNFI_C implements CRFFI { } } - @TruffleBoundary - private static String getSignature(Object[] args, Object[] nargs) { - StringBuilder sb = new StringBuilder(); - sb.append('('); - for (int i = 0; i < args.length; i++) { - Object arg = args[i]; - if (arg instanceof int[]) { - sb.append("[sint32]"); - } else if (arg instanceof double[]) { - sb.append("[double]"); - } else if (arg instanceof StringWrapper) { - sb.append("pointer"); - } else { - throw RInternalError.unimplemented(".C type: " + arg.getClass().getSimpleName()); - } - nargs[i] = JavaInterop.asTruffleObject(arg); - if (i < args.length - 1) { - sb.append(", "); - } - } - sb.append("): void"); - return sb.toString(); - } - @Override public InvokeCNode createInvokeCNode() { return TruffleNFI_InvokeCNodeGen.create(); diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java index 74bf04cb475ed478f986f1cc298faedf974df61a..b32d2ebd75900d2c15806c897424d872d08c8b33 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java @@ -235,32 +235,36 @@ public class TruffleNFI_Call implements CallRFFI { } } + private static final String[] SIGNATURES = new String[32]; + + private static String getSignatureForArity(int arity) { + CompilerAsserts.neverPartOfCompilation(); + if (arity >= SIGNATURES.length || SIGNATURES[arity] == null) { + StringBuilder str = new StringBuilder().append('('); + for (int i = 0; i < arity; i++) { + str.append(i > 0 ? ", " : ""); + str.append("pointer"); + } + String signature = str.append("): pointer").toString(); + if (arity < SIGNATURES.length) { + SIGNATURES[arity] = signature; + } + return signature; + } else { + return SIGNATURES[arity]; + } + } + @ImportStatic(FFIWrapNode.class) public abstract static class TruffleNFI_InvokeCallNode extends Node implements InvokeCallNode { - private static final String[] SIGNATURES = new String[32]; @Child private Node bindNode = Message.createInvoke(1).createNode(); @TruffleBoundary protected TruffleObject getFunction(TruffleObject address, int arity) { // cache signatures - String signature; - if (arity >= SIGNATURES.length || SIGNATURES[arity] == null) { - StringBuilder str = new StringBuilder().append('('); - for (int i = 0; i < arity; i++) { - str.append(i > 0 ? ", " : ""); - str.append("pointer"); - } - str.append("): pointer"); - signature = str.toString(); - if (arity < SIGNATURES.length) { - SIGNATURES[arity] = signature; - } - } else { - signature = SIGNATURES[arity]; - } try { - return (TruffleObject) ForeignAccess.sendInvoke(bindNode, address, "bind", signature); + return (TruffleObject) ForeignAccess.sendInvoke(bindNode, address, "bind", getSignatureForArity(arity)); } catch (InteropException ex) { throw RInternalError.shouldNotReachHere(ex); } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java index 627f2be72dd4a0721820fff6d945ce9c28d99d83..80a67a01665d7323df703d4a08221b147eac5c69 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java @@ -24,8 +24,6 @@ package com.oracle.truffle.r.ffi.impl.nfi; import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.guaranteeInstanceOf; -import java.nio.charset.StandardCharsets; - import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.CanResolve; import com.oracle.truffle.api.interop.ForeignAccess; @@ -37,18 +35,12 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.r.ffi.impl.common.JavaUpCallsRFFIImpl; -import com.oracle.truffle.r.ffi.impl.common.RFFIUtils; -import com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter; -import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_C.StringWrapper; import com.oracle.truffle.r.ffi.impl.upcalls.FFIUnwrapNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; -import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RIntVector; import com.oracle.truffle.r.runtime.data.RLogicalVector; -import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.data.RVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper; import com.oracle.truffle.r.runtime.ffi.DLL; import com.oracle.truffle.r.runtime.ffi.DLL.CEntry; @@ -57,8 +49,6 @@ import com.oracle.truffle.r.runtime.ffi.DLL.DotSymbol; import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle; import com.oracle.truffle.r.runtime.gnur.SEXPTYPE; -import sun.misc.Unsafe; - public class TruffleNFI_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl { private static final String SETSYMBOL_SIGNATURE = "(pointer, sint32, pointer, sint32): pointer"; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java index 5960ab3be7d4a1c7c74fe33c98b62aaaadeebfec..b126fa2ddfd67c2e0e033f99e2f2977066edb89d 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java @@ -70,6 +70,7 @@ import com.oracle.truffle.r.library.utils.TypeConvertNodeGen; import com.oracle.truffle.r.library.utils.UnzipNodeGen; import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode; import com.oracle.truffle.r.nodes.builtin.RInternalCodeBuiltinNode; +import com.oracle.truffle.r.nodes.builtin.base.foreign.LookupAdapter.ExtractNativeCallInfoNode; import com.oracle.truffle.r.nodes.objects.GetPrimNameNodeGen; import com.oracle.truffle.r.nodes.objects.NewObjectNodeGen; import com.oracle.truffle.r.runtime.FastROptions; @@ -653,10 +654,11 @@ public class CallAndExternalFunctions { */ @SuppressWarnings("unused") @Specialization(limit = "2", guards = {"cached == symbol", "builtin == null"}) - protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName, + protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, Object packageName, @Cached("symbol") RList cached, @Cached("lookupBuiltin(symbol)") RExternalBuiltinNode builtin, - @Cached("extractSymbolInfo.execute(frame, symbol)") NativeCallInfo nativeCallInfo) { + @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo, + @Cached("extractSymbolInfo.execute(symbol)") NativeCallInfo nativeCallInfo) { return callRFFINode.execute(nativeCallInfo, args.getArguments()); } @@ -666,12 +668,13 @@ public class CallAndExternalFunctions { */ @SuppressWarnings("unused") @Specialization(replaces = {"callNamedFunction", "doExternal"}) - protected Object callNamedFunctionGeneric(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName) { + protected Object callNamedFunctionGeneric(RList symbol, RArgsValuesAndNames args, Object packageName, + @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) { RExternalBuiltinNode builtin = lookupBuiltin(symbol); if (builtin != null) { throw RInternalError.shouldNotReachHere("Cache for .Calls with FastR reimplementation (lookupBuiltin(...) != null) exceeded the limit"); } - NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol); + NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol); return callRFFINode.execute(nativeCallInfo, args.getArguments()); } @@ -786,9 +789,10 @@ public class CallAndExternalFunctions { @SuppressWarnings("unused") @Specialization(limit = "1", guards = {"cached == symbol"}) - protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName, + protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, Object packageName, @Cached("symbol") RList cached, - @Cached("extractSymbolInfo.execute(frame, symbol)") NativeCallInfo nativeCallInfo) { + @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo, + @Cached("extractSymbolInfo.execute(symbol)") NativeCallInfo nativeCallInfo) { Object list = encodeArgumentPairList(args, nativeCallInfo.name); return callRFFINode.execute(nativeCallInfo, new Object[]{list}); } @@ -882,9 +886,10 @@ public class CallAndExternalFunctions { @SuppressWarnings("unused") @Specialization(limit = "1", guards = {"cached == symbol"}) - protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName, + protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, Object packageName, @Cached("symbol") RList cached, - @Cached("extractSymbolInfo.execute(frame, symbol)") NativeCallInfo nativeCallInfo) { + @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo, + @Cached("extractSymbolInfo.execute(symbol)") NativeCallInfo nativeCallInfo) { Object list = encodeArgumentPairList(args, nativeCallInfo.name); return callRFFINode.execute(nativeCallInfo, new Object[]{CALL, getOp(), list, RHO}); } @@ -938,8 +943,9 @@ public class CallAndExternalFunctions { } @Specialization - protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName) { - NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol); + protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName, + @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) { + NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol); Object list = encodeArgumentPairList(args, nativeCallInfo.name); return callRFFINode.execute(nativeCallInfo, new Object[]{list}); } @@ -1000,8 +1006,9 @@ public class CallAndExternalFunctions { } @Specialization - protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName) { - NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol); + protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName, + @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) { + NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol); return callRFFINode.execute(nativeCallInfo, args.getArguments()); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java index 6455ab0ee486f962cf03849c96a4620e7fa0e49f..a0f416daf666ceb721b1951bccafda1284f39c27 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java @@ -27,7 +27,6 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode; import com.oracle.truffle.r.nodes.builtin.base.foreign.FortranAndCFunctionsFactory.FortranResultNamesSetterNodeGen; import com.oracle.truffle.r.nodes.builtin.base.foreign.LookupAdapter.ExtractNativeCallInfoNode; -import com.oracle.truffle.r.nodes.builtin.base.foreign.LookupAdapterFactory.ExtractNativeCallInfoNodeGen; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RRuntime; @@ -56,7 +55,6 @@ public class FortranAndCFunctions { protected abstract static class CRFFIAdapter extends RBuiltinNode.Arg6 { - @Child protected ExtractNativeCallInfoNode extractSymbolInfo = ExtractNativeCallInfoNodeGen.create(); @Child protected CRFFI.InvokeCNode invokeCNode = RFFIFactory.getCRFFI().createInvokeCNode(); @Override @@ -101,16 +99,15 @@ public class FortranAndCFunctions { @SuppressWarnings("unused") @Specialization(limit = "1", guards = {"cached == symbol", "builtin != null"}) - protected Object doExternal(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, byte naok, byte dup, Object rPackage, RMissing encoding, - @Cached("symbol") RList cached, + protected Object doExternal(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, byte naok, byte dup, Object rPackage, RMissing encoding, @Cached("symbol") RList cached, @Cached("lookupBuiltin(symbol)") RExternalBuiltinNode builtin) { return resNamesSetter.execute(builtin.call(frame, args), args); } @Specialization(guards = "lookupBuiltin(symbol) == null") - protected RList c(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, byte naok, byte dup, @SuppressWarnings("unused") Object rPackage, - @SuppressWarnings("unused") RMissing encoding) { - NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol); + protected RList c(RList symbol, RArgsValuesAndNames args, byte naok, byte dup, @SuppressWarnings("unused") Object rPackage, @SuppressWarnings("unused") RMissing encoding, + @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) { + NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol); return invokeCNode.dispatch(nativeCallInfo, naok, dup, args); } @@ -179,8 +176,9 @@ public class FortranAndCFunctions { } @Specialization - protected RList c(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, byte naok, byte dup, @SuppressWarnings("unused") Object rPackage, @SuppressWarnings("unused") RMissing encoding) { - NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(frame, symbol); + protected RList c(RList symbol, RArgsValuesAndNames args, byte naok, byte dup, @SuppressWarnings("unused") Object rPackage, @SuppressWarnings("unused") RMissing encoding, + @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) { + NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol); return invokeCNode.dispatch(nativeCallInfo, naok, dup, args); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java index 881a395fcde021c36f781208e3960553207e47c7..7ab299db0127e37ac02c2a93d6afda9a1f0ddcb9 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/LookupAdapter.java @@ -23,10 +23,7 @@ package com.oracle.truffle.r.nodes.builtin.base.foreign; import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.r.library.stats.RandFunctionsNodes; import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode; @@ -34,7 +31,6 @@ import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode; import com.oracle.truffle.r.nodes.builtin.RInternalCodeBuiltinNode; -import com.oracle.truffle.r.nodes.builtin.base.foreign.LookupAdapterFactory.ExtractNativeCallInfoNodeGen; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalCode; import com.oracle.truffle.r.runtime.RInternalError; @@ -72,7 +68,6 @@ interface Lookup { * still implemented by native code. */ abstract class LookupAdapter extends RBuiltinNode.Arg3 implements Lookup { - @Child protected ExtractNativeCallInfoNode extractSymbolInfo = ExtractNativeCallInfoNodeGen.create(); protected static class UnimplementedExternal extends RExternalBuiltinNode { private final String name; @@ -139,35 +134,19 @@ abstract class LookupAdapter extends RBuiltinNode.Arg3 implements Lookup { * Extracts the salient information needed for a native call from the {@link RList} value * provided from R. */ - public abstract static class ExtractNativeCallInfoNode extends Node { + public static final class ExtractNativeCallInfoNode extends Node { @Child private ExtractVectorNode nameExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true); @Child private ExtractVectorNode addressExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true); @Child private ExtractVectorNode packageExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true); @Child private ExtractVectorNode infoExtract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true); - protected abstract NativeCallInfo execute(VirtualFrame frame, RList symbol); - - @Specialization - protected NativeCallInfo extractNativeCallInfo(RList symbol) { - if (nameExtract == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - nameExtract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true)); - } - if (addressExtract == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - addressExtract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true)); - } - if (packageExtract == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - packageExtract = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true)); - } + protected NativeCallInfo execute(RList symbol) { String name = RRuntime.asString(nameExtract.applyAccessField(symbol, "name")); SymbolHandle address = ((RExternalPtr) addressExtract.applyAccessField(symbol, "address")).getAddr(); // field name may be "package" or "dll", but always at (R) index 3 RList packageList = (RList) packageExtract.apply(symbol, new Object[]{3}, RLogical.valueOf(false), RMissing.instance); - DLLInfo dllInfo = (DLLInfo) ((RExternalPtr) addressExtract.applyAccessField(packageList, "info")).getExternalObject(); + DLLInfo dllInfo = (DLLInfo) ((RExternalPtr) infoExtract.applyAccessField(packageList, "info")).getExternalObject(); return new NativeCallInfo(name, address, dllInfo); - } } 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 80d1e70c8706ca9f0459912d7f7a0aec2b1fe1f1..bcb0a801559bbe7faf54e60d9d85b835a8fa5881 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 @@ -22,140 +22,370 @@ */ package com.oracle.truffle.r.runtime.ffi; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.interop.CanResolve; +import com.oracle.truffle.api.interop.ForeignAccess; +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.nodes.Node; +import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; +import com.oracle.truffle.r.runtime.data.RComplex; +import com.oracle.truffle.r.runtime.data.RComplexVector; import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RDoubleVector; +import com.oracle.truffle.r.runtime.data.RIntVector; import com.oracle.truffle.r.runtime.data.RList; +import com.oracle.truffle.r.runtime.data.RLogicalVector; +import com.oracle.truffle.r.runtime.data.RRawVector; 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.RAbstractComplexVector; 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.RAbstractRawVector; import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.nodes.RBaseNode; -/** - * Support for the {.C} and {.Fortran} calls. - */ -public interface CRFFI { +import sun.misc.Unsafe; - public static abstract class InvokeCNode extends RBaseNode { +@MessageResolution(receiverType = TemporaryWrapper.class) +class TemporaryWrapperMR { - public enum ArgumentType { - VECTOR_DOUBLE, - VECTOR_INT, - VECTOR_LOGICAL, - VECTOR_STRING; + @Resolve(message = "IS_POINTER") + public abstract static class TemporaryWrapperIsPointerNode extends Node { + protected Object access(@SuppressWarnings("unused") TemporaryWrapper receiver) { + return true; } + } - private static final Charset charset = StandardCharsets.US_ASCII; + @Resolve(message = "AS_POINTER") + public abstract static class TemporaryWrapperAsPointerNode extends Node { + private final ConditionProfile profile = ConditionProfile.createBinaryProfile(); - /** - * Invoke the native method identified by {@code symbolInfo} passing it the arguments in - * {@code args}. The values in {@code args} should be native types,e.g., {@code double[]} - * not {@code RDoubleVector}. Strings are already converted to 2-dimensional byte arrays. - * - * @param hasStrings if {@code true}, then the {@code args} array may contain one or more - * values of type {@code byte[][]}, which represent arrays of strings in ASCII - * encoding. - */ - protected abstract void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings); - - @TruffleBoundary - protected Object getNativeArgument(int index, ArgumentType type, RAbstractAtomicVector vector) { - CompilerAsserts.neverPartOfCompilation(); - switch (type) { - case VECTOR_DOUBLE: { - double[] data = ((RAbstractDoubleVector) vector).materialize().getDataCopy(); - for (int i = 0; i < data.length; i++) { - if (!RRuntime.isFinite(data[i])) { - throw error(RError.Message.NA_NAN_INF_IN_FOREIGN_FUNCTION_CALL, index + 1); - } - } - return data; - } - case VECTOR_INT: { - int[] data = ((RAbstractIntVector) vector).materialize().getDataCopy(); - for (int i = 0; i < data.length; i++) { - if (RRuntime.isNA(data[i])) { - throw error(RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, index + 1); - } - } - return data; - } - case VECTOR_LOGICAL: { - // passed as int[] - byte[] data = ((RAbstractLogicalVector) vector).materialize().getDataWithoutCopying(); - int[] dataAsInt = new int[data.length]; - for (int j = 0; j < data.length; j++) { - // An NA is an error but the error handling happens in checkNAs - dataAsInt[j] = RRuntime.isNA(data[j]) ? RRuntime.INT_NA : data[j]; - } - for (int i = 0; i < dataAsInt.length; i++) { - if (RRuntime.isNA(dataAsInt[i])) { - throw error(RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, index + 1); - } - } - return dataAsInt; - } - case VECTOR_STRING: { - RAbstractStringVector data = (RAbstractStringVector) vector; - for (int i = 0; i < data.getLength(); i++) { - if (RRuntime.isNA(data.getDataAt(i))) { - throw error(RError.Message.NA_IN_FOREIGN_FUNCTION_CALL, index + 1); - } - } - return encodeStrings((RAbstractStringVector) vector); - } - default: - throw RInternalError.shouldNotReachHere(); + protected Object access(TemporaryWrapper receiver) { + long address = receiver.address; + if (profile.profile(address == 0)) { + receiver.address = address = receiver.allocate(); } + return address; } + } - 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]); - } - return nativeArgs; + @Resolve(message = "READ") + public abstract static class TemporaryWrapperReadNode extends Node { + protected Object access(TemporaryWrapper receiver, long index) { + return receiver.read(index); } - @TruffleBoundary - protected Object postProcessArgument(ArgumentType type, RAbstractAtomicVector vector, Object nativeArgument) { - switch (type) { - case VECTOR_STRING: - return ((RAbstractStringVector) vector).materialize().copyResetData(decodeStrings((byte[][]) nativeArgument)); - case VECTOR_DOUBLE: - return ((RAbstractDoubleVector) vector).materialize().copyResetData((double[]) nativeArgument); - case VECTOR_INT: - return ((RAbstractIntVector) vector).materialize().copyResetData((int[]) nativeArgument); - case VECTOR_LOGICAL: { - int[] intData = (int[]) nativeArgument; - byte[] byteData = new byte[intData.length]; - for (int j = 0; j < intData.length; j++) { - byteData[j] = RRuntime.isNA(intData[j]) ? RRuntime.LOGICAL_NA : RRuntime.asLogical(intData[j] != 0); - } - return ((RAbstractLogicalVector) vector).materialize().copyResetData(byteData); - } - default: - throw RInternalError.shouldNotReachHere(); + protected Object access(TemporaryWrapper receiver, int index) { + return receiver.read(index); + } + } + + @Resolve(message = "WRITE") + public abstract static class TemporaryWrapperWriteNode extends Node { + protected Object access(TemporaryWrapper receiver, long index, Object value) { + receiver.write(index, value); + return value; + } + + protected Object access(TemporaryWrapper receiver, int index, Object value) { + receiver.write(index, value); + return value; + } + } + + @CanResolve + public abstract static class TemporaryWrapperCheck extends Node { + protected static boolean test(TruffleObject receiver) { + return receiver instanceof TemporaryWrapper; + } + } +} + +abstract class TemporaryWrapper implements TruffleObject { + + protected long address; + protected RAbstractAtomicVector vector; + + public TemporaryWrapper(RAbstractAtomicVector vector) { + this.vector = vector; + } + + public abstract long allocate(); + + public Object read(long index) { + throw RInternalError.unimplemented("read at " + index); + } + + public void write(long index, Object value) { + throw RInternalError.unimplemented("write of value " + value + " at index " + index); + } + + @Override + public final ForeignAccess getForeignAccess() { + return TemporaryWrapperMRForeign.ACCESS; + } + + public final RAbstractAtomicVector cleanup() { + if (address == 0) { + return vector; + } else { + return copyBack(); + } + } + + protected abstract RAbstractAtomicVector copyBack(); +} + +final class StringWrapper extends TemporaryWrapper { + + public StringWrapper(RAbstractStringVector vector) { + super(vector); + } + + @Override + @TruffleBoundary + public long allocate() { + RAbstractStringVector v = (RAbstractStringVector) vector; + int length = v.getLength(); + int size = length * 8; + byte[][] bytes = new byte[length][]; + for (int i = 0; i < length; i++) { + String element = v.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; + } + + @Override + @TruffleBoundary + protected RStringVector copyBack() { + RStringVector result = ((RAbstractStringVector) vector).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); + } +} - 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]); +final class IntWrapper extends TemporaryWrapper { + + public IntWrapper(RAbstractIntVector vector) { + super(vector); + } + + @Override + @TruffleBoundary + public long allocate() { + RAbstractIntVector v = (RAbstractIntVector) vector; + int length = v.getLength(); + long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_INT_INDEX_SCALE); + for (int i = 0; i < length; i++) { + UnsafeAdapter.UNSAFE.putInt(memory + (i * Unsafe.ARRAY_INT_INDEX_SCALE), v.getDataAt(i)); + } + return memory; + } + + @Override + @TruffleBoundary + protected RIntVector copyBack() { + RIntVector result = ((RAbstractIntVector) vector).materialize(); + int[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy(); + UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_INT_BASE_OFFSET, vector.getLength() * Unsafe.ARRAY_INT_INDEX_SCALE); + UnsafeAdapter.UNSAFE.freeMemory(address); + return RDataFactory.createIntVector(data, false); + } +} + +final class LogicalWrapper extends TemporaryWrapper { + + public LogicalWrapper(RAbstractLogicalVector vector) { + super(vector); + } + + @Override + @TruffleBoundary + public long allocate() { + RAbstractLogicalVector v = (RAbstractLogicalVector) vector; + int length = v.getLength(); + long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_INT_INDEX_SCALE); + for (int i = 0; i < length; i++) { + UnsafeAdapter.UNSAFE.putInt(memory + (i * Unsafe.ARRAY_INT_INDEX_SCALE), RRuntime.logical2int(v.getDataAt(i))); + } + return memory; + } + + @Override + @TruffleBoundary + protected RLogicalVector copyBack() { + RLogicalVector result = ((RAbstractLogicalVector) vector).materialize(); + byte[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy(); + int length = vector.getLength(); + for (int i = 0; i < length; i++) { + data[i] = RRuntime.int2logical(UnsafeAdapter.UNSAFE.getInt(address + (i * Unsafe.ARRAY_INT_INDEX_SCALE))); + } + UnsafeAdapter.UNSAFE.freeMemory(address); + return RDataFactory.createLogicalVector(data, false); + } +} + +final class DoubleWrapper extends TemporaryWrapper { + + public DoubleWrapper(RAbstractDoubleVector vector) { + super(vector); + } + + @Override + @TruffleBoundary + public long allocate() { + RAbstractDoubleVector v = (RAbstractDoubleVector) vector; + int length = v.getLength(); + long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + for (int i = 0; i < length; i++) { + UnsafeAdapter.UNSAFE.putDouble(memory + (i * Unsafe.ARRAY_DOUBLE_INDEX_SCALE), v.getDataAt(i)); + } + return memory; + } + + @Override + @TruffleBoundary + protected RDoubleVector copyBack() { + RDoubleVector result = ((RAbstractDoubleVector) vector).materialize(); + double[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy(); + UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, vector.getLength() * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + UnsafeAdapter.UNSAFE.freeMemory(address); + return RDataFactory.createDoubleVector(data, false); + } +} + +final class ComplexWrapper extends TemporaryWrapper { + + public ComplexWrapper(RAbstractComplexVector vector) { + super(vector); + } + + @Override + @TruffleBoundary + public long allocate() { + RAbstractComplexVector v = (RAbstractComplexVector) vector; + int length = v.getLength(); + long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE * 2); + for (int i = 0; i < length; i++) { + RComplex element = v.getDataAt(i); + UnsafeAdapter.UNSAFE.putDouble(memory + (i * Unsafe.ARRAY_DOUBLE_INDEX_SCALE * 2), element.getRealPart()); + UnsafeAdapter.UNSAFE.putDouble(memory + (i * Unsafe.ARRAY_DOUBLE_INDEX_SCALE * 2) + 8, element.getImaginaryPart()); + } + return memory; + } + + @Override + @TruffleBoundary + protected RComplexVector copyBack() { + RComplexVector result = ((RAbstractComplexVector) vector).materialize(); + double[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy(); + UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, vector.getLength() * 2 * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + UnsafeAdapter.UNSAFE.freeMemory(address); + return RDataFactory.createComplexVector(data, false); + } +} + +final class RawWrapper extends TemporaryWrapper { + + public RawWrapper(RAbstractRawVector vector) { + super(vector); + } + + @Override + @TruffleBoundary + public long allocate() { + RAbstractRawVector v = (RAbstractRawVector) vector; + int length = v.getLength(); + long memory = UnsafeAdapter.UNSAFE.allocateMemory(length * Unsafe.ARRAY_BYTE_INDEX_SCALE); + for (int i = 0; i < length; i++) { + UnsafeAdapter.UNSAFE.putByte(memory + (i * Unsafe.ARRAY_BYTE_INDEX_SCALE), v.getRawDataAt(i)); + } + return memory; + } + + @Override + @TruffleBoundary + protected RRawVector copyBack() { + RRawVector result = ((RAbstractRawVector) vector).materialize(); + byte[] data = result.isTemporary() ? result.getDataWithoutCopying() : result.getDataCopy(); + UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_BYTE_BASE_OFFSET, vector.getLength() * Unsafe.ARRAY_BYTE_INDEX_SCALE); + UnsafeAdapter.UNSAFE.freeMemory(address); + return RDataFactory.createRawVector(data); + } +} + +/** + * Support for the {.C} and {.Fortran} calls. + */ +public interface CRFFI { + + public static abstract class InvokeCNode extends RBaseNode { + + /** + * Invoke the native method identified by {@code symbolInfo} passing it the arguments in + * {@code args}. The values in {@code args} should be support the IS_POINTER/AS_POINTER + * messages. + */ + protected abstract void execute(NativeCallInfo nativeCallInfo, Object[] args); + + @TruffleBoundary + protected TemporaryWrapper getNativeArgument(int index, Object vector) { + if (vector instanceof RAbstractDoubleVector) { + return new DoubleWrapper((RAbstractDoubleVector) vector); + } else if (vector instanceof RAbstractIntVector) { + return new IntWrapper((RAbstractIntVector) vector); + } else if (vector instanceof RAbstractLogicalVector) { + return new LogicalWrapper((RAbstractLogicalVector) vector); + } else if (vector instanceof RAbstractComplexVector) { + return new ComplexWrapper((RAbstractComplexVector) vector); + } else if (vector instanceof RAbstractStringVector) { + return new StringWrapper((RAbstractStringVector) vector); + } else if (vector instanceof RAbstractRawVector) { + return new RawWrapper((RAbstractRawVector) vector); + } else if (vector instanceof String) { + return new StringWrapper(RDataFactory.createStringVectorFromScalar((String) vector)); + } else if (vector instanceof Double) { + return new DoubleWrapper(RDataFactory.createDoubleVectorFromScalar((double) vector)); + } else if (vector instanceof Integer) { + return new IntWrapper(RDataFactory.createIntVectorFromScalar((int) vector)); + } else if (vector instanceof Byte) { + return new LogicalWrapper(RDataFactory.createLogicalVectorFromScalar((byte) vector)); + } else { + throw error(RError.Message.UNIMPLEMENTED_ARG_TYPE, index + 1); } - return results; } @TruffleBoundary @@ -164,52 +394,22 @@ public interface CRFFI { boolean dupArgs = RRuntime.fromLogical(dup); @SuppressWarnings("unused") boolean checkNA = RRuntime.fromLogical(naok); + // Analyze the args, making copies (ignoring dup for now) Object[] array = new Object[args.getLength()]; - ArgumentType[] argTypes = new ArgumentType[array.length]; - boolean hasStrings = false; for (int i = 0; i < array.length; i++) { - Object arg = args.getArgument(i); - ArgumentType type; - RAbstractAtomicVector vector; - if (arg instanceof RAbstractDoubleVector) { - vector = (RAbstractDoubleVector) arg; - type = ArgumentType.VECTOR_DOUBLE; - } else if (arg instanceof RAbstractIntVector) { - vector = (RAbstractIntVector) arg; - type = ArgumentType.VECTOR_INT; - } else if (arg instanceof RAbstractLogicalVector) { - vector = (RAbstractLogicalVector) arg; - type = ArgumentType.VECTOR_LOGICAL; - } else if (arg instanceof RAbstractStringVector) { - hasStrings = true; - vector = (RAbstractStringVector) arg; - type = ArgumentType.VECTOR_STRING; - } else if (arg instanceof String) { - hasStrings = true; - vector = RDataFactory.createStringVectorFromScalar((String) arg); - type = ArgumentType.VECTOR_STRING; - } else if (arg instanceof Double) { - vector = RDataFactory.createDoubleVectorFromScalar((double) arg); - type = ArgumentType.VECTOR_DOUBLE; - } else if (arg instanceof Integer) { - vector = RDataFactory.createIntVectorFromScalar((int) arg); - type = ArgumentType.VECTOR_INT; - } else if (arg instanceof Byte) { - vector = RDataFactory.createLogicalVectorFromScalar((byte) arg); - type = ArgumentType.VECTOR_LOGICAL; - } else { - throw error(RError.Message.UNIMPLEMENTED_ARG_TYPE, i + 1); - } - argTypes[i] = type; - array[i] = vector; + array[i] = getNativeArgument(i, args.getArgument(i)); } - Object[] nativeArgs = getNativeArguments(array, argTypes); - execute(nativeCallInfo, nativeArgs, hasStrings); + + execute(nativeCallInfo, array); + // we have to assume that the native method updated everything - RStringVector listNames = validateArgNames(array.length, args.getSignature()); - Object[] results = postProcessArguments(array, argTypes, nativeArgs); - return RDataFactory.createList(results, listNames); + Object[] results = new Object[array.length]; + for (int i = 0; i < array.length; i++) { + results[i] = ((TemporaryWrapper) array[i]).cleanup(); + } + + return RDataFactory.createList(results, validateArgNames(array.length, args.getSignature())); } private static RStringVector validateArgNames(int argsLength, ArgumentsSignature signature) { @@ -223,34 +423,6 @@ public interface CRFFI { } return RDataFactory.createStringVector(listArgNames, RDataFactory.COMPLETE_VECTOR); } - - private static Object encodeStrings(RAbstractStringVector vector) { - byte[][] result = new byte[vector.getLength()][]; - for (int i = 0; i < vector.getLength(); i++) { - result[i] = encodeString(vector.getDataAt(i)); - } - return result; - } - - private static byte[] encodeString(String str) { - byte[] bytes = str.getBytes(charset); - byte[] result = new byte[bytes.length + 1]; - System.arraycopy(bytes, 0, result, 0, bytes.length); - return result; - } - - private static String[] decodeStrings(byte[][] bytes) { - String[] result = new String[bytes.length]; - for (int i = 0; i < bytes.length; i++) { - int length = 0; - while (length < bytes[i].length && bytes[i][length] != 0) { - length++; - } - result[i] = new String(bytes[i], 0, length, charset); - } - return result; - } - } InvokeCNode createInvokeCNode(); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java index 35b3f1ec87c75f89563972b3406c20e2fe0dc068..81d536b6a788c1ec995091ebb4c1084fed52853e 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java @@ -13,6 +13,7 @@ package com.oracle.truffle.r.runtime.ffi; import java.io.File; import java.util.ArrayList; +import java.util.HashSet; import java.util.concurrent.atomic.AtomicInteger; import com.oracle.truffle.api.CompilerAsserts; @@ -190,6 +191,7 @@ public class DLL { private boolean forceSymbols; private final DotSymbol[][] nativeSymbols = new DotSymbol[NativeSymbolType.values().length][]; private ArrayList<CEntry> cEntryTable = null; + private final HashSet<String> unsuccessfulLookups = new HashSet<>(); private DLLInfo(String name, String path, boolean dynamicLookup, Object handle) { this.id = ID.getAndIncrement(); @@ -581,7 +583,7 @@ public class DLL { } public static final class RFindSymbolNode extends Node { - @Child RdlsymNode rdlsymNode = new RdlsymNode(); + @Child private RdlsymNode rdlsymNode = new RdlsymNode(); /** * Directly analogous to the GnuR function {@code R_FindSymbol}. @@ -681,9 +683,13 @@ public class DLL { mName = name + "_"; } try { + if (dllInfo.unsuccessfulLookups.contains(mName)) { + return SYMBOL_NOT_FOUND; + } SymbolHandle symValue = dlSymNode.execute(dllInfo.handle, mName); return symValue; } catch (UnsatisfiedLinkError ex) { + dllInfo.unsuccessfulLookups.add(mName); return SYMBOL_NOT_FOUND; } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..1dd44d213fb6d24f221fffb8c326ec15b33877d2 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.r.runtime.ffi; + +import java.lang.reflect.Field; + +import sun.misc.Unsafe; + +public class UnsafeAdapter { + public static final Unsafe UNSAFE = initUnsafe(); + + private static Unsafe initUnsafe() { + try { + return Unsafe.getUnsafe(); + } catch (SecurityException se) { + try { + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + return (Unsafe) theUnsafe.get(Unsafe.class); + } catch (Exception e) { + throw new RuntimeException("exception while trying to get Unsafe", e); + } + } + } +}