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 cf1869d34abf3725d9e315d708ad376bbfcb4b72..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
@@ -67,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);
-                chmodNode.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 47711c05520d525342e80b1f5be7d79adca5bea1..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
@@ -1219,7 +1219,7 @@ public class FileFunctions {
 
         private boolean mkdir(BaseRFFI.MkdirNode mkdirNode, String path, boolean showWarnings, int mode) {
             try {
-                mkdirNode.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 64761054941928af1132c19042bfc5b25604f6ec..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
@@ -40,7 +40,7 @@ public abstract class Getwd extends RBuiltinNode {
     @Specialization
     @TruffleBoundary
     protected Object getwd() {
-        String result = getwdNode.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 af7c7482e08fbecc2aca56c3bacca8dc781d69f5..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
@@ -53,13 +53,13 @@ public abstract class Setwd extends RBuiltinNode {
     protected Object setwd(String path,
                     @Cached("create()") BaseRFFI.GetwdNode getwdNode,
                     @Cached("create()") BaseRFFI.SetwdNode setwdNode) {
-        String owd = getwdNode.getwd();
+        String owd = getwdNode.execute();
         String nwd = Utils.tildeExpand(path);
-        int rc = setwdNode.setwd(nwd);
+        int rc = setwdNode.execute(nwd);
         if (rc != 0) {
             throw RError.error(this, RError.Message.CANNOT_CHANGE_DIRECTORY);
         } else {
-            String nwdAbs = getwdNode.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 8796f70527f38597b830961c78008c7e45d4ac3a..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
@@ -81,7 +81,7 @@ public class SysFunctions {
         @Specialization
         @TruffleBoundary
         protected Object sysGetPid() {
-            int pid = getpidNode.getpid();
+            int pid = getpidNode.execute();
             return RDataFactory.createIntVectorFromScalar(pid);
         }
     }
@@ -276,7 +276,7 @@ public class SysFunctions {
         private static String doSysReadLink(String path, BaseRFFI.ReadlinkNode readlinkNode) {
             String s;
             try {
-                s = readlinkNode.readlink(path);
+                s = readlinkNode.execute(path);
                 if (s == null) {
                     s = "";
                 }
@@ -306,7 +306,7 @@ public class SysFunctions {
                 if (path.length() == 0 || RRuntime.isNA(path)) {
                     continue;
                 }
-                int result = chmodNode.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);
@@ -348,7 +348,7 @@ public class SysFunctions {
         @Specialization
         @TruffleBoundary
         protected Object sysTime() {
-            UtsName utsname = unameNode.uname();
+            UtsName utsname = unameNode.execute();
             String[] data = new String[NAMES.length];
             data[0] = utsname.sysname();
             data[1] = utsname.release();
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 1288d98ba224a42a0852df649a584312b8f724b7..0533e1597b70f83f476d274da9f49c9077d70098 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();
     }
 
     /**
@@ -661,7 +661,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());
         }
 
         /**
@@ -672,7 +672,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());
         }
 
         /**
@@ -681,7 +681,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);
         }
 
@@ -692,13 +692,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")
@@ -780,27 +780,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
@@ -858,20 +858,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();
@@ -879,7 +879,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
@@ -919,18 +919,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) {
@@ -938,7 +938,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
@@ -977,26 +977,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 d96a06215adb7f9aabc8a44f03d5990573edda90..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
@@ -30,14 +30,14 @@ import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 public class JNI_Base implements BaseRFFI {
     public static class JNI_GetpidNode extends GetpidNode {
         @Override
-        public int getpid() {
+        public int execute() {
             return native_getpid();
         }
     }
 
     public static class JNI_GetwdNode extends GetwdNode {
         @Override
-        public String getwd() {
+        public String execute() {
             byte[] buf = new byte[4096];
             int rc = native_getwd(buf, buf.length);
             if (rc == 0) {
@@ -54,7 +54,7 @@ public class JNI_Base implements BaseRFFI {
 
     public static class JNI_SetwdNode extends SetwdNode {
         @Override
-        public int setwd(String dir) {
+        public int execute(String dir) {
             return native_setwd(dir);
         }
     }
@@ -63,7 +63,7 @@ public class JNI_Base implements BaseRFFI {
         private static final int EINVAL = 22;
 
         @Override
-        public String readlink(String path) throws IOException {
+        public String execute(String path) throws IOException {
             int[] errno = new int[]{0};
             String s = native_readlink(path, errno);
             if (s == null) {
@@ -80,7 +80,7 @@ public class JNI_Base implements BaseRFFI {
 
     public static class JNI_MkdtempNode extends MkdtempNode {
         @Override
-        public String mkdtemp(String template) {
+        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.
@@ -100,7 +100,7 @@ public class JNI_Base implements BaseRFFI {
 
     public static class JNI_MkdirNode extends MkdirNode {
         @Override
-        public void mkdir(String dir, int mode) throws IOException {
+        public void execute(String dir, int mode) throws IOException {
             int rc = native_mkdir(dir, mode);
             if (rc != 0) {
                 throw new IOException("mkdir " + dir + " failed");
@@ -110,14 +110,14 @@ public class JNI_Base implements BaseRFFI {
 
     public static class JNI_ChmodNode extends ChmodNode {
         @Override
-        public int chmod(String path, int mode) {
+        public int execute(String path, int mode) {
             return native_chmod(path, mode);
         }
     }
 
     public static class JNI_StrolNode extends StrolNode {
         @Override
-        public long strtol(String s, int base) throws IllegalArgumentException {
+        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) {
@@ -130,7 +130,7 @@ public class JNI_Base implements BaseRFFI {
 
     public static class JNI_UnameNode extends UnameNode {
         @Override
-        public UtsName uname() {
+        public UtsName execute() {
             return JNI_UtsName.get();
         }
     }
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/ffi/BaseRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/BaseRFFI.java
index d3124d1a933a09fdda6c9e2a35cae3d6fc851253..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
@@ -25,12 +25,8 @@ package com.oracle.truffle.r.runtime.ffi;
 import java.io.IOException;
 import java.util.ArrayList;
 
-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.Node;
-import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.r.runtime.context.RContext;
 
 /**
  * A statically typed interface to exactly those native functions required by the R {@code base}
@@ -39,7 +35,7 @@ import com.oracle.truffle.r.runtime.context.RContext;
  */
 public interface BaseRFFI {
     abstract class GetpidNode extends Node {
-        public abstract int getpid();
+        public abstract int execute();
 
         public static GetpidNode create() {
             return RFFIFactory.getRFFI().getBaseRFFI().createGetpidNode();
@@ -50,7 +46,7 @@ public interface BaseRFFI {
         /**
          * Returns the current working directory, in the face of calls to {@code setwd}.
          */
-        public abstract String getwd();
+        public abstract String execute();
 
         public static GetwdNode create() {
             return RFFIFactory.getRFFI().getBaseRFFI().createGetwdNode();
@@ -63,7 +59,7 @@ public interface BaseRFFI {
          *
          * @return 0 if successful.
          */
-        public abstract int setwd(String dir);
+        public abstract int execute(String dir);
 
         public static SetwdNode create() {
             return RFFIFactory.getRFFI().getBaseRFFI().createSetwdNode();
@@ -74,7 +70,7 @@ public interface BaseRFFI {
         /**
          * Create directory with given mode. Exception is thrown omn error.
          */
-        public abstract void mkdir(String dir, int mode) throws IOException;
+        public abstract void execute(String dir, int mode) throws IOException;
 
         public static MkdirNode create() {
             return RFFIFactory.getRFFI().getBaseRFFI().createMkdirNode();
@@ -89,7 +85,7 @@ public interface BaseRFFI {
          * @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 readlink(String path) throws IOException;
+        public abstract String execute(String path) throws IOException;
 
         public static ReadlinkNode create() {
             return RFFIFactory.getRFFI().getBaseRFFI().createReadlinkNode();
@@ -101,7 +97,7 @@ public interface BaseRFFI {
          * Creates a temporary directory using {@code template} and return the resulting path or
          * {@code null} if error.
          */
-        public abstract String mkdtemp(String template);
+        public abstract String execute(String template);
 
         public static MkdtempNode create() {
             return RFFIFactory.getRFFI().getBaseRFFI().createMkdtempNode();
@@ -112,7 +108,7 @@ public interface BaseRFFI {
         /**
          * Change the file mode of {@code path}.
          */
-        public abstract int chmod(String path, int mode);
+        public abstract int execute(String path, int mode);
 
         public static ChmodNode create() {
             return RFFIFactory.getRFFI().getBaseRFFI().createChmodNode();
@@ -123,7 +119,7 @@ public interface BaseRFFI {
         /**
          * Convert string to long.
          */
-        public abstract long strtol(String s, int base) throws IllegalArgumentException;
+        public abstract long execute(String s, int base) throws IllegalArgumentException;
 
         public static StrolNode create() {
             return RFFIFactory.getRFFI().getBaseRFFI().createStrolNode();
@@ -146,7 +142,7 @@ public interface BaseRFFI {
         /**
          * Return {@code utsname} info.
          */
-        public abstract UtsName uname();
+        public abstract UtsName execute();
 
         public static UnameNode create() {
             return RFFIFactory.getRFFI().getBaseRFFI().createUnameNode();
@@ -196,17 +192,7 @@ public interface BaseRFFI {
      * Some functions are called from non-Truffle contexts, which requires a RootNode
      */
 
-    abstract class BaseRFFIRootNode<T extends Node> extends RootNode {
-        @Child T baseRFFINode;
-
-        private BaseRFFIRootNode(T baseRFFINode) {
-            super(RContext.getRRuntimeASTAccess().getTruffleRLanguage(), null, new FrameDescriptor());
-            this.baseRFFINode = baseRFFINode;
-            Truffle.getRuntime().createCallTarget(this);
-        }
-    }
-
-    final class GetpidRootNode extends BaseRFFIRootNode<GetpidNode> {
+    final class GetpidRootNode extends RFFIRootNode<GetpidNode> {
         private static GetpidRootNode getpidRootNode;
 
         private GetpidRootNode() {
@@ -215,7 +201,7 @@ public interface BaseRFFI {
 
         @Override
         public Object execute(VirtualFrame frame) {
-            return baseRFFINode.getpid();
+            return rffiNode.execute();
         }
 
         public static GetpidRootNode create() {
@@ -226,7 +212,7 @@ public interface BaseRFFI {
         }
     }
 
-    final class GetwdRootNode extends BaseRFFIRootNode<GetwdNode> {
+    final class GetwdRootNode extends RFFIRootNode<GetwdNode> {
         private static GetwdRootNode getwdRootNode;
 
         private GetwdRootNode() {
@@ -235,7 +221,7 @@ public interface BaseRFFI {
 
         @Override
         public Object execute(VirtualFrame frame) {
-            return baseRFFINode.getwd();
+            return rffiNode.execute();
         }
 
         public static GetwdRootNode create() {
@@ -246,7 +232,7 @@ public interface BaseRFFI {
         }
     }
 
-    final class MkdtempRootNode extends BaseRFFIRootNode<MkdtempNode> {
+    final class MkdtempRootNode extends RFFIRootNode<MkdtempNode> {
         private static MkdtempRootNode mkdtempRootNode;
 
         private MkdtempRootNode() {
@@ -256,7 +242,7 @@ public interface BaseRFFI {
         @Override
         public Object execute(VirtualFrame frame) {
             Object[] args = frame.getArguments();
-            return baseRFFINode.mkdtemp((String) args[0]);
+            return rffiNode.execute((String) args[0]);
         }
 
         public static MkdtempRootNode create() {
@@ -267,7 +253,7 @@ public interface BaseRFFI {
         }
     }
 
-    final class UnameRootNode extends BaseRFFIRootNode<UnameNode> {
+    final class UnameRootNode extends RFFIRootNode<UnameNode> {
         private static UnameRootNode unameRootNode;
 
         private UnameRootNode() {
@@ -276,7 +262,7 @@ public interface BaseRFFI {
 
         @Override
         public Object execute(VirtualFrame frame) {
-            return baseRFFINode.uname();
+            return rffiNode.execute();
         }
 
         public static UnameRootNode create() {
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;
+        }
+    }
 
 }