From 3cff6ab6f47f53b010361e93b6819b1c11efd2cb Mon Sep 17 00:00:00 2001
From: Mick Jordan <mick.jordan@oracle.com>
Date: Mon, 5 Dec 2016 19:03:26 -0800
Subject: [PATCH] =?UTF-8?q?rffi=20upcalls=20don=E2=80=99t=20over-constrain?=
 =?UTF-8?q?=20return=20type?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../truffle/r/library/graphics/RGraphics.java |   2 +-
 .../fficall/src/jni/Rinternals.c              |   8 +-
 .../r/nodes/builtin/base/foreign/Fft.java     |  12 +-
 .../base/foreign/ForeignFunctions.java        |  26 +--
 .../r/runtime/ffi/JavaUpCallsRFFI.java        |   8 +-
 .../r/runtime/ffi/generic/Generic_Grid.java   |   4 +-
 .../r/runtime/ffi/generic/Generic_Tools.java  |   2 +-
 .../truffle/r/runtime/ffi/jni/JNI_Call.java   | 169 +++++++++---------
 .../truffle/r/runtime/ffi/jni/JNI_Stats.java  |  60 ++++---
 .../runtime/ffi/jni/TraceUpCallsAdapter.java  |   8 +-
 .../truffle/r/runtime/TempPathName.java       |   2 +-
 .../truffle/r/runtime/context/RContext.java   |   2 +-
 .../truffle/r/runtime/ffi/CallRFFI.java       |  46 ++---
 .../com/oracle/truffle/r/runtime/ffi/DLL.java |   7 +-
 .../truffle/r/runtime/ffi/StatsRFFI.java      |  17 +-
 .../truffle/r/runtime/ffi/UpCallsRFFI.java    |   8 +-
 16 files changed, 211 insertions(+), 170 deletions(-)

diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java
index 728a1efd4b..bfb6cdde08 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java
@@ -64,7 +64,7 @@ public class RGraphics {
                 DLL.RegisteredNativeSymbol rns = DLL.RegisteredNativeSymbol.any();
                 DLL.SymbolHandle func = DLL.findSymbol("InitGraphics", null, rns);
                 assert func != DLL.SYMBOL_NOT_FOUND;
-                RFFIFactory.getRFFI().getCallRFFI().invokeVoidCall(new NativeCallInfo("InitGraphics", func, DLL.findLibrary("graphics")), new Object[0]);
+                RFFIFactory.getRFFI().getCallRFFI().callRFFINode().invokeVoidCall(new NativeCallInfo("InitGraphics", func, DLL.findLibrary("graphics")), new Object[0]);
             }
         }
     }
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
index aa4b3352b6..540621ad16 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
@@ -187,10 +187,10 @@ void init_internals(JNIEnv *env) {
 	SET_SYMVALUE_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_SYMVALUE", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
 	SET_STRING_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_STRING_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)V", 0);
 	SET_VECTOR_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_VECTOR_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)V", 0);
-	RAW_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "RAW", "(Ljava/lang/Object;)[B", 0);
-	REAL_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "REAL", "(Ljava/lang/Object;)[D", 0);
-	LOGICAL_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "LOGICAL", "(Ljava/lang/Object;)[B", 0);
-	INTEGER_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "INTEGER", "(Ljava/lang/Object;)[I", 0);
+	RAW_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "RAW", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	REAL_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "REAL", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	LOGICAL_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "LOGICAL", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	INTEGER_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "INTEGER", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	STRING_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "STRING_ELT", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
 	VECTOR_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "VECTOR_ELT", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
 	LENGTH_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "LENGTH", "(Ljava/lang/Object;)I", 0);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java
index c15e41e818..16254566d5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java
@@ -20,11 +20,13 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
 
 public abstract class Fft extends RExternalBuiltinNode.Arg2 {
 
     private final ConditionProfile zVecLgt1 = ConditionProfile.createBinaryProfile();
     private final ConditionProfile noDims = ConditionProfile.createBinaryProfile();
+    @Child private StatsRFFI.FFTNode fftNode = RFFIFactory.getRFFI().getStatsRFFI().fftNode();
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -44,14 +46,14 @@ public abstract class Fft extends RExternalBuiltinNode.Arg2 {
             int[] maxp = new int[1];
             if (noDims.profile(zVec.getDimensions() == null)) {
                 int n = zVec.getLength();
-                RFFIFactory.getRFFI().getStatsRFFI().fft_factor(n, maxf, maxp);
+                fftNode.executeFactor(n, maxf, maxp);
                 if (maxf[0] == 0) {
                     errorProfile.enter();
                     throw RError.error(this, RError.Message.FFT_FACTORIZATION);
                 }
                 double[] work = new double[4 * maxf[0]];
                 int[] iwork = new int[maxp[0]];
-                retCode = RFFIFactory.getRFFI().getStatsRFFI().fft_work(z, 1, n, 1, inv, work, iwork);
+                retCode = fftNode.executeWork(z, 1, n, 1, inv, work, iwork);
             } else {
                 int maxmaxf = 1;
                 int maxmaxp = 1;
@@ -60,7 +62,7 @@ public abstract class Fft extends RExternalBuiltinNode.Arg2 {
                 /* do whole loop just for error checking and maxmax[fp] .. */
                 for (int i = 0; i < ndims; i++) {
                     if (d[i] > 1) {
-                        RFFIFactory.getRFFI().getStatsRFFI().fft_factor(d[i], maxf, maxp);
+                        fftNode.executeFactor(d[i], maxf, maxp);
                         if (maxf[0] == 0) {
                             errorProfile.enter();
                             throw RError.error(this, RError.Message.FFT_FACTORIZATION);
@@ -83,8 +85,8 @@ public abstract class Fft extends RExternalBuiltinNode.Arg2 {
                         nspn *= n;
                         n = d[i];
                         nseg /= n;
-                        RFFIFactory.getRFFI().getStatsRFFI().fft_factor(n, maxf, maxp);
-                        RFFIFactory.getRFFI().getStatsRFFI().fft_work(z, nseg, n, nspn, inv, work, iwork);
+                        fftNode.executeFactor(n, maxf, maxp);
+                        fftNode.executeWork(z, nseg, n, nspn, inv, work, iwork);
                     }
                 }
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
index 0d79bffd1a..652e1209f8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
@@ -109,6 +109,7 @@ import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.ffi.CallRFFI;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
@@ -150,6 +151,9 @@ public class ForeignFunctions {
      * handle.
      */
     protected abstract static class LookupAdapter extends RBuiltinNode {
+        @Child private ExtractNativeCallInfoNode extractSymbolInfoNode = ExtractNativeCallInfoNodeGen.create();
+        @Child protected CallRFFI.CallRFFINode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().callRFFINode();
+
         protected static class UnimplementedExternal extends RExternalBuiltinNode {
             private final String name;
 
@@ -198,8 +202,6 @@ public class ForeignFunctions {
             throw RError.nyi(this, getRBuiltin().name() + " specialization failure: " + (name == null ? "<unknown>" : name));
         }
 
-        @Child private ExtractNativeCallInfoNode extractSymbolInfoNode = ExtractNativeCallInfoNodeGen.create();
-
         protected NativeCallInfo extractSymbolInfo(VirtualFrame frame, RList symbol) {
             return (NativeCallInfo) extractSymbolInfoNode.execute(frame, symbol);
         }
@@ -609,7 +611,7 @@ public class ForeignFunctions {
         protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName, //
                         @Cached("symbol") RList cached, //
                         @Cached("extractSymbolInfo(frame, symbol)") NativeCallInfo nativeCallInfo) {
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(nativeCallInfo, args.getArguments());
+            return callRFFINode.invokeCall(nativeCallInfo, args.getArguments());
         }
 
         /**
@@ -633,7 +635,7 @@ public class ForeignFunctions {
                 errorProfile.enter();
                 throw RError.error(this, RError.Message.SYMBOL_NOT_IN_TABLE, symbol, "Call", packageName);
             }
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(new NativeCallInfo(symbol, func, rns.getDllInfo()), args.getArguments());
+            return callRFFINode.invokeCall(new NativeCallInfo(symbol, func, rns.getDllInfo()), args.getArguments());
         }
 
         @SuppressWarnings("unused")
@@ -712,7 +714,7 @@ public class ForeignFunctions {
                         @Cached("symbol") RList cached, //
                         @Cached("extractSymbolInfo(frame, symbol)") NativeCallInfo nativeCallInfo) {
             Object list = encodeArgumentPairList(args, nativeCallInfo.name);
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(nativeCallInfo, new Object[]{list});
+            return callRFFINode.invokeCall(nativeCallInfo, new Object[]{list});
         }
 
         @Specialization
@@ -730,7 +732,7 @@ public class ForeignFunctions {
                 throw RError.error(this, RError.Message.SYMBOL_NOT_IN_TABLE, symbol, "External", packageName);
             }
             Object list = encodeArgumentPairList(args, symbol);
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(new NativeCallInfo(symbol, func, rns.getDllInfo()), new Object[]{list});
+            return callRFFINode.invokeCall(new NativeCallInfo(symbol, func, rns.getDllInfo()), new Object[]{list});
         }
 
         @Fallback
@@ -788,7 +790,7 @@ public class ForeignFunctions {
                         @Cached("extractSymbolInfo(frame, symbol)") NativeCallInfo nativeCallInfo) {
             Object list = encodeArgumentPairList(args, nativeCallInfo.name);
             // TODO: provide proper values for the CALL, OP and RHO parameters
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(nativeCallInfo, new Object[]{CALL, OP, list, RHO});
+            return callRFFINode.invokeCall(nativeCallInfo, new Object[]{CALL, OP, list, RHO});
         }
 
         @Specialization
@@ -807,7 +809,7 @@ public class ForeignFunctions {
             }
             Object list = encodeArgumentPairList(args, symbol);
             // TODO: provide proper values for the CALL, OP and RHO parameters
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(new NativeCallInfo(symbol, func, rns.getDllInfo()), new Object[]{CALL, OP, list, RHO});
+            return callRFFINode.invokeCall(new NativeCallInfo(symbol, func, rns.getDllInfo()), new Object[]{CALL, OP, list, RHO});
         }
 
         @Fallback
@@ -847,7 +849,7 @@ public class ForeignFunctions {
         protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName) {
             NativeCallInfo nativeCallInfo = extractSymbolInfo(frame, symbol);
             Object list = encodeArgumentPairList(args, nativeCallInfo.name);
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(nativeCallInfo, new Object[]{list});
+            return callRFFINode.invokeCall(nativeCallInfo, new Object[]{list});
         }
 
         @Specialization
@@ -864,7 +866,7 @@ public class ForeignFunctions {
                 throw RError.error(this, RError.Message.C_SYMBOL_NOT_IN_TABLE, name);
             }
             Object list = encodeArgumentPairList(args, name);
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(new NativeCallInfo(name, func, rns.getDllInfo()), new Object[]{list});
+            return callRFFINode.invokeCall(new NativeCallInfo(name, func, rns.getDllInfo()), new Object[]{list});
         }
 
         @Fallback
@@ -903,7 +905,7 @@ public class ForeignFunctions {
         @Specialization
         protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName) {
             NativeCallInfo nativeCallInfo = extractSymbolInfo(frame, symbol);
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(nativeCallInfo, args.getArguments());
+            return callRFFINode.invokeCall(nativeCallInfo, args.getArguments());
         }
 
         @Specialization
@@ -920,7 +922,7 @@ public class ForeignFunctions {
                 errorProfile.enter();
                 throw RError.error(this, RError.Message.C_SYMBOL_NOT_IN_TABLE, name);
             }
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(new NativeCallInfo(name, func, rns.getDllInfo()), args.getArguments());
+            return callRFFINode.invokeCall(new NativeCallInfo(name, func, rns.getDllInfo()), args.getArguments());
         }
 
         @Fallback
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFI.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFI.java
index 4f0611ede2..d51986b501 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFI.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/JavaUpCallsRFFI.java
@@ -627,7 +627,7 @@ public class JavaUpCallsRFFI implements UpCallsRFFI {
     }
 
     @Override
-    public byte[] RAW(Object x) {
+    public Object RAW(Object x) {
         if (tracer != null) {
             tracer.RAW(x);
         }
@@ -641,7 +641,7 @@ public class JavaUpCallsRFFI implements UpCallsRFFI {
     }
 
     @Override
-    public byte[] LOGICAL(Object x) {
+    public Object LOGICAL(Object x) {
         if (tracer != null) {
             tracer.LOGICAL(x);
         }
@@ -655,7 +655,7 @@ public class JavaUpCallsRFFI implements UpCallsRFFI {
     }
 
     @Override
-    public int[] INTEGER(Object x) {
+    public Object INTEGER(Object x) {
         if (tracer != null) {
             tracer.INTEGER(x);
         }
@@ -679,7 +679,7 @@ public class JavaUpCallsRFFI implements UpCallsRFFI {
     }
 
     @Override
-    public double[] REAL(Object x) {
+    public Object REAL(Object x) {
         if (tracer != null) {
             tracer.REAL(x);
         }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Grid.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Grid.java
index 4b79d033cc..8e1df26de4 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Grid.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Grid.java
@@ -66,12 +66,12 @@ public class Generic_Grid implements GridRFFI {
     @Override
     public Object initGrid(REnvironment gridEvalEnv) {
         long initGrid = GridProvider.gridProvider().getInitGrid();
-        return RFFIFactory.getRFFI().getCallRFFI().invokeCall(new NativeCallInfo("L_initGrid", new SymbolHandle(initGrid), DLL.findLibrary("grid")), new Object[]{gridEvalEnv});
+        return RFFIFactory.getRFFI().getCallRFFI().callRFFINode().invokeCall(new NativeCallInfo("L_initGrid", new SymbolHandle(initGrid), DLL.findLibrary("grid")), new Object[]{gridEvalEnv});
     }
 
     @Override
     public Object killGrid() {
         long killGrid = GridProvider.gridProvider().getKillGrid();
-        return RFFIFactory.getRFFI().getCallRFFI().invokeCall(new NativeCallInfo("L_killGrid", new SymbolHandle(killGrid), DLL.findLibrary("grid")), new Object[0]);
+        return RFFIFactory.getRFFI().getCallRFFI().callRFFINode().invokeCall(new NativeCallInfo("L_killGrid", new SymbolHandle(killGrid), DLL.findLibrary("grid")), new Object[0]);
     }
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Tools.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Tools.java
index fedd559f04..bd5724e4bb 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Tools.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Tools.java
@@ -72,7 +72,7 @@ public class Generic_Tools implements ToolsRFFI {
         try {
             parseRdCritical.acquire();
             long parseRd = ToolsProvider.toolsProvider().getParseRd();
-            return RFFIFactory.getRFFI().getCallRFFI().invokeCall(new NativeCallInfo("parseRd", new SymbolHandle(parseRd), DLL.findLibrary("tools")),
+            return RFFIFactory.getRFFI().getCallRFFI().callRFFINode().invokeCall(new NativeCallInfo("parseRd", new SymbolHandle(parseRd), DLL.findLibrary("tools")),
                             new Object[]{con, srcfile, verbose, fragment, basename, warningCalls, macros, warndups});
         } catch (Throwable ex) {
             throw RInternalError.shouldNotReachHere(ex, "error during Rd parsing");
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java
index 2108c61447..8a36622c13 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java
@@ -49,12 +49,97 @@ import com.oracle.truffle.r.runtime.ffi.UpCallsRFFIFactory;
  */
 public class JNI_Call implements CallRFFI {
 
-    protected JNI_Call() {
-        loadLibrary();
+    public static class JNI_CallRFFINode extends CallRFFINode {
+
+        @Override
+        @TruffleBoundary
+        public synchronized Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args) {
+            long address = nativeCallInfo.address.asAddress();
+            Object result = null;
+            if (traceEnabled()) {
+                traceDownCall(nativeCallInfo.name, args);
+            }
+            try {
+                switch (args.length) {
+            // @formatter:off
+            case 0: result = call0(address); break;
+            case 1: result = call1(address, args[0]); break;
+            case 2: result = call2(address, args[0], args[1]); break;
+            case 3: result = call3(address, args[0], args[1], args[2]); break;
+            case 4: result = call4(address, args[0], args[1], args[2], args[3]); break;
+            case 5: result = call5(address, args[0], args[1], args[2], args[3], args[4]); break;
+            case 6: result = call6(address, args[0], args[1], args[2], args[3], args[4], args[5]); break;
+            case 7: result = call7(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); break;
+            case 8: result = call8(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); break;
+            case 9: result = call9(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); break;
+            default:
+                result = call(address, args); break;
+                // @formatter:on
+                }
+                return result;
+            } finally {
+                if (traceEnabled()) {
+                    traceDownCallReturn(nativeCallInfo.name, result);
+                }
+            }
+        }
+
+        @Override
+        @TruffleBoundary
+        public synchronized void invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args) {
+            if (traceEnabled()) {
+                traceDownCall(nativeCallInfo.name, args);
+            }
+            long address = nativeCallInfo.address.asAddress();
+            try {
+                switch (args.length) {
+                    case 0:
+                        callVoid0(address);
+                        break;
+                    case 1:
+                        callVoid1(address, args[0]);
+                        break;
+                    default:
+                        throw RInternalError.shouldNotReachHere();
+                }
+            } finally {
+                if (traceEnabled()) {
+                    traceDownCallReturn(nativeCallInfo.name, null);
+                }
+            }
+        }
+
+        @Override
+        public synchronized void setTempDir(String tempDir) {
+            if (traceEnabled()) {
+                traceDownCall("setTempDir", tempDir);
+            }
+            RFFIVariables.setTempDir(tempDir);
+            nativeSetTempDir(tempDir);
+            if (traceEnabled()) {
+                traceDownCallReturn("setTempDir", null);
+            }
+        }
+
+        @Override
+        public synchronized void setInteractive(boolean interactive) {
+            if (traceEnabled()) {
+                traceDownCall("setInteractive", interactive);
+            }
+            nativeSetInteractive(interactive);
+            if (traceEnabled()) {
+                traceDownCallReturn("setInteractive", null);
+            }
+        }
+
     }
 
     private static final boolean ForceRTLDGlobal = false;
 
+    protected JNI_Call() {
+        loadLibrary();
+    }
+
     /**
      * Load the {@code libR} library. N.B. this library defines some non-JNI global symbols that are
      * referenced by C code in R packages. Unfortunately, {@link System#load(String)} uses
@@ -91,39 +176,6 @@ public class JNI_Call implements CallRFFI {
         }
     }
 
-    @Override
-    @TruffleBoundary
-    public synchronized Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args) {
-        long address = nativeCallInfo.address.asAddress();
-        Object result = null;
-        if (traceEnabled()) {
-            traceDownCall(nativeCallInfo.name, args);
-        }
-        try {
-            switch (args.length) {
-            // @formatter:off
-            case 0: result = call0(address); break;
-            case 1: result = call1(address, args[0]); break;
-            case 2: result = call2(address, args[0], args[1]); break;
-            case 3: result = call3(address, args[0], args[1], args[2]); break;
-            case 4: result = call4(address, args[0], args[1], args[2], args[3]); break;
-            case 5: result = call5(address, args[0], args[1], args[2], args[3], args[4]); break;
-            case 6: result = call6(address, args[0], args[1], args[2], args[3], args[4], args[5]); break;
-            case 7: result = call7(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); break;
-            case 8: result = call8(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); break;
-            case 9: result = call9(address, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); break;
-            default:
-                result = call(address, args); break;
-                // @formatter:on
-            }
-            return result;
-        } finally {
-            if (traceEnabled()) {
-                traceDownCallReturn(nativeCallInfo.name, result);
-            }
-        }
-    }
-
     private static native void initialize(UpCallsRFFI upCallRFFI, RFFIVariables[] variables);
 
     private static native void nativeSetTempDir(String tempDir);
@@ -152,56 +204,13 @@ public class JNI_Call implements CallRFFI {
 
     private static native Object call9(long address, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9);
 
-    @Override
-    @TruffleBoundary
-    public synchronized void invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args) {
-        if (traceEnabled()) {
-            traceDownCall(nativeCallInfo.name, args);
-        }
-        long address = nativeCallInfo.address.asAddress();
-        try {
-            switch (args.length) {
-                case 0:
-                    callVoid0(address);
-                    break;
-                case 1:
-                    callVoid1(address, args[0]);
-                    break;
-                default:
-                    throw RInternalError.shouldNotReachHere();
-            }
-        } finally {
-            if (traceEnabled()) {
-                traceDownCallReturn(nativeCallInfo.name, null);
-            }
-        }
-    }
-
     private static native void callVoid0(long address);
 
     private static native void callVoid1(long address, Object arg1);
 
     @Override
-    public synchronized void setTempDir(String tempDir) {
-        if (traceEnabled()) {
-            traceDownCall("setTempDir", tempDir);
-        }
-        RFFIVariables.setTempDir(tempDir);
-        nativeSetTempDir(tempDir);
-        if (traceEnabled()) {
-            traceDownCallReturn("setTempDir", null);
-        }
-    }
-
-    @Override
-    public synchronized void setInteractive(boolean interactive) {
-        if (traceEnabled()) {
-            traceDownCall("setInteractive", interactive);
-        }
-        nativeSetInteractive(interactive);
-        if (traceEnabled()) {
-            traceDownCallReturn("setInteractive", null);
-        }
+    public CallRFFINode callRFFINode() {
+        return new JNI_CallRFFINode();
     }
 
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Stats.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Stats.java
index 1f0564d90b..9d9c6e1867 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Stats.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Stats.java
@@ -29,42 +29,52 @@ import com.oracle.truffle.r.runtime.ffi.DLL.DLLInfo;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
 
-// Checkstyle: stop method name
 public class JNI_Stats implements StatsRFFI {
-    @Override
-    @TruffleBoundary
-    public void fft_factor(int n, int[] pmaxf, int[] pmaxp) {
-        native_fft_factor(fft_factor_address().asAddress(), n, pmaxf, pmaxp);
-    }
 
-    @Override
-    @TruffleBoundary
-    public int fft_work(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
-        return native_fft_work(fft_work_address().asAddress(), a, nseg, n, nspn, isn, work, iwork);
-    }
+    public static class JNI_FFTNode extends FFTNode {
+        private SymbolHandle fftWorkAddress;
+        private SymbolHandle fftFactorAddress;
 
-    private SymbolHandle fft_factor_address;
-    private SymbolHandle fft_work_address;
+        @Override
+        @TruffleBoundary
+        public int executeWork(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
+            initialize();
+            return native_fft_work(fftWorkAddress.asAddress(), a, nseg, n, nspn, isn, work, iwork);
+        }
+
+        @Override
+        @TruffleBoundary
+        public void executeFactor(int n, int[] pmaxf, int[] pmaxp) {
+            initialize();
+            native_fft_factor(fftFactorAddress.asAddress(), n, pmaxf, pmaxp);
 
-    private SymbolHandle fft_factor_address() {
-        if (fft_factor_address == null) {
-            DLLInfo dllInfo = DLL.findLibrary("stats");
-            fft_factor_address = RFFIFactory.getRFFI().getDLLRFFI().dlsym(dllInfo.handle, "fft_factor");
-            assert fft_factor_address != DLL.SYMBOL_NOT_FOUND;
         }
-        return fft_factor_address;
-    }
 
-    private SymbolHandle fft_work_address() {
-        if (fft_work_address == null) {
+        private void initialize() {
+            if (fftWorkAddress == null) {
+                fftWorkAddress = fftAddress("fft_work");
+                fftFactorAddress = fftAddress("fft_factor");
+            }
+        }
+
+        private static SymbolHandle fftAddress(String symbol) {
+            SymbolHandle fftAddress;
             DLLInfo dllInfo = DLL.findLibrary("stats");
-            fft_work_address = RFFIFactory.getRFFI().getDLLRFFI().dlsym(dllInfo.handle, "fft_work");
-            assert fft_work_address != DLL.SYMBOL_NOT_FOUND;
+            fftAddress = RFFIFactory.getRFFI().getDLLRFFI().dlsym(dllInfo.handle, symbol);
+            assert fftAddress != DLL.SYMBOL_NOT_FOUND;
+            return fftAddress;
         }
-        return fft_work_address;
+
     }
 
+    @Override
+    public FFTNode fftNode() {
+        return new JNI_FFTNode();
+    }
+
+    // Checkstyle: stop method name
     private static native void native_fft_factor(long address, int n, int[] pmaxf, int[] pmaxp);
 
     private static native int native_fft_work(long address, double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork);
+
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/TraceUpCallsAdapter.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/TraceUpCallsAdapter.java
index 461863208e..1b6d8aab81 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/TraceUpCallsAdapter.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/TraceUpCallsAdapter.java
@@ -298,7 +298,7 @@ public class TraceUpCallsAdapter implements UpCallsRFFI {
     }
 
     @Override
-    public byte[] RAW(Object x) {
+    public Object RAW(Object x) {
         if (RFFIUtils.traceEnabled()) {
             RFFIUtils.traceUpCall("RAW", x);
         }
@@ -306,7 +306,7 @@ public class TraceUpCallsAdapter implements UpCallsRFFI {
     }
 
     @Override
-    public byte[] LOGICAL(Object x) {
+    public Object LOGICAL(Object x) {
         if (RFFIUtils.traceEnabled()) {
             RFFIUtils.traceUpCall("LOGICAL", x);
         }
@@ -314,7 +314,7 @@ public class TraceUpCallsAdapter implements UpCallsRFFI {
     }
 
     @Override
-    public int[] INTEGER(Object x) {
+    public Object INTEGER(Object x) {
         if (RFFIUtils.traceEnabled()) {
             RFFIUtils.traceUpCall("INTEGER", x);
         }
@@ -322,7 +322,7 @@ public class TraceUpCallsAdapter implements UpCallsRFFI {
     }
 
     @Override
-    public double[] REAL(Object x) {
+    public Object REAL(Object x) {
         if (RFFIUtils.traceEnabled()) {
             RFFIUtils.traceUpCall("REAL", x);
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java
index 050047c15b..cfea6f2476 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java
@@ -68,7 +68,7 @@ public class TempPathName {
             } else {
                 Utils.rSuicide("cannot create 'R_TempDir'");
             }
-            RFFIFactory.getRFFI().getCallRFFI().setTempDir(tempDirPath);
+            RFFIFactory.getRFFI().getCallRFFI().callRFFINode().setTempDir(tempDirPath);
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
index 63b0cf0575..1f60bc3c9a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -547,7 +547,7 @@ public final class RContext extends ExecutionContext implements TruffleObject {
             this.methodTableDispatchOn = info.getParent().methodTableDispatchOn;
         }
         if (initial && !embedded) {
-            RFFIFactory.getRFFI().getCallRFFI().setInteractive(isInteractive());
+            RFFIFactory.getRFFI().getCallRFFI().callRFFINode().setInteractive(isInteractive());
             initialContextInitialized = true;
         }
         return this;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFI.java
index 3029ffce07..5930f8d17e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFI.java
@@ -22,32 +22,38 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
+import com.oracle.truffle.api.nodes.Node;
+
 /**
  * Support for the {.Call} and {.External} calls.
  */
 public interface CallRFFI {
-    /**
-     * Invoke the native function identified by {@code symbolInfo} passing it the arguments in
-     * {@code args}. The values in {@code args} can be any of the types used to represent {@code R}
-     * values in the implementation.
-     */
-    Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args);
+    public abstract static class CallRFFINode extends Node {
+        /**
+         * Invoke the native function identified by {@code symbolInfo} passing it the arguments in
+         * {@code args}. The values in {@code args} can be any of the types used to represent
+         * {@code R} values in the implementation.
+         */
+        public abstract Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args);
+
+        /**
+         * Variant that does not return a result (primarily for library "init" methods).
+         */
+        public abstract void invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args);
 
-    /**
-     * Variant that does not return a result (primarily for library "init" methods).
-     */
-    void invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args);
+        /**
+         * This interface is instantiated very early and sets the FFI global variables as part of
+         * that process. However, at that stage {@code tempDir} is not established so this call
+         * exists to set the value later.
+         */
+        public abstract void setTempDir(String tempDir);
 
-    /**
-     * This interface is instantiated very early and sets the FFI global variables as part of that
-     * process. However, at that stage {@code tempDir} is not established so this call exists to set
-     * the value later.
-     */
-    void setTempDir(String tempDir);
+        /**
+         * Sets the {@code R_Interactive} FFI variable. Similar rationale to {#link setTmpDir}.
+         */
+        public abstract void setInteractive(boolean interactive);
+    }
 
-    /**
-     * Sets the {@code R_Interactive} FFI variable. Similar rationale to {#link setTmpDir}.
-     */
-    void setInteractive(boolean interactive);
+    CallRFFINode callRFFINode();
 
 }
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 bceaacfbd4..f776c52e0e 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
@@ -34,6 +34,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
+import com.oracle.truffle.r.runtime.ffi.CallRFFI.CallRFFINode;
 
 /**
  * Support for Dynamically Loaded Libraries.
@@ -345,6 +346,7 @@ public class DLL {
     }
 
     private static final String R_INIT_PREFIX = "R_init_";
+    private static CallRFFINode callRFFINode;
 
     @TruffleBoundary
     public static DLLInfo loadPackageDLL(String path, boolean local, boolean now) throws DLLException {
@@ -355,7 +357,10 @@ public class DLL {
         if (initFunc != SYMBOL_NOT_FOUND) {
             synchronized (DLL.class) {
                 try {
-                    RFFIFactory.getRFFI().getCallRFFI().invokeVoidCall(new NativeCallInfo(pkgInit, initFunc, dllInfo), new Object[]{dllInfo});
+                    if (callRFFINode == null) {
+                        callRFFINode = RFFIFactory.getRFFI().getCallRFFI().callRFFINode();
+                    }
+                    callRFFINode.invokeVoidCall(new NativeCallInfo(pkgInit, initFunc, dllInfo), new Object[]{dllInfo});
                 } catch (ReturnException ex) {
                     // An error call can, due to condition handling, throw this which we must
                     // propogate
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java
index a796d4fb61..a6940fbd86 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -22,13 +22,20 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
+import com.oracle.truffle.api.nodes.Node;
+
 /**
- * Interface to native (C) methods provided by the {@code stats} package.
+ * Interface to native (C) methods provided by the {@code stats} package that are used to implement
+ * {@code.Call(C_fft)}. The implementation is split into a Java part which calls the
+ * {@code fft_factor} and {@code fft_work}. functions from the GNU R C code.
  */
 public interface StatsRFFI {
-    // Checkstyle: stop method name
-    void fft_factor(int n, int[] pmaxf, int[] pmaxp);
+    public abstract static class FFTNode extends Node {
+        public abstract void executeFactor(int n, int[] pmaxf, int[] pmaxp);
+
+        public abstract int executeWork(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork);
+    }
 
-    int fft_work(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork);
+    FFTNode fftNode();
 
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java
index d643ba908f..ccfb5fa901 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java
@@ -116,13 +116,13 @@ public interface UpCallsRFFI {
 
     void SET_VECTOR_ELT(Object x, int i, Object v);
 
-    byte[] RAW(Object x);
+    Object RAW(Object x);
 
-    byte[] LOGICAL(Object x);
+    Object LOGICAL(Object x);
 
-    int[] INTEGER(Object x);
+    Object INTEGER(Object x);
 
-    double[] REAL(Object x);
+    Object REAL(Object x);
 
     Object STRING_ELT(Object x, int i);
 
-- 
GitLab