diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_C.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_C.java
index e2eb33c9f70afe2c9fe855efc8b08a817f78bfdf..2102384fc2bc023c0356a92d3c6eb72bf7cb49c6 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_C.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_C.java
@@ -32,16 +32,16 @@ import com.oracle.truffle.r.engine.interop.NativeRawArray;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
-import com.oracle.truffle.r.runtime.ffi.jni.JNI_C;
+import com.oracle.truffle.r.runtime.ffi.jni.JNI_C.JNI_InvokeCNode;
 import com.oracle.truffle.r.runtime.ffi.truffle.TruffleRFFIFrameHelper;
 
 class TruffleLLVM_C implements CRFFI {
-    private static class TruffleCRFFINode extends JNI_C.JNI_CRFFINode {
+    private static class TruffleLLVM_InvokeCNode extends JNI_InvokeCNode {
 
         @Override
-        public synchronized void invoke(NativeCallInfo nativeCallInfo, Object[] args) {
+        public synchronized void execute(NativeCallInfo nativeCallInfo, Object[] args) {
             if (nativeCallInfo.address.value instanceof Long) {
-                super.invoke(nativeCallInfo, args);
+                super.execute(nativeCallInfo, args);
             } else {
                 VirtualFrame frame = TruffleRFFIFrameHelper.create();
                 TruffleLLVM_DLL.ensureParsed(nativeCallInfo);
@@ -76,7 +76,7 @@ class TruffleLLVM_C implements CRFFI {
     }
 
     @Override
-    public CRFFINode createCRFFINode() {
-        return new TruffleCRFFINode();
+    public InvokeCNode createInvokeCNode() {
+        return new TruffleLLVM_InvokeCNode();
     }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_CAccess.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_CAccess.java
index f94c7ffa21d6a8675990da8aad36b29ae1629a3f..16f57a750e1f42de0a24077faf6f9109547ed6ad 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_CAccess.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_CAccess.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.engine.interop.ffi.llvm;
 
 import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
 import com.oracle.truffle.r.runtime.ffi.jni.JNI_DLL;
 import com.oracle.truffle.r.runtime.rng.user.UserRNG;
 
@@ -49,7 +50,7 @@ public class TruffleLLVM_CAccess {
             if (symbolHandle == null) {
                 // This would be the generic path
                 // symbolHandle = DLL.findSymbol(cName(), null);
-                symbolHandle = (DLL.SymbolHandle) DLL.DLLFFIRootNode.create().getCallTarget().call(DLL.DLLFFIRootNode.DLSYM, handle, cName());
+                symbolHandle = (DLL.SymbolHandle) DLLRFFI.DLSymRootNode.create().getCallTarget().call(handle, cName());
                 assert symbolHandle != null;
             }
             return symbolHandle;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Call.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Call.java
index 56e4a10fa488ead85dbae0138b622944979d57b1..8c62773207559b9885e623ef6159b54b1c87745f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Call.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Call.java
@@ -43,7 +43,6 @@ import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.RFFIVariables;
 import com.oracle.truffle.r.runtime.ffi.jni.JNI_Call;
-import com.oracle.truffle.r.runtime.ffi.jni.JNI_Call.JNI_CallRFFINode;
 import com.oracle.truffle.r.runtime.ffi.truffle.TruffleRFFIFrameHelper;
 
 class TruffleLLVM_Call implements CallRFFI {
@@ -125,15 +124,19 @@ class TruffleLLVM_Call implements CallRFFI {
         }
     }
 
-    public static class InvokeJNI extends Node {
-        @Child JNI_CallRFFINode jniCall = new JNI_CallRFFINode();
+    public static class InvokeJNICall extends Node {
+        @Child CallRFFI.InvokeCallNode jniCall = new JNI_Call.JNI_InvokeCallNode();
 
-        public Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args) {
-            return jniCall.invokeCall(nativeCallInfo, args);
+        public Object execute(NativeCallInfo nativeCallInfo, Object[] args) {
+            return jniCall.execute(nativeCallInfo, args);
         }
+    }
+
+    public static class InvokeJNIVoidCall extends Node {
+        @Child CallRFFI.InvokeVoidCallNode jniCall = new JNI_Call.JNI_InvokeVoidCallNode();
 
-        public Object invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args) {
-            jniCall.invokeVoidCall(nativeCallInfo, args);
+        public Object execute(NativeCallInfo nativeCallInfo, Object[] args) {
+            jniCall.execute(nativeCallInfo, args);
             return RNull.instance;
         }
     }
@@ -194,15 +197,15 @@ class TruffleLLVM_Call implements CallRFFI {
 
         @Specialization(guards = {"isJNICall(nativeCallInfo)", "!voidCall"})
         protected Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args, @SuppressWarnings("unused") boolean voidCall,
-                        @Cached("new()") InvokeJNI invokeJNI) {
-            return invokeJNI.invokeCall(nativeCallInfo, args);
+                        @Cached("new()") InvokeJNICall invokeJNI) {
+            return invokeJNI.execute(nativeCallInfo, args);
 
         }
 
         @Specialization(guards = {"isJNICall(nativeCallInfo)", "voidCall"})
         protected Object invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args, @SuppressWarnings("unused") boolean voidCall,
-                        @Cached("new()") InvokeJNI invokeJNI) {
-            return invokeJNI.invokeVoidCall(nativeCallInfo, args);
+                        @Cached("new()") InvokeJNICall invokeJNI) {
+            return invokeJNI.execute(nativeCallInfo, args);
 
         }
 
@@ -217,17 +220,21 @@ class TruffleLLVM_Call implements CallRFFI {
         }
     }
 
-    private static class TruffleCallRFFINode extends CallRFFINode {
+    private static class TruffleLLVM_InvokeCallNode extends InvokeCallNode {
         @Child SplitTruffleCallRFFINode splitTruffleCallRFFINode = SplitTruffleCallRFFINodeGen.create();
 
         @Override
-        public synchronized Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args) {
+        public synchronized Object execute(NativeCallInfo nativeCallInfo, Object[] args) {
             return splitTruffleCallRFFINode.execute(nativeCallInfo, args, false);
 
         }
+    }
+
+    private static class TruffleLLVM_InvokeVoidCallNode extends InvokeVoidCallNode {
+        @Child SplitTruffleCallRFFINode splitTruffleCallRFFINode = SplitTruffleCallRFFINodeGen.create();
 
         @Override
-        public synchronized void invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args) {
+        public synchronized void execute(NativeCallInfo nativeCallInfo, Object[] args) {
             splitTruffleCallRFFINode.execute(nativeCallInfo, args, true);
         }
 
@@ -243,7 +250,12 @@ class TruffleLLVM_Call implements CallRFFI {
     }
 
     @Override
-    public CallRFFINode createCallRFFINode() {
-        return new TruffleCallRFFINode();
+    public InvokeCallNode createInvokeCallNode() {
+        return new TruffleLLVM_InvokeCallNode();
+    }
+
+    @Override
+    public InvokeVoidCallNode createInvokeVoidCallNode() {
+        return new TruffleLLVM_InvokeVoidCallNode();
     }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_DLL.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_DLL.java
index 8df55ed11fd6f328d15f85c4f61406e11161757d..3137ffd0abb16599f00e23643b2709ec1ff5e785 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_DLL.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_DLL.java
@@ -197,16 +197,16 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
         boolean match(String name);
     }
 
-    private static class TruffleLLVM_DLLRFFINode extends JNI_DLLRFFINode {
+    private static class TruffleLLVM_DLOpenNode extends JNI_DLOpenNode {
 
         /**
          * If a library is enabled for LLVM, the IR for all the modules is retrieved and analyzed.
          * Every exported symbol in the module added to the parseStatus map for the current
-         * {@link RContext}. This allows {@link #dlsym} to definitively locate any symbol, even if
+         * {@link RContext}. This allows {@code dlsym} to definitively locate any symbol, even if
          * the IR has not been parsed yet.
          */
         @Override
-        public Object dlopen(String path, boolean local, boolean now) {
+        public Object execute(String path, boolean local, boolean now) {
             try {
                 LLVM_IR[] irs = LLVM_IR.getLLVMIR(path);
                 String libName = getLibName(path);
@@ -216,7 +216,7 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
                     truffleDLL.libRModules = irs;
                 }
                 if (irs == null || isBlacklisted(libName)) {
-                    return super.dlopen(path, local, now);
+                    return super.execute(path, local, now);
                 } else {
                     ContextStateImpl contextState = getContextState();
                     for (int i = 0; i < irs.length; i++) {
@@ -229,9 +229,11 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
                 return null;
             }
         }
+    }
 
+    private static class TruffleLLVM_DLSymNode extends JNI_DLSymNode {
         @Override
-        public SymbolHandle dlsym(Object handle, String symbol) {
+        public SymbolHandle execute(Object handle, String symbol) {
             if (handle instanceof LLVM_Handle) {
                 // If the symbol exists it will be in the map
                 ParseStatus parseStatus = getContextState().parseStatusMap.get(symbol);
@@ -248,23 +250,36 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
                     return null;
                 }
             } else {
-                return super.dlsym(handle, symbol);
+                return super.execute(handle, symbol);
             }
         }
+    }
 
+    private static class TruffleLLVM_DLCloseNode extends JNI_DLCloseNode {
         @Override
-        public int dlclose(Object handle) {
+        public int execute(Object handle) {
             if (handle instanceof LLVM_Handle) {
                 return 0;
             } else {
-                return super.dlclose(handle);
+                return super.execute(handle);
             }
         }
+
+    }
+
+    @Override
+    public DLOpenNode createDLOpenNode() {
+        return new TruffleLLVM_DLOpenNode();
+    }
+
+    @Override
+    public DLSymNode createDLSymNode() {
+        return new TruffleLLVM_DLSymNode();
     }
 
     @Override
-    public DLLRFFINode createDLLRFFINode() {
-        return new TruffleLLVM_DLLRFFINode();
+    public DLCloseNode createDLCloseNode() {
+        return new TruffleLLVM_DLCloseNode();
     }
 
     private static void addExportsToMap(ContextStateImpl contextState, String libName, LLVM_IR ir, ModuleNameMatch moduleNameMatch) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Stats.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Stats.java
index 48ea00d353985ca18eada1651fc1f8ddddae364b..594866624b3332f4439df6b7c2f57b0f1611f92e 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Stats.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Stats.java
@@ -40,7 +40,7 @@ import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 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.DLLRFFI.DLLRFFINode;
+import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
 import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
 import com.oracle.truffle.r.runtime.ffi.truffle.TruffleRFFIFrameHelper;
 
@@ -87,13 +87,13 @@ public class TruffleLLVM_Stats implements StatsRFFI {
     }
 
     public abstract static class LookupAdapter extends Node {
-        @Child DLLRFFINode dllRFFINode = RFFIFactory.getRFFI().getDLLRFFI().createDLLRFFINode();
+        @Child DLLRFFI.DLSymNode dllSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
 
         public SymbolHandle lookup(String name) {
             DLLInfo dllInfo = DLL.findLibrary("stats");
             // cannot go through DLL because stats does not allow dynamic lookup
             // and these symbols are not registered (only fft)
-            SymbolHandle result = dllRFFINode.dlsym(dllInfo.handle, name);
+            SymbolHandle result = dllSymNode.execute(dllInfo.handle, name);
             if (result == DLL.SYMBOL_NOT_FOUND) {
                 @SuppressWarnings("unused")
                 TruffleLLVM_RFFIContextState cs = TruffleLLVM_RFFIContextState.getContextState();
@@ -187,23 +187,31 @@ public class TruffleLLVM_Stats implements StatsRFFI {
         }
     }
 
-    public static class Truffle_FFTNode extends FFTNode {
-        @Child ExecuteWork executeWork = ExecuteWork.create();
+    public static class Truffle_FactorNode extends FactorNode {
         @Child ExecuteFactor executeFactor = ExecuteFactor.create();
 
         @Override
-        public int executeWork(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
-            return executeWork.execute(a, nseg, n, nspn, isn, work, iwork, RContext.getInstance());
+        public void execute(int n, int[] pmaxf, int[] pmaxp) {
+            executeFactor.execute(n, pmaxf, pmaxp, RContext.getInstance());
         }
+    }
+
+    public static class Truffle_WorkNode extends WorkNode {
+        @Child ExecuteWork executeWork = ExecuteWork.create();
 
         @Override
-        public void executeFactor(int n, int[] pmaxf, int[] pmaxp) {
-            executeFactor.execute(n, pmaxf, pmaxp, RContext.getInstance());
+        public int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
+            return executeWork.execute(a, nseg, n, nspn, isn, work, iwork, RContext.getInstance());
         }
     }
 
     @Override
-    public FFTNode createFFTNode() {
-        return new Truffle_FFTNode();
+    public FactorNode createFactorNode() {
+        return new Truffle_FactorNode();
+    }
+
+    @Override
+    public WorkNode createWorkNode() {
+        return new Truffle_WorkNode();
     }
 }
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 fa71bc689436f22c66c0f3054baa5796b11dacc5..11c4a71ce6b38c8afb385a9a5be2670ce5a16a14 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
@@ -26,7 +26,7 @@ import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.ffi.DLL;
-import com.oracle.truffle.r.runtime.ffi.CallRFFIRootNode;
+import com.oracle.truffle.r.runtime.ffi.CallRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
 /**
@@ -64,7 +64,7 @@ public class RGraphics {
                 DLL.DLLInfo dllInfo = DLL.findLibrary("graphics");
                 DLL.SymbolHandle symbolHandle = DLL.findSymbol("InitGraphics", dllInfo);
                 assert symbolHandle != DLL.SYMBOL_NOT_FOUND;
-                CallRFFIRootNode.create().getCallTarget().call(new NativeCallInfo("InitGraphics", symbolHandle, dllInfo), true, new Object[0]);
+                CallRFFI.InvokeVoidCallRootNode.create().getCallTarget().call(new NativeCallInfo("InitGraphics", symbolHandle, dllInfo), new Object[0]);
             }
         }
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grid/GridFunctions.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grid/GridFunctions.java
index b5bca848f34ad5a305ef9ecc3213e0d1a1d5ad85..ca5d398f2254e9c8d27908c53d18b498ca4ed1d2 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grid/GridFunctions.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grid/GridFunctions.java
@@ -35,12 +35,12 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 public class GridFunctions {
 
     public abstract static class InitGrid extends RExternalBuiltinNode.Arg1 {
-        @Child GridRFFI.GridRFFINode gridRFFINode = RFFIFactory.getRFFI().getGridRFFI().createGridRFFINode();
+        @Child GridRFFI.InitGridNode initGridNode = RFFIFactory.getRFFI().getGridRFFI().createInitGridNode();
 
         @Specialization
         @TruffleBoundary
         protected Object initGrid(REnvironment gridEvalEnv) {
-            return gridRFFINode.initGrid(gridEvalEnv);
+            return initGridNode.execute(gridEvalEnv);
         }
 
         @Fallback
@@ -50,12 +50,12 @@ public class GridFunctions {
     }
 
     public static final class KillGrid extends RExternalBuiltinNode {
-        @Child GridRFFI.GridRFFINode gridRFFINode = RFFIFactory.getRFFI().getGridRFFI().createGridRFFINode();
+        @Child GridRFFI.KillGridNode killGridNode = RFFIFactory.getRFFI().getGridRFFI().createKillGridNode();
 
         @Override
         @TruffleBoundary
         public Object call(RArgsValuesAndNames args) {
-            return gridRFFINode.killGrid();
+            return killGridNode.execute();
         }
     }
 
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
index a67b35e7adb2026afbfdb1121c058b2408b4b523..07a64735ebe6b4b72ad4bdb44296960b1865acf1 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
@@ -45,7 +45,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
 
 public abstract class C_ParseRd extends RExternalBuiltinNode.Arg9 {
-    @Child ToolsRFFI.ToolsRFFINode toolsRFFINode = RFFIFactory.getRFFI().getToolsRFFI().createToolsRFFINode();
+    @Child ToolsRFFI.ParseRdNode parseRdNode = RFFIFactory.getRFFI().getToolsRFFI().createParseRdNode();
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -78,7 +78,7 @@ public abstract class C_ParseRd extends RExternalBuiltinNode.Arg9 {
         // fromIndex checks validity of con and throws error if not
         try (RConnection openConn = RConnection.fromIndex(con).forceOpen("r")) {
             // @formatter:off
-            return toolsRFFINode.parseRd(openConn, srcfile,
+            return parseRdNode.execute(openConn, srcfile,
                             verbose.materialize(),
                             RDataFactory.createLogicalVectorFromScalar((byte) fragment),
                             basename.materialize(),
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java
index 0bee29b13cd098a4bb25d737a10475daf6d74b85..0be43a71887efe09c50a4fc1aa6663f986a67046 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/DirChmod.java
@@ -24,6 +24,7 @@ import java.util.Iterator;
 import java.util.stream.Stream;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
@@ -32,7 +33,7 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 public abstract class DirChmod extends RExternalBuiltinNode.Arg2 {
 
@@ -49,7 +50,8 @@ public abstract class DirChmod extends RExternalBuiltinNode.Arg2 {
 
     @Specialization
     @TruffleBoundary
-    protected RNull dirChmod(String pathName, boolean setGroupWrite) {
+    protected RNull dirChmod(String pathName, boolean setGroupWrite,
+                    @Cached("create()") BaseRFFI.ChmodNode chmodNode) {
         Path path = FileSystems.getDefault().getPath(pathName);
         int fileMask = setGroupWrite ? GRPWRITE_FILE_MASK : FILE_MASK;
         int dirMask = setGroupWrite ? GRPWRITE_DIR_MASK : DIR_MASK;
@@ -65,7 +67,7 @@ public abstract class DirChmod extends RExternalBuiltinNode.Arg2 {
                 int elementMode = Utils.intFilePermissions(pfa.permissions());
                 int newMode = Files.isDirectory(element) ? elementMode | dirMask : elementMode | fileMask;
                 // System.out.printf("path %s: old %o, new %o%n", element, elementMode, newMode);
-                RFFIFactory.getRFFI().getBaseRFFI().chmod(element.toString(), newMode);
+                chmodNode.execute(element.toString(), newMode);
             }
         } catch (IOException ex) {
             // ignore
diff --git a/com.oracle.truffle.r.native/fficall/src/jniboot/jniboot.c b/com.oracle.truffle.r.native/fficall/src/jniboot/jniboot.c
index 22aab408ee0576d4ec8e32674505e01acdc169fe..09796b4e9c08a30ab76c04ae716c6ece2f9e8118 100644
--- a/com.oracle.truffle.r.native/fficall/src/jniboot/jniboot.c
+++ b/com.oracle.truffle.r.native/fficall/src/jniboot/jniboot.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 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
@@ -27,23 +27,11 @@
 #include <dlfcn.h>
 #include <jni.h>
 
-// It seems that an internal (JVM) dlsym call can occur between a call to these functions and dlerror
-// (probably resolving the JNI dlerror symbol, so we capture it here (N.B. depends on single
-// threaded limitation).
+static jclass unsatisfiedLinkError;
 
-static jobject last_dlerror = NULL;
-
-static void create_dlerror_string(JNIEnv *env) {
-	if (last_dlerror != NULL) {
-		(*env)->DeleteGlobalRef(env, last_dlerror);
-		last_dlerror = NULL;
-	}
-	char *err = dlerror();
-	if (err == NULL) {
-		last_dlerror = NULL;
-	} else {
-//    	printf("dlerror: %s\n", err);
-		last_dlerror = (*env)->NewGlobalRef(env, (*env)->NewStringUTF(env, err));
+static void initUnsatisfiedLinkError(JNIEnv *env) {
+	if (unsatisfiedLinkError == NULL) {
+		unsatisfiedLinkError = (jclass) (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/lang/UnsatisfiedLinkError"));
 	}
 }
 
@@ -53,7 +41,10 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1DLL_native_1dlopen(JNIEnv *env, j
     int flags = (local ? RTLD_LOCAL : RTLD_GLOBAL) | (now ? RTLD_NOW : RTLD_LAZY);
     void *handle = dlopen(path, flags);
     if (handle == NULL) {
-        create_dlerror_string(env);
+    	char *err = dlerror();
+    	initUnsatisfiedLinkError(env);
+//        (*env)->ReleaseStringUTFChars(env, jpath, path);
+    	(*env)->ThrowNew(env, unsatisfiedLinkError, err);
     }
     (*env)->ReleaseStringUTFChars(env, jpath, path);
     return (jlong) handle;
@@ -63,7 +54,14 @@ JNIEXPORT jlong JNICALL
 Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1DLL_native_1dlsym(JNIEnv *env, jclass c, jlong handle, jstring jsymbol) {
     const char *symbol = (*env)->GetStringUTFChars(env, jsymbol, NULL);
     void *address = dlsym((void *)handle, symbol);
-    create_dlerror_string(env);
+    if (address == NULL) {
+    	char *err = dlerror();
+    	if (err != NULL) {
+        	initUnsatisfiedLinkError(env);
+//            (*env)->ReleaseStringUTFChars(env, jsymbol, symbol);
+        	(*env)->ThrowNew(env, unsatisfiedLinkError, err);
+    	}
+    }
     (*env)->ReleaseStringUTFChars(env, jsymbol, symbol);
     return (jlong) address;
 }
@@ -74,8 +72,4 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1DLL_native_1dlclose(JNIEnv *env,
     return rc;
 }
 
-JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1DLL_native_1dlerror(JNIEnv *env, jclass c) {
-   	return last_dlerror;
-}
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
index ccc8d32b7a19cadae1f1a396e9c0efe61cf5c2e2..5caba014473e6eb07e761b757e17bc4050bf4530 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
@@ -41,7 +41,6 @@ import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClass
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 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.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -78,10 +77,7 @@ public class DynLoadFunctions {
                 DLLInfo dllInfo = loadPackageDLLNode.execute(lib, local, now);
                 return dllInfo.toRList();
             } catch (DLLException ex) {
-                // This is not a recoverable error
-                System.out.println("exception while loading " + lib + ":");
-                ex.printStackTrace();
-                throw RInternalError.shouldNotReachHere(ex);
+                throw RError.error(RError.SHOW_CALLER, ex);
             }
         }
     }
@@ -132,7 +128,7 @@ public class DynLoadFunctions {
 
     @RBuiltin(name = "is.loaded", kind = INTERNAL, parameterNames = {"symbol", "PACKAGE", "type"}, behavior = READS_STATE)
     public abstract static class IsLoaded extends RBuiltinNode {
-        @Child DLL.FindSymbolNode findSymbolNode = DLL.FindSymbolNode.create();
+        @Child DLL.RFindSymbolNode findSymbolNode = DLL.RFindSymbolNode.create();
 
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -168,7 +164,7 @@ public class DynLoadFunctions {
 
     @RBuiltin(name = "getSymbolInfo", kind = INTERNAL, parameterNames = {"symbol", "package", "withRegistrationInfo"}, behavior = READS_STATE)
     public abstract static class GetSymbolInfo extends RBuiltinNode {
-        @Child DLL.FindSymbolNode findSymbolNode = DLL.FindSymbolNode.create();
+        @Child DLL.RFindSymbolNode findSymbolNode = DLL.RFindSymbolNode.create();
 
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -191,7 +187,7 @@ public class DynLoadFunctions {
         @Specialization(guards = "isDLLInfo(externalPtr)")
         @TruffleBoundary
         protected Object getSymbolInfo(RAbstractStringVector symbolVec, RExternalPtr externalPtr, boolean withReg, //
-                        @Cached("create()") DLL.DlsymNode dlsymNode) {
+                        @Cached("create()") DLL.RdlsymNode dlsymNode) {
             DLL.DLLInfo dllInfo = (DLLInfo) externalPtr.getExternalObject();
             if (dllInfo == null) {
                 throw RError.error(this, RError.Message.REQUIRES_NAME_DLLINFO);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
index 3dd5c7dd8d9a6158a069930370f5d63a5afbce2d..c6e17bcdab114c70593b55d210bc37bc62186916 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
@@ -56,6 +56,7 @@ import java.util.stream.Stream;
 
 import com.oracle.truffle.api.CompilerDirectives;
 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.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
@@ -80,7 +81,7 @@ import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 // Much of this code was influences/transcribed from GnuR src/main/platform.c
 
@@ -1184,7 +1185,8 @@ public class FileFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected byte dirCreate(String pathIn, boolean showWarnings, boolean recursive, int octMode) {
+        protected byte dirCreate(String pathIn, boolean showWarnings, boolean recursive, int octMode,
+                        @Cached("create()") BaseRFFI.MkdirNode mkdirNode) {
             boolean ok;
             if (RRuntime.isNA(pathIn)) {
                 ok = false;
@@ -1192,32 +1194,32 @@ public class FileFunctions {
                 ok = true;
                 String path = Utils.tildeExpand(pathIn);
                 if (recursive) {
-                    ok = mkparentdirs(new File(path).getAbsoluteFile().getParentFile(), showWarnings, octMode);
+                    ok = mkparentdirs(mkdirNode, new File(path).getAbsoluteFile().getParentFile(), showWarnings, octMode);
                 }
                 if (ok) {
-                    ok = mkdir(path, showWarnings, octMode);
+                    ok = mkdir(mkdirNode, path, showWarnings, octMode);
                 }
             }
             return RRuntime.asLogical(ok);
         }
 
-        private boolean mkparentdirs(File file, boolean showWarnings, int mode) {
+        private boolean mkparentdirs(BaseRFFI.MkdirNode mkdirNode, File file, boolean showWarnings, int mode) {
             if (file.isDirectory()) {
                 return true;
             }
             if (file.exists()) {
                 return false;
             }
-            if (mkparentdirs(file.getParentFile(), showWarnings, mode)) {
-                return mkdir(file.getAbsolutePath(), showWarnings, mode);
+            if (mkparentdirs(mkdirNode, file.getParentFile(), showWarnings, mode)) {
+                return mkdir(mkdirNode, file.getAbsolutePath(), showWarnings, mode);
             } else {
                 return false;
             }
         }
 
-        private boolean mkdir(String path, boolean showWarnings, int mode) {
+        private boolean mkdir(BaseRFFI.MkdirNode mkdirNode, String path, boolean showWarnings, int mode) {
             try {
-                RFFIFactory.getRFFI().getBaseRFFI().mkdir(path, mode);
+                mkdirNode.execute(path, mode);
                 return true;
             } catch (IOException ex) {
                 if (showWarnings) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Getwd.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Getwd.java
index d0833557089e978f985b61f9d8a5eab136c11894..1e5ab0b3745b9f0e3134bd0d4c8857ab05fc4cc7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Getwd.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Getwd.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -30,15 +30,17 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 @RBuiltin(name = "getwd", kind = INTERNAL, parameterNames = {}, behavior = IO)
 public abstract class Getwd extends RBuiltinNode {
 
+    @Child private BaseRFFI.GetwdNode getwdNode = BaseRFFI.GetwdNode.create();
+
     @Specialization
     @TruffleBoundary
     protected Object getwd() {
-        String result = RFFIFactory.getRFFI().getBaseRFFI().getwd();
+        String result = getwdNode.execute();
         return RDataFactory.createStringVector(result);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Setwd.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Setwd.java
index 9180e76850957de0adc25607cb1ceccbad79fb3b..dc905f1abfc369b303d658b087e620fc4c756eb8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Setwd.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Setwd.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -31,13 +31,14 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 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.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 @RBuiltin(name = "setwd", visibility = OFF, kind = INTERNAL, parameterNames = "path", behavior = IO)
 public abstract class Setwd extends RBuiltinNode {
@@ -49,14 +50,16 @@ public abstract class Setwd extends RBuiltinNode {
 
     @Specialization
     @TruffleBoundary
-    protected Object setwd(String path) {
-        String owd = RFFIFactory.getRFFI().getBaseRFFI().getwd();
+    protected Object setwd(String path,
+                    @Cached("create()") BaseRFFI.GetwdNode getwdNode,
+                    @Cached("create()") BaseRFFI.SetwdNode setwdNode) {
+        String owd = getwdNode.execute();
         String nwd = Utils.tildeExpand(path);
-        int rc = RFFIFactory.getRFFI().getBaseRFFI().setwd(nwd);
+        int rc = setwdNode.execute(nwd);
         if (rc != 0) {
             throw RError.error(this, RError.Message.CANNOT_CHANGE_DIRECTORY);
         } else {
-            String nwdAbs = RFFIFactory.getRFFI().getBaseRFFI().getwd();
+            String nwdAbs = getwdNode.execute();
             Utils.updateCurwd(nwdAbs);
             return owd;
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java
index e62c27d3e45cdc1ca7a0623ead849a614c8377ac..4e845a663b43d772f5df41e0ecba7a17efdcc8f8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java
@@ -45,6 +45,7 @@ import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 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.frame.Frame;
 import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
@@ -68,17 +69,19 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI.UtsName;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public class SysFunctions {
 
     @RBuiltin(name = "Sys.getpid", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class SysGetpid extends RBuiltinNode {
+        @Child private BaseRFFI.GetpidNode getpidNode = BaseRFFI.GetpidNode.create();
+
         @Specialization
         @TruffleBoundary
         protected Object sysGetPid() {
-            int pid = RFFIFactory.getRFFI().getBaseRFFI().getpid();
+            int pid = getpidNode.execute();
             return RDataFactory.createIntVectorFromScalar(pid);
         }
     }
@@ -251,7 +254,8 @@ public class SysFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object sysReadlink(RAbstractStringVector vector) {
+        protected Object sysReadlink(RAbstractStringVector vector,
+                        @Cached("create()") BaseRFFI.ReadlinkNode readlinkNode) {
             String[] paths = new String[vector.getLength()];
             boolean complete = RDataFactory.COMPLETE_VECTOR;
             for (int i = 0; i < paths.length; i++) {
@@ -259,7 +263,7 @@ public class SysFunctions {
                 if (RRuntime.isNA(path)) {
                     paths[i] = path;
                 } else {
-                    paths[i] = doSysReadLink(path);
+                    paths[i] = doSysReadLink(path, readlinkNode);
                 }
                 if (RRuntime.isNA(paths[i])) {
                     complete = RDataFactory.INCOMPLETE_VECTOR;
@@ -269,10 +273,10 @@ public class SysFunctions {
         }
 
         @TruffleBoundary
-        private static String doSysReadLink(String path) {
+        private static String doSysReadLink(String path, BaseRFFI.ReadlinkNode readlinkNode) {
             String s;
             try {
-                s = RFFIFactory.getRFFI().getBaseRFFI().readlink(path);
+                s = readlinkNode.execute(path);
                 if (s == null) {
                     s = "";
                 }
@@ -294,14 +298,15 @@ public class SysFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RLogicalVector sysChmod(RAbstractStringVector pathVec, RAbstractIntVector octmode, @SuppressWarnings("unused") boolean useUmask) {
+        protected RLogicalVector sysChmod(RAbstractStringVector pathVec, RAbstractIntVector octmode, @SuppressWarnings("unused") boolean useUmask,
+                        @Cached("create()") BaseRFFI.ChmodNode chmodNode) {
             byte[] data = new byte[pathVec.getLength()];
             for (int i = 0; i < data.length; i++) {
                 String path = Utils.tildeExpand(pathVec.getDataAt(i));
                 if (path.length() == 0 || RRuntime.isNA(path)) {
                     continue;
                 }
-                int result = RFFIFactory.getRFFI().getBaseRFFI().chmod(path, octmode.getDataAt(i % octmode.getLength()));
+                int result = chmodNode.execute(path, octmode.getDataAt(i % octmode.getLength()));
                 data[i] = RRuntime.asLogical(result == 0);
             }
             return RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR);
@@ -338,10 +343,12 @@ public class SysFunctions {
         private static final String[] NAMES = new String[]{"sysname", "release", "version", "nodename", "machine", "login", "user", "effective_user"};
         private static final RStringVector NAMES_ATTR = RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR);
 
+        @Child private BaseRFFI.UnameNode unameNode = BaseRFFI.UnameNode.create();
+
         @Specialization
         @TruffleBoundary
         protected Object sysTime() {
-            UtsName utsname = RFFIFactory.getRFFI().getBaseRFFI().uname();
+            UtsName utsname = unameNode.execute();
             String[] data = new String[NAMES.length];
             data[0] = utsname.sysname();
             data[1] = utsname.release();
@@ -367,7 +374,8 @@ public class SysFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected Object sysGlob(RAbstractStringVector pathVec, @SuppressWarnings("unused") boolean dirMask) {
+        protected Object sysGlob(RAbstractStringVector pathVec, @SuppressWarnings("unused") boolean dirMask,
+                        @Cached("create()") BaseRFFI.GlobNode globNode) {
             ArrayList<String> matches = new ArrayList<>();
             // Sys.glob closure already called path.expand
             for (int i = 0; i < pathVec.getLength(); i++) {
@@ -375,7 +383,7 @@ public class SysFunctions {
                 if (pathPattern.length() == 0 || RRuntime.isNA(pathPattern)) {
                     continue;
                 }
-                ArrayList<String> pathPatternMatches = RFFIFactory.getRFFI().getBaseRFFI().glob(pathPattern);
+                ArrayList<String> pathPatternMatches = globNode.glob(pathPattern);
                 matches.addAll(pathPatternMatches);
             }
             String[] data = new String[matches.size()];
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 038de21300612e0f70d281f17b0da2d3ccf399d4..7acf32697d27496154580c1ef625c3cb9d935009 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
@@ -202,7 +202,7 @@ public class CallAndExternalFunctions {
     }
 
     abstract static class CallRFFIAdapter extends LookupAdapter {
-        @Child CallRFFI.CallRFFINode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createCallRFFINode();
+        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
     }
 
     /**
@@ -667,7 +667,7 @@ public class CallAndExternalFunctions {
         protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName,
                         @Cached("symbol") RList cached,
                         @Cached("extractSymbolInfo(frame, symbol)") NativeCallInfo nativeCallInfo) {
-            return callRFFINode.invokeCall(nativeCallInfo, args.getArguments());
+            return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
 
         /**
@@ -678,7 +678,7 @@ public class CallAndExternalFunctions {
         @Specialization(replaces = "callNamedFunction")
         protected Object callNamedFunctionGeneric(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName) {
             NativeCallInfo nativeCallInfo = extractSymbolInfo(frame, symbol);
-            return callRFFINode.invokeCall(nativeCallInfo, args.getArguments());
+            return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
 
         /**
@@ -687,7 +687,7 @@ public class CallAndExternalFunctions {
         @Specialization
         protected Object callNamedFunction(String symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName,
                         @Cached("createRegisteredNativeSymbol(CallNST)") DLL.RegisteredNativeSymbol rns,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             return callNamedFunctionWithPackage(symbol, args, null, rns, findSymbolNode);
         }
 
@@ -698,13 +698,13 @@ public class CallAndExternalFunctions {
         @Specialization
         protected Object callNamedFunctionWithPackage(String symbol, RArgsValuesAndNames args, String packageName,
                         @Cached("createRegisteredNativeSymbol(CallNST)") DLL.RegisteredNativeSymbol rns,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             DLL.SymbolHandle func = findSymbolNode.execute(symbol, packageName, rns);
             if (func == DLL.SYMBOL_NOT_FOUND) {
                 errorProfile.enter();
                 throw RError.error(this, RError.Message.SYMBOL_NOT_IN_TABLE, symbol, "Call", packageName);
             }
-            return callRFFINode.invokeCall(new NativeCallInfo(symbol, func, rns.getDllInfo()), args.getArguments());
+            return callRFFINode.execute(new NativeCallInfo(symbol, func, rns.getDllInfo()), args.getArguments());
         }
 
         @SuppressWarnings("unused")
@@ -786,27 +786,27 @@ public class CallAndExternalFunctions {
                         @Cached("symbol") RList cached,
                         @Cached("extractSymbolInfo(frame, symbol)") NativeCallInfo nativeCallInfo) {
             Object list = encodeArgumentPairList(args, nativeCallInfo.name);
-            return callRFFINode.invokeCall(nativeCallInfo, new Object[]{list});
+            return callRFFINode.execute(nativeCallInfo, new Object[]{list});
         }
 
         @Specialization
         protected Object callNamedFunction(String symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName,
                         @Cached("createRegisteredNativeSymbol(ExternalNST)") DLL.RegisteredNativeSymbol rns,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             return callNamedFunctionWithPackage(symbol, args, null, rns, findSymbolNode);
         }
 
         @Specialization
         protected Object callNamedFunctionWithPackage(String symbol, RArgsValuesAndNames args, String packageName,
                         @Cached("createRegisteredNativeSymbol(ExternalNST)") DLL.RegisteredNativeSymbol rns,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             DLL.SymbolHandle func = findSymbolNode.execute(symbol, packageName, rns);
             if (func == DLL.SYMBOL_NOT_FOUND) {
                 errorProfile.enter();
                 throw RError.error(this, RError.Message.SYMBOL_NOT_IN_TABLE, symbol, "External", packageName);
             }
             Object list = encodeArgumentPairList(args, symbol);
-            return callRFFINode.invokeCall(new NativeCallInfo(symbol, func, rns.getDllInfo()), new Object[]{list});
+            return callRFFINode.execute(new NativeCallInfo(symbol, func, rns.getDllInfo()), new Object[]{list});
         }
 
         @Fallback
@@ -864,20 +864,20 @@ public class CallAndExternalFunctions {
                         @Cached("extractSymbolInfo(frame, symbol)") NativeCallInfo nativeCallInfo) {
             Object list = encodeArgumentPairList(args, nativeCallInfo.name);
             // TODO: provide proper values for the CALL, OP and RHO parameters
-            return callRFFINode.invokeCall(nativeCallInfo, new Object[]{CALL, OP, list, RHO});
+            return callRFFINode.execute(nativeCallInfo, new Object[]{CALL, OP, list, RHO});
         }
 
         @Specialization
         protected Object callNamedFunction(String symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName,
                         @Cached("createRegisteredNativeSymbol(ExternalNST)") DLL.RegisteredNativeSymbol rns,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             return callNamedFunctionWithPackage(symbol, args, null, rns, findSymbolNode);
         }
 
         @Specialization
         protected Object callNamedFunctionWithPackage(String symbol, RArgsValuesAndNames args, String packageName,
                         @Cached("createRegisteredNativeSymbol(ExternalNST)") DLL.RegisteredNativeSymbol rns,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             DLL.SymbolHandle func = findSymbolNode.execute(symbol, packageName, rns);
             if (func == DLL.SYMBOL_NOT_FOUND) {
                 errorProfile.enter();
@@ -885,7 +885,7 @@ public class CallAndExternalFunctions {
             }
             Object list = encodeArgumentPairList(args, symbol);
             // TODO: provide proper values for the CALL, OP and RHO parameters
-            return callRFFINode.invokeCall(new NativeCallInfo(symbol, func, rns.getDllInfo()), new Object[]{CALL, OP, list, RHO});
+            return callRFFINode.execute(new NativeCallInfo(symbol, func, rns.getDllInfo()), new Object[]{CALL, OP, list, RHO});
         }
 
         @Fallback
@@ -925,18 +925,18 @@ public class CallAndExternalFunctions {
         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 callRFFINode.invokeCall(nativeCallInfo, new Object[]{list});
+            return callRFFINode.execute(nativeCallInfo, new Object[]{list});
         }
 
         @Specialization
         protected Object callNamedFunction(String name, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             return callNamedFunctionWithPackage(name, args, null, findSymbolNode);
         }
 
         @Specialization
         protected Object callNamedFunctionWithPackage(String name, RArgsValuesAndNames args, String packageName,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             DLL.RegisteredNativeSymbol rns = new DLL.RegisteredNativeSymbol(DLL.NativeSymbolType.External, null, null);
             DLL.SymbolHandle func = findSymbolNode.execute(name, packageName, rns);
             if (func == DLL.SYMBOL_NOT_FOUND) {
@@ -944,7 +944,7 @@ public class CallAndExternalFunctions {
                 throw RError.error(this, RError.Message.C_SYMBOL_NOT_IN_TABLE, name);
             }
             Object list = encodeArgumentPairList(args, name);
-            return callRFFINode.invokeCall(new NativeCallInfo(name, func, rns.getDllInfo()), new Object[]{list});
+            return callRFFINode.execute(new NativeCallInfo(name, func, rns.getDllInfo()), new Object[]{list});
         }
 
         @Fallback
@@ -983,26 +983,26 @@ public class CallAndExternalFunctions {
         @Specialization
         protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName) {
             NativeCallInfo nativeCallInfo = extractSymbolInfo(frame, symbol);
-            return callRFFINode.invokeCall(nativeCallInfo, args.getArguments());
+            return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
 
         @Specialization
         protected Object callNamedFunction(String name, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             return callNamedFunctionWithPackage(name, args, null, findSymbolNode);
         }
 
         @Specialization
         @TruffleBoundary
         protected Object callNamedFunctionWithPackage(String name, RArgsValuesAndNames args, String packageName,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode) {
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode) {
             DLL.RegisteredNativeSymbol rns = new DLL.RegisteredNativeSymbol(DLL.NativeSymbolType.Call, null, null);
             DLL.SymbolHandle func = findSymbolNode.execute(name, packageName, rns);
             if (func == DLL.SYMBOL_NOT_FOUND) {
                 errorProfile.enter();
                 throw RError.error(this, RError.Message.C_SYMBOL_NOT_IN_TABLE, name);
             }
-            return callRFFINode.invokeCall(new NativeCallInfo(name, func, rns.getDllInfo()), args.getArguments());
+            return callRFFINode.execute(new NativeCallInfo(name, func, rns.getDllInfo()), args.getArguments());
         }
 
         @Fallback
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 e28bccf67dcab325f38d2b4a2abaa50decd19b15..23bbc0cf05bdce9d6c73b4779a5010dc56034b37 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
@@ -21,14 +21,12 @@ import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 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().createFFTNode();
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -39,7 +37,9 @@ public abstract class Fft extends RExternalBuiltinNode.Arg2 {
     // TODO: handle more argument types (this is sufficient to run the b25 benchmarks)
     @Specialization
     public Object execute(RAbstractComplexVector zVec, boolean inverse,
-                    @Cached("create()") GetDimAttributeNode getDimNode) {
+                    @Cached("create()") GetDimAttributeNode getDimNode,
+                    @Cached("create()") StatsRFFI.FactorNode factorNode,
+                    @Cached("create()") StatsRFFI.WorkNode workNode) {
         double[] z = zVec.materialize().getDataTemp();
         int inv = inverse ? 2 : -2;
         int[] d = getDimNode.getDimensions(zVec);
@@ -50,14 +50,14 @@ public abstract class Fft extends RExternalBuiltinNode.Arg2 {
             int[] maxp = new int[1];
             if (noDims.profile(d == null)) {
                 int n = zVec.getLength();
-                fftNode.executeFactor(n, maxf, maxp);
+                factorNode.execute(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 = fftNode.executeWork(z, 1, n, 1, inv, work, iwork);
+                retCode = workNode.execute(z, 1, n, 1, inv, work, iwork);
             } else {
                 int maxmaxf = 1;
                 int maxmaxp = 1;
@@ -65,7 +65,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) {
-                        fftNode.executeFactor(d[i], maxf, maxp);
+                        factorNode.execute(d[i], maxf, maxp);
                         if (maxf[0] == 0) {
                             errorProfile.enter();
                             throw RError.error(this, RError.Message.FFT_FACTORIZATION);
@@ -88,8 +88,8 @@ public abstract class Fft extends RExternalBuiltinNode.Arg2 {
                         nspn *= n;
                         n = d[i];
                         nseg /= n;
-                        fftNode.executeFactor(n, maxf, maxp);
-                        fftNode.executeWork(z, nseg, n, nspn, inv, work, iwork);
+                        factorNode.execute(n, maxf, maxp);
+                        workNode.execute(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/FortranAndCFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/FortranAndCFunctions.java
index eb091e30ff3f12ce5bd3bda4fb47c9b7aac8e206..5bf8f743dbe2429af74dda69ba61190cc560643c 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
@@ -62,7 +62,7 @@ public class FortranAndCFunctions {
         private static final int VECTOR_LOGICAL = 12;
         @SuppressWarnings("unused") private static final int VECTOR_STRING = 12;
 
-        @Child private CRFFI.CRFFINode cRFFINode = RFFIFactory.getRFFI().getCRFFI().createCRFFINode();
+        @Child private CRFFI.InvokeCNode invokeCNode = RFFIFactory.getRFFI().getCRFFI().createInvokeCNode();
 
         @Override
         public Object[] getDefaultParameterValues() {
@@ -110,7 +110,7 @@ public class FortranAndCFunctions {
                     throw RError.error(node, RError.Message.UNIMPLEMENTED_ARG_TYPE, i + 1);
                 }
             }
-            cRFFINode.invoke(nativeCallInfo, nativeArgs);
+            invokeCNode.execute(nativeCallInfo, nativeArgs);
             // we have to assume that the native method updated everything
             RStringVector listNames = validateArgNames(array.length, args.getSignature());
             Object[] results = new Object[array.length];
@@ -222,7 +222,7 @@ public class FortranAndCFunctions {
 
         @Specialization
         protected RList c(RAbstractStringVector symbol, RArgsValuesAndNames args, byte naok, byte dup, Object rPackage, @SuppressWarnings("unused") RMissing encoding,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode,
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode,
                         @Cached("create()") BranchProfile errorProfile) {
             String libName = checkPackageArg(rPackage, errorProfile);
             DLL.RegisteredNativeSymbol rns = new DLL.RegisteredNativeSymbol(DLL.NativeSymbolType.Fortran, null, null);
@@ -260,7 +260,7 @@ public class FortranAndCFunctions {
         @SuppressWarnings("unused")
         @Specialization
         protected RList c(RAbstractStringVector symbol, RArgsValuesAndNames args, byte naok, byte dup, Object rPackage, RMissing encoding,
-                        @Cached("create()") DLL.FindSymbolNode findSymbolNode,
+                        @Cached("create()") DLL.RFindSymbolNode findSymbolNode,
                         @Cached("create()") BranchProfile errorProfile) {
             String libName = null;
             if (!(rPackage instanceof RMissing)) {
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 8e1f42b715fed94df95de1fb04020edcd4ae3a05..8aad787c5526a3ef2f7f9b8bbb80790a34762857 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
@@ -33,43 +33,55 @@ import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public class Generic_Grid implements GridRFFI {
-    private static class Generic_GridRFFINode extends GridRFFINode {
-        private static final String L_InitGrid = "L_initGrid";
-        private static final String L_KillGrid = "L_killGrid";
-        private static final String GRID = "grid";
-        @Child private CallRFFI.CallRFFINode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createCallRFFINode();
-        @Child DLL.FindSymbolNode findSymbolNode = DLL.FindSymbolNode.create();
+    private static final String GRID = "grid";
 
+    private static class Generic_InitGridNode extends InitGridNode {
+        private static final String L_InitGrid = "L_initGrid";
+        @Child private CallRFFI.InvokeCallNode invokeCallNode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
+        @Child DLL.RFindSymbolNode findSymbolNode = DLL.RFindSymbolNode.create();
         @CompilationFinal private NativeCallInfo initNativeCallInfo;
-        @CompilationFinal private NativeCallInfo killNativeCallInfo;
 
         @Override
-        public Object initGrid(REnvironment gridEvalEnv) {
+        public Object execute(REnvironment gridEvalEnv) {
             if (initNativeCallInfo == null) {
-                initNativeCallInfo = createNativeCallInfo(L_InitGrid);
+                initNativeCallInfo = createNativeCallInfo(L_InitGrid, findSymbolNode);
             }
-            return callRFFINode.invokeCall(initNativeCallInfo, new Object[]{gridEvalEnv});
+            return invokeCallNode.execute(initNativeCallInfo, new Object[]{gridEvalEnv});
         }
+    }
+
+    private static class Generic_KillGridNode extends KillGridNode {
+        private static final String L_KillGrid = "L_killGrid";
+        @Child private CallRFFI.InvokeCallNode invokeCallNode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
+        @Child DLL.RFindSymbolNode findSymbolNode = DLL.RFindSymbolNode.create();
+        @CompilationFinal private NativeCallInfo killNativeCallInfo;
 
         @Override
-        public Object killGrid() {
+        public Object execute() {
             if (killNativeCallInfo == null) {
-                killNativeCallInfo = createNativeCallInfo(L_KillGrid);
+                killNativeCallInfo = createNativeCallInfo(L_KillGrid, findSymbolNode);
             }
-            return callRFFINode.invokeCall(killNativeCallInfo, new Object[0]);
+            return invokeCallNode.execute(killNativeCallInfo, new Object[0]);
         }
 
-        private NativeCallInfo createNativeCallInfo(String call) {
-            DLLInfo dllInfo = DLL.findLibrary(GRID);
-            assert dllInfo != null;
-            SymbolHandle symbolHandle = findSymbolNode.execute(call, GRID, DLL.RegisteredNativeSymbol.any());
-            assert symbolHandle != DLL.SYMBOL_NOT_FOUND;
-            return new NativeCallInfo(call, symbolHandle, dllInfo);
-        }
+    }
+
+    private static NativeCallInfo createNativeCallInfo(String call, DLL.RFindSymbolNode findSymbolNode) {
+        DLLInfo dllInfo = DLL.findLibrary(GRID);
+        assert dllInfo != null;
+        SymbolHandle symbolHandle = findSymbolNode.execute(call, GRID, DLL.RegisteredNativeSymbol.any());
+        assert symbolHandle != DLL.SYMBOL_NOT_FOUND;
+        return new NativeCallInfo(call, symbolHandle, dllInfo);
     }
 
     @Override
-    public GridRFFINode createGridRFFINode() {
-        return new Generic_GridRFFINode();
+    public InitGridNode createInitGridNode() {
+        return new Generic_InitGridNode();
     }
+
+    @Override
+    public KillGridNode createKillGridNode() {
+        return new Generic_KillGridNode();
+    }
+
 }
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 893746938a2f4cd224be523c5df72178f250e401..170f820fd28611c2b6d286f668ec8320198a6862 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
@@ -37,12 +37,12 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
 
 public class Generic_Tools implements ToolsRFFI {
-    private static class Generic_ToolsRFFINode extends ToolsRFFINode {
+    private static class Generic_ToolsRFFINode extends ParseRdNode {
         private static final String C_PARSE_RD = "C_parseRd";
         private static final String TOOLS = "tools";
 
-        @Child private CallRFFI.CallRFFINode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createCallRFFINode();
-        @Child DLL.FindSymbolNode findSymbolNode = DLL.FindSymbolNode.create();
+        @Child private CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
+        @Child DLL.RFindSymbolNode findSymbolNode = DLL.RFindSymbolNode.create();
 
         @CompilationFinal private NativeCallInfo nativeCallInfo;
 
@@ -50,7 +50,7 @@ public class Generic_Tools implements ToolsRFFI {
          * Invoke C implementation, N.B., code is not thread safe.
          */
         @Override
-        public synchronized Object parseRd(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls, Object macros,
+        public synchronized Object execute(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls, Object macros,
                         RLogicalVector warndups) {
             try {
                 if (nativeCallInfo == null) {
@@ -61,7 +61,7 @@ public class Generic_Tools implements ToolsRFFI {
                     assert symbolHandle != DLL.SYMBOL_NOT_FOUND;
                     nativeCallInfo = new NativeCallInfo(C_PARSE_RD, symbolHandle, toolsDLLInfo);
                 }
-                return callRFFINode.invokeCall(nativeCallInfo,
+                return callRFFINode.execute(nativeCallInfo,
                                 new Object[]{con, srcfile, verbose, fragment, basename, warningCalls, macros, warndups});
             } catch (Throwable ex) {
                 throw RInternalError.shouldNotReachHere(ex, "error during Rd parsing");
@@ -70,7 +70,7 @@ public class Generic_Tools implements ToolsRFFI {
     }
 
     @Override
-    public ToolsRFFINode createToolsRFFINode() {
+    public ParseRdNode createParseRdNode() {
         return new Generic_ToolsRFFINode();
     }
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Base.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Base.java
index 08c7928e005e53460b1df305a80ac1336a90e747..fe6a15df17212d616284c82f32ab06c5d4471ae7 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Base.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Base.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -28,98 +28,118 @@ import java.util.ArrayList;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 public class JNI_Base implements BaseRFFI {
-    @Override
-    public int getpid() {
-        return native_getpid();
-    }
-
-    @Override
-    public int setwd(String dir) {
-        return native_setwd(dir);
+    public static class JNI_GetpidNode extends GetpidNode {
+        @Override
+        public int execute() {
+            return native_getpid();
+        }
     }
 
-    @Override
-    public String getwd() {
-        byte[] buf = new byte[4096];
-        int rc = native_getwd(buf, buf.length);
-        if (rc == 0) {
-            return null;
-        } else {
-            int i = 0;
-            while (buf[i] != 0 && i < buf.length) {
-                i++;
+    public static class JNI_GetwdNode extends GetwdNode {
+        @Override
+        public String execute() {
+            byte[] buf = new byte[4096];
+            int rc = native_getwd(buf, buf.length);
+            if (rc == 0) {
+                return null;
+            } else {
+                int i = 0;
+                while (buf[i] != 0 && i < buf.length) {
+                    i++;
+                }
+                return new String(buf, 0, i);
             }
-            return new String(buf, 0, i);
         }
     }
 
-    private static final int EINVAL = 22;
+    public static class JNI_SetwdNode extends SetwdNode {
+        @Override
+        public int execute(String dir) {
+            return native_setwd(dir);
+        }
+    }
 
-    @Override
-    public String readlink(String path) throws IOException {
-        int[] errno = new int[]{0};
-        String s = native_readlink(path, errno);
-        if (s == null) {
-            if (errno[0] == EINVAL) {
-                // not a link
-            } else {
-                // some other error
-                throw new IOException("readlink failed: " + errno[0]);
+    public static class JNI_ReadlinkNode extends ReadlinkNode {
+        private static final int EINVAL = 22;
+
+        @Override
+        public String execute(String path) throws IOException {
+            int[] errno = new int[]{0};
+            String s = native_readlink(path, errno);
+            if (s == null) {
+                if (errno[0] == EINVAL) {
+                    // not a link
+                } else {
+                    // some other error
+                    throw new IOException("readlink failed: " + errno[0]);
+                }
             }
+            return s;
         }
-        return s;
     }
 
-    @Override
-    public String mkdtemp(String template) {
-        /*
-         * Not only must the (C) string end in XXXXXX it must also be null-terminated. Since it is
-         * modified by mkdtemp we must make a copy.
-         */
-        byte[] bytes = template.getBytes();
-        byte[] ztbytes = new byte[bytes.length + 1];
-        System.arraycopy(bytes, 0, ztbytes, 0, bytes.length);
-        ztbytes[bytes.length] = 0;
-        long result = native_mkdtemp(ztbytes);
-        if (result == 0) {
-            return null;
-        } else {
-            return new String(ztbytes, 0, bytes.length);
+    public static class JNI_MkdtempNode extends MkdtempNode {
+        @Override
+        public String execute(String template) {
+            /*
+             * Not only must the (C) string end in XXXXXX it must also be null-terminated. Since it
+             * is modified by mkdtemp we must make a copy.
+             */
+            byte[] bytes = template.getBytes();
+            byte[] ztbytes = new byte[bytes.length + 1];
+            System.arraycopy(bytes, 0, ztbytes, 0, bytes.length);
+            ztbytes[bytes.length] = 0;
+            long result = native_mkdtemp(ztbytes);
+            if (result == 0) {
+                return null;
+            } else {
+                return new String(ztbytes, 0, bytes.length);
+            }
         }
     }
 
-    @Override
-    public void mkdir(String dir, int mode) throws IOException {
-        int rc = native_mkdir(dir, mode);
-        if (rc != 0) {
-            throw new IOException("mkdir " + dir + " failed");
+    public static class JNI_MkdirNode extends MkdirNode {
+        @Override
+        public void execute(String dir, int mode) throws IOException {
+            int rc = native_mkdir(dir, mode);
+            if (rc != 0) {
+                throw new IOException("mkdir " + dir + " failed");
+            }
         }
     }
 
-    @Override
-    public int chmod(String path, int mode) {
-        return native_chmod(path, mode);
+    public static class JNI_ChmodNode extends ChmodNode {
+        @Override
+        public int execute(String path, int mode) {
+            return native_chmod(path, mode);
+        }
     }
 
-    @Override
-    public long strtol(String s, int base) throws IllegalArgumentException {
-        int[] errno = new int[]{0};
-        long result = native_strtol(s, base, errno);
-        if (errno[0] != 0) {
-            throw new IllegalArgumentException("strtol failure");
-        } else {
-            return result;
+    public static class JNI_StrolNode extends StrolNode {
+        @Override
+        public long execute(String s, int base) throws IllegalArgumentException {
+            int[] errno = new int[]{0};
+            long result = native_strtol(s, base, errno);
+            if (errno[0] != 0) {
+                throw new IllegalArgumentException("strtol failure");
+            } else {
+                return result;
+            }
         }
     }
 
-    @Override
-    public UtsName uname() {
-        return JNI_UtsName.get();
+    public static class JNI_UnameNode extends UnameNode {
+        @Override
+        public UtsName execute() {
+            return JNI_UtsName.get();
+        }
     }
 
-    @Override
-    public ArrayList<String> glob(String pattern) {
-        return JNI_Glob.glob(pattern);
+    public static class JNI_GlobNode extends GlobNode {
+        @Override
+        public ArrayList<String> glob(String pattern) {
+            return JNI_Glob.glob(pattern);
+        }
     }
 
     // Checkstyle: stop method name
@@ -139,4 +159,54 @@ public class JNI_Base implements BaseRFFI {
     private static native long native_strtol(String s, int base, int[] errno);
 
     private static native String native_readlink(String s, int[] errno);
+
+    @Override
+    public GetpidNode createGetpidNode() {
+        return new JNI_GetpidNode();
+    }
+
+    @Override
+    public GetwdNode createGetwdNode() {
+        return new JNI_GetwdNode();
+    }
+
+    @Override
+    public SetwdNode createSetwdNode() {
+        return new JNI_SetwdNode();
+    }
+
+    @Override
+    public MkdirNode createMkdirNode() {
+        return new JNI_MkdirNode();
+    }
+
+    @Override
+    public ReadlinkNode createReadlinkNode() {
+        return new JNI_ReadlinkNode();
+    }
+
+    @Override
+    public MkdtempNode createMkdtempNode() {
+        return new JNI_MkdtempNode();
+    }
+
+    @Override
+    public ChmodNode createChmodNode() {
+        return new JNI_ChmodNode();
+    }
+
+    @Override
+    public StrolNode createStrolNode() {
+        return new JNI_StrolNode();
+    }
+
+    @Override
+    public UnameNode createUnameNode() {
+        return new JNI_UnameNode();
+    }
+
+    @Override
+    public GlobNode createGlobNode() {
+        return new JNI_GlobNode();
+    }
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_C.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_C.java
index 26f233a1fad2f1da4d59a33118043d9f4f9b7a91..93311fa0b1a565cba34338c6cb1d354ed507a210 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_C.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_C.java
@@ -30,7 +30,7 @@ import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
 public class JNI_C implements CRFFI {
-    public static class JNI_CRFFINode extends CRFFINode {
+    public static class JNI_InvokeCNode extends InvokeCNode {
         /**
          * This is rather similar to {@link JNI_Call}, except the objects are guaranteed to be
          * native array types, no upcalls are possible, and no result is returned. However, the
@@ -39,8 +39,8 @@ public class JNI_C implements CRFFI {
          */
         @Override
         @TruffleBoundary
-        public void invoke(NativeCallInfo nativeCallInfo, Object[] args) {
-            synchronized (JNI_CRFFINode.class) {
+        public void execute(NativeCallInfo nativeCallInfo, Object[] args) {
+            synchronized (JNI_C.class) {
                 if (traceEnabled()) {
                     traceDownCall(nativeCallInfo.name, args);
                 }
@@ -52,7 +52,7 @@ public class JNI_C implements CRFFI {
     private static native void c(long address, Object[] args);
 
     @Override
-    public CRFFINode createCRFFINode() {
-        return new JNI_CRFFINode();
+    public InvokeCNode createInvokeCNode() {
+        return new JNI_InvokeCNode();
     }
 }
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 b2153137e4102976c034a8364252110ef3ede9b9..d7154d5a2cdd42a3f668d7cceb8a4b398a60dadd 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
@@ -42,17 +42,17 @@ import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
  * efficient).
  *
  * The JNI layer is not (currently) MT safe, so all calls are single threaded. N.B. Since the calls
- * take place from a {@link JNI_CallRFFINode}, and this is duplicated in separate contexts, we must
- * synchronize on the class.
+ * take place from nodes, and these may be duplicated in separate contexts, we must synchronize on
+ * the class.
  */
 public class JNI_Call implements CallRFFI {
 
-    public static class JNI_CallRFFINode extends CallRFFINode {
+    public static class JNI_InvokeCallNode extends InvokeCallNode {
 
         @Override
         @TruffleBoundary
-        public Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args) {
-            synchronized (JNI_CallRFFINode.class) {
+        public Object execute(NativeCallInfo nativeCallInfo, Object[] args) {
+            synchronized (JNI_Call.class) {
                 long address = nativeCallInfo.address.asAddress();
                 Object result = null;
                 if (traceEnabled()) {
@@ -60,20 +60,20 @@ public class JNI_Call implements CallRFFI {
                 }
                 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
+                    // @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 {
@@ -83,11 +83,14 @@ public class JNI_Call implements CallRFFI {
                 }
             }
         }
+    }
+
+    public static class JNI_InvokeVoidCallNode extends InvokeVoidCallNode {
 
         @Override
         @TruffleBoundary
-        public void invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args) {
-            synchronized (JNI_CallRFFINode.class) {
+        public void execute(NativeCallInfo nativeCallInfo, Object[] args) {
+            synchronized (JNI_Call.class) {
                 if (traceEnabled()) {
                     traceDownCall(nativeCallInfo.name, args);
                 }
@@ -166,7 +169,12 @@ public class JNI_Call implements CallRFFI {
     private static native void callVoid1(long address, Object arg1);
 
     @Override
-    public CallRFFINode createCallRFFINode() {
-        return new JNI_CallRFFINode();
+    public InvokeCallNode createInvokeCallNode() {
+        return new JNI_InvokeCallNode();
+    }
+
+    @Override
+    public InvokeVoidCallNode createInvokeVoidCallNode() {
+        return new JNI_InvokeVoidCallNode();
     }
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_DLL.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_DLL.java
index 72a4140eba606e35675a68fd6b20745b8cde601a..697fe085b4f669cb57b35bf0076bd284dc74d25b 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_DLL.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_DLL.java
@@ -28,44 +28,33 @@ import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
 
 public class JNI_DLL implements DLLRFFI {
 
-    public static class JNI_DLLRFFINode extends DLLRFFINode {
+    public static class JNI_DLOpenNode extends DLOpenNode {
         @Override
         @TruffleBoundary
-        public Object dlopen(String path, boolean local, boolean now) {
+        public Object execute(String path, boolean local, boolean now) throws UnsatisfiedLinkError {
             long handle = native_dlopen(path, local, now);
-            if (handle == 0) {
-                return null;
-            } else {
-                return new Long(handle);
-            }
+            return new Long(handle);
         }
+    }
 
+    public static class JNI_DLSymNode extends DLSymNode {
         @Override
         @TruffleBoundary
-        public SymbolHandle dlsym(Object handle, String symbol) {
+        public SymbolHandle execute(Object handle, String symbol) throws UnsatisfiedLinkError {
             long nativeHandle = (Long) handle;
             long symv = native_dlsym(nativeHandle, symbol);
-            if (symv == 0) {
-                // symbol might actually be zero
-                if (dlerror() != null) {
-                    return null;
-                }
-            }
             return new SymbolHandle(symv);
         }
+    }
 
+    public static class JNI_DLCloseNode extends DLCloseNode {
         @Override
         @TruffleBoundary
-        public int dlclose(Object handle) {
+        public int execute(Object handle) {
             long nativeHandle = (Long) handle;
             return native_dlclose(nativeHandle);
         }
 
-        @Override
-        @TruffleBoundary
-        public String dlerror() {
-            return native_dlerror();
-        }
     }
 
     // Checkstyle: stop method name check
@@ -79,8 +68,18 @@ public class JNI_DLL implements DLLRFFI {
     private static native long native_dlsym(long handle, String symbol);
 
     @Override
-    public DLLRFFINode createDLLRFFINode() {
-        return new JNI_DLLRFFINode();
+    public DLOpenNode createDLOpenNode() {
+        return new JNI_DLOpenNode();
+    }
+
+    @Override
+    public DLSymNode createDLSymNode() {
+        return new JNI_DLSymNode();
+    }
+
+    @Override
+    public DLCloseNode createDLCloseNode() {
+        return new JNI_DLCloseNode();
     }
 
 }
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 842c1861a1414ff911f7c70a57ca2e57817a9c9f..fc8d45174a5bc527b2e5e5ae734aaaedba3bdc3e 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
@@ -26,53 +26,60 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 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.DLLRFFI.DLLRFFINode;
+import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
 
 public class JNI_Stats implements StatsRFFI {
 
-    public static class JNI_FFTNode extends FFTNode {
-        @Child DLLRFFINode dllRFFINode = RFFIFactory.getRFFI().getDLLRFFI().createDLLRFFINode();
+    public static class JNI_WorkNode extends WorkNode {
+        @Child DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
 
         private SymbolHandle fftWorkAddress;
-        private SymbolHandle fftFactorAddress;
 
         @Override
         @TruffleBoundary
-        public int executeWork(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
-            initialize();
+        public int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
+            if (fftWorkAddress == null) {
+                fftWorkAddress = fftAddress("fft_work", dlSymNode);
+            }
             return native_fft_work(fftWorkAddress.asAddress(), a, nseg, n, nspn, isn, work, iwork);
         }
+    }
+
+    public static class JNI_FactorNode extends FactorNode {
+        @Child DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        private SymbolHandle fftFactorAddress;
 
         @Override
         @TruffleBoundary
-        public void executeFactor(int n, int[] pmaxf, int[] pmaxp) {
-            initialize();
+        public void execute(int n, int[] pmaxf, int[] pmaxp) {
+            if (fftFactorAddress == null) {
+                fftFactorAddress = fftAddress("fft_factor", dlSymNode);
+            }
             native_fft_factor(fftFactorAddress.asAddress(), n, pmaxf, pmaxp);
 
         }
 
-        private void initialize() {
-            if (fftWorkAddress == null) {
-                fftWorkAddress = fftAddress("fft_work");
-                fftFactorAddress = fftAddress("fft_factor");
-            }
-        }
+    }
 
-        private SymbolHandle fftAddress(String symbol) {
-            SymbolHandle fftAddress;
-            DLLInfo dllInfo = DLL.findLibrary("stats");
-            assert dllInfo != null;
-            fftAddress = dllRFFINode.dlsym(dllInfo.handle, symbol);
-            assert fftAddress != DLL.SYMBOL_NOT_FOUND;
-            return fftAddress;
-        }
+    private static SymbolHandle fftAddress(String symbol, DLLRFFI.DLSymNode dlSymNode) {
+        SymbolHandle fftAddress;
+        DLLInfo dllInfo = DLL.findLibrary("stats");
+        assert dllInfo != null;
+        fftAddress = dlSymNode.execute(dllInfo.handle, symbol);
+        assert fftAddress != DLL.SYMBOL_NOT_FOUND;
+        return fftAddress;
+    }
+
+    @Override
+    public FactorNode createFactorNode() {
+        return new JNI_FactorNode();
     }
 
     @Override
-    public FFTNode createFFTNode() {
-        return new JNI_FFTNode();
+    public WorkNode createWorkNode() {
+        return new JNI_WorkNode();
     }
 
     // Checkstyle: stop method name
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Zip.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Zip.java
index 452f10e67820918e49b697d238a416d5597a037d..7c93d004beeb22c062767dde7627518ee6c517b9 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Zip.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Zip.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -30,18 +30,22 @@ import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
  */
 public class JNI_Zip implements ZipRFFI {
 
-    @Override
-    @TruffleBoundary
-    public int compress(byte[] dest, byte[] source) {
-        int rc = native_compress(dest, dest.length, source, source.length);
-        return rc;
+    public static class JNI_CompressNode extends ZipRFFI.CompressNode {
+        @Override
+        @TruffleBoundary
+        public int execute(byte[] dest, byte[] source) {
+            int rc = native_compress(dest, dest.length, source, source.length);
+            return rc;
+        }
     }
 
-    @Override
-    @TruffleBoundary
-    public int uncompress(byte[] dest, byte[] source) {
-        int rc = native_uncompress(dest, dest.length, source, source.length);
-        return rc;
+    public static class JNI_UncompressNode extends ZipRFFI.UncompressNode {
+        @Override
+        @TruffleBoundary
+        public int execute(byte[] dest, byte[] source) {
+            int rc = native_uncompress(dest, dest.length, source, source.length);
+            return rc;
+        }
     }
 
     // Checkstyle: stop method name
@@ -49,4 +53,14 @@ public class JNI_Zip implements ZipRFFI {
     private static native int native_compress(byte[] dest, long destlen, byte[] source, long sourcelen);
 
     private static native int native_uncompress(byte[] dest, long destlen, byte[] source, long sourcelen);
+
+    @Override
+    public CompressNode createCompressNode() {
+        return new JNI_CompressNode();
+    }
+
+    @Override
+    public UncompressNode createUncompressNode() {
+        return new JNI_UncompressNode();
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCompression.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCompression.java
index 4cd19d58d717319b3708bed8aee98e1818ba806f..a1d9f81682824f9db676bb090289431879662522 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCompression.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCompression.java
@@ -37,7 +37,7 @@ import java.util.zip.GZIPInputStream;
 
 import org.tukaani.xz.LZMA2InputStream;
 
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
 /**
  * Abstracts the implementation of the various forms of compression used in R. Since the C API for
@@ -148,12 +148,12 @@ public class RCompression {
     }
 
     private static boolean gzipCompress(byte[] udata, byte[] cdata) {
-        int rc = RFFIFactory.getRFFI().getZipRFFI().compress(cdata, udata);
+        int rc = (int) ZipRFFI.CompressRootNode.create().getCallTarget().call(cdata, udata);
         return rc == 0;
     }
 
     private static boolean gzipUncompress(byte[] udata, byte[] data) {
-        int rc = RFFIFactory.getRFFI().getZipRFFI().uncompress(udata, data);
+        int rc = (int) ZipRFFI.UncompressRootNode.create().getCallTarget().call(udata, data);
         return rc == 0;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
index d6045a767b3ac4421b6787dd3fa30c3e14ac7869..86e5fd3060d651cf32dabc5af790f02b2f28b6bd 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/REnvVars.java
@@ -37,7 +37,7 @@ import java.util.Map;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.vm.PolyglotEngine;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 /**
  * Repository for environment variables, including those set by FastR itself, e.g.
@@ -88,7 +88,7 @@ public final class REnvVars implements RContext.ContextState {
             String userFile = envVars.get("R_ENVIRON_USER");
             if (userFile == null) {
                 String dotRenviron = ".Renviron";
-                userFile = fileSystem.getPath(RFFIFactory.getRFFI().getBaseRFFI().getwd(), dotRenviron).toString();
+                userFile = fileSystem.getPath((String) BaseRFFI.GetwdRootNode.create().getCallTarget().call(), dotRenviron).toString();
                 if (!new File(userFile).exists()) {
                     userFile = fileSystem.getPath(System.getProperty("user.home"), dotRenviron).toString();
                 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
index 3af2daa50e1ea8b7403d0e223a2bc9e9ddf3bfbf..acd5193d5732cb56b231ee7a303c9bd8e5df8726 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -30,7 +30,7 @@ import java.nio.file.Path;
 
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 /**
  * Handles the setup of system, site and user profile code. N.B. this class only reads the files and
@@ -68,7 +68,7 @@ public final class RProfile implements RContext.ContextState {
             String userProfilePath = envVars.get("R_PROFILE_USER");
             if (userProfilePath == null) {
                 String dotRenviron = ".Rprofile";
-                userProfilePath = fileSystem.getPath(RFFIFactory.getRFFI().getBaseRFFI().getwd(), dotRenviron).toString();
+                userProfilePath = fileSystem.getPath((String) BaseRFFI.GetwdRootNode.create().getCallTarget().call(), dotRenviron).toString();
                 if (!new File(userProfilePath).exists()) {
                     userProfilePath = fileSystem.getPath(System.getProperty("user.home"), dotRenviron).toString();
                 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSrcref.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSrcref.java
index 5de8f818ee41e43b3b4ae684b28555650bcddb28..c2b9e89aa77c9d472386e78a541739af19bd1a22 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSrcref.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSrcref.java
@@ -27,6 +27,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
@@ -79,7 +80,7 @@ public class RSrcref {
         env.safePut(SrcrefFields.timestamp.name(), mtime);
         env.safePut(SrcrefFields.filename.name(), path.toString());
         env.safePut(SrcrefFields.isFile.name(), RRuntime.LOGICAL_TRUE);
-        env.safePut(SrcrefFields.wd.name(), RFFIFactory.getRFFI().getBaseRFFI().getwd());
+        env.safePut(SrcrefFields.wd.name(), BaseRFFI.GetwdRootNode.create().getCallTarget().call());
         env.setClassAttr(SRCFILE_ATTR);
         return env;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RVersionInfo.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RVersionInfo.java
index 8e640363492c8c3018af371a3cbc723a3cf4c323..6336fb8835fb96f7a1b51dd13271d8cf0c45778f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RVersionInfo.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RVersionInfo.java
@@ -24,8 +24,8 @@ package com.oracle.truffle.r.runtime;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI.UtsName;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public enum RVersionInfo {
     // @formatter:off
@@ -82,7 +82,7 @@ public enum RVersionInfo {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             ListValues = new String[VALUES.length];
             ListNames = new String[VALUES.length];
-            UtsName utsname = RFFIFactory.getRFFI().getBaseRFFI().uname();
+            UtsName utsname = (UtsName) BaseRFFI.UnameRootNode.create().getCallTarget().call();
             String osName = toFirstLower(utsname.sysname());
             String vendor = osName.equals("darwin") ? "apple" : "unknown";
             OS.value = osName + utsname.release();
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 f829eb438a33bf83fb0c63ee5e22a040dd6f0151..d0783f13cffee5b279ee86f5b0d07b9da968cd97 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
@@ -29,7 +29,7 @@ import java.util.Random;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 /**
  *
@@ -63,7 +63,7 @@ public class TempPathName implements RContext.ContextState {
         if (!startingTempDirPath.isAbsolute()) {
             startingTempDirPath = startingTempDirPath.toAbsolutePath();
         }
-        String t = RFFIFactory.getRFFI().getBaseRFFI().mkdtemp(startingTempDirPath.toString() + "XXXXXX");
+        String t = (String) BaseRFFI.MkdtempRootNode.create().getCallTarget().call(startingTempDirPath.toString() + "XXXXXX");
         if (t != null) {
             tempDirPath = t;
         } else {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index 639ece39b61c35134e72b5eb93608c50409fde85..19bb59be1350811d3a3a9589b789120af257b0a2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -60,6 +60,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
@@ -284,7 +285,7 @@ public final class Utils {
      */
     public static Path getLogPath(String fileName) {
         String root = RContext.isEmbedded() ? "/tmp" : REnvVars.rHome();
-        int pid = RFFIFactory.getRFFI().getBaseRFFI().getpid();
+        int pid = (int) BaseRFFI.GetpidRootNode.create().getCallTarget().call();
         String baseName = RContext.isEmbedded() ? fileName + "-" + Integer.toString(pid) : fileName;
         return FileSystems.getDefault().getPath(root, baseName);
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/BaseRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/BaseRFFI.java
index fe950fa2a41f0ae9b4e6480338f82257e3ad64a9..29afedc8a40c5c747d5969575a8bcff44d5706af 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/BaseRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/BaseRFFI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -25,55 +25,106 @@ package com.oracle.truffle.r.runtime.ffi;
 import java.io.IOException;
 import java.util.ArrayList;
 
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+
 /**
  * A statically typed interface to exactly those native functions required by the R {@code base}
  * package, because the functionality is not provided by the JDK. These methods do not necessarily
  * map 1-1 to a native function, they may involve the invocation of several native functions.
  */
 public interface BaseRFFI {
-    int getpid();
+    abstract class GetpidNode extends Node {
+        public abstract int execute();
 
-    /**
-     * Returns the current working directory, in the face of calls to {@link #setwd}.
-     */
-    String getwd();
+        public static GetpidNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createGetpidNode();
+        }
+    }
 
-    /**
-     * Sets the current working directory to {@code dir}. (cf. Unix {@code chdir}).
-     *
-     * @return 0 if successful.
-     */
-    int setwd(String dir);
+    abstract class GetwdNode extends Node {
+        /**
+         * Returns the current working directory, in the face of calls to {@code setwd}.
+         */
+        public abstract String execute();
 
-    /**
-     * Create directory with given mode. Exception is thrown omn error.
-     */
-    void mkdir(String dir, int mode) throws IOException;
-
-    /**
-     * Try to convert a symbolic link to it's target.
-     *
-     * @param path the link path
-     * @return the target if {@code path} is a link else {@code null}
-     * @throws IOException for any other error except "not a link"
-     */
-    String readlink(String path) throws IOException;
+        public static GetwdNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createGetwdNode();
+        }
+    }
 
-    /**
-     * Creates a temporary directory using {@code template} and return the resulting path or
-     * {@code null} if error.
-     */
-    String mkdtemp(String template);
+    abstract class SetwdNode extends Node {
+        /**
+         * Sets the current working directory to {@code dir}. (cf. Unix {@code chdir}).
+         *
+         * @return 0 if successful.
+         */
+        public abstract int execute(String dir);
 
-    /**
-     * Change the file mode of {@code path}.
-     */
-    int chmod(String path, int mode);
+        public static SetwdNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createSetwdNode();
+        }
+    }
 
-    /**
-     * Convert string to long.
-     */
-    long strtol(String s, int base) throws IllegalArgumentException;
+    abstract class MkdirNode extends Node {
+        /**
+         * Create directory with given mode. Exception is thrown omn error.
+         */
+        public abstract void execute(String dir, int mode) throws IOException;
+
+        public static MkdirNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createMkdirNode();
+        }
+    }
+
+    abstract class ReadlinkNode extends Node {
+        /**
+         * Try to convert a symbolic link to it's target.
+         *
+         * @param path the link path
+         * @return the target if {@code path} is a link else {@code null}
+         * @throws IOException for any other error except "not a link"
+         */
+        public abstract String execute(String path) throws IOException;
+
+        public static ReadlinkNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createReadlinkNode();
+        }
+    }
+
+    abstract class MkdtempNode extends Node {
+        /**
+         * Creates a temporary directory using {@code template} and return the resulting path or
+         * {@code null} if error.
+         */
+        public abstract String execute(String template);
+
+        public static MkdtempNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createMkdtempNode();
+        }
+    }
+
+    abstract class ChmodNode extends Node {
+        /**
+         * Change the file mode of {@code path}.
+         */
+        public abstract int execute(String path, int mode);
+
+        public static ChmodNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createChmodNode();
+        }
+    }
+
+    abstract class StrolNode extends Node {
+        /**
+         * Convert string to long.
+         */
+        public abstract long execute(String s, int base) throws IllegalArgumentException;
+
+        public static StrolNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createStrolNode();
+        }
+    }
 
     public interface UtsName {
         String sysname();
@@ -87,14 +138,138 @@ public interface BaseRFFI {
         String nodename();
     }
 
-    /**
-     * Return {@code utsname} info.
+    abstract class UnameNode extends Node {
+        /**
+         * Return {@code utsname} info.
+         */
+        public abstract UtsName execute();
+
+        public static UnameNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createUnameNode();
+        }
+    }
+
+    abstract class GlobNode extends Node {
+        /**
+         * Returns an array of pathnames that match {@code pattern} using the OS glob function. This
+         * is done in native code because it is very hard to write in Java in the face of
+         * {@code setwd}.
+         */
+        public abstract ArrayList<String> glob(String pattern);
+
+        public static GlobNode create() {
+            return RFFIFactory.getRFFI().getBaseRFFI().createGlobNode();
+        }
+    }
+
+    /*
+     * The RFFI implementation influences exactly what subclass of the above nodes is created. Each
+     * implementation must therefore, implement these methods that are called by the associated
+     * "public static create()" methods above.
      */
-    UtsName uname();
 
-    /**
-     * Returns an array of pathnames that match {@code pattern} using the OS glob function. This is
-     * done in native code because it is very hard to write in Java in the face of {@link #setwd}.
+    GetpidNode createGetpidNode();
+
+    GetwdNode createGetwdNode();
+
+    SetwdNode createSetwdNode();
+
+    MkdirNode createMkdirNode();
+
+    ReadlinkNode createReadlinkNode();
+
+    MkdtempNode createMkdtempNode();
+
+    ChmodNode createChmodNode();
+
+    StrolNode createStrolNode();
+
+    UnameNode createUnameNode();
+
+    GlobNode createGlobNode();
+
+    /*
+     * Some functions are called from non-Truffle contexts, which requires a RootNode
      */
-    ArrayList<String> glob(String pattern);
+
+    final class GetpidRootNode extends RFFIRootNode<GetpidNode> {
+        private static GetpidRootNode getpidRootNode;
+
+        private GetpidRootNode() {
+            super(RFFIFactory.getRFFI().getBaseRFFI().createGetpidNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            return rffiNode.execute();
+        }
+
+        public static GetpidRootNode create() {
+            if (getpidRootNode == null) {
+                getpidRootNode = new GetpidRootNode();
+            }
+            return getpidRootNode;
+        }
+    }
+
+    final class GetwdRootNode extends RFFIRootNode<GetwdNode> {
+        private static GetwdRootNode getwdRootNode;
+
+        private GetwdRootNode() {
+            super(RFFIFactory.getRFFI().getBaseRFFI().createGetwdNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            return rffiNode.execute();
+        }
+
+        public static GetwdRootNode create() {
+            if (getwdRootNode == null) {
+                getwdRootNode = new GetwdRootNode();
+            }
+            return getwdRootNode;
+        }
+    }
+
+    final class MkdtempRootNode extends RFFIRootNode<MkdtempNode> {
+        private static MkdtempRootNode mkdtempRootNode;
+
+        private MkdtempRootNode() {
+            super(RFFIFactory.getRFFI().getBaseRFFI().createMkdtempNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            return rffiNode.execute((String) args[0]);
+        }
+
+        public static MkdtempRootNode create() {
+            if (mkdtempRootNode == null) {
+                mkdtempRootNode = new MkdtempRootNode();
+            }
+            return mkdtempRootNode;
+        }
+    }
+
+    final class UnameRootNode extends RFFIRootNode<UnameNode> {
+        private static UnameRootNode unameRootNode;
+
+        private UnameRootNode() {
+            super(RFFIFactory.getRFFI().getBaseRFFI().createUnameNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            return rffiNode.execute();
+        }
+
+        public static UnameRootNode create() {
+            if (unameRootNode == null) {
+                unameRootNode = new UnameRootNode();
+            }
+            return unameRootNode;
+        }
+    }
 }
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 e8f6e117dc99f28a7193e8c03c849a5072386021..8d0949631ee56fc97910b13fafdbb658d5434617 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -28,14 +28,14 @@ import com.oracle.truffle.api.nodes.Node;
  * Support for the {.C} and {.Fortran} calls.
  */
 public interface CRFFI {
-    abstract class CRFFINode extends Node {
+    abstract class InvokeCNode extends Node {
         /**
          * 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}.
          */
-        public abstract void invoke(NativeCallInfo nativeCallInfo, Object[] args);
+        public abstract void execute(NativeCallInfo nativeCallInfo, Object[] args);
     }
 
-    CRFFINode createCRFFINode();
+    InvokeCNode createInvokeCNode();
 }
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 08472c79a9713b3a000fef002dbc1409db3c2897..19de11e00e6a455f942f8dec4ed711f07faf0708 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,27 +22,76 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.data.RNull;
 
 /**
  * Support for the {.Call} and {.External} calls.
  */
 public interface CallRFFI {
-    abstract class CallRFFINode extends Node {
+    abstract class InvokeCallNode 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);
+        public abstract Object execute(NativeCallInfo nativeCallInfo, Object[] args);
+    }
 
+    abstract class InvokeVoidCallNode extends Node {
         /**
          * Variant that does not return a result (primarily for library "init" methods).
          */
-        public abstract void invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args);
+        public abstract void execute(NativeCallInfo nativeCallInfo, Object[] args);
+
+    }
+
+    InvokeCallNode createInvokeCallNode();
+
+    InvokeVoidCallNode createInvokeVoidCallNode();
+
+    final class InvokeCallRootNode extends RFFIRootNode<InvokeCallNode> {
+        private static InvokeCallRootNode invokeCallRootNode;
 
+        private InvokeCallRootNode() {
+            super(RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            return rffiNode.execute((NativeCallInfo) args[0], (Object[]) args[1]);
+        }
+
+        public static InvokeCallRootNode create() {
+            if (invokeCallRootNode == null) {
+                invokeCallRootNode = new InvokeCallRootNode();
+            }
+            return invokeCallRootNode;
+        }
     }
 
-    CallRFFINode createCallRFFINode();
+    final class InvokeVoidCallRootNode extends RFFIRootNode<InvokeVoidCallNode> {
+        private static InvokeVoidCallRootNode InvokeVoidCallRootNode;
+
+        private InvokeVoidCallRootNode() {
+            super(RFFIFactory.getRFFI().getCallRFFI().createInvokeVoidCallNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            rffiNode.execute((NativeCallInfo) args[0], (Object[]) args[1]);
+            return RNull.instance; // unused
+        }
+
+        public static InvokeVoidCallRootNode create() {
+            if (InvokeVoidCallRootNode == null) {
+                InvokeVoidCallRootNode = new InvokeVoidCallRootNode();
+            }
+            return InvokeVoidCallRootNode;
+        }
+    }
 
 }
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 469cf471c58bafe37adf5d1d1c37c679359a4327..00825a42b45ddb1fd518cc46d09cd67265ba48aa 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
@@ -16,6 +16,7 @@ import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.FrameDescriptor;
@@ -38,8 +39,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;
-import com.oracle.truffle.r.runtime.ffi.DLLRFFI.DLLRFFINode;
+import com.oracle.truffle.r.runtime.ffi.CallRFFI.InvokeVoidCallNode;
 import com.oracle.truffle.r.runtime.rng.user.UserRNG;
 
 /**
@@ -99,7 +99,7 @@ public class DLL {
             if (context.getKind() != RContext.ContextKind.SHARE_PARENT_RW) {
                 for (int i = 1; i < list.size(); i++) {
                     DLLInfo dllInfo = list.get(i);
-                    DLLFFIRootNode.create().getCallTarget().call(DLLFFIRootNode.DLCLOSE, dllInfo.handle);
+                    DLLRFFI.DLCloseRootNode.create().getCallTarget().call(dllInfo.handle);
                 }
             }
             list = null;
@@ -367,7 +367,7 @@ public class DLL {
      */
     public static void loadLibR(String path) {
         RContext context = RContext.getInstance();
-        Object handle = DLLFFIRootNode.create().getCallTarget().call(DLLFFIRootNode.DLOPEN, path, false, false);
+        Object handle = DLLRFFI.DLOpenRootNode.create().getCallTarget().call(path, false, false);
         if (handle == null) {
             throw Utils.rSuicide("error loading libR from: " + path + "\n");
         }
@@ -388,8 +388,9 @@ public class DLL {
     public static final String R_INIT_PREFIX = "R_init_";
 
     public static class LoadPackageDLLNode extends Node {
-        @Child private CallRFFINode callRFFINode;
-        @Child private DLLRFFINode dllRFFINode = RFFIFactory.getRFFI().getDLLRFFI().createDLLRFFINode();
+        @Child private InvokeVoidCallNode invokeVoidCallNode;
+        @Child private DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child private DLLRFFI.DLOpenNode dlOpenNode = RFFIFactory.getRFFI().getDLLRFFI().createDLOpenNode();
 
         public static LoadPackageDLLNode create() {
             return new LoadPackageDLLNode();
@@ -407,15 +408,16 @@ public class DLL {
             }
             DLLInfo dllInfo = doLoad(absPath, local, now, true);
 
-            // Search for init method
+            // Search for an init method
             String pkgInit = R_INIT_PREFIX + dllInfo.name;
-            SymbolHandle initFunc = dllRFFINode.dlsym(dllInfo.handle, pkgInit);
-            if (initFunc != SYMBOL_NOT_FOUND) {
+            try {
+                SymbolHandle initFunc = dlSymNode.execute(dllInfo.handle, pkgInit);
                 try {
-                    if (callRFFINode == null) {
-                        callRFFINode = insert(RFFIFactory.getRFFI().getCallRFFI().createCallRFFINode());
+                    if (invokeVoidCallNode == null) {
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                        invokeVoidCallNode = insert(RFFIFactory.getRFFI().getCallRFFI().createInvokeVoidCallNode());
                     }
-                    callRFFINode.invokeVoidCall(new NativeCallInfo(pkgInit, initFunc, dllInfo), new Object[]{dllInfo});
+                    invokeVoidCallNode.execute(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
@@ -427,6 +429,8 @@ public class DLL {
                         throw Utils.rSuicide(RError.Message.DLL_RINIT_ERROR.message + " on default package: " + path);
                     }
                 }
+            } catch (UnsatisfiedLinkError ex) {
+                // no such symbol, that's ok
             }
             return dllInfo;
         }
@@ -439,23 +443,24 @@ public class DLL {
          * that errors loading (user) packages added to R_DEFAULT_PACKAGES do throw RErrors.
          */
         private synchronized DLLInfo doLoad(String absPath, boolean local, boolean now, boolean addToList) throws DLLException {
-            Object handle = dllRFFINode.dlopen(absPath, local, now);
-            if (handle == null) {
-                String dlError = dllRFFINode.dlerror();
+            try {
+                Object handle = dlOpenNode.execute(absPath, local, now);
+                DLLInfo dllInfo = DLLInfo.create(libName(absPath), absPath, true, handle, addToList);
+                return dllInfo;
+            } catch (UnsatisfiedLinkError ex) {
+                String dlError = ex.getMessage();
                 if (RContext.isInitialContextInitialized()) {
                     throw new DLLException(RError.Message.DLL_LOAD_ERROR, absPath, dlError);
                 } else {
                     throw Utils.rSuicide("error loading default package: " + absPath + "\n" + dlError);
                 }
             }
-            DLLInfo dllInfo = DLLInfo.create(libName(absPath), absPath, true, handle, addToList);
-            return dllInfo;
         }
 
     }
 
     public static class UnloadNode extends Node {
-        @Child private DLLRFFINode dllRFFINode = RFFIFactory.getRFFI().getDLLRFFI().createDLLRFFINode();
+        @Child private DLLRFFI.DLCloseNode dlCloseNode = RFFIFactory.getRFFI().getDLLRFFI().createDLCloseNode();
 
         @TruffleBoundary
         public void execute(String path) throws DLLException {
@@ -463,7 +468,7 @@ public class DLL {
             ContextStateImpl contextState = getContextState();
             for (DLLInfo info : contextState.list) {
                 if (info.path.equals(absPath)) {
-                    int rc = dllRFFINode.dlclose(info.handle);
+                    int rc = dlCloseNode.execute(info.handle);
                     if (rc != 0) {
                         throw new DLLException(RError.Message.DLL_LOAD_ERROR, path, "");
                     }
@@ -525,9 +530,8 @@ public class DLL {
         return SYMBOL_NOT_FOUND;
     }
 
-    public static final class FindSymbolNode extends Node {
-        @Child DLLRFFINode dllRFFINode = RFFIFactory.getRFFI().getDLLRFFI().createDLLRFFINode();
-        @Child DlsymNode dlsymNode = new DlsymNode();
+    public static final class RFindSymbolNode extends Node {
+        @Child RdlsymNode rdlsymNode = new RdlsymNode();
 
         /**
          * Directly analogous to the GnuR function {@code R_FindSymbol}.
@@ -547,7 +551,7 @@ public class DLL {
                     continue;
                 }
                 if (all || dllInfo.name.equals(libName)) {
-                    SymbolHandle func = dlsymNode.execute(dllInfo, name, rns);
+                    SymbolHandle func = rdlsymNode.execute(dllInfo, name, rns);
                     if (func != SYMBOL_NOT_FOUND) {
                         if (rns != null) {
                             rns.dllInfo = dllInfo;
@@ -562,18 +566,18 @@ public class DLL {
             return SYMBOL_NOT_FOUND;
         }
 
-        public static FindSymbolNode create() {
-            return new FindSymbolNode();
+        public static RFindSymbolNode create() {
+            return new RFindSymbolNode();
         }
 
     }
 
-    private static final class FindSymbolRootNode extends RootNode {
-        private static FindSymbolRootNode findSymbolRootNode;
+    private static final class RFindSymbolRootNode extends RootNode {
+        private static RFindSymbolRootNode findSymbolRootNode;
 
-        @Child FindSymbolNode findSymbolNode = FindSymbolNode.create();
+        @Child RFindSymbolNode findSymbolNode = RFindSymbolNode.create();
 
-        private FindSymbolRootNode() {
+        private RFindSymbolRootNode() {
             super(RContext.getRRuntimeASTAccess().getTruffleRLanguage(), null, new FrameDescriptor());
         }
 
@@ -583,9 +587,9 @@ public class DLL {
             return findSymbolNode.execute((String) args[0], (String) args[1], (RegisteredNativeSymbol) args[2]);
         }
 
-        private static FindSymbolRootNode create() {
+        private static RFindSymbolRootNode create() {
             if (findSymbolRootNode == null) {
-                findSymbolRootNode = new FindSymbolRootNode();
+                findSymbolRootNode = new RFindSymbolRootNode();
                 Truffle.getRuntime().createCallTarget(findSymbolRootNode);
             }
             return findSymbolRootNode;
@@ -593,13 +597,17 @@ public class DLL {
 
     }
 
-    public static final class DlsymNode extends Node {
-        @Child DLLRFFINode dllRFFINode = RFFIFactory.getRFFI().getDLLRFFI().createDLLRFFINode();
+    public static final class RdlsymNode extends Node {
+        @Child DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
 
         /**
          * Directly analogous to the GnuR function {@code R_dlsym}. Checks first for a
          * {@link RegisteredNativeSymbol} using {@code rns}, then, unless dynamic lookup has been
          * disabled, looks up the symbol using the {@code dlopen} machinery.
+         *
+         * N.B. Unlike the underlying {@link DLLRFFI.DLSymNode} node this does <b>not</b> throw
+         * {@link UnsatisfiedLinkError} if the symbol is not found; it returns
+         * {@link #SYMBOL_NOT_FOUND}.
          */
         @TruffleBoundary
         public SymbolHandle execute(DLLInfo dllInfo, String name, RegisteredNativeSymbol rns) {
@@ -617,16 +625,16 @@ public class DLL {
             if (rns != null && rns.nst == NativeSymbolType.Fortran) {
                 mName = name + "_";
             }
-            SymbolHandle symValue = dllRFFINode.dlsym(dllInfo.handle, mName);
-            if (symValue != null) {
+            try {
+                SymbolHandle symValue = dlSymNode.execute(dllInfo.handle, mName);
                 return symValue;
-            } else {
+            } catch (UnsatisfiedLinkError ex) {
                 return SYMBOL_NOT_FOUND;
             }
         }
 
-        public static DlsymNode create() {
-            return new DlsymNode();
+        public static RdlsymNode create() {
+            return new RdlsymNode();
         }
     }
 
@@ -636,7 +644,7 @@ public class DLL {
      */
     public static DLLInfo findLibraryContainingSymbol(String symbol) {
         RegisteredNativeSymbol rns = RegisteredNativeSymbol.any();
-        SymbolHandle func = (SymbolHandle) FindSymbolRootNode.create().getCallTarget().call(symbol, null, rns);
+        SymbolHandle func = (SymbolHandle) RFindSymbolRootNode.create().getCallTarget().call(symbol, null, rns);
         if (func == SYMBOL_NOT_FOUND) {
             return null;
         } else {
@@ -666,9 +674,9 @@ public class DLL {
      */
     public static SymbolHandle findSymbol(String name, DLLInfo dllInfo) {
         if (dllInfo != null) {
-            return (SymbolHandle) DLLFFIRootNode.create().getCallTarget().call(DLLFFIRootNode.DLSYM, dllInfo.handle, name);
+            return (SymbolHandle) DLLRFFI.DLSymRootNode.create().getCallTarget().call(dllInfo.handle, name);
         } else {
-            return (SymbolHandle) FindSymbolRootNode.create().getCallTarget().call(name, null, RegisteredNativeSymbol.any());
+            return (SymbolHandle) RFindSymbolRootNode.create().getCallTarget().call(name, null, RegisteredNativeSymbol.any());
         }
     }
 
@@ -699,47 +707,4 @@ public class DLL {
         return result;
     }
 
-    /**
-     * {@link RootNode} class for the unusual case where we are not already in a Truffle AST
-     * execution.
-     *
-     */
-    public static final class DLLFFIRootNode extends RootNode {
-        public static final int DLOPEN = 0;
-        public static final int DLCLOSE = 1;
-        public static final int DLSYM = 2;
-
-        private static DLLFFIRootNode dllFFIRootNode;
-
-        @Child private DLLRFFINode dllRFFINode = RFFIFactory.getRFFI().getDLLRFFI().createDLLRFFINode();
-
-        private DLLFFIRootNode() {
-            super(RContext.getRRuntimeASTAccess().getTruffleRLanguage(), null, new FrameDescriptor());
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame) {
-            Object[] args = frame.getArguments();
-            int method = (int) args[0];
-            switch (method) {
-                case DLOPEN:
-                    return dllRFFINode.dlopen((String) args[1], (boolean) args[2], (boolean) args[3]);
-                case DLCLOSE:
-                    return dllRFFINode.dlclose(args[1]);
-                case DLSYM:
-                    return dllRFFINode.dlsym(args[1], (String) args[2]);
-                default:
-                    throw RInternalError.shouldNotReachHere();
-            }
-        }
-
-        public static DLLFFIRootNode create() {
-            if (dllFFIRootNode == null) {
-                dllFFIRootNode = new DLLFFIRootNode();
-                Truffle.getRuntime().createCallTarget(dllFFIRootNode);
-            }
-            return dllFFIRootNode;
-        }
-
-    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLLRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLLRFFI.java
index 029f8a4398a5fc1523465cad3d9e55a4ec852003..fecf157ec83894882d71052ca23f81d31f587cf8 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLLRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLLRFFI.java
@@ -22,44 +22,118 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 
-/**
- * Caller should not assume that this interface is implemented in a thread-safe manner. In
- * particular, pairs of {@link DLLRFFINode#dlopen}/{@link DLLRFFINode#dlerror} and
- * {@link DLLRFFINode#dlsym}/{@link DLLRFFINode#dlerror} should be atomic in the caller.
- *
- */
 public interface DLLRFFI {
-    abstract class DLLRFFINode extends Node {
+    abstract class DLOpenNode extends Node {
         /**
          * Open a DLL.
          *
          * @return {@code null} on error, opaque handle for following calls otherwise.
          */
-        public abstract Object dlopen(String path, boolean local, boolean now);
+        public abstract Object execute(String path, boolean local, boolean now) throws UnsatisfiedLinkError;
+
+        public static DLOpenNode create() {
+            return RFFIFactory.getRFFI().getDLLRFFI().createDLOpenNode();
+        }
+    }
 
+    abstract class DLSymNode extends Node {
         /**
          * Search for {@code symbol} in DLL specified by {@code handle}. To accommodate differing
          * implementations of this interface the result is {@link SymbolHandle}. For the standard OS
          * implementation this will encapsulate a {@link Long} or {@code null} if an error occurred.
          *
          */
-        public abstract SymbolHandle dlsym(Object handle, String symbol);
+        public abstract SymbolHandle execute(Object handle, String symbol) throws UnsatisfiedLinkError;
 
+        public static DLSymNode create() {
+            return RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        }
+    }
+
+    abstract class DLCloseNode extends Node {
         /**
          * Close DLL specified by {@code handle}.
          */
-        public abstract int dlclose(Object handle);
+        public abstract int execute(Object handle);
 
-        /**
-         * Get any error message.
-         *
-         * @return {@code null} if no error, message otherwise.
-         */
-        public abstract String dlerror();
+        public static DLCloseNode create() {
+            return RFFIFactory.getRFFI().getDLLRFFI().createDLCloseNode();
+        }
+    }
+
+    DLOpenNode createDLOpenNode();
+
+    DLSymNode createDLSymNode();
+
+    DLCloseNode createDLCloseNode();
+
+    // RootNodes
+
+    final class DLOpenRootNode extends RFFIRootNode<DLOpenNode> {
+        private static DLOpenRootNode dlOpenRootNode;
+
+        private DLOpenRootNode() {
+            super(RFFIFactory.getRFFI().getDLLRFFI().createDLOpenNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            return rffiNode.execute((String) args[0], (boolean) args[1], (boolean) args[2]);
+        }
+
+        public static DLOpenRootNode create() {
+            if (dlOpenRootNode == null) {
+                dlOpenRootNode = new DLOpenRootNode();
+            }
+            return dlOpenRootNode;
+        }
+    }
+
+    final class DLSymRootNode extends RFFIRootNode<DLSymNode> {
+        private static DLSymRootNode dlSymRootNode;
+
+        private DLSymRootNode() {
+            super(RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            return rffiNode.execute(args[0], (String) args[1]);
+        }
+
+        public static DLSymRootNode create() {
+            if (dlSymRootNode == null) {
+                dlSymRootNode = new DLSymRootNode();
+            }
+            return dlSymRootNode;
+        }
+    }
+
+    final class DLCloseRootNode extends RFFIRootNode<DLCloseNode> {
+        private static DLCloseRootNode dlCloseRootNode;
+
+        private DLCloseRootNode() {
+            super(RFFIFactory.getRFFI().getDLLRFFI().createDLCloseNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            return rffiNode.execute(args[0]);
+        }
+
+        public static DLCloseRootNode create() {
+            if (dlCloseRootNode == null) {
+                dlCloseRootNode = new DLCloseRootNode();
+            }
+            return dlCloseRootNode;
+        }
     }
 
-    DLLRFFINode createDLLRFFINode();
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/GridRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/GridRFFI.java
index 9d410b1054b72fa146627a30012e09c103dbdcc8..43082161ce14e5abaca49d55af3ce7b998993ad2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/GridRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/GridRFFI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -26,11 +26,24 @@ import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
 public interface GridRFFI {
-    abstract class GridRFFINode extends Node {
-        public abstract Object initGrid(REnvironment gridEvalEnv);
+    abstract class InitGridNode extends Node {
+        public abstract Object execute(REnvironment gridEvalEnv);
 
-        public abstract Object killGrid();
+        public static InitGridNode create() {
+            return RFFIFactory.getRFFI().getGridRFFI().createInitGridNode();
+        }
     }
 
-    GridRFFINode createGridRFFINode();
+    abstract class KillGridNode extends Node {
+
+        public abstract Object execute();
+
+        public static KillGridNode create() {
+            return RFFIFactory.getRFFI().getGridRFFI().createKillGridNode();
+        }
+    }
+
+    InitGridNode createInitGridNode();
+
+    KillGridNode createKillGridNode();
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFIRootNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java
similarity index 53%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFIRootNode.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java
index b97a7f2ca52c9a23e2245e99fc5dafb2678c504b..71d8f98278e42b662db1ed71c61af3c27d70ff00 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CallRFFIRootNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -24,39 +24,17 @@ package com.oracle.truffle.r.runtime.ffi;
 
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.frame.FrameDescriptor;
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RNull;
 
-public final class CallRFFIRootNode extends RootNode {
-    private static CallRFFIRootNode callRFFIRootNode;
-    @Child private CallRFFI.CallRFFINode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createCallRFFINode();
+public abstract class RFFIRootNode<T extends Node> extends RootNode {
+    @Child T rffiNode;
 
-    public CallRFFIRootNode() {
+    protected RFFIRootNode(T baseRFFINode) {
         super(RContext.getRRuntimeASTAccess().getTruffleRLanguage(), null, new FrameDescriptor());
-
-    }
-
-    @Override
-    public Object execute(VirtualFrame frame) {
-        Object[] args = frame.getArguments();
-        NativeCallInfo nativeCallInfo = (NativeCallInfo) args[0];
-        boolean isVoidCall = (boolean) args[1];
-        Object[] callArgs = (Object[]) args[2];
-        if (isVoidCall) {
-            callRFFINode.invokeVoidCall(nativeCallInfo, callArgs);
-            return RNull.instance;
-        } else {
-            return callRFFINode.invokeCall(nativeCallInfo, callArgs);
-        }
+        this.rffiNode = baseRFFINode;
+        Truffle.getRuntime().createCallTarget(this);
     }
 
-    public static CallRFFIRootNode create() {
-        if (callRFFIRootNode == null) {
-            callRFFIRootNode = new CallRFFIRootNode();
-            Truffle.getRuntime().createCallTarget(callRFFIRootNode);
-        }
-        return callRFFIRootNode;
-    }
 }
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 67adab0639211e28786f15af9be6578e12abcdb6..d1dcd13fc0be31779570e816926bfa3cf6b1d3e3 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, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -30,12 +30,24 @@ import com.oracle.truffle.api.nodes.Node;
  * {@code fft_factor} and {@code fft_work}. functions from the GNU R C code.
  */
 public interface StatsRFFI {
-    abstract class FFTNode extends Node {
-        public abstract void executeFactor(int n, int[] pmaxf, int[] pmaxp);
+    abstract class FactorNode extends Node {
+        public abstract void execute(int n, int[] pmaxf, int[] pmaxp);
 
-        public abstract int executeWork(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork);
+        public static FactorNode create() {
+            return RFFIFactory.getRFFI().getStatsRFFI().createFactorNode();
+        }
     }
 
-    FFTNode createFFTNode();
+    abstract class WorkNode extends Node {
+        public abstract int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork);
+
+        public static WorkNode create() {
+            return RFFIFactory.getRFFI().getStatsRFFI().createWorkNode();
+        }
+    }
+
+    FactorNode createFactorNode();
+
+    WorkNode createWorkNode();
 
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ToolsRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ToolsRFFI.java
index 290daad5f02a030ce1c177c3b461f7332fb3fae4..7d797e62f112dfb5f13ca953d802bd2c0f3086d2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ToolsRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ToolsRFFI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -32,7 +32,7 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
  * Interface to native (C) methods provided by the {@code tools} package.
  */
 public interface ToolsRFFI {
-    abstract class ToolsRFFINode extends Node {
+    abstract class ParseRdNode extends Node {
         /**
          * This invokes the Rd parser, written in C, and part of GnuR, that does its work using the
          * R FFI interface. The R code initially invokes this via {@code .External2(C_parseRd, ...)}
@@ -41,9 +41,9 @@ public interface ToolsRFFI {
          * code. We can't go straight to the GnuR C entry point as that makes GnuR-specific
          * assumptions about, for example, how connections are implemented.
          */
-        public abstract Object parseRd(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls, Object macros,
+        public abstract Object execute(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls, Object macros,
                         RLogicalVector warndups);
     }
 
-    ToolsRFFINode createToolsRFFINode();
+    ParseRdNode createParseRdNode();
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ZipRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ZipRFFI.java
index dc99a9ddb7a8e116cb0023e4084e7f45b16f82e1..1eecb127e95bfe8e20a69978b753c93811f79a2b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ZipRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/ZipRFFI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -22,21 +22,82 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+
+/**
+ * zip compression/uncompression.
+ */
 public interface ZipRFFI {
-    // zip compression/uncompression
-
-    /**
-     * compress {@code source} into {@code dest}.
-     *
-     * @return standard return code (0 ok)
-     */
-    int compress(byte[] dest, byte[] source);
-
-    /**
-     * uncompress {@code source} into {@code dest}.
-     *
-     * @return standard return code (0 ok)
-     */
-    int uncompress(byte[] dest, byte[] source);
+
+    abstract class CompressNode extends Node {
+        /**
+         * compress {@code source} into {@code dest}.
+         *
+         * @return standard return code (0 ok)
+         */
+        public abstract int execute(byte[] dest, byte[] source);
+
+        public static CompressNode create() {
+            return RFFIFactory.getRFFI().getZipRFFI().createCompressNode();
+        }
+    }
+
+    abstract class UncompressNode extends Node {
+        /**
+         * uncompress {@code source} into {@code dest}.
+         *
+         * @return standard return code (0 ok)
+         */
+        public abstract int execute(byte[] dest, byte[] source);
+    }
+
+    CompressNode createCompressNode();
+
+    UncompressNode createUncompressNode();
+
+    // RootNodes for calling when not in Truffle context
+
+    final class CompressRootNode extends RFFIRootNode<CompressNode> {
+        private static CompressRootNode compressRootNode;
+
+        private CompressRootNode() {
+            super(RFFIFactory.getRFFI().getZipRFFI().createCompressNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            return rffiNode.execute((byte[]) args[0], (byte[]) args[1]);
+        }
+
+        public static CompressRootNode create() {
+            if (compressRootNode == null) {
+                compressRootNode = new CompressRootNode();
+            }
+            return compressRootNode;
+        }
+    }
+
+    final class UncompressRootNode extends RFFIRootNode<UncompressNode> {
+        private static UncompressRootNode uncompressRootNode;
+
+        private UncompressRootNode() {
+            super(RFFIFactory.getRFFI().getZipRFFI().createUncompressNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            return rffiNode.execute((byte[]) args[0], (byte[]) args[1]);
+        }
+
+        public static UncompressRootNode create() {
+            if (uncompressRootNode == null) {
+                uncompressRootNode = new UncompressRootNode();
+            }
+            return uncompressRootNode;
+        }
+    }
 
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
index 7ab4c8007bc955514a23a30604fd170cedddcd11..db3f531e93a4643264ef25d0b473c6eb5a429035 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
@@ -25,7 +25,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.env.REnvironment;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 import com.oracle.truffle.r.runtime.rng.mm.MarsagliaMulticarry;
 import com.oracle.truffle.r.runtime.rng.mt.MersenneTwister;
 import com.oracle.truffle.r.runtime.rng.user.UserRNG;
@@ -294,7 +294,7 @@ public class RRNG {
      * Create a random integer.
      */
     public static Integer timeToSeed() {
-        int pid = RFFIFactory.getRFFI().getBaseRFFI().getpid();
+        int pid = (int) BaseRFFI.GetpidRootNode.create().getCallTarget().call();
         int millis = (int) (System.currentTimeMillis() & 0xFFFFFFFFL);
         return (millis << 16) ^ pid;
     }