diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java
index 7987279b356232315ddb1b71ef39c7f42abc900e..df5ee9b5ec6870758839cdfc5d89b3fef497a123 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java
@@ -56,7 +56,7 @@ public class EmbeddedConsoleHandler extends ConsoleHandler {
     @TruffleBoundary
     private REmbedRFFI getREmbedRFFI() {
         if (rEmbedRFFI == null) {
-            rEmbedRFFI = RFFIFactory.getRFFI().getREmbedRFFI();
+            rEmbedRFFI = RFFIFactory.getREmbedRFFI();
             if (!(RInterfaceCallbacks.R_WriteConsole.isOverridden() || RInterfaceCallbacks.R_ReadConsole.isOverridden())) {
                 if (startParams.noReadline()) {
                     delegate = new DefaultConsoleHandler(System.in, System.out);
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/DownCallNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/DownCallNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..95d2bbafb9cd14fd5b7920fcc1623b367421d48c
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/DownCallNode.java
@@ -0,0 +1,59 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.ffi.impl.common;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.InteropException;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.RInternalError;
+
+public abstract class DownCallNode<T extends NativeFunction> extends Node {
+
+    @Child private Node message;
+
+    protected abstract TruffleObject getTarget();
+
+    protected abstract T getFunction();
+
+    protected final Object call(Object... args) {
+        try {
+            if (message == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                message = insert(Message.createExecute(getFunction().getArgumentCount()).createNode());
+            }
+            wrapArguments(args);
+            return ForeignAccess.sendExecute(message, getTarget(), args);
+        } catch (InteropException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        } finally {
+            finishArguments(args);
+        }
+    }
+
+    protected abstract void wrapArguments(Object[] args);
+
+    protected abstract void finishArguments(Object[] args);
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/Generic_Tools.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/Generic_Tools.java
index 3bda331b58afd4ce4e566509123480be2b41dfd2..f8ca6ded284e5886eed5f9e9de131f5a5369b1d2 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/Generic_Tools.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/Generic_Tools.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.ffi.impl.common;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
@@ -37,11 +38,11 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
 
 public class Generic_Tools implements ToolsRFFI {
-    public static class Generic_ToolsRFFINode extends ParseRdNode {
+    public static class Generic_ToolsRFFINode extends Node implements ParseRdNode {
         private static final String C_PARSE_RD = "C_parseRd";
         protected static final String TOOLS = "tools";
 
-        @Child private CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
+        @Child private CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getCallRFFI().createInvokeCallNode();
         @Child private DLL.RFindSymbolNode findSymbolNode = DLL.RFindSymbolNode.create();
 
         @CompilationFinal private NativeCallInfo nativeCallInfo;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/NativeFunction.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/NativeFunction.java
new file mode 100644
index 0000000000000000000000000000000000000000..dca5d7ddc72378f8c859088778df052414d7bf7d
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/NativeFunction.java
@@ -0,0 +1,29 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.truffle.r.ffi.impl.common;
+
+public interface NativeFunction {
+
+    int getArgumentCount();
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/UpCallUnwrap.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/UpCallUnwrap.java
index feac93f6afefa4645328330d64fcb10c8949d214..cbaa000be27e05852271586265fab913b3d6bba5 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/UpCallUnwrap.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/UpCallUnwrap.java
@@ -47,10 +47,10 @@ public final class UpCallUnwrap extends Node {
      * <li>For an {@link RTruffleObject} there is nothing to do, and indeed, calling {@code unbox}
      * would be disastrous, as that means, e.g., for a RVector, extract the first element!</li>
      * <li>Or we could get a {@code TruffleObject} from another language domain, e.g a
-     * {@code JavaObject} that wraps, say, an {@code Integer}.S Such a value has to be unboxed.
-     * Similarly a {@code NativePointer} encoding, say, a C char array. One special case in the LLVM
-     * implementation is {@code NativePointer} that represents an object stored to memory, which
-     * requires a lookup (and not an {@code UNBOX}).</li>
+     * {@code JavaObject} that wraps, e.g., an {@code Integer}. Such a value has to be unboxed.
+     * Similarly a {@code NativePointer} encoding, e.g., a C char array. One special case in the
+     * LLVM implementation is {@code NativePointer} that represents an object stored to memory,
+     * which requires a lookup (and not an {@code UNBOX}).</li>
      * <li>We could also get a plain {@link Integer} or similar type in which case there is nothing
      * to do.</li>
      * </ul>
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArray.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArray.java
index 55d6e3e0d6a347af46cd039152fc3f2aa319c04a..05267fdf39e9010c930420429531a0e73bfa315c 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArray.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArray.java
@@ -24,20 +24,15 @@ package com.oracle.truffle.r.ffi.impl.interop;
 
 import static com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter.UNSAFE;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 
 import sun.misc.Unsafe;
 
-public final class NativeDoubleArray extends NativeNACheck implements RTruffleObject {
+public final class NativeDoubleArray extends NativeNACheck<double[]> implements RTruffleObject {
+
     public final double[] value;
-    /**
-     * If the array escapes the Truffle world via {@link #convertToNative()}, this value will be
-     * non-zero and is used exclusively thereafter.
-     */
-    @CompilationFinal protected long nativeAddress;
 
     public NativeDoubleArray(Object obj, double[] value) {
         super(obj);
@@ -64,21 +59,18 @@ public final class NativeDoubleArray extends NativeNACheck implements RTruffleOb
         }
     }
 
-    long convertToNative() {
-        if (nativeAddress == 0) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            nativeAddress = UNSAFE.allocateMemory(value.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
-            UNSAFE.copyMemory(value, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, null, nativeAddress, value.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
-        }
-        return nativeAddress;
+    @Override
+    @TruffleBoundary
+    protected void allocateNative() {
+        nativeAddress = UNSAFE.allocateMemory(value.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+        UNSAFE.copyMemory(value, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, null, nativeAddress, value.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
     }
 
-    public double[] getValue() {
-        if (nativeAddress != 0) {
-            // copy back
-            UNSAFE.copyMemory(null, nativeAddress, value, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, value.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
-        }
-        return value;
+    @Override
+    @TruffleBoundary
+    protected void copyBackFromNative() {
+        // copy back
+        UNSAFE.copyMemory(null, nativeAddress, value, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, value.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
     }
 
     @Override
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArray.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArray.java
index fd86d545e72236b6388d8e050a34743e63f6e335..17b238ed20aea5e69543eedfba4d5ac88e5901bf 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArray.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArray.java
@@ -24,20 +24,15 @@ package com.oracle.truffle.r.ffi.impl.interop;
 
 import static com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter.UNSAFE;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 
 import sun.misc.Unsafe;
 
-public final class NativeIntegerArray extends NativeNACheck implements RTruffleObject {
+public final class NativeIntegerArray extends NativeNACheck<int[]> implements RTruffleObject {
+
     public final int[] value;
-    /**
-     * If the array escapes the Truffle world via {@link #convertToNative()}, this value will be
-     * non-zero and is used exclusively thereafter.
-     */
-    @CompilationFinal protected long nativeAddress;
 
     public NativeIntegerArray(Object obj, int[] value) {
         super(obj);
@@ -64,21 +59,18 @@ public final class NativeIntegerArray extends NativeNACheck implements RTruffleO
         }
     }
 
-    long convertToNative() {
-        if (nativeAddress == 0) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            nativeAddress = UNSAFE.allocateMemory(value.length * Unsafe.ARRAY_INT_INDEX_SCALE);
-            UNSAFE.copyMemory(value, Unsafe.ARRAY_INT_BASE_OFFSET, null, nativeAddress, value.length * Unsafe.ARRAY_INT_INDEX_SCALE);
-        }
-        return nativeAddress;
+    @Override
+    @TruffleBoundary
+    protected void allocateNative() {
+        nativeAddress = UNSAFE.allocateMemory(value.length * Unsafe.ARRAY_INT_INDEX_SCALE);
+        UNSAFE.copyMemory(value, Unsafe.ARRAY_INT_BASE_OFFSET, null, nativeAddress, value.length * Unsafe.ARRAY_INT_INDEX_SCALE);
     }
 
-    public int[] getValue() {
-        if (nativeAddress != 0) {
-            // copy back
-            UNSAFE.copyMemory(null, nativeAddress, value, Unsafe.ARRAY_INT_BASE_OFFSET, value.length * Unsafe.ARRAY_INT_INDEX_SCALE);
-        }
-        return value;
+    @Override
+    @TruffleBoundary
+    protected void copyBackFromNative() {
+        // copy back
+        UNSAFE.copyMemory(null, nativeAddress, value, Unsafe.ARRAY_INT_BASE_OFFSET, value.length * Unsafe.ARRAY_INT_INDEX_SCALE);
     }
 
     @Override
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArray.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArray.java
index 0ea52d5c5fda09b468b907afdf0bbbb7723bb759..9819981f46b37646dc0a11b1a44640c4cbcd2055 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArray.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArray.java
@@ -22,8 +22,9 @@
  */
 package com.oracle.truffle.r.ffi.impl.interop;
 
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 
@@ -31,8 +32,9 @@ import com.oracle.truffle.r.runtime.data.RTruffleObject;
  * Handles the requirement that the R FFI sees "logical" arrays as {@code int[]} but the actual
  * array in FastR is represented as {@code byte[]}.
  */
-public final class NativeLogicalArray extends NativeNACheck implements RTruffleObject {
-    @CompilationFinal(dimensions = 1) public final byte[] data;
+public final class NativeLogicalArray extends NativeNACheck<byte[]> implements RTruffleObject {
+
+    public final byte[] data;
 
     public NativeLogicalArray(Object obj, byte[] value) {
         super(obj);
@@ -58,4 +60,15 @@ public final class NativeLogicalArray extends NativeNACheck implements RTruffleO
     public ForeignAccess getForeignAccess() {
         return NativeLogicalArrayMRForeign.ACCESS;
     }
+
+    @Override
+    @TruffleBoundary
+    protected void allocateNative() {
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    protected void copyBackFromNative() {
+        throw RInternalError.shouldNotReachHere();
+    }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArrayMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArrayMR.java
index ceb96f5a3f21fb6c9410edaca4904fb6414bea0d..6cadbb1a64d93009b616adbd20d40f59a7cb2ba7 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArrayMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArrayMR.java
@@ -54,7 +54,6 @@ public class NativeLogicalArrayMR {
 
     @CanResolve
     public abstract static class NLACheck extends Node {
-
         protected static boolean test(TruffleObject receiver) {
             return receiver instanceof NativeLogicalArray;
         }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeNACheck.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeNACheck.java
index 3648cf500b9714611897d50a84876bfbe4761c62..b6aa85883db201fd5dbfc0c283b421996d471698 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeNACheck.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeNACheck.java
@@ -22,6 +22,7 @@
  */
 package com.oracle.truffle.r.ffi.impl.interop;
 
+import static com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter.UNSAFE;
 import com.oracle.truffle.r.runtime.data.RVector;
 
 /**
@@ -29,9 +30,16 @@ import com.oracle.truffle.r.runtime.data.RVector;
  * native code.
  *
  */
-public class NativeNACheck {
+public abstract class NativeNACheck<T> implements AutoCloseable {
+
     private final RVector<?> vec;
 
+    /**
+     * If the array escapes the Truffle world via {@link #convertToNative()}, this value will be
+     * non-zero and is used exclusively thereafter.
+     */
+    protected long nativeAddress;
+
     protected NativeNACheck(Object x) {
         if (x instanceof RVector<?>) {
             vec = (RVector<?>) x;
@@ -46,4 +54,23 @@ public class NativeNACheck {
             vec.setComplete(false);
         }
     }
+
+    protected abstract void allocateNative();
+
+    protected abstract void copyBackFromNative();
+
+    final long convertToNative() {
+        if (nativeAddress == 0) {
+            allocateNative();
+        }
+        return nativeAddress;
+    }
+
+    @Override
+    public final void close() {
+        if (nativeAddress != 0) {
+            copyBackFromNative();
+            UNSAFE.freeMemory(nativeAddress);
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java
index 6ef8b6f8a9079e2f6d1ad20c73e5cbc730df15d1..b8b73f6378c2b02ff692b92b425fa8d69a5526d4 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.ffi.impl.interop;
 
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.nodes.Node;
@@ -31,18 +30,15 @@ import com.oracle.truffle.api.nodes.Node;
 public class NativePointerMR {
     @Resolve(message = "IS_POINTER")
     public abstract static class AcceptIsPointer extends Node {
-        @SuppressWarnings("unused")
-        public Object access(VirtualFrame frame, NativePointer object) {
+        public Object access(@SuppressWarnings("unused") NativePointer object) {
             return true;
         }
     }
 
     @Resolve(message = "AS_POINTER")
     public abstract static class AcceptAsPointer extends Node {
-        @SuppressWarnings("unused")
-        public long access(VirtualFrame frame, NativePointer object) {
-            long result = object.asPointer();
-            return result;
+        public long access(NativePointer object) {
+            return object.asPointer();
         }
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java
index 4ecff42f806bc92b5db7585989b990e45535e347..4bf73b78c4c4c7517fd31ffb4dd4c53f95beed58 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java
@@ -24,8 +24,8 @@ package com.oracle.truffle.r.ffi.impl.interop;
 
 import static com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter.UNSAFE;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 
 import sun.misc.Unsafe;
@@ -42,6 +42,7 @@ import sun.misc.Unsafe;
  * similar for {@link #write}.
  */
 public abstract class NativeUInt8Array implements RTruffleObject {
+
     public final byte[] bytes;
 
     /**
@@ -97,21 +98,30 @@ public abstract class NativeUInt8Array implements RTruffleObject {
 
     long convertToNative() {
         if (nativeAddress == 0) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            nativeAddress = UNSAFE.allocateMemory(effectiveLength);
-            UNSAFE.copyMemory(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, nativeAddress, bytes.length);
-            if (fakeNull()) {
-                UNSAFE.putByte(nativeAddress + bytes.length, (byte) 0);
-            }
+            allocateNative();
         }
         return nativeAddress;
     }
 
+    @TruffleBoundary
+    private void allocateNative() {
+        nativeAddress = UNSAFE.allocateMemory(effectiveLength);
+        UNSAFE.copyMemory(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, nativeAddress, bytes.length);
+        if (fakeNull()) {
+            UNSAFE.putByte(nativeAddress + bytes.length, (byte) 0);
+        }
+    }
+
     public byte[] getValue() {
         if (nativeAddress != 0) {
-            // copy back
-            UNSAFE.copyMemory(null, nativeAddress, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, bytes.length);
+            copyBackFromNative();
         }
         return bytes;
     }
+
+    @TruffleBoundary
+    private void copyBackFromNative() {
+        // copy back
+        UNSAFE.copyMemory(null, nativeAddress, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, bytes.length);
+    }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Base.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Base.java
index a854b68fd1a6bfabd203358102530a9564e426a7..e45722e0c97dc72648074bfd67fa23564691c2a0 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Base.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Base.java
@@ -26,10 +26,11 @@ import java.io.IOException;
 import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 public class JNI_Base implements BaseRFFI {
-    public static class JNI_GetpidNode extends GetpidNode {
+    private static class JNI_GetpidNode extends Node implements GetpidNode {
         @TruffleBoundary
         @Override
         public int execute() {
@@ -37,7 +38,7 @@ public class JNI_Base implements BaseRFFI {
         }
     }
 
-    public static class JNI_GetwdNode extends GetwdNode {
+    private static class JNI_GetwdNode extends Node implements GetwdNode {
         @TruffleBoundary
         @Override
         public String execute() {
@@ -55,7 +56,7 @@ public class JNI_Base implements BaseRFFI {
         }
     }
 
-    public static class JNI_SetwdNode extends SetwdNode {
+    private static class JNI_SetwdNode extends Node implements SetwdNode {
         @TruffleBoundary
         @Override
         public int execute(String dir) {
@@ -63,7 +64,7 @@ public class JNI_Base implements BaseRFFI {
         }
     }
 
-    public static class JNI_ReadlinkNode extends ReadlinkNode {
+    private static class JNI_ReadlinkNode extends Node implements ReadlinkNode {
         private static final int EINVAL = 22;
 
         @TruffleBoundary
@@ -83,7 +84,7 @@ public class JNI_Base implements BaseRFFI {
         }
     }
 
-    public static class JNI_MkdtempNode extends MkdtempNode {
+    private static class JNI_MkdtempNode extends Node implements MkdtempNode {
         @TruffleBoundary
         @Override
         public String execute(String template) {
@@ -104,7 +105,7 @@ public class JNI_Base implements BaseRFFI {
         }
     }
 
-    public static class JNI_MkdirNode extends MkdirNode {
+    private static class JNI_MkdirNode extends Node implements MkdirNode {
         @TruffleBoundary
         @Override
         public void execute(String dir, int mode) throws IOException {
@@ -115,7 +116,7 @@ public class JNI_Base implements BaseRFFI {
         }
     }
 
-    public static class JNI_ChmodNode extends ChmodNode {
+    private static class JNI_ChmodNode extends Node implements ChmodNode {
         @TruffleBoundary
         @Override
         public int execute(String path, int mode) {
@@ -123,7 +124,7 @@ public class JNI_Base implements BaseRFFI {
         }
     }
 
-    public static class JNI_StrolNode extends StrolNode {
+    private static class JNI_StrolNode extends Node implements StrolNode {
         @TruffleBoundary
         @Override
         public long execute(String s, int base) throws IllegalArgumentException {
@@ -137,7 +138,7 @@ public class JNI_Base implements BaseRFFI {
         }
     }
 
-    public static class JNI_UnameNode extends UnameNode {
+    private static class JNI_UnameNode extends Node implements UnameNode {
         @TruffleBoundary
         @Override
         public UtsName execute() {
@@ -145,7 +146,7 @@ public class JNI_Base implements BaseRFFI {
         }
     }
 
-    public static class JNI_GlobNode extends GlobNode {
+    private static class JNI_GlobNode extends Node implements GlobNode {
         @TruffleBoundary
         @Override
         public ArrayList<String> glob(String pattern) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java
index 2148bacc3b634dd3ddeab1d32df7ff12128e914e..17325b33650a5e6e338a27d9e0fc71eda1a57871 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java
@@ -26,11 +26,12 @@ import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCall;
 import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceEnabled;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
 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_InvokeCNode extends InvokeCNode {
+    private static class JNI_InvokeCNode extends Node implements 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
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Call.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Call.java
index f995b4bf7dda403692d1bf2a669219fb86f1bf7c..ee3d5222ab9ef5942554bf0221b1231271614bd5 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Call.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Call.java
@@ -27,6 +27,7 @@ import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCallReturn
 import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceEnabled;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
 import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -46,7 +47,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIVariables;
  */
 public class JNI_Call implements CallRFFI {
 
-    public static class JNI_InvokeCallNode extends InvokeCallNode {
+    private static class JNI_InvokeCallNode extends Node implements InvokeCallNode {
 
         @Override
         @TruffleBoundary
@@ -103,7 +104,7 @@ public class JNI_Call implements CallRFFI {
         }
     }
 
-    public static class JNI_InvokeVoidCallNode extends InvokeVoidCallNode {
+    private static class JNI_InvokeVoidCallNode extends Node implements InvokeVoidCallNode {
 
         @Override
         @TruffleBoundary
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_DLL.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_DLL.java
index 9c7c5dc240506ad8761772ab3c793ebcc3da6f52..08d72f24712217f3f0bfd0088fef79f332ab0b97 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_DLL.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_DLL.java
@@ -22,13 +22,14 @@
  */
 package com.oracle.truffle.r.ffi.impl.jni;
 
-import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
 
 public class JNI_DLL implements DLLRFFI {
 
-    public static class JNI_DLOpenNode extends DLOpenNode {
+    private static class JNI_DLOpenNode extends Node implements DLOpenNode {
         @Override
         @TruffleBoundary
         public Object execute(String path, boolean local, boolean now) throws UnsatisfiedLinkError {
@@ -37,7 +38,7 @@ public class JNI_DLL implements DLLRFFI {
         }
     }
 
-    public static class JNI_DLSymNode extends DLSymNode {
+    private static class JNI_DLSymNode extends Node implements DLSymNode {
         @Override
         @TruffleBoundary
         public SymbolHandle execute(Object handle, String symbol) throws UnsatisfiedLinkError {
@@ -47,7 +48,7 @@ public class JNI_DLL implements DLLRFFI {
         }
     }
 
-    public static class JNI_DLCloseNode extends DLCloseNode {
+    private static class JNI_DLCloseNode extends Node implements DLCloseNode {
         @Override
         @TruffleBoundary
         public int execute(Object handle) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Lapack.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Lapack.java
index 7317964c5a09121250c9aa962a9ee1f26ae43c0e..ac528594c16af414a6d0705f5093bed69452387d 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Lapack.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Lapack.java
@@ -23,10 +23,11 @@
 package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
 
 public class JNI_Lapack implements LapackRFFI {
-    private static class JNI_IlaverNode extends IlaverNode {
+    private static class JNI_IlaverNode extends Node implements IlaverNode {
         @Override
         @TruffleBoundary
         public void execute(int[] version) {
@@ -34,7 +35,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DgeevNode extends DgeevNode {
+    private static class JNI_DgeevNode extends Node implements DgeevNode {
         @Override
         @TruffleBoundary
         public int execute(char jobVL, char jobVR, int n, double[] a, int lda, double[] wr, double[] wi, double[] vl, int ldvl, double[] vr, int ldvr, double[] work, int lwork) {
@@ -42,7 +43,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_Dgeqp3Node extends Dgeqp3Node {
+    private static class JNI_Dgeqp3Node extends Node implements Dgeqp3Node {
         @Override
         @TruffleBoundary
         public int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork) {
@@ -50,7 +51,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DormqrNode extends DormqrNode {
+    private static class JNI_DormqrNode extends Node implements DormqrNode {
         @Override
         @TruffleBoundary
         public int execute(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork) {
@@ -58,7 +59,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DtrtrsNode extends DtrtrsNode {
+    private static class JNI_DtrtrsNode extends Node implements DtrtrsNode {
         @Override
         @TruffleBoundary
         public int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
@@ -66,7 +67,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DgetrfNode extends DgetrfNode {
+    private static class JNI_DgetrfNode extends Node implements DgetrfNode {
         @Override
         @TruffleBoundary
         public int execute(int m, int n, double[] a, int lda, int[] ipiv) {
@@ -74,7 +75,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DpotrfNode extends DpotrfNode {
+    private static class JNI_DpotrfNode extends Node implements DpotrfNode {
         @Override
         @TruffleBoundary
         public int execute(char uplo, int n, double[] a, int lda) {
@@ -82,7 +83,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DpotriNode extends DpotriNode {
+    private static class JNI_DpotriNode extends Node implements DpotriNode {
         @Override
         @TruffleBoundary
         public int execute(char uplo, int n, double[] a, int lda) {
@@ -90,7 +91,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DpstrfNode extends DpstrfNode {
+    private static class JNI_DpstrfNode extends Node implements DpstrfNode {
         @Override
         @TruffleBoundary
         public int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work) {
@@ -98,7 +99,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DgesvNode extends DgesvNode {
+    private static class JNI_DgesvNode extends Node implements DgesvNode {
         @Override
         @TruffleBoundary
         public int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb) {
@@ -106,7 +107,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DlangeNode extends DlangeNode {
+    private static class JNI_DlangeNode extends Node implements DlangeNode {
         @Override
         @TruffleBoundary
         public double execute(char norm, int m, int n, double[] a, int lda, double[] work) {
@@ -114,7 +115,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DgeconNode extends DgeconNode {
+    private static class JNI_DgeconNode extends Node implements DgeconNode {
         @Override
         @TruffleBoundary
         public int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork) {
@@ -122,7 +123,7 @@ public class JNI_Lapack implements LapackRFFI {
         }
     }
 
-    private static class JNI_DsyevrNode extends DsyevrNode {
+    private static class JNI_DsyevrNode extends Node implements DsyevrNode {
         @Override
         public int execute(char jobz, char range, char uplo, int n, double[] a, int lda, double vl, double vu, int il, int iu, double abstol, int[] m,
                         double[] w, double[] z, int ldz, int[] isuppz, double[] work, int lwork, int[] iwork, int liwork) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Misc.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Misc.java
index 19c651622a984bb3d5d85dbe11f0227d1a4968e4..57cc3b98b7fe4d96b83224ef34d09ad7587d949b 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Misc.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Misc.java
@@ -26,11 +26,12 @@ import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCall;
 import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCallReturn;
 import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceEnabled;
 
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 
 public class JNI_Misc implements MiscRFFI {
 
-    private static class JNI_ExactSumNode extends ExactSumNode {
+    private static class JNI_ExactSumNode extends Node implements ExactSumNode {
         @Override
         public double execute(double[] values, boolean hasNa, boolean naRm) {
             if (traceEnabled()) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PCRE.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PCRE.java
index e0bc0f550f6c5b2b604c40291d5118321793cbe8..6a5b359dce6ea904afd999f9d81521bbb4eec5f8 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PCRE.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PCRE.java
@@ -23,26 +23,27 @@
 package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 
 public class JNI_PCRE implements PCRERFFI {
-    private static class JNI_MaketablesNode extends MaketablesNode {
+    private static class JNI_MaketablesNode extends Node implements MaketablesNode {
         @Override
         public long execute() {
             return nativeMaketables();
         }
     }
 
-    private static class JNI_CompileNode extends CompileNode {
+    private static class JNI_CompileNode extends Node implements CompileNode {
         @Override
         public Result execute(String pattern, int options, long tables) {
             return nativeCompile(pattern, options, tables);
         }
     }
 
-    private static class JNI_GetCaptureCountNode extends GetCaptureCountNode {
+    private static class JNI_GetCaptureCountNode extends Node implements GetCaptureCountNode {
         @Override
         public int execute(long code, long extra) {
             int res = nativeGetCaptureCount(code, extra);
@@ -54,7 +55,7 @@ public class JNI_PCRE implements PCRERFFI {
         }
     }
 
-    private static class JNI_GetCaptureNamesNode extends GetCaptureNamesNode {
+    private static class JNI_GetCaptureNamesNode extends Node implements GetCaptureNamesNode {
         @Override
         public String[] execute(long code, long extra, int captureCount) {
             String[] ret = new String[captureCount];
@@ -67,14 +68,14 @@ public class JNI_PCRE implements PCRERFFI {
         }
     }
 
-    private static class JNI_StudyNode extends StudyNode {
+    private static class JNI_StudyNode extends Node implements StudyNode {
         @Override
         public Result execute(long code, int options) {
             throw RInternalError.unimplemented("pcre_study");
         }
     }
 
-    private static class JNI_ExecNode extends ExecNode {
+    private static class JNI_ExecNode extends Node implements ExecNode {
         @Override
         public int execute(long code, long extra, String subject, int offset, int options, int[] ovector) {
             return nativeExec(code, extra, subject, offset, options, ovector, ovector.length);
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RAppl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RAppl.java
index ac83274ac58ee01a5aa1b5297200aeb1e05fc0bb..a641c2d3411846ea7c71444b7a4e4f174cd0bc96 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RAppl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RAppl.java
@@ -23,10 +23,11 @@
 package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 
 public class JNI_RAppl implements RApplRFFI {
-    private static class JNI_Dqrdc2Node extends Dqrdc2Node {
+    private static class JNI_Dqrdc2Node extends Node implements Dqrdc2Node {
         @Override
         @TruffleBoundary
         public void execute(double[] x, int ldx, int n, int p, double tol, int[] rank, double[] qraux, int[] pivot, double[] work) {
@@ -34,7 +35,7 @@ public class JNI_RAppl implements RApplRFFI {
         }
     }
 
-    private static class JNI_DqrcfNode extends DqrcfNode {
+    private static class JNI_DqrcfNode extends Node implements DqrcfNode {
 
         @Override
         @TruffleBoundary
@@ -43,7 +44,7 @@ public class JNI_RAppl implements RApplRFFI {
         }
     }
 
-    private static class JNI_DqrlsNode extends DqrlsNode {
+    private static class JNI_DqrlsNode extends Node implements DqrlsNode {
         @Override
         @TruffleBoundary
         public void execute(double[] x, int n, int p, double[] y, int ny, double tol, double[] b, double[] rsd, double[] qty, int[] k, int[] jpvt, double[] qraux, double[] work) {
@@ -51,7 +52,7 @@ public class JNI_RAppl implements RApplRFFI {
         }
     }
 
-    private static class JNI_DqrqtyNode extends DqrqtyNode {
+    private static class JNI_DqrqtyNode extends Node implements DqrqtyNode {
         @Override
         @TruffleBoundary
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] qty) {
@@ -59,7 +60,7 @@ public class JNI_RAppl implements RApplRFFI {
         }
     }
 
-    private static class JNI_DqrqyNode extends DqrqyNode {
+    private static class JNI_DqrqyNode extends Node implements DqrqyNode {
         @Override
         @TruffleBoundary
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] qy) {
@@ -67,7 +68,7 @@ public class JNI_RAppl implements RApplRFFI {
         }
     }
 
-    private static class JNI_DqrrsdNode extends DqrrsdNode {
+    private static class JNI_DqrrsdNode extends Node implements DqrrsdNode {
         @Override
         @TruffleBoundary
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] rsd) {
@@ -75,7 +76,7 @@ public class JNI_RAppl implements RApplRFFI {
         }
     }
 
-    private static class JNI_DqrxbNode extends DqrxbNode {
+    private static class JNI_DqrxbNode extends Node implements DqrxbNode {
         @Override
         @TruffleBoundary
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] xb) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RFFIFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RFFIFactory.java
index b107f685253c61ba77d02d59979e0f33f827016d..65b89a043a15c476fe69f3a099a52da511e3cf7e 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RFFIFactory.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RFFIFactory.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.ffi.impl.jni;
 
-import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.r.ffi.impl.common.Generic_Tools;
 import com.oracle.truffle.r.ffi.impl.common.LibPaths;
@@ -48,10 +48,7 @@ import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 /**
  * JNI-based factory. The majority of the FFI instances are instantiated on demand.
  */
-public class JNI_RFFIFactory extends RFFIFactory implements RFFI {
-
-    public JNI_RFFIFactory() {
-    }
+public class JNI_RFFIFactory extends RFFIFactory {
 
     private static class ContextStateImpl implements RContext.ContextState {
         @Override
@@ -86,145 +83,151 @@ public class JNI_RFFIFactory extends RFFIFactory implements RFFI {
 
     @Override
     protected RFFI createRFFI() {
-        return this;
-    }
-
-    @CompilationFinal private BaseRFFI baseRFFI;
-
-    @Override
-    public BaseRFFI getBaseRFFI() {
-        if (baseRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            baseRFFI = new JNI_Base();
-        }
-        return baseRFFI;
-    }
+        CompilerAsserts.neverPartOfCompilation();
+        return new RFFI() {
+
+            @CompilationFinal private BaseRFFI baseRFFI;
+
+            @Override
+            public BaseRFFI getBaseRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (baseRFFI == null) {
+                    baseRFFI = new JNI_Base();
+                }
+                return baseRFFI;
+            }
 
-    @CompilationFinal private LapackRFFI lapackRFFI;
+            @CompilationFinal private LapackRFFI lapackRFFI;
 
-    @Override
-    public LapackRFFI getLapackRFFI() {
-        if (lapackRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            lapackRFFI = new JNI_Lapack();
-        }
-        return lapackRFFI;
-    }
+            @Override
+            public LapackRFFI getLapackRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (lapackRFFI == null) {
+                    lapackRFFI = new JNI_Lapack();
+                }
+                return lapackRFFI;
+            }
 
-    @CompilationFinal private RApplRFFI rApplRFFI;
+            @CompilationFinal private RApplRFFI rApplRFFI;
 
-    @Override
-    public RApplRFFI getRApplRFFI() {
-        if (rApplRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            rApplRFFI = new JNI_RAppl();
-        }
-        return rApplRFFI;
-    }
+            @Override
+            public RApplRFFI getRApplRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (rApplRFFI == null) {
+                    rApplRFFI = new JNI_RAppl();
+                }
+                return rApplRFFI;
+            }
 
-    @CompilationFinal private StatsRFFI statsRFFI;
+            @CompilationFinal private StatsRFFI statsRFFI;
 
-    @Override
-    public StatsRFFI getStatsRFFI() {
-        if (statsRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            statsRFFI = new JNI_Stats();
-        }
-        return statsRFFI;
-    }
+            @Override
+            public StatsRFFI getStatsRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (statsRFFI == null) {
+                    statsRFFI = new JNI_Stats();
+                }
+                return statsRFFI;
+            }
 
-    @CompilationFinal private ToolsRFFI toolsRFFI;
+            @CompilationFinal private ToolsRFFI toolsRFFI;
 
-    @Override
-    public ToolsRFFI getToolsRFFI() {
-        if (toolsRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            toolsRFFI = new Generic_Tools();
-        }
-        return toolsRFFI;
-    }
+            @Override
+            public ToolsRFFI getToolsRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (toolsRFFI == null) {
+                    toolsRFFI = new Generic_Tools();
+                }
+                return toolsRFFI;
+            }
 
-    @CompilationFinal private UserRngRFFI userRngRFFI;
+            @CompilationFinal private UserRngRFFI userRngRFFI;
 
-    @Override
-    public UserRngRFFI getUserRngRFFI() {
-        if (userRngRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            userRngRFFI = new JNI_UserRng();
-        }
-        return userRngRFFI;
-    }
+            @Override
+            public UserRngRFFI getUserRngRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (userRngRFFI == null) {
+                    userRngRFFI = new JNI_UserRng();
+                }
+                return userRngRFFI;
+            }
 
-    @CompilationFinal private CRFFI cRFFI;
+            @CompilationFinal private CRFFI cRFFI;
 
-    @Override
-    public CRFFI getCRFFI() {
-        if (cRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            cRFFI = new JNI_C();
-        }
-        return cRFFI;
-    }
+            @Override
+            public CRFFI getCRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (cRFFI == null) {
+                    cRFFI = new JNI_C();
+                }
+                return cRFFI;
+            }
 
-    @CompilationFinal private CallRFFI callRFFI;
+            @CompilationFinal private CallRFFI callRFFI;
 
-    @Override
-    public CallRFFI getCallRFFI() {
-        if (callRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            callRFFI = new JNI_Call();
-        }
-        return callRFFI;
-    }
+            @Override
+            public CallRFFI getCallRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (callRFFI == null) {
+                    callRFFI = new JNI_Call();
+                }
+                return callRFFI;
+            }
 
-    @CompilationFinal private ZipRFFI zipRFFI;
+            @CompilationFinal private ZipRFFI zipRFFI;
 
-    @Override
-    public ZipRFFI getZipRFFI() {
-        if (zipRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            zipRFFI = new JNI_Zip();
-        }
-        return zipRFFI;
-    }
+            @Override
+            public ZipRFFI getZipRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (zipRFFI == null) {
+                    zipRFFI = new JNI_Zip();
+                }
+                return zipRFFI;
+            }
 
-    @CompilationFinal private PCRERFFI pcreRFFI;
+            @CompilationFinal private PCRERFFI pcreRFFI;
 
-    @Override
-    public PCRERFFI getPCRERFFI() {
-        if (pcreRFFI == null) {
-            pcreRFFI = new JNI_PCRE();
-        }
-        return pcreRFFI;
-    }
+            @Override
+            public PCRERFFI getPCRERFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (pcreRFFI == null) {
+                    pcreRFFI = new JNI_PCRE();
+                }
+                return pcreRFFI;
+            }
 
-    private DLLRFFI dllRFFI;
+            private DLLRFFI dllRFFI;
 
-    @Override
-    public DLLRFFI getDLLRFFI() {
-        if (dllRFFI == null) {
-            dllRFFI = new JNI_DLL();
-        }
-        return dllRFFI;
-    }
+            @Override
+            public DLLRFFI getDLLRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (dllRFFI == null) {
+                    dllRFFI = new JNI_DLL();
+                }
+                return dllRFFI;
+            }
 
-    private REmbedRFFI rEmbedRFFI;
+            private REmbedRFFI rEmbedRFFI;
 
-    @Override
-    public REmbedRFFI getREmbedRFFI() {
-        if (rEmbedRFFI == null) {
-            rEmbedRFFI = new JNI_REmbed();
-        }
-        return rEmbedRFFI;
-    }
+            @Override
+            public REmbedRFFI getREmbedRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (rEmbedRFFI == null) {
+                    rEmbedRFFI = new JNI_REmbed();
+                }
+                return rEmbedRFFI;
+            }
 
-    private MiscRFFI miscRFFI;
+            private MiscRFFI miscRFFI;
 
-    @Override
-    public MiscRFFI getMiscRFFI() {
-        if (miscRFFI == null) {
-            miscRFFI = new JNI_Misc();
-        }
-        return miscRFFI;
+            @Override
+            public MiscRFFI getMiscRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (miscRFFI == null) {
+                    miscRFFI = new JNI_Misc();
+                }
+                return miscRFFI;
+            }
+        };
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Stats.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Stats.java
index 2f030fed7fa63f0c9f16754f169517728c124e05..b9c0ee7e1d74ff714197b68791a2b4f1a65dcb21 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Stats.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Stats.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLL.DLLInfo;
@@ -33,9 +34,9 @@ import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
 
 public class JNI_Stats implements StatsRFFI {
 
-    public static class JNI_WorkNode extends WorkNode {
+    private static class JNI_WorkNode extends Node implements WorkNode {
         private static final String FFT_WORK = "fft_work";
-        @Child private DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child private DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getDLLRFFI().createDLSymNode();
         private SymbolHandle fftWorkAddress;
 
         @Override
@@ -48,9 +49,9 @@ public class JNI_Stats implements StatsRFFI {
         }
     }
 
-    public static class JNI_FactorNode extends FactorNode {
+    private static class JNI_FactorNode extends Node implements FactorNode {
         private static final String FFT_FACTOR = "fft_factor";
-        @Child private DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child private DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getDLLRFFI().createDLSymNode();
         private SymbolHandle fftFactorAddress;
 
         @Override
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UserRng.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UserRng.java
index 356f7cebbbdf10922ae518d0403678bc9d929383..b3ab9dd00af9ffbf479e8b002f09abd18771746d 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UserRng.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UserRng.java
@@ -22,41 +22,62 @@
  */
 package com.oracle.truffle.r.ffi.impl.jni;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
 import com.oracle.truffle.r.runtime.rng.user.UserRNG.Function;
 
 public class JNI_UserRng implements UserRngRFFI {
-    private static class JNI_UserRngRFFINode extends UserRngRFFINode {
+
+    private static final class JNI_InitNode extends Node implements InitNode {
+
         @Override
-        @TruffleBoundary
-        public void init(int seed) {
+        public void execute(int seed) {
             nativeInit(Function.Init.getSymbolHandle().asAddress(), seed);
-
         }
+    }
+
+    private static final class JNI_RandNode extends Node implements RandNode {
 
         @Override
-        @TruffleBoundary
-        public double rand() {
+        public double execute() {
             return nativeRand(Function.Rand.getSymbolHandle().asAddress());
         }
+    }
+
+    private static final class JNI_NSeedNode extends Node implements NSeedNode {
 
         @Override
-        @TruffleBoundary
-        public int nSeed() {
+        public int execute() {
             return nativeNSeed(Function.NSeed.getSymbolHandle().asAddress());
         }
+    }
+
+    private static final class JNI_SeedsNode extends Node implements SeedsNode {
 
         @Override
-        @TruffleBoundary
-        public void seeds(int[] n) {
+        public void execute(int[] n) {
             nativeSeeds(Function.Seedloc.getSymbolHandle().asAddress(), n);
         }
     }
 
     @Override
-    public UserRngRFFINode createUserRngRFFINode() {
-        return new JNI_UserRngRFFINode();
+    public InitNode createInitNode() {
+        return new JNI_InitNode();
+    }
+
+    @Override
+    public RandNode createRandNode() {
+        return new JNI_RandNode();
+    }
+
+    @Override
+    public NSeedNode createNSeedNode() {
+        return new JNI_NSeedNode();
+    }
+
+    @Override
+    public SeedsNode createSeedsNode() {
+        return new JNI_SeedsNode();
     }
 
     private static native void nativeInit(long address, int seed);
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Zip.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Zip.java
index 6afb225e5ca6ab6e53695f2e0b7fa35638631cb3..28de1e5ac7129b1064285bdb3dc551b32add060c 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Zip.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Zip.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
 /**
@@ -30,7 +31,7 @@ import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
  */
 public class JNI_Zip implements ZipRFFI {
 
-    public static class JNI_CompressNode extends ZipRFFI.CompressNode {
+    private static class JNI_CompressNode extends Node implements ZipRFFI.CompressNode {
         @Override
         @TruffleBoundary
         public int execute(byte[] dest, byte[] source) {
@@ -39,7 +40,7 @@ public class JNI_Zip implements ZipRFFI {
         }
     }
 
-    public static class JNI_UncompressNode extends ZipRFFI.UncompressNode {
+    private static class JNI_UncompressNode extends Node implements ZipRFFI.UncompressNode {
         @Override
         @TruffleBoundary
         public int execute(byte[] dest, byte[] source) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVMFunction.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVMFunction.java
index 77a916458515a73a453dc41670da79467aeb7696..64a348261062e442a2af2d9eb78b49d6c16596f5 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVMFunction.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVMFunction.java
@@ -25,6 +25,9 @@ package com.oracle.truffle.r.ffi.impl.llvm;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.common.NativeFunction;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 
 /**
  * Enumerates all the C functions that are internal to the implementation and called via Truffle
@@ -33,7 +36,7 @@ import com.oracle.truffle.api.nodes.Node;
  * just have LLVM handle the underlying native call. The wrapper functions names are all of the form
  * {@code call_xxx_function}, where {@code xxx} is the subsystem.
  */
-public enum LLVMFunction {
+public enum LLVMFunction implements NativeFunction {
     // base
     getpid(0, "call_base_"),
     getwd(2, "call_base_"),
@@ -72,18 +75,29 @@ public enum LLVMFunction {
     dgesv(7, "call_lapack_"),
     dlange(6, "call_lapack_"),
     dgecon(8, "call_lapack_"),
-    dsyevr(20, "call_lapack_");
+    dsyevr(20, "call_lapack_"),
+    // misc
+    exactSumFunc(4, "");
 
-    private final int argCount;
-    final String callName;
+    private final int argumentCount;
+    private final String callName;
 
     LLVMFunction(int argCount, String prefix) {
-        this.argCount = argCount;
-        callName = prefix + name();
+        this.argumentCount = argCount;
+        this.callName = prefix + name();
     }
 
     Node createMessage() {
         CompilerAsserts.neverPartOfCompilation();
-        return Message.createExecute(argCount).createNode();
+        return Message.createExecute(argumentCount).createNode();
+    }
+
+    SymbolHandle createSymbol() {
+        return DLL.findSymbol(callName, null);
+    }
+
+    @Override
+    public int getArgumentCount() {
+        return argumentCount;
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Base.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Base.java
index c111372360bfe01d73d9a439191bb05afb327e31..f5edb934b7590372d586ad7d3c34880daf584761 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Base.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Base.java
@@ -25,147 +25,116 @@ package com.oracle.truffle.r.ffi.impl.llvm;
 import java.io.IOException;
 import java.util.ArrayList;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.interop.NativeCharArray;
 import com.oracle.truffle.r.ffi.impl.interop.base.GlobResult;
 import com.oracle.truffle.r.ffi.impl.interop.base.ReadlinkResult;
 import com.oracle.truffle.r.ffi.impl.interop.base.StrtolResult;
 import com.oracle.truffle.r.ffi.impl.interop.base.UnameResult;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
-import com.oracle.truffle.r.runtime.ffi.DLL;
-import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 
 public class TruffleLLVM_Base implements BaseRFFI {
-    public static class TruffleLLVM_GetpidNode extends GetpidNode {
-        @Child private Node message = LLVMFunction.getpid.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_GetpidNode extends TruffleLLVM_DownCallNode implements GetpidNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.getpid;
+        }
 
         @Override
         public int execute() {
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.getpid.callName, null);
-                }
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject());
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call();
         }
     }
 
-    public static class TruffleLLVM_GetwdNode extends GetwdNode {
-        @Child private Node message = LLVMFunction.getwd.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_GetwdNode extends TruffleLLVM_DownCallNode implements GetwdNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.getwd;
+        }
 
         @Override
         public String execute() {
             byte[] buf = new byte[4096];
             NativeCharArray nativeBuf = new NativeCharArray(buf);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.getwd.callName, null);
-                }
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativeBuf, buf.length);
-                if (result == 0) {
-                    return null;
-                } else {
-                    byte[] mbuf = nativeBuf.getValue();
-                    int i = 0;
-                    while (mbuf[i] != 0 && i < mbuf.length) {
-                        i++;
-                    }
-                    return new String(mbuf, 0, i);
+            int result = (int) call(nativeBuf, buf.length);
+            if (result == 0) {
+                return null;
+            } else {
+                byte[] mbuf = nativeBuf.getValue();
+                int i = 0;
+                while (mbuf[i] != 0 && i < mbuf.length) {
+                    i++;
                 }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+                return new String(mbuf, 0, i);
             }
         }
     }
 
-    public static class TruffleLLVM_SetwdNode extends SetwdNode {
-        @Child private Node message = LLVMFunction.setwd.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_SetwdNode extends TruffleLLVM_DownCallNode implements SetwdNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.setwd;
+        }
 
         @Override
         public int execute(String dir) {
             NativeCharArray nativeBuf = new NativeCharArray(dir.getBytes());
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.setwd.callName, null);
-                }
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativeBuf);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(nativeBuf);
         }
     }
 
-    public static class TruffleLLVM_MkdirNode extends MkdirNode {
-        @Child private Node message = LLVMFunction.mkdir.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_MkdirNode extends TruffleLLVM_DownCallNode implements MkdirNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.mkdir;
+        }
 
         @Override
         public void execute(String dir, int mode) throws IOException {
             NativeCharArray nativeBuf = new NativeCharArray(dir.getBytes());
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.mkdir.callName, null);
-                }
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativeBuf, mode);
-                if (result != 0) {
-                    throw new IOException("mkdir " + dir + " failed");
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+            int result = (int) call(nativeBuf, mode);
+            if (result != 0) {
+                throw new IOException("mkdir " + dir + " failed");
             }
         }
     }
 
-    public static class TruffleLLVM_ReadlinkNode extends ReadlinkNode {
+    private static class TruffleLLVM_ReadlinkNode extends TruffleLLVM_DownCallNode implements ReadlinkNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.readlink;
+        }
+
         private static final int EINVAL = 22;
-        @Child private Node message = LLVMFunction.readlink.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
 
         @Override
         public String execute(String path) throws IOException {
             NativeCharArray nativePath = new NativeCharArray(path.getBytes());
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.readlink.callName, null);
-                }
-                ReadlinkResult callback = new ReadlinkResult();
-                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), callback, nativePath);
-                String link = callback.getLink();
-                if (link == null) {
-                    if (callback.getErrno() == EINVAL) {
-                        return path;
-                    } else {
-                        // some other error
-                        throw new IOException("readlink failed: " + callback.getErrno());
-                    }
+            ReadlinkResult callback = new ReadlinkResult();
+            call(callback, nativePath);
+            String link = callback.getLink();
+            if (link == null) {
+                if (callback.getErrno() == EINVAL) {
+                    return path;
+                } else {
+                    // some other error
+                    throw new IOException("readlink failed: " + callback.getErrno());
                 }
-                return link;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
             }
+            return link;
         }
     }
 
-    public static class TruffleLLVM_MkdtempNode extends MkdtempNode {
-        @Child private Node message = LLVMFunction.mkdtemp.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_MkdtempNode extends TruffleLLVM_DownCallNode implements MkdtempNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.mkdtemp;
+        }
 
         @Override
         public String execute(String template) {
@@ -178,108 +147,78 @@ public class TruffleLLVM_Base implements BaseRFFI {
             System.arraycopy(bytes, 0, ztbytes, 0, bytes.length);
             ztbytes[bytes.length] = 0;
             NativeCharArray nativeZtbytes = new NativeCharArray(ztbytes);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.mkdtemp.callName, null);
-                }
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativeZtbytes);
-                if (result == 0) {
-                    return null;
-                } else {
-                    byte[] mztBytes = nativeZtbytes.getValue();
-                    String path = new String(mztBytes, 0, bytes.length);
-                    return path;
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+            int result = (int) call(nativeZtbytes);
+            if (result == 0) {
+                return null;
+            } else {
+                byte[] mztBytes = nativeZtbytes.getValue();
+                return new String(mztBytes, 0, bytes.length);
             }
         }
     }
 
-    public static class TruffleLLVM_ChmodNode extends ChmodNode {
-        @Child private Node message = LLVMFunction.chmod.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_ChmodNode extends TruffleLLVM_DownCallNode implements ChmodNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.chmod;
+        }
 
         @Override
         public int execute(String path, int mode) {
             NativeCharArray nativePath = new NativeCharArray(path.getBytes());
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.chmod.callName, null);
-                }
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativePath, mode);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(nativePath, mode);
         }
     }
 
-    public static class TruffleLLVM_StrolNode extends StrolNode {
-        @Child private Node message = LLVMFunction.strtol.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_StrolNode extends TruffleLLVM_DownCallNode implements StrolNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.strtol;
+        }
 
         @Override
         public long execute(String s, int base) throws IllegalArgumentException {
             NativeCharArray nativeString = new NativeCharArray(s.getBytes());
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.strtol.callName, null);
-                }
-                StrtolResult callback = new StrtolResult();
-                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), callback, nativeString, base);
-                if (callback.getErrno() != 0) {
-                    throw new IllegalArgumentException("strtol failure");
-                } else {
-                    return callback.getResult();
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+            StrtolResult callback = new StrtolResult();
+            call(callback, nativeString, base);
+            if (callback.getErrno() != 0) {
+                throw new IllegalArgumentException("strtol failure");
+            } else {
+                return callback.getResult();
             }
         }
     }
 
-    public static class TruffleLLVM_UnameNode extends UnameNode {
-        @Child private Node message = LLVMFunction.uname.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_UnameNode extends TruffleLLVM_DownCallNode implements UnameNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.uname;
+        }
 
         @Override
         public UtsName execute() {
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.uname.callName, null);
-                }
-                UnameResult baseUnameResultCallback = new UnameResult();
-                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), baseUnameResultCallback);
-                return baseUnameResultCallback;
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
+            UnameResult baseUnameResultCallback = new UnameResult();
+            call(baseUnameResultCallback);
+            return baseUnameResultCallback;
         }
     }
 
-    public static class TruffleLLVM_GlobNode extends GlobNode {
-        @Child private Node message = LLVMFunction.glob.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_GlobNode extends TruffleLLVM_DownCallNode implements GlobNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.glob;
+        }
 
         @Override
         public ArrayList<String> glob(String pattern) {
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.glob.callName, null);
-                }
-                NativeCharArray nativePattern = new NativeCharArray(pattern.getBytes());
-                GlobResult baseGlobResultCallback = new GlobResult();
-                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), baseGlobResultCallback, nativePattern);
-                return baseGlobResultCallback.getPaths();
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
+            NativeCharArray nativePattern = new NativeCharArray(pattern.getBytes());
+            GlobResult baseGlobResultCallback = new GlobResult();
+            call(baseGlobResultCallback, nativePattern);
+            return baseGlobResultCallback.getPaths();
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java
index ae1c62d397faac2f1a85ffa2a38dd958fca95e7a..b6ee57ebda72247b1c49aa79ea353eadfa6abea9 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java
@@ -34,7 +34,7 @@ import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
 class TruffleLLVM_C implements CRFFI {
-    private static class TruffleLLVM_InvokeCNode extends InvokeCNode {
+    private static class TruffleLLVM_InvokeCNode extends Node implements InvokeCNode {
 
         @Child private Node messageNode;
         private int numArgs;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java
index daac71f497204fc3e14800658059dfe7cccdd2e6..42d3e293c6d2bfacf3ca147241961a59faf19293 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java
@@ -70,7 +70,7 @@ final class TruffleLLVM_Call implements CallRFFI {
         @Override
         public ContextState initialize(RContext contextA) {
             this.context = contextA;
-            RFFIFactory.getRFFI().getCallRFFI();
+            RFFIFactory.getCallRFFI();
             if (!initDone) {
                 initVariables(context);
                 initCallbacks(context, upCallsRFFI);
@@ -185,7 +185,7 @@ final class TruffleLLVM_Call implements CallRFFI {
     }
 
     @ImportStatic({Message.class})
-    public abstract static class TruffleLLVM_InvokeCallNode extends InvokeCallNode {
+    abstract static class TruffleLLVM_InvokeCallNode extends Node implements InvokeCallNode {
 
         @Child private UpCallUnwrap unwrap;
         private final boolean isVoid;
@@ -248,7 +248,7 @@ final class TruffleLLVM_Call implements CallRFFI {
         }
     }
 
-    private static class TruffleLLVM_InvokeVoidCallNode extends InvokeVoidCallNode {
+    private static class TruffleLLVM_InvokeVoidCallNode extends Node implements InvokeVoidCallNode {
         @Child private TruffleLLVM_InvokeCallNode invokeCallNode = TruffleLLVM_InvokeCallNodeGen.create(true);
 
         @Override
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java
index 93b1a6ebbbd54039583ee499e524af2c1ef7f393..6d8d4240178cf9a391ebb7c61476c636e3549cfc 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java
@@ -38,6 +38,7 @@ import java.util.zip.ZipInputStream;
 import javax.naming.NameAlreadyBoundException;
 
 import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -162,7 +163,7 @@ public class TruffleLLVM_DLL implements DLLRFFI {
         }
     }
 
-    private static class TruffleLLVM_DLOpenNode extends DLOpenNode {
+    private static class TruffleLLVM_DLOpenNode extends Node implements DLOpenNode {
         @Child private TruffleLLVM_NativeDLL.TruffleLLVM_NativeDLOpen nativeDLLOpenNode;
 
         /**
@@ -202,7 +203,7 @@ public class TruffleLLVM_DLL implements DLLRFFI {
         }
     }
 
-    private static class TruffleLLVM_DLSymNode extends DLSymNode {
+    private static class TruffleLLVM_DLSymNode extends Node implements DLSymNode {
         @Override
         public SymbolHandle execute(Object handle, String symbol) throws UnsatisfiedLinkError {
             assert handle instanceof LLVM_Handle;
@@ -214,7 +215,7 @@ public class TruffleLLVM_DLL implements DLLRFFI {
         }
     }
 
-    private static class TruffleLLVM_DLCloseNode extends DLCloseNode {
+    private static class TruffleLLVM_DLCloseNode extends Node implements DLCloseNode {
         @Override
         public int execute(Object handle) {
             assert handle instanceof LLVM_Handle;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc3b81c00cb05b966214040a9ee533152cb87969
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNode.java
@@ -0,0 +1,71 @@
+/*
+ * 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.ffi.impl.llvm;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.r.ffi.impl.common.DownCallNode;
+import com.oracle.truffle.r.ffi.impl.interop.NativeDoubleArray;
+import com.oracle.truffle.r.ffi.impl.interop.NativeIntegerArray;
+import com.oracle.truffle.r.ffi.impl.interop.NativeNACheck;
+
+public abstract class TruffleLLVM_DownCallNode extends DownCallNode<LLVMFunction> {
+
+    @CompilationFinal private TruffleObject target;
+
+    @Override
+    protected final TruffleObject getTarget() {
+        if (target == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            target = getFunction().createSymbol().asTruffleObject();
+        }
+        return target;
+    }
+
+    @Override
+    @ExplodeLoop
+    protected void wrapArguments(Object[] args) {
+        for (int i = 0; i < args.length; i++) {
+            Object obj = args[i];
+            if (obj instanceof double[]) {
+                args[i] = new NativeDoubleArray((double[]) obj);
+            } else if (obj instanceof int[]) {
+                args[i] = new NativeIntegerArray((int[]) obj);
+            } else if (obj == null) {
+                args[i] = 0;
+            }
+        }
+    }
+
+    @Override
+    @ExplodeLoop
+    protected void finishArguments(Object[] args) {
+        for (Object obj : args) {
+            if (obj instanceof NativeNACheck<?>) {
+                ((NativeNACheck<?>) obj).close();
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Lapack.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Lapack.java
index bb37da5ddb5a1226022cce5129b39349ea01e459..0cc25fa7ab99e656715ce48d29a5cf9e8fe87759 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Lapack.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Lapack.java
@@ -22,19 +22,9 @@
  */
 package com.oracle.truffle.r.ffi.impl.llvm;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.RootCallTarget;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.common.LibPaths;
-import com.oracle.truffle.r.ffi.impl.interop.NativeDoubleArray;
-import com.oracle.truffle.r.ffi.impl.interop.NativeIntegerArray;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.ffi.DLL;
-import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
 import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
 
@@ -84,342 +74,173 @@ public class TruffleLLVM_Lapack implements LapackRFFI {
         return rootNode.getCallTarget();
     }
 
-    private static class TruffleLLVM_IlaverNode extends IlaverNode {
-        @Child private Node message = LLVMFunction.ilaver.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_IlaverNode extends TruffleLLVM_DownCallNode implements IlaverNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.ilaver;
+        }
 
         @Override
         public void execute(int[] version) {
-            NativeIntegerArray versionN = new NativeIntegerArray(version);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.ilaver.callName, null);
-                }
-                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), versionN);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                versionN.getValue();
-            }
+            call(version);
         }
     }
 
-    private static class TruffleLLVM_DgeevNode extends DgeevNode {
-        @Child private Node message = LLVMFunction.dgeev.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DgeevNode extends TruffleLLVM_DownCallNode implements DgeevNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dgeev;
+        }
 
         @Override
         public int execute(char jobVL, char jobVR, int n, double[] a, int lda, double[] wr, double[] wi, double[] vl, int ldvl, double[] vr, int ldvr, double[] work, int lwork) {
-            // vl, vr may be null
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            NativeDoubleArray wrN = new NativeDoubleArray(wr);
-            NativeDoubleArray wiN = new NativeDoubleArray(wi);
-            Object vlN = vl == null ? 0 : new NativeDoubleArray(vl);
-            Object vrN = vr == null ? 0 : new NativeDoubleArray(vr);
-            NativeDoubleArray workN = new NativeDoubleArray(work);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dgeev.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), jobVL, jobVR, n, aN, lda,
-                                wrN, wiN, vlN, ldvl, vrN, ldvr, workN, lwork);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                aN.getValue();
-                wrN.getValue();
-                wiN.getValue();
-                if (vl != null) {
-                    ((NativeDoubleArray) vlN).getValue();
-                }
-                if (vr != null) {
-                    ((NativeDoubleArray) vrN).getValue();
-                }
-                workN.getValue();
-            }
+            return (int) call(jobVL, jobVR, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork);
         }
     }
 
-    private static class TruffleLLVM_Dgeqp3Node extends Dgeqp3Node {
-        @Child private Node message = LLVMFunction.dgeqp3.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_Dgeqp3Node extends TruffleLLVM_DownCallNode implements Dgeqp3Node {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dgeqp3;
+        }
 
         @Override
         public int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            NativeIntegerArray jpvtN = new NativeIntegerArray(jpvt);
-            NativeDoubleArray tauN = new NativeDoubleArray(tau);
-            NativeDoubleArray workN = new NativeDoubleArray(work);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dgeqp3.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), m, n, aN, lda, jpvtN,
-                                tauN, workN, lwork);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                aN.getValue();
-                jpvtN.getValue();
-                tauN.getValue();
-                workN.getValue();
-            }
+            return (int) call(m, n, a, lda, jpvt, tau, work, lwork);
         }
     }
 
-    private static class TruffleLLVM_DormqrNode extends DormqrNode {
-        @Child private Node message = LLVMFunction.dormq.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DormqrNode extends TruffleLLVM_DownCallNode implements DormqrNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dormq;
+        }
 
         @Override
         public int execute(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            NativeDoubleArray tauN = new NativeDoubleArray(tau);
-            NativeDoubleArray cN = new NativeDoubleArray(c);
-            NativeDoubleArray workN = new NativeDoubleArray(work);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dormq.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), side, trans, m, n, k, aN, lda,
-                                tauN, cN, ldc, workN, lwork);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                cN.getValue();
-                workN.getValue();
-            }
+            return (int) call(side, trans, m, n, k, a, lda, tau, c, ldc, work, lwork);
         }
     }
 
-    private static class TruffleLLVM_DtrtrsNode extends DtrtrsNode {
-        @Child private Node message = LLVMFunction.dtrtrs.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DtrtrsNode extends TruffleLLVM_DownCallNode implements DtrtrsNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dtrtrs;
+        }
 
         @Override
         public int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            NativeDoubleArray bN = new NativeDoubleArray(b);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dtrtrs.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), uplo, trans, diag, n, nrhs, aN, lda,
-                                bN, ldb);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                bN.getValue();
-            }
+            return (int) call(uplo, trans, diag, n, nrhs, a, lda, b, ldb);
         }
     }
 
-    private static class TruffleLLVM_DgetrfNode extends DgetrfNode {
-        @Child private Node message = LLVMFunction.dgetrf.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DgetrfNode extends TruffleLLVM_DownCallNode implements DgetrfNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dgetrf;
+        }
 
         @Override
         public int execute(int m, int n, double[] a, int lda, int[] ipiv) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            NativeIntegerArray ipivN = new NativeIntegerArray(ipiv);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dgetrf.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), m, n, aN, lda, ipivN);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                aN.getValue();
-                ipivN.getValue();
-            }
+            return (int) call(m, n, a, lda, ipiv);
         }
     }
 
-    private static class TruffleLLVM_DpotrfNode extends DpotrfNode {
-        @Child private Node message = LLVMFunction.dpotrf.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DpotrfNode extends TruffleLLVM_DownCallNode implements DpotrfNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dpotrf;
+        }
 
         @Override
         public int execute(char uplo, int n, double[] a, int lda) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dpotrf.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), uplo, n, aN, lda);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                aN.getValue();
-            }
+            return (int) call(uplo, n, a, lda);
         }
     }
 
-    private static class TruffleLLVM_DpotriNode extends DpotriNode {
-        @Child private Node message = LLVMFunction.dpotri.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DpotriNode extends TruffleLLVM_DownCallNode implements DpotriNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dpotri;
+        }
 
         @Override
         public int execute(char uplo, int n, double[] a, int lda) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dpotri.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), uplo, n, aN, lda);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                aN.getValue();
-            }
+            return (int) call(uplo, n, a, lda);
         }
     }
 
-    private static class TruffleLLVM_DpstrfNode extends DpstrfNode {
-        @Child private Node message = LLVMFunction.dpstrf.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DpstrfNode extends TruffleLLVM_DownCallNode implements DpstrfNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dpstrf;
+        }
 
         @Override
         public int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            NativeIntegerArray pivN = new NativeIntegerArray(piv);
-            NativeIntegerArray rankN = new NativeIntegerArray(rank);
-            NativeDoubleArray workN = new NativeDoubleArray(work);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dpstrf.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), uplo, n, aN, lda,
-                                pivN, rankN, tol, workN);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                aN.getValue();
-                pivN.getValue();
-                rankN.getValue();
-                workN.getValue();
-            }
+            return (int) call(uplo, n, a, lda, piv, rank, tol, work);
         }
     }
 
-    private static class TruffleLLVM_DgesvNode extends DgesvNode {
-        @Child private Node message = LLVMFunction.dgesv.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DgesvNode extends TruffleLLVM_DownCallNode implements DgesvNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dgesv;
+        }
 
         @Override
         public int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            NativeIntegerArray ipivN = new NativeIntegerArray(ipiv);
-            NativeDoubleArray bN = new NativeDoubleArray(b);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dgesv.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), n, nrhs, aN, lda, ipivN, bN, ldb);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                aN.getValue();
-                ipivN.getValue();
-                bN.getValue();
-            }
+            return (int) call(n, nrhs, a, lda, ipiv, b, ldb);
         }
     }
 
-    private static class TruffleLLVM_DlangeNode extends DlangeNode {
-        @Child private Node message = LLVMFunction.dlange.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DlangeNode extends TruffleLLVM_DownCallNode implements DlangeNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dlange;
+        }
 
         @Override
         public double execute(char norm, int m, int n, double[] a, int lda, double[] work) {
-            // work may be null
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            Object workN = work == null ? 0 : new NativeDoubleArray(work);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dlange.callName, null);
-                }
-                return (double) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), norm, m, n, aN, lda, workN);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (double) call(norm, m, n, a, lda, work);
         }
     }
 
-    private static class TruffleLLVM_DgeconNode extends DgeconNode {
-        @Child private Node message = LLVMFunction.dgecon.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DgeconNode extends TruffleLLVM_DownCallNode implements DgeconNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dgecon;
+        }
 
         @Override
         public int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            NativeDoubleArray rcondN = new NativeDoubleArray(rcond);
-            NativeDoubleArray workN = new NativeDoubleArray(work);
-            NativeIntegerArray iworkN = new NativeIntegerArray(iwork);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dgecon.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), norm, n, aN, lda, anorm, rcondN, workN, iworkN);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                rcondN.getValue();
-                workN.getValue();
-                iworkN.getValue();
-            }
+            return (int) call(norm, n, a, lda, anorm, rcond, work, iwork);
         }
     }
 
-    private static class TruffleLLVM_DsyevrNode extends DsyevrNode {
-        @Child private Node message = LLVMFunction.dsyevr.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DsyevrNode extends TruffleLLVM_DownCallNode implements DsyevrNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dsyevr;
+        }
 
         @Override
         public int execute(char jobz, char range, char uplo, int n, double[] a, int lda, double vl, double vu, int il, int iu, double abstol, int[] m, double[] w, double[] z, int ldz, int[] isuppz,
                         double[] work, int lwork, int[] iwork, int liwork) {
-            NativeDoubleArray aN = new NativeDoubleArray(a);
-            NativeIntegerArray mN = new NativeIntegerArray(m);
-            NativeDoubleArray wN = new NativeDoubleArray(w);
-            Object zN = z == null ? 0 : new NativeDoubleArray(z);
-            NativeIntegerArray isuppzN = new NativeIntegerArray(isuppz);
-            NativeDoubleArray workN = new NativeDoubleArray(work);
-            NativeIntegerArray iworkN = new NativeIntegerArray(iwork);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dsyevr.callName, null);
-                }
-                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), jobz, range, uplo, n, aN,
-                                lda, vl, vu, il, iu, abstol, mN, wN, zN, ldz,
-                                isuppzN, workN, lwork, iworkN, liwork);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            } finally {
-                aN.getValue();
-                mN.getValue();
-                wN.getValue();
-                if (z != null) {
-                    ((NativeDoubleArray) zN).getValue();
-
-                }
-                isuppzN.getValue();
-                workN.getValue();
-                iworkN.getValue();
-            }
+            return (int) call(jobz, range, uplo, n, a, lda, vl, vu, il, iu, abstol, m, w, z, ldz, isuppz, work, lwork, iwork, liwork);
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Misc.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Misc.java
index d4b5259068e1a22c855bbb261aeadb6352274620..40daead3685fb00b54a69b813f3cc2981138bd02 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Misc.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Misc.java
@@ -22,36 +22,20 @@
  */
 package com.oracle.truffle.r.ffi.impl.llvm;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.ffi.impl.interop.NativeDoubleArray;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.ffi.DLL;
-import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 
 public class TruffleLLVM_Misc implements MiscRFFI {
 
-    private static class TruffleLLVM_ExactSumNode extends ExactSumNode {
-        @CompilationFinal private SymbolHandle symbolHandle;
-        @Child private Node executeNode = Message.createExecute(4).createNode();
+    private static class TruffleLLVM_ExactSumNode extends TruffleLLVM_DownCallNode implements ExactSumNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.exactSumFunc;
+        }
 
         @Override
         public double execute(double[] values, boolean hasNa, boolean naRm) {
-            NativeDoubleArray nativeValues = new NativeDoubleArray(values);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol("exactSum", null);
-                }
-                return (double) ForeignAccess.sendExecute(executeNode, symbolHandle.asTruffleObject(), nativeValues, values.length, hasNa ? 1 : 0, naRm ? 1 : 0);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (double) call(values, values.length, hasNa ? 1 : 0, naRm ? 1 : 0);
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_PCRE.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_PCRE.java
index 62819af68bd76528acf11f727fab1235efebbff4..aad99cf04ad09b15c406f7d8297e4414c6608f9a 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_PCRE.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_PCRE.java
@@ -23,20 +23,14 @@
 package com.oracle.truffle.r.ffi.impl.llvm;
 
 import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.common.LibPaths;
 import com.oracle.truffle.r.ffi.impl.interop.NativeCharArray;
-import com.oracle.truffle.r.ffi.impl.interop.NativeIntegerArray;
 import com.oracle.truffle.r.ffi.impl.interop.pcre.CaptureNamesResult;
 import com.oracle.truffle.r.ffi.impl.interop.pcre.CompileResult;
+import com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_Utils.AsPointerNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.ffi.DLL;
-import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 
 public class TruffleLLVM_PCRE implements PCRERFFI {
@@ -47,115 +41,78 @@ public class TruffleLLVM_PCRE implements PCRERFFI {
         TruffleLLVM_NativeDLL.NativeDLOpenRootNode.create().getCallTarget().call(pcrePath, false, true);
     }
 
-    private static class TruffleLLVM_MaketablesNode extends MaketablesNode {
-        @Child private Node message = LLVMFunction.maketables.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_MaketablesNode extends TruffleLLVM_DownCallNode implements MaketablesNode {
+
+        @Child private AsPointerNode asPointer = new AsPointerNode();
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.maketables;
+        }
 
         @Override
         public long execute() {
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.maketables.callName, null);
-                }
-                TruffleObject callResult = (TruffleObject) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject());
-                long result = TruffleLLVM_Utils.getNativeAddress(callResult);
-                return result;
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
+            return asPointer.execute((TruffleObject) call());
         }
     }
 
-    private static class TruffleLLVM_GetCaptureCountNode extends GetCaptureCountNode {
-        @Child private Node message = LLVMFunction.getcapturecount.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_GetCaptureCountNode extends TruffleLLVM_DownCallNode implements GetCaptureCountNode {
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.getcapturecount;
+        }
 
         @Override
         public int execute(long code, long extra) {
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.getcapturecount.callName, null);
-                }
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), code, extra);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(code, extra);
         }
     }
 
-    public static class TruffleLLVM_GetCaptureNamesNode extends GetCaptureNamesNode {
-        @Child private Node message = LLVMFunction.getcapturenames.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_GetCaptureNamesNode extends TruffleLLVM_DownCallNode implements GetCaptureNamesNode {
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.getcapturenames;
+        }
 
         @Override
         public String[] execute(long code, long extra, int captureCount) {
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.getcapturenames.callName, null);
-                }
-                CaptureNamesResult captureNamesCallback = new CaptureNamesResult(captureCount);
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(),
-                                captureNamesCallback, code, extra);
-                if (result < 0) {
-                    CompilerDirectives.transferToInterpreter();
-                    throw RError.error(RError.NO_CALLER, RError.Message.WRONG_PCRE_INFO, result);
-                } else {
-                    return captureNamesCallback.getCaptureNames();
-                }
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
+            CaptureNamesResult captureNamesCallback = new CaptureNamesResult(captureCount);
+            int result = (int) call(captureNamesCallback, code, extra);
+            if (result < 0) {
+                CompilerDirectives.transferToInterpreter();
+                throw RError.error(RError.NO_CALLER, RError.Message.WRONG_PCRE_INFO, result);
+            } else {
+                return captureNamesCallback.getCaptureNames();
             }
         }
     }
 
-    public static class TruffleLLVM_CompileNode extends CompileNode {
-        @Child private Node message = LLVMFunction.compile.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_CompileNode extends TruffleLLVM_DownCallNode implements CompileNode {
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.compile;
+        }
 
         @Override
         public Result execute(String pattern, int options, long tables) {
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.compile.callName, null);
-                }
-                NativeCharArray pattenChars = new NativeCharArray(pattern.getBytes());
-                CompileResult data = new CompileResult();
-                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), data, pattenChars, options, tables);
-                return data.getResult();
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
+            NativeCharArray pattenChars = new NativeCharArray(pattern.getBytes());
+            CompileResult data = new CompileResult();
+            call(data, pattenChars, options, tables);
+            return data.getResult();
         }
     }
 
-    private static class TruffleLLVM_ExecNode extends ExecNode {
-        @Child private Node message = LLVMFunction.exec.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_ExecNode extends TruffleLLVM_DownCallNode implements ExecNode {
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.exec;
+        }
 
         @Override
         public int execute(long code, long extra, String subject, int offset, int options, int[] ovector) {
-            NativeIntegerArray nativeOvector = new NativeIntegerArray(ovector);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.exec.callName, null);
-                }
-                byte[] subjectBytes = subject.getBytes();
-                NativeCharArray subjectChars = new NativeCharArray(subjectBytes);
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), code, extra,
-                                subjectChars, subjectBytes.length,
-                                offset, options, nativeOvector, ovector.length);
-                return result;
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            } finally {
-                nativeOvector.getValue();
-            }
+            byte[] subjectBytes = subject.getBytes();
+            NativeCharArray subjectChars = new NativeCharArray(subjectBytes);
+            return (int) call(code, extra, subjectChars, subjectBytes.length, offset, options, ovector, ovector.length);
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RAppl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RAppl.java
index c162b65dc494ea2d51063dde4dd92ca97bddd1e3..1deb3356d337d22f3a1143dd48ef435486b00022 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RAppl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RAppl.java
@@ -22,16 +22,7 @@
  */
 package com.oracle.truffle.r.ffi.impl.llvm;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.ffi.impl.interop.NativeDoubleArray;
-import com.oracle.truffle.r.ffi.impl.interop.NativeIntegerArray;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.ffi.DLL;
-import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 
 /**
@@ -39,98 +30,42 @@ import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
  */
 public class TruffleLLVM_RAppl implements RApplRFFI {
 
-    private static class TruffleLLVM_Dqrdc2Node extends Dqrdc2Node {
-        @Child private Node message = LLVMFunction.dqrdc2.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_Dqrdc2Node extends TruffleLLVM_DownCallNode implements Dqrdc2Node {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dqrdc2;
+        }
 
         @Override
         public void execute(double[] x, int ldx, int n, int p, double tol, int[] rank, double[] qraux, int[] pivot, double[] work) {
-            NativeDoubleArray xN = new NativeDoubleArray(x);
-            NativeIntegerArray rankN = new NativeIntegerArray(rank);
-            NativeDoubleArray qrauxN = new NativeDoubleArray(qraux);
-            NativeIntegerArray pivotN = new NativeIntegerArray(pivot);
-            NativeDoubleArray workN = new NativeDoubleArray(work);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dqrdc2.callName, null);
-                }
-                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), xN, ldx, n, p, tol, rankN, qrauxN, pivotN, workN);
-                // sync up in case copied to native memory
-                xN.getValue();
-                rankN.getValue();
-                qrauxN.getValue();
-                pivotN.getValue();
-                workN.getValue();
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
+            call(x, ldx, n, p, tol, rank, qraux, pivot, work);
         }
     }
 
-    private static class TruffleLLVM_DqrcfNode extends DqrcfNode {
-        @Child private Node message = LLVMFunction.dqrcf.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DqrcfNode extends TruffleLLVM_DownCallNode implements DqrcfNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dqrcf;
+        }
 
         @Override
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] b, int[] info) {
-            NativeDoubleArray xN = new NativeDoubleArray(x);
-            NativeDoubleArray qrauxN = new NativeDoubleArray(qraux);
-            NativeDoubleArray yN = new NativeDoubleArray(y);
-            NativeDoubleArray bN = new NativeDoubleArray(b);
-            NativeIntegerArray infoN = new NativeIntegerArray(info);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dqrcf.callName, null);
-                }
-                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), xN, n, k, qrauxN, yN, ny, bN, infoN);
-                // sync up in case copied to native memory
-                xN.getValue();
-                qrauxN.getValue();
-                yN.getValue();
-                bN.getValue();
-                infoN.getValue();
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
+            call(x, n, k, qraux, y, ny, b, info);
         }
     }
 
-    private static class TruffleLLVM_DqrlsNode extends DqrlsNode {
-        @Child private Node message = LLVMFunction.dqrls.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static final class TruffleLLVM_DqrlsNode extends TruffleLLVM_DownCallNode implements DqrlsNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.dqrls;
+        }
 
         @Override
         public void execute(double[] x, int n, int p, double[] y, int ny, double tol, double[] b, double[] rsd, double[] qty, int[] k, int[] jpvt, double[] qraux, double[] work) {
-            NativeDoubleArray xN = new NativeDoubleArray(x);
-            NativeDoubleArray yN = new NativeDoubleArray(y);
-            NativeDoubleArray bN = new NativeDoubleArray(b);
-            NativeDoubleArray rsdN = new NativeDoubleArray(rsd);
-            NativeDoubleArray qtyN = new NativeDoubleArray(qty);
-            NativeIntegerArray kN = new NativeIntegerArray(k);
-            NativeIntegerArray jpvtN = new NativeIntegerArray(jpvt);
-            NativeDoubleArray qrauxN = new NativeDoubleArray(qraux);
-            NativeDoubleArray workN = new NativeDoubleArray(work);
-            try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.dqrls.callName, null);
-                }
-                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), xN, n, p, yN, ny, tol, bN, rsdN, qtyN, kN, jpvtN, qrauxN, workN);
-                // sync up in case copied to native memory
-                xN.getValue();
-                yN.getValue();
-                bN.getValue();
-                rsdN.getValue();
-                qtyN.getValue();
-                kN.getValue();
-                jpvtN.getValue();
-                qrauxN.getValue();
-                workN.getValue();
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
+            call(x, n, p, y, ny, tol, b, rsd, qty, k, jpvt, qraux, work);
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIFactory.java
index d36306b51ec5d9c316d81d9d7397bba098c50025..bf6b5230a57161cdddbd2b469f605ff2e8c25877 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIFactory.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIFactory.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.ffi.impl.llvm;
 
-import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.r.runtime.context.RContext.ContextState;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
@@ -41,7 +41,7 @@ import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
 import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
 import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
-public class TruffleLLVM_RFFIFactory extends RFFIFactory implements RFFI {
+public class TruffleLLVM_RFFIFactory extends RFFIFactory {
 
     @Override
     public ContextState newContextState() {
@@ -50,141 +50,151 @@ public class TruffleLLVM_RFFIFactory extends RFFIFactory implements RFFI {
 
     @Override
     protected RFFI createRFFI() {
-        return this;
-    }
-
-    @CompilationFinal private BaseRFFI baseRFFI;
-
-    @Override
-    public BaseRFFI getBaseRFFI() {
-        if (baseRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            baseRFFI = new TruffleLLVM_Base();
-        }
-        return baseRFFI;
-    }
-
-    @CompilationFinal private CRFFI cRFFI;
-
-    @Override
-    public CRFFI getCRFFI() {
-        if (cRFFI == null) {
-            cRFFI = new TruffleLLVM_C();
-        }
-        return cRFFI;
-    }
-
-    @CompilationFinal private DLLRFFI dllRFFI;
-
-    @Override
-    public DLLRFFI getDLLRFFI() {
-        if (dllRFFI == null) {
-            dllRFFI = new TruffleLLVM_DLL();
-        }
-        return dllRFFI;
-    }
-
-    @CompilationFinal private UserRngRFFI truffleUserRngRFFI;
-
-    @Override
-    public UserRngRFFI getUserRngRFFI() {
-        if (truffleUserRngRFFI == null) {
-            truffleUserRngRFFI = new TruffleLLVM_UserRng();
-        }
-        return truffleUserRngRFFI;
-    }
-
-    @CompilationFinal private CallRFFI truffleCallRFFI;
-
-    @Override
-    public CallRFFI getCallRFFI() {
-        if (truffleCallRFFI == null) {
-            truffleCallRFFI = new TruffleLLVM_Call();
-        }
-        return truffleCallRFFI;
-    }
-
-    @CompilationFinal private StatsRFFI truffleStatsRFFI;
-
-    @Override
-    public StatsRFFI getStatsRFFI() {
-        if (truffleStatsRFFI == null) {
-            truffleStatsRFFI = new TruffleLLVM_Stats();
-        }
-        return truffleStatsRFFI;
-    }
-
-    @CompilationFinal private RApplRFFI rApplRFFI;
-
-    @Override
-    public RApplRFFI getRApplRFFI() {
-        if (rApplRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            rApplRFFI = new TruffleLLVM_RAppl();
-        }
-        return rApplRFFI;
-    }
-
-    @CompilationFinal private LapackRFFI lapackRFFI;
-
-    @Override
-    public LapackRFFI getLapackRFFI() {
-        if (lapackRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            lapackRFFI = new TruffleLLVM_Lapack();
-        }
-        return lapackRFFI;
-    }
-
-    @CompilationFinal private ToolsRFFI toolsRFFI;
-
-    @Override
-    public ToolsRFFI getToolsRFFI() {
-        if (toolsRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            toolsRFFI = new TruffleLLVM_Tools();
-        }
-        return toolsRFFI;
-    }
-
-    @CompilationFinal private PCRERFFI pcreRFFI;
-
-    @Override
-    public PCRERFFI getPCRERFFI() {
-        if (pcreRFFI == null) {
-            pcreRFFI = new TruffleLLVM_PCRE();
-        }
-        return pcreRFFI;
-    }
-
-    @CompilationFinal private ZipRFFI zipRFFI;
-
-    @Override
-    public ZipRFFI getZipRFFI() {
-        if (zipRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            zipRFFI = new TruffleLLVM_Zip();
-        }
-        return zipRFFI;
-    }
-
-    @CompilationFinal private MiscRFFI miscRFFI;
-
-    @Override
-    public MiscRFFI getMiscRFFI() {
-        if (miscRFFI == null) {
-            miscRFFI = new TruffleLLVM_Misc();
-        }
-        return miscRFFI;
-    }
-
-    private REmbedRFFI rEmbedRFFI;
-
-    @Override
-    public REmbedRFFI getREmbedRFFI() {
-        if (rEmbedRFFI == null) {
-            rEmbedRFFI = new TruffleLLVM_REmbed();
-        }
-        return rEmbedRFFI;
+        CompilerAsserts.neverPartOfCompilation();
+        return new RFFI() {
+
+            @CompilationFinal private BaseRFFI baseRFFI;
+
+            @Override
+            public BaseRFFI getBaseRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (baseRFFI == null) {
+                    baseRFFI = new TruffleLLVM_Base();
+                }
+                return baseRFFI;
+            }
+
+            @CompilationFinal private CRFFI cRFFI;
+
+            @Override
+            public CRFFI getCRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (cRFFI == null) {
+                    cRFFI = new TruffleLLVM_C();
+                }
+                return cRFFI;
+            }
+
+            @CompilationFinal private DLLRFFI dllRFFI;
+
+            @Override
+            public DLLRFFI getDLLRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (dllRFFI == null) {
+                    dllRFFI = new TruffleLLVM_DLL();
+                }
+                return dllRFFI;
+            }
+
+            @CompilationFinal private UserRngRFFI truffleUserRngRFFI;
+
+            @Override
+            public UserRngRFFI getUserRngRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (truffleUserRngRFFI == null) {
+                    truffleUserRngRFFI = new TruffleLLVM_UserRng();
+                }
+                return truffleUserRngRFFI;
+            }
+
+            @CompilationFinal private CallRFFI truffleCallRFFI;
+
+            @Override
+            public CallRFFI getCallRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (truffleCallRFFI == null) {
+                    truffleCallRFFI = new TruffleLLVM_Call();
+                }
+                return truffleCallRFFI;
+            }
+
+            @CompilationFinal private StatsRFFI truffleStatsRFFI;
+
+            @Override
+            public StatsRFFI getStatsRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (truffleStatsRFFI == null) {
+                    truffleStatsRFFI = new TruffleLLVM_Stats();
+                }
+                return truffleStatsRFFI;
+            }
+
+            @CompilationFinal private RApplRFFI rApplRFFI;
+
+            @Override
+            public RApplRFFI getRApplRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (rApplRFFI == null) {
+                    rApplRFFI = new TruffleLLVM_RAppl();
+                }
+                return rApplRFFI;
+            }
+
+            @CompilationFinal private LapackRFFI lapackRFFI;
+
+            @Override
+            public LapackRFFI getLapackRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (lapackRFFI == null) {
+                    lapackRFFI = new TruffleLLVM_Lapack();
+                }
+                return lapackRFFI;
+            }
+
+            @CompilationFinal private ToolsRFFI toolsRFFI;
+
+            @Override
+            public ToolsRFFI getToolsRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (toolsRFFI == null) {
+                    toolsRFFI = new TruffleLLVM_Tools();
+                }
+                return toolsRFFI;
+            }
+
+            @CompilationFinal private PCRERFFI pcreRFFI;
+
+            @Override
+            public PCRERFFI getPCRERFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (pcreRFFI == null) {
+                    pcreRFFI = new TruffleLLVM_PCRE();
+                }
+                return pcreRFFI;
+            }
+
+            @CompilationFinal private ZipRFFI zipRFFI;
+
+            @Override
+            public ZipRFFI getZipRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (zipRFFI == null) {
+                    zipRFFI = new TruffleLLVM_Zip();
+                }
+                return zipRFFI;
+            }
+
+            @CompilationFinal private MiscRFFI miscRFFI;
+
+            @Override
+            public MiscRFFI getMiscRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (miscRFFI == null) {
+                    miscRFFI = new TruffleLLVM_Misc();
+                }
+                return miscRFFI;
+            }
+
+            private REmbedRFFI rEmbedRFFI;
+
+            @Override
+            public REmbedRFFI getREmbedRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (rEmbedRFFI == null) {
+                    rEmbedRFFI = new TruffleLLVM_REmbed();
+                }
+                return rEmbedRFFI;
+            }
+        };
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Stats.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Stats.java
index 7570499af7ccb59520e9836cac45644617d23bac..21a0da11b492b410fbd742947d4235f317118b94 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Stats.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Stats.java
@@ -49,7 +49,7 @@ public class TruffleLLVM_Stats implements StatsRFFI {
     }
 
     public abstract static class LookupAdapter extends Node {
-        @Child private DLLRFFI.DLSymNode dllSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child private DLLRFFI.DLSymNode dllSymNode = RFFIFactory.getDLLRFFI().createDLSymNode();
 
         public SymbolHandle lookup(String name) {
             DLLInfo dllInfo = DLL.findLibrary("stats");
@@ -81,10 +81,9 @@ public class TruffleLLVM_Stats implements StatsRFFI {
         }
 
         private static int doWork(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork, Node messageNode, SymbolHandle fftWork) {
-            NativeDoubleArray na = new NativeDoubleArray(a);
-            NativeDoubleArray nwork = new NativeDoubleArray(work);
-            NativeIntegerArray niwork = new NativeIntegerArray(iwork);
-            try {
+            try (NativeDoubleArray na = new NativeDoubleArray(a);
+                            NativeDoubleArray nwork = new NativeDoubleArray(work);
+                            NativeIntegerArray niwork = new NativeIntegerArray(iwork)) {
                 return (int) ForeignAccess.sendExecute(messageNode, fftWork.asTruffleObject(), na, nseg, n, nspn, isn, nwork, niwork);
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere(t);
@@ -122,10 +121,8 @@ public class TruffleLLVM_Stats implements StatsRFFI {
         }
 
         private static void doFactor(int n, int[] pmaxf, int[] pmaxp, Node messageNode, SymbolHandle fftFactor) {
-            NativeIntegerArray npmaxf = new NativeIntegerArray(pmaxf);
-            NativeIntegerArray npmaxp = new NativeIntegerArray(pmaxp);
-
-            try {
+            try (NativeIntegerArray npmaxf = new NativeIntegerArray(pmaxf);
+                            NativeIntegerArray npmaxp = new NativeIntegerArray(pmaxp)) {
                 ForeignAccess.sendExecute(messageNode, fftFactor.asTruffleObject(), n, npmaxf, npmaxp);
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere(t);
@@ -145,7 +142,7 @@ public class TruffleLLVM_Stats implements StatsRFFI {
         }
     }
 
-    public static class Truffle_FactorNode extends FactorNode {
+    private static class Truffle_FactorNode extends Node implements FactorNode {
         @Child private ExecuteFactor executeFactor = ExecuteFactor.create();
 
         @Override
@@ -154,7 +151,7 @@ public class TruffleLLVM_Stats implements StatsRFFI {
         }
     }
 
-    public static class Truffle_WorkNode extends WorkNode {
+    private static class Truffle_WorkNode extends Node implements WorkNode {
         @Child private ExecuteWork executeWork = ExecuteWork.create();
 
         @Override
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Tools.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Tools.java
index ec1f11d50a6e766bc0a137e472c66e0dd92ddc97..b04deaa0d01f0fea5a97a92e402b60f37267c9dd 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Tools.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Tools.java
@@ -62,8 +62,7 @@ public class TruffleLLVM_Tools implements ToolsRFFI {
         public synchronized Object execute(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls, Object macros,
                         RLogicalVector warndups) {
             addCallback();
-            Object result = super.execute(con, srcfile, verbose, fragment, basename, warningCalls, macros, warndups);
-            return result;
+            return super.execute(con, srcfile, verbose, fragment, basename, warningCalls, macros, warndups);
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UpCallsRFFIImpl.java
index 84767ed9979d15a3684dd19dbbcac9ab167d7734..bc64dc1163904a85a76a9ab7825252884f976789 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UpCallsRFFIImpl.java
@@ -70,18 +70,24 @@ public class TruffleLLVM_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
     @Override
     public Object RAW(Object x) {
         byte[] value = (byte[]) super.RAW(x);
+
+        // TODO: this will leak memory if the pointer escapes
         return new NativeRawArray(value);
     }
 
     @Override
     public Object LOGICAL(Object x) {
         byte[] value = (byte[]) super.LOGICAL(x);
+
+        // TODO: this will leak memory if the pointer escapes
         return new NativeLogicalArray(x, value);
     }
 
     @Override
     public Object INTEGER(Object x) {
         int[] value = (int[]) super.INTEGER(x);
+
+        // TODO: this will leak memory if the pointer escapes
         return new NativeIntegerArray(x, value);
     }
 
@@ -89,6 +95,8 @@ public class TruffleLLVM_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
     public Object REAL(Object x) {
         // Special handling in Truffle variant
         double[] value = (double[]) super.REAL(x);
+
+        // TODO: this will leak memory if the pointer escapes
         return new NativeDoubleArray(x, value);
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UserRng.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UserRng.java
index 05bb289e372dc64d8aee31e634feb48fd4ba1924..63cdf6286422c023c84116d1a67adc202d697615 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UserRng.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UserRng.java
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.ffi.impl.llvm;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.nodes.Node;
@@ -30,60 +32,68 @@ import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
 import com.oracle.truffle.r.runtime.rng.user.UserRNG.Function;
 
 public class TruffleLLVM_UserRng implements UserRngRFFI {
-    private static class TruffleUserRngRFFINode extends UserRngRFFINode {
-        Node initMessage;
-        Node randMessage;
-        Node nSeedMessage;
-        Node seedsMessage;
-        Node readPointerNode = Message.createExecute(1).createNode();
 
-        @Override
-        public void init(int seed) {
-            if (initMessage == null) {
-                initMessage = Message.createExecute(1).createNode();
+    private abstract static class RNGNode extends Node {
+
+        @CompilationFinal protected Node message;
+        @CompilationFinal protected Node readPointerNode = Message.createExecute(1).createNode();
+
+        protected void init(int argumentCount) {
+            if (message == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                message = Message.createExecute(argumentCount).createNode();
             }
+        }
+    }
+
+    private static final class TruffleLLVMInitNode extends RNGNode implements InitNode {
+
+        @Override
+        public void execute(int seed) {
+            init(1);
             try {
-                ForeignAccess.sendExecute(initMessage, Function.Init.getSymbolHandle().asTruffleObject(), seed);
+                ForeignAccess.sendExecute(message, Function.Init.getSymbolHandle().asTruffleObject(), seed);
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere();
             }
         }
+    }
+
+    private static final class TruffleLLVM_RandNode extends RNGNode implements RandNode {
 
         @Override
-        public double rand() {
-            if (randMessage == null) {
-                randMessage = Message.createExecute(0).createNode();
-            }
+        public double execute() {
+            init(0);
             try {
-                Object address = ForeignAccess.sendExecute(randMessage, Function.Rand.getSymbolHandle().asTruffleObject());
-                Object value = ForeignAccess.sendExecute(readPointerNode, TruffleLLVM_CAccess.Function.READ_POINTER_DOUBLE.getSymbolHandle().asTruffleObject(), address);
-                return (double) value;
+                Object address = ForeignAccess.sendExecute(message, Function.Rand.getSymbolHandle().asTruffleObject());
+                return (double) ForeignAccess.sendExecute(readPointerNode, TruffleLLVM_CAccess.Function.READ_POINTER_DOUBLE.getSymbolHandle().asTruffleObject(), address);
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere();
             }
         }
+    }
+
+    private static final class TruffleLLVM_NSeedNode extends RNGNode implements NSeedNode {
 
         @Override
-        public int nSeed() {
-            if (nSeedMessage == null) {
-                nSeedMessage = Message.createExecute(0).createNode();
-            }
+        public int execute() {
+            init(0);
             try {
-                Object address = ForeignAccess.sendExecute(nSeedMessage, Function.NSeed.getSymbolHandle().asTruffleObject());
-                Object n = ForeignAccess.sendExecute(readPointerNode, TruffleLLVM_CAccess.Function.READ_POINTER_INT.getSymbolHandle().asTruffleObject(), address);
-                return (int) n;
+                Object address = ForeignAccess.sendExecute(message, Function.NSeed.getSymbolHandle().asTruffleObject());
+                return (int) ForeignAccess.sendExecute(readPointerNode, TruffleLLVM_CAccess.Function.READ_POINTER_INT.getSymbolHandle().asTruffleObject(), address);
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere();
             }
         }
+    }
+
+    private static final class TruffleLLVM_SeedsNode extends RNGNode implements SeedsNode {
 
         @Override
-        public void seeds(int[] n) {
-            if (seedsMessage == null) {
-                seedsMessage = Message.createExecute(0).createNode();
-            }
+        public void execute(int[] n) {
+            init(0);
             try {
-                Object address = ForeignAccess.sendExecute(seedsMessage, Function.Seedloc.getSymbolHandle().asTruffleObject());
+                Object address = ForeignAccess.sendExecute(message, Function.Seedloc.getSymbolHandle().asTruffleObject());
                 for (int i = 0; i < n.length; i++) {
                     Object seed = ForeignAccess.sendExecute(readPointerNode, TruffleLLVM_CAccess.Function.READ_ARRAY_INT.getSymbolHandle().asTruffleObject(), address, i);
                     n[i] = (int) seed;
@@ -95,7 +105,22 @@ public class TruffleLLVM_UserRng implements UserRngRFFI {
     }
 
     @Override
-    public UserRngRFFINode createUserRngRFFINode() {
-        return new TruffleUserRngRFFINode();
+    public InitNode createInitNode() {
+        return new TruffleLLVMInitNode();
+    }
+
+    @Override
+    public RandNode createRandNode() {
+        return new TruffleLLVM_RandNode();
+    }
+
+    @Override
+    public NSeedNode createNSeedNode() {
+        return new TruffleLLVM_NSeedNode();
+    }
+
+    @Override
+    public SeedsNode createSeedsNode() {
+        return new TruffleLLVM_SeedsNode();
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Utils.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Utils.java
index 600f8ea846b0bcfd0c959f07e718c53c17056505..4f1c0237bb0f6ae198d12f35d9c6dde193c231bb 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Utils.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Utils.java
@@ -54,13 +54,12 @@ public class TruffleLLVM_Utils {
 
     private static AsPointerRootNode asPointerRootNode;
 
-    private static class AsPointerNode extends Node {
+    static final class AsPointerNode extends Node {
         @Child private Node asPointerMessageNode = Message.AS_POINTER.createNode();
 
         public long execute(TruffleObject pointer) {
             try {
-                long result = ForeignAccess.sendAsPointer(asPointerMessageNode, pointer);
-                return result;
+                return ForeignAccess.sendAsPointer(asPointerMessageNode, pointer);
             } catch (InteropException ex) {
                 throw RInternalError.shouldNotReachHere(ex);
             }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Zip.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Zip.java
index c6a8fab0edb5601224c93967e81907c04cb1bb8d..f414a6233990f7a6581d4acb7365101eb3df822f 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Zip.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Zip.java
@@ -22,60 +22,42 @@
  */
 package com.oracle.truffle.r.ffi.impl.llvm;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.interop.NativeRawArray;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.ffi.DLL;
-import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
 public class TruffleLLVM_Zip implements ZipRFFI {
-    private static class TruffleLLVM_CompressNode extends ZipRFFI.CompressNode {
-        @Child private Node message = LLVMFunction.compress.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_CompressNode extends TruffleLLVM_DownCallNode implements CompressNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.compress;
+        }
 
         @Override
         public int execute(byte[] dest, byte[] source) {
             NativeRawArray nativeDest = new NativeRawArray(dest);
             NativeRawArray nativeSource = new NativeRawArray(source);
             try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.compress.callName, null);
-                }
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(),
-                                nativeDest, dest.length, nativeSource, source.length);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+                return (int) call(nativeDest, dest.length, nativeSource, source.length);
             } finally {
                 nativeDest.getValue();
             }
         }
     }
 
-    private static class TruffleLLVM_UncompressNode extends ZipRFFI.UncompressNode {
-        @Child private Node message = LLVMFunction.uncompress.createMessage();
-        @CompilationFinal private SymbolHandle symbolHandle;
+    private static class TruffleLLVM_UncompressNode extends TruffleLLVM_DownCallNode implements UncompressNode {
+
+        @Override
+        protected LLVMFunction getFunction() {
+            return LLVMFunction.uncompress;
+        }
 
         @Override
         public int execute(byte[] dest, byte[] source) {
             NativeRawArray nativeDest = new NativeRawArray(dest);
             NativeRawArray nativeSource = new NativeRawArray(source);
             try {
-                if (symbolHandle == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    symbolHandle = DLL.findSymbol(LLVMFunction.uncompress.callName, null);
-                }
-                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(),
-                                nativeDest, dest.length, nativeSource, source.length);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+                return (int) call(nativeDest, dest.length, nativeSource, source.length);
             } finally {
                 nativeDest.getValue();
             }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java
index 81661a2c8fc7ea0dc9f1fe423657545fb7cd0892..7bc9be3b72b1bc2149d3eb8613dc0d20bdf04853 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java
@@ -22,10 +22,11 @@
  */
 package com.oracle.truffle.r.ffi.impl.managed;
 
+import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextState;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
@@ -48,173 +49,203 @@ import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
  * Operations that can be, at least partially, implemented in Java are implemented, other operations
  * throw {@link RError}.
  */
-public class Managed_RFFIFactory extends RFFIFactory implements RFFI {
+public class Managed_RFFIFactory extends RFFIFactory {
     @Override
     protected RFFI createRFFI() {
-        return this;
-    }
-
-    @Override
-    public BaseRFFI getBaseRFFI() {
-        return new Managed_Base();
-    }
+        CompilerAsserts.neverPartOfCompilation();
+        return new RFFI() {
 
-    @Override
-    public LapackRFFI getLapackRFFI() {
-        return new Managed_LapackRFFI();
-    }
-
-    @Override
-    public RApplRFFI getRApplRFFI() {
-        return new RApplRFFI() {
             @Override
-            public Dqrdc2Node createDqrdc2Node() {
-                throw unsupported("dqrdc");
+            public BaseRFFI getBaseRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new Managed_Base();
             }
 
             @Override
-            public DqrcfNode createDqrcfNode() {
-                throw unsupported("dqrcf");
+            public LapackRFFI getLapackRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new Managed_LapackRFFI();
             }
 
             @Override
-            public DqrlsNode createDqrlsNode() {
-                throw unsupported("dqrls");
-            }
+            public RApplRFFI getRApplRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new RApplRFFI() {
+                    @Override
+                    public Dqrdc2Node createDqrdc2Node() {
+                        throw unsupported("dqrdc");
+                    }
 
-            @Override
-            public DqrqtyNode createDqrqtyNode() {
-                throw RInternalError.unimplemented();
-            }
+                    @Override
+                    public DqrcfNode createDqrcfNode() {
+                        throw unsupported("dqrcf");
+                    }
 
-            @Override
-            public DqrqyNode createDqrqyNode() {
-                throw RInternalError.unimplemented();
-            }
+                    @Override
+                    public DqrlsNode createDqrlsNode() {
+                        throw unsupported("dqrls");
+                    }
 
-            @Override
-            public DqrrsdNode createDqrrsdNode() {
-                throw RInternalError.unimplemented();
-            }
+                    @Override
+                    public DqrqtyNode createDqrqtyNode() {
+                        throw RInternalError.unimplemented();
+                    }
 
-            @Override
-            public DqrxbNode createDqrxbNode() {
-                throw RInternalError.unimplemented();
-            }
-        };
-    }
+                    @Override
+                    public DqrqyNode createDqrqyNode() {
+                        throw RInternalError.unimplemented();
+                    }
 
-    @Override
-    public StatsRFFI getStatsRFFI() {
-        return new StatsRFFI() {
-            @Override
-            public FactorNode createFactorNode() {
-                throw unsupported("factor");
-            }
+                    @Override
+                    public DqrrsdNode createDqrrsdNode() {
+                        throw RInternalError.unimplemented();
+                    }
 
-            @Override
-            public WorkNode createWorkNode() {
-                throw unsupported("work");
+                    @Override
+                    public DqrxbNode createDqrxbNode() {
+                        throw RInternalError.unimplemented();
+                    }
+                };
             }
-        };
-    }
 
-    @Override
-    public ToolsRFFI getToolsRFFI() {
-        return new ToolsRFFI() {
             @Override
-            public ParseRdNode createParseRdNode() {
-                throw unsupported("parseRD");
+            public StatsRFFI getStatsRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new StatsRFFI() {
+                    @Override
+                    public FactorNode createFactorNode() {
+                        throw unsupported("factor");
+                    }
+
+                    @Override
+                    public WorkNode createWorkNode() {
+                        throw unsupported("work");
+                    }
+                };
             }
-        };
-    }
 
-    @Override
-    public CRFFI getCRFFI() {
-        return new CRFFI() {
             @Override
-            public InvokeCNode createInvokeCNode() {
-                throw unsupported("invoke");
+            public ToolsRFFI getToolsRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new ToolsRFFI() {
+                    @Override
+                    public ParseRdNode createParseRdNode() {
+                        throw unsupported("parseRD");
+                    }
+                };
             }
-        };
-    }
 
-    @Override
-    public CallRFFI getCallRFFI() {
-        return new CallRFFI() {
             @Override
-            public InvokeCallNode createInvokeCallNode() {
-                throw unsupported("native code invocation");
+            public CRFFI getCRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new CRFFI() {
+                    @Override
+                    public InvokeCNode createInvokeCNode() {
+                        throw unsupported("invoke");
+                    }
+                };
             }
 
             @Override
-            public InvokeVoidCallNode createInvokeVoidCallNode() {
-                throw unsupported("native code invocation");
+            public CallRFFI getCallRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new CallRFFI() {
+                    @Override
+                    public InvokeCallNode createInvokeCallNode() {
+                        throw unsupported("native code invocation");
+                    }
+
+                    @Override
+                    public InvokeVoidCallNode createInvokeVoidCallNode() {
+                        throw unsupported("native code invocation");
+                    }
+                };
             }
-        };
-    }
 
-    @Override
-    public UserRngRFFI getUserRngRFFI() {
-        return new UserRngRFFI() {
             @Override
-            public UserRngRFFINode createUserRngRFFINode() {
-                throw unsupported("user defined RNG");
-            }
-        };
-    }
+            public UserRngRFFI getUserRngRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new UserRngRFFI() {
+                    @Override
+                    public InitNode createInitNode() {
+                        throw unsupported("user defined RNG");
+                    }
 
-    @Override
-    public PCRERFFI getPCRERFFI() {
-        return new Managed_PCRERFFI();
-    }
+                    @Override
+                    public RandNode createRandNode() {
+                        throw unsupported("user defined RNG");
+                    }
 
-    @Override
-    public ZipRFFI getZipRFFI() {
-        return new ZipRFFI() {
-            @Override
-            public CompressNode createCompressNode() {
-                throw unsupported("zip compression");
+                    @Override
+                    public NSeedNode createNSeedNode() {
+                        throw unsupported("user defined RNG");
+                    }
+
+                    @Override
+                    public SeedsNode createSeedsNode() {
+                        throw unsupported("user defined RNG");
+                    }
+                };
             }
 
             @Override
-            public UncompressNode createUncompressNode() {
-                throw unsupported("zip decompression");
+            public PCRERFFI getPCRERFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new Managed_PCRERFFI();
             }
-        };
-    }
 
-    @Override
-    public DLLRFFI getDLLRFFI() {
-        return new DLLRFFI() {
             @Override
-            public DLOpenNode createDLOpenNode() {
-                throw unsupported("DLL open");
+            public ZipRFFI getZipRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new ZipRFFI() {
+                    @Override
+                    public CompressNode createCompressNode() {
+                        throw unsupported("zip compression");
+                    }
+
+                    @Override
+                    public UncompressNode createUncompressNode() {
+                        throw unsupported("zip decompression");
+                    }
+                };
             }
 
             @Override
-            public DLSymNode createDLSymNode() {
-                throw unsupported("createDLSym");
+            public DLLRFFI getDLLRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new DLLRFFI() {
+                    @Override
+                    public DLOpenNode createDLOpenNode() {
+                        throw unsupported("DLL open");
+                    }
+
+                    @Override
+                    public DLSymNode createDLSymNode() {
+                        throw unsupported("createDLSym");
+                    }
+
+                    @Override
+                    public DLCloseNode createDLCloseNode() {
+                        throw unsupported("createDLClose");
+                    }
+                };
             }
 
             @Override
-            public DLCloseNode createDLCloseNode() {
-                throw unsupported("createDLClose");
+            public REmbedRFFI getREmbedRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new Managed_REmbedRFFI();
             }
-        };
-    }
 
-    @Override
-    public REmbedRFFI getREmbedRFFI() {
-        return new Managed_REmbedRFFI();
-    }
-
-    @Override
-    public MiscRFFI getMiscRFFI() {
-        return new MiscRFFI() {
             @Override
-            public ExactSumNode createExactSumNode() {
-                throw unsupported("exactsum");
+            public MiscRFFI getMiscRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                return new MiscRFFI() {
+                    @Override
+                    public ExactSumNode createExactSumNode() {
+                        throw unsupported("exactsum");
+                    }
+                };
             }
         };
     }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/NFIFunction.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/NFIFunction.java
index 8d53d9e9213cbf7fdde9350783eb94ba2512c8ef..90562d62fc373cda980e45bbbc08aba20c44768a 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/NFIFunction.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/NFIFunction.java
@@ -22,12 +22,15 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
+import java.util.function.BiFunction;
+
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.common.NativeFunction;
 
 /**
  * Enumerates all the C functions that are internal to the implementation and called via Truffle
@@ -37,7 +40,7 @@ import com.oracle.truffle.api.nodes.Node;
  * {@code xxx} is the subsystem.
  *
  */
-enum NFIFunction {
+enum NFIFunction implements NativeFunction {
     // base
     getpid("(): sint32"),
     getcwd("([uint8], sint32): sint32"),
@@ -80,38 +83,56 @@ enum NFIFunction {
     dgesv("(sint32, sint32, [double], sint32, [sint32], [double], sint32) : sint32", "call_lapack_"),
     dlange("(uint8, sint32, sint32, [double], sint32, [double]) : double", "call_lapack_"),
     dgecon("(uint8, sint32, [double], sint32, double, [double], [double], [sint32]) : sint32", "call_lapack_"),
-    //@formatter:off
     dsyevr("(uint8, uint8, uint8, sint32, [double], sint32, double, double, sint32, sint32, double, [sint32], [double], [double], sint32, [sint32], [double], sint32, " +
-                    "[sint32], sint32) : sint32", "call_lapack_");
-    //@formatter:on
+                    "[sint32], sint32) : sint32", "call_lapack_"),
+    // misc
+    exactSumFunc("([double], sint32, sint32, sint32): double"),
+    // stats
+    fft_factor("(sint32, [sint32], [sint32]): void", TruffleNFI_Utils::lookupAndBindStats),
+    fft_work("([double], sint32, sint32, sint32, sint32, [double], [sint32]): sint32", TruffleNFI_Utils::lookupAndBindStats);
 
-    private final int argCount;
+    private final int argumentCount;
     private final String signature;
     private final String callName;
+    private final BiFunction<String, String, TruffleObject> lookup;
     @CompilationFinal private TruffleObject function;
 
     NFIFunction(String signature) {
-        this.argCount = TruffleNFI_Utils.getArgCount(signature);
+        this.argumentCount = TruffleNFI_Utils.getArgCount(signature);
         this.signature = signature;
         this.callName = name();
+        this.lookup = TruffleNFI_Utils::lookupAndBind;
+    }
+
+    NFIFunction(String signature, BiFunction<String, String, TruffleObject> lookup) {
+        this.argumentCount = TruffleNFI_Utils.getArgCount(signature);
+        this.signature = signature;
+        this.callName = name();
+        this.lookup = lookup;
     }
 
     NFIFunction(String signature, String prefix) {
-        this.argCount = TruffleNFI_Utils.getArgCount(signature);
+        this.argumentCount = TruffleNFI_Utils.getArgCount(signature);
         this.signature = signature;
         this.callName = prefix + name();
+        this.lookup = TruffleNFI_Utils::lookupAndBind;
     }
 
     Node createMessage() {
         CompilerAsserts.neverPartOfCompilation();
-        return Message.createExecute(argCount).createNode();
+        return Message.createExecute(argumentCount).createNode();
     }
 
     TruffleObject getFunction() {
         if (function == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            function = TruffleNFI_Utils.lookupAndBind(callName, signature);
+            function = lookup.apply(callName, signature);
         }
         return function;
     }
+
+    @Override
+    public int getArgumentCount() {
+        return argumentCount;
+    }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Base.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Base.java
index 837a2ad3c8a1e3263ac68aa998550d565a1405ee..eb2cb9fa8784114a4243351aaacc16d828c5cfcb 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Base.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Base.java
@@ -26,113 +26,110 @@ import java.io.IOException;
 import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.interop.base.GlobResult;
 import com.oracle.truffle.r.ffi.impl.interop.base.ReadlinkResult;
 import com.oracle.truffle.r.ffi.impl.interop.base.StrtolResult;
 import com.oracle.truffle.r.ffi.impl.interop.base.UnameResult;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 
 public class TruffleNFI_Base implements BaseRFFI {
 
-    public static class TruffleNFI_GetpidNode extends GetpidNode {
-        @Child private Node message = NFIFunction.getpid.createMessage();
+    private static class TruffleNFI_GetpidNode extends TruffleNFI_DownCallNode implements GetpidNode {
+
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.getpid;
+        }
 
         @Override
         public int execute() {
-            try {
-                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.getpid.getFunction());
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call();
         }
     }
 
-    public static class TruffleNFI_GetwdNode extends GetwdNode {
-        @Child private Node message = NFIFunction.getcwd.createMessage();
+    private static final class TruffleNFI_GetwdNode extends TruffleNFI_DownCallNode implements GetwdNode {
+
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.getcwd;
+        }
 
         @TruffleBoundary
         @Override
         public String execute() {
-            try {
-                byte[] buf = new byte[4096];
-                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.getcwd.getFunction(), JavaInterop.asTruffleObject(buf), buf.length);
-                if (result == 0) {
-                    return null;
-                } else {
-                    int i = 0;
-                    while (buf[i] != 0 && i < buf.length) {
-                        i++;
-                    }
-                    return new String(buf, 0, i);
+            byte[] buf = new byte[4096];
+            int result = (int) call(JavaInterop.asTruffleObject(buf), buf.length);
+            if (result == 0) {
+                return null;
+            } else {
+                int i = 0;
+                while (buf[i] != 0 && i < buf.length) {
+                    i++;
                 }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+                return new String(buf, 0, i);
             }
         }
     }
 
-    public static class TruffleNFI_SetwdNode extends SetwdNode {
-        @Child private Node message = NFIFunction.chdir.createMessage();
+    private static class TruffleNFI_SetwdNode extends TruffleNFI_DownCallNode implements SetwdNode {
+
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.chdir;
+        }
 
         @Override
         public int execute(String dir) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.chdir.getFunction(), dir);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(dir);
         }
     }
 
-    public static class TruffleNFI_MkdirNode extends MkdirNode {
-        @Child private Node message = NFIFunction.mkdir.createMessage();
+    private static class TruffleNFI_MkdirNode extends TruffleNFI_DownCallNode implements MkdirNode {
+
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.mkdir;
+        }
 
         @Override
         public void execute(String dir, int mode) throws IOException {
-            try {
-                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.mkdir.getFunction(), dir, mode);
-                if (result != 0) {
-                    throw new IOException("mkdir " + dir + " failed");
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+            if ((int) call(dir, mode) != 0) {
+                throw new IOException("mkdir " + dir + " failed");
             }
         }
     }
 
-    public static class TruffleNFI_ReadlinkNode extends ReadlinkNode {
+    private static class TruffleNFI_ReadlinkNode extends TruffleNFI_DownCallNode implements ReadlinkNode {
         private static final int EINVAL = 22;
 
-        @Child private Node message = NFIFunction.readlink.createMessage();
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.readlink;
+        }
 
         @Override
         public String execute(String path) throws IOException {
-            try {
-                ReadlinkResult data = new ReadlinkResult();
-                ForeignAccess.sendExecute(message, NFIFunction.readlink.getFunction(), data, path);
-                if (data.getLink() == null) {
-                    if (data.getErrno() == EINVAL) {
-                        return path;
-                    } else {
-                        // some other error
-                        throw new IOException("readlink failed: " + data.getErrno());
-                    }
+            ReadlinkResult data = new ReadlinkResult();
+            call(data, path);
+            if (data.getLink() == null) {
+                if (data.getErrno() == EINVAL) {
+                    return path;
+                } else {
+                    // some other error
+                    throw new IOException("readlink failed: " + data.getErrno());
                 }
-                return data.getLink();
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
             }
+            return data.getLink();
         }
     }
 
-    public static class TruffleNFI_MkdtempNode extends MkdtempNode {
-        @Child private Node message = NFIFunction.mkdtemp.createMessage();
+    private static class TruffleNFI_MkdtempNode extends TruffleNFI_DownCallNode implements MkdtempNode {
+
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.mkdtemp;
+        }
 
         @TruffleBoundary
         @Override
@@ -145,80 +142,73 @@ public class TruffleNFI_Base implements BaseRFFI {
             byte[] ztbytes = new byte[bytes.length + 1];
             System.arraycopy(bytes, 0, ztbytes, 0, bytes.length);
             ztbytes[bytes.length] = 0;
-            try {
-                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.mkdtemp.getFunction(), JavaInterop.asTruffleObject(ztbytes));
-                if (result == 0) {
-                    return null;
-                } else {
-                    return new String(ztbytes, 0, bytes.length);
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+            int result = (int) call(JavaInterop.asTruffleObject(ztbytes));
+            if (result == 0) {
+                return null;
+            } else {
+                return new String(ztbytes, 0, bytes.length);
             }
         }
     }
 
-    public static class TruffleNFI_ChmodNode extends ChmodNode {
-        @Child private Node message = NFIFunction.chmod.createMessage();
+    private static class TruffleNFI_ChmodNode extends TruffleNFI_DownCallNode implements ChmodNode {
+
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.chmod;
+        }
 
         @Override
         public int execute(String path, int mode) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.chmod.getFunction(), path, mode);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(path, mode);
         }
     }
 
-    public static class TruffleNFI_StrolNode extends StrolNode {
+    private static class TruffleNFI_StrolNode extends TruffleNFI_DownCallNode implements StrolNode {
 
-        @Child private Node message = NFIFunction.strtol.createMessage();
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.strtol;
+        }
 
         @Override
         public long execute(String s, int base) throws IllegalArgumentException {
-            try {
-                StrtolResult data = new StrtolResult();
-                ForeignAccess.sendExecute(message, NFIFunction.strtol.getFunction(), data, s, base);
-                if (data.getErrno() != 0) {
-                    throw new IllegalArgumentException("strtol failure");
-                } else {
-                    return data.getResult();
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+            StrtolResult data = new StrtolResult();
+            call(data, s, base);
+            if (data.getErrno() != 0) {
+                throw new IllegalArgumentException("strtol failure");
+            } else {
+                return data.getResult();
             }
         }
     }
 
-    public static class TruffleNFI_UnameNode extends UnameNode {
+    private static class TruffleNFI_UnameNode extends TruffleNFI_DownCallNode implements UnameNode {
 
-        @Child private Node message = NFIFunction.uname.createMessage();
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.uname;
+        }
 
         @Override
         public UtsName execute() {
             UnameResult data = new UnameResult();
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.uname.getFunction(), data);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(data);
             return data;
         }
     }
 
-    public static class TruffleNFI_GlobNode extends GlobNode {
+    private static class TruffleNFI_GlobNode extends TruffleNFI_DownCallNode implements GlobNode {
 
-        @Child private Node message = NFIFunction.glob.createMessage();
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.glob;
+        }
 
         @Override
         public ArrayList<String> glob(String pattern) {
             GlobResult data = new GlobResult();
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.glob.getFunction(), data, pattern);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(data, pattern);
             return data.getPaths();
         }
     }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
index b43f66a02900b2e66ebc1f6cb94acec6be5a0057..e869188edb5536668845518bb77468b88a57c634 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
@@ -37,7 +37,7 @@ import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
 public class TruffleNFI_C implements CRFFI {
-    public abstract static class TruffleNFI_InvokeCNode extends InvokeCNode {
+    abstract static class TruffleNFI_InvokeCNode extends Node implements InvokeCNode {
 
         @Child private Node bindNode = Message.createInvoke(1).createNode();
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java
index ebe2179f81ec629c08d27d5cdf4f908732dc2cef..4c1547d092df23ed89a23f5a5ca64df3d0070e19 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java
@@ -230,7 +230,7 @@ public class TruffleNFI_Call implements CallRFFI {
         }
     }
 
-    public abstract static class TruffleNFI_InvokeCallNode extends InvokeCallNode {
+    public abstract static class TruffleNFI_InvokeCallNode extends Node implements InvokeCallNode {
         @Child private Node bindNode = Message.createInvoke(1).createNode();
 
         @Specialization(guards = "args.length == 0")
@@ -416,7 +416,7 @@ public class TruffleNFI_Call implements CallRFFI {
         }
     }
 
-    public static class TruffleNFI_InvokeVoidCallNode extends InvokeVoidCallNode {
+    private static class TruffleNFI_InvokeVoidCallNode extends Node implements InvokeVoidCallNode {
         private static final String CallVoid1Sig = "(object): void";
         private static final String CallVoid0Sig = "(): void";
         @Child private Node bindNode = Message.createInvoke(1).createNode();
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java
index e8938136e5337e6a1853f971a0a0946d704042ea..a2ce1c70649a0e2787ef0b95954bdaa82de29395 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java
@@ -49,7 +49,7 @@ public class TruffleNFI_DLL implements DLLRFFI {
         }
     }
 
-    private static class TruffleNFI_DLOpenNode extends DLLRFFI.DLOpenNode {
+    private static class TruffleNFI_DLOpenNode extends Node implements DLLRFFI.DLOpenNode {
 
         @TruffleBoundary
         @Override
@@ -74,7 +74,7 @@ public class TruffleNFI_DLL implements DLLRFFI {
         return sb.toString();
     }
 
-    private static class TruffleNFI_DLSymNode extends DLLRFFI.DLSymNode {
+    private static class TruffleNFI_DLSymNode extends Node implements DLLRFFI.DLSymNode {
 
         @Override
         public SymbolHandle execute(Object handle, String symbol) {
@@ -92,7 +92,7 @@ public class TruffleNFI_DLL implements DLLRFFI {
         }
     }
 
-    private static class TruffleNFI_DLCloseNode extends DLLRFFI.DLCloseNode {
+    private static class TruffleNFI_DLCloseNode extends Node implements DLLRFFI.DLCloseNode {
 
         @Override
         public int execute(Object handle) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..49cbaf030ac49313f348b8048431bee700c7f1fa
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNode.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.ffi.impl.nfi;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.r.ffi.impl.common.DownCallNode;
+import com.oracle.truffle.r.ffi.impl.interop.NativeNACheck;
+
+public abstract class TruffleNFI_DownCallNode extends DownCallNode<NFIFunction> {
+
+    @Override
+    protected final TruffleObject getTarget() {
+        return getFunction().getFunction();
+    }
+
+    @SuppressWarnings("cast")
+    @Override
+    @ExplodeLoop
+    protected void wrapArguments(Object[] args) {
+        for (int i = 0; i < args.length; i++) {
+            Object obj = args[i];
+            if (obj instanceof double[]) {
+                args[i] = JavaInterop.asTruffleObject((double[]) obj);
+            } else if (obj instanceof int[] || obj == null) {
+                args[i] = JavaInterop.asTruffleObject((int[]) obj);
+            }
+        }
+    }
+
+    @Override
+    @ExplodeLoop
+    protected void finishArguments(Object[] args) {
+        for (Object obj : args) {
+            if (obj instanceof NativeNACheck<?>) {
+                ((NativeNACheck<?>) obj).close();
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Lapack.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Lapack.java
index 5dc7f533d36a373415f54c21a462bc8acdbda00b..af4e79315c5192ddc5b84582b02ec928c60052dc 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Lapack.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Lapack.java
@@ -22,193 +22,164 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
 
 public class TruffleNFI_Lapack implements LapackRFFI {
 
-    private static class TruffleNFI_IlaverNode extends IlaverNode {
-        @Child private Node message = NFIFunction.ilaver.createMessage();
+    private static class TruffleNFI_IlaverNode extends TruffleNFI_DownCallNode implements IlaverNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.ilaver;
+        }
 
         @Override
         public void execute(int[] version) {
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.ilaver.getFunction(), JavaInterop.asTruffleObject(version));
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(version);
         }
     }
 
-    private static class TruffleNFI_DgeevNode extends DgeevNode {
-        @Child private Node message = NFIFunction.dgeev.createMessage();
+    private static class TruffleNFI_DgeevNode extends TruffleNFI_DownCallNode implements DgeevNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dgeev;
+        }
 
         @Override
         public int execute(char jobVL, char jobVR, int n, double[] a, int lda, double[] wr, double[] wi, double[] vl, int ldvl, double[] vr, int ldvr, double[] work, int lwork) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgeev.getFunction(), jobVL, jobVR, n, JavaInterop.asTruffleObject(a), lda,
-                                JavaInterop.asTruffleObject(wr), JavaInterop.asTruffleObject(wi), JavaInterop.asTruffleObject(vl), ldvl,
-                                JavaInterop.asTruffleObject(vr), ldvr, JavaInterop.asTruffleObject(work), lwork);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(jobVL, jobVR, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork);
         }
     }
 
-    private static class TruffleNFI_Dgeqp3Node extends Dgeqp3Node {
-        @Child private Node message = NFIFunction.dgeqp3.createMessage();
+    private static class TruffleNFI_Dgeqp3Node extends TruffleNFI_DownCallNode implements Dgeqp3Node {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dgeqp3;
+        }
 
         @Override
         public int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgeqp3.getFunction(), m, n, JavaInterop.asTruffleObject(a), lda, JavaInterop.asTruffleObject(jpvt),
-                                JavaInterop.asTruffleObject(tau), JavaInterop.asTruffleObject(work), lwork);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(m, n, a, lda, jpvt, tau, work, lwork);
         }
     }
 
-    private static class TruffleNFI_DormqrNode extends DormqrNode {
-        @Child private Node message = NFIFunction.dormq.createMessage();
+    private static class TruffleNFI_DormqrNode extends TruffleNFI_DownCallNode implements DormqrNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dormq;
+        }
 
         @Override
         public int execute(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dormq.getFunction(), side, trans, m, n, k, JavaInterop.asTruffleObject(a), lda,
-                                JavaInterop.asTruffleObject(tau), JavaInterop.asTruffleObject(c), ldc, JavaInterop.asTruffleObject(work), lwork);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(side, trans, m, n, k, a, lda, tau, c, ldc, work, lwork);
         }
     }
 
-    private static class TruffleNFI_DtrtrsNode extends DtrtrsNode {
-        @Child private Node message = NFIFunction.dtrtrs.createMessage();
+    private static class TruffleNFI_DtrtrsNode extends TruffleNFI_DownCallNode implements DtrtrsNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dtrtrs;
+        }
 
         @Override
         public int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dtrtrs.getFunction(), uplo, trans, diag, n, nrhs, JavaInterop.asTruffleObject(a), lda,
-                                JavaInterop.asTruffleObject(b), ldb);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(uplo, trans, diag, n, nrhs, a, lda, b, ldb);
         }
     }
 
-    private static class TruffleNFI_DgetrfNode extends DgetrfNode {
-        @Child private Node message = NFIFunction.dgetrf.createMessage();
+    private static class TruffleNFI_DgetrfNode extends TruffleNFI_DownCallNode implements DgetrfNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dgetrf;
+        }
 
         @Override
         public int execute(int m, int n, double[] a, int lda, int[] ipiv) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgetrf.getFunction(), m, n, JavaInterop.asTruffleObject(a), lda, JavaInterop.asTruffleObject(ipiv));
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(m, n, a, lda, ipiv);
         }
     }
 
-    private static class TruffleNFI_DpotrfNode extends DpotrfNode {
-        @Child private Node message = NFIFunction.dpotrf.createMessage();
+    private static class TruffleNFI_DpotrfNode extends TruffleNFI_DownCallNode implements DpotrfNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dpotrf;
+        }
 
         @Override
         public int execute(char uplo, int n, double[] a, int lda) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dpotrf.getFunction(), uplo, n, JavaInterop.asTruffleObject(a), lda);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(uplo, n, a, lda);
         }
     }
 
-    private static class TruffleNFI_DpotriNode extends DpotriNode {
-        @Child private Node message = NFIFunction.dpotri.createMessage();
+    private static class TruffleNFI_DpotriNode extends TruffleNFI_DownCallNode implements DpotriNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dpotri;
+        }
 
         @Override
         public int execute(char uplo, int n, double[] a, int lda) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dpotri.getFunction(), uplo, n, JavaInterop.asTruffleObject(a), lda);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(uplo, n, a, lda);
         }
     }
 
-    private static class TruffleNFI_DpstrfNode extends DpstrfNode {
-        @Child private Node message = NFIFunction.dpstrf.createMessage();
+    private static class TruffleNFI_DpstrfNode extends TruffleNFI_DownCallNode implements DpstrfNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dpstrf;
+        }
 
         @Override
         public int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dpstrf.getFunction(), uplo, n, JavaInterop.asTruffleObject(a), lda,
-                                JavaInterop.asTruffleObject(piv), JavaInterop.asTruffleObject(rank), tol, JavaInterop.asTruffleObject(work));
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(uplo, n, a, lda, piv, rank, tol, work);
         }
     }
 
-    private static class TruffleNFI_DgesvNode extends DgesvNode {
-        @Child private Node message = NFIFunction.dgesv.createMessage();
+    private static class TruffleNFI_DgesvNode extends TruffleNFI_DownCallNode implements DgesvNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dgesv;
+        }
 
         @Override
         public int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgesv.getFunction(), n, nrhs, JavaInterop.asTruffleObject(a), lda, JavaInterop.asTruffleObject(ipiv),
-                                JavaInterop.asTruffleObject(b), ldb);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(n, nrhs, a, lda, ipiv, b, ldb);
         }
     }
 
-    private static class TruffleNFI_DlangeNode extends DlangeNode {
-        @Child private Node message = NFIFunction.dlange.createMessage();
+    private static class TruffleNFI_DlangeNode extends TruffleNFI_DownCallNode implements DlangeNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dlange;
+        }
 
         @Override
         public double execute(char norm, int m, int n, double[] a, int lda, double[] work) {
-            try {
-                return (double) ForeignAccess.sendExecute(message, NFIFunction.dlange.getFunction(), norm, m, n, JavaInterop.asTruffleObject(a), lda,
-                                JavaInterop.asTruffleObject(work));
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (double) call(norm, m, n, a, lda, work);
         }
     }
 
-    private static class TruffleNFI_DgeconNode extends DgeconNode {
-        @Child private Node message = NFIFunction.dgecon.createMessage();
+    private static class TruffleNFI_DgeconNode extends TruffleNFI_DownCallNode implements DgeconNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dgecon;
+        }
 
         @Override
         public int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgecon.getFunction(), norm, n, JavaInterop.asTruffleObject(a), lda, anorm, JavaInterop.asTruffleObject(rcond),
-                                JavaInterop.asTruffleObject(work), JavaInterop.asTruffleObject(iwork));
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(norm, n, a, lda, anorm, rcond, work, iwork);
         }
     }
 
-    private static class TruffleNFI_DsyevrNode extends DsyevrNode {
-        @Child private Node message = NFIFunction.dsyevr.createMessage();
+    private static class TruffleNFI_DsyevrNode extends TruffleNFI_DownCallNode implements DsyevrNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dsyevr;
+        }
 
         @Override
         public int execute(char jobz, char range, char uplo, int n, double[] a, int lda, double vl, double vu, int il, int iu, double abstol, int[] m, double[] w, double[] z, int ldz, int[] isuppz,
                         double[] work, int lwork, int[] iwork, int liwork) {
-            try {
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.dsyevr.getFunction(), jobz, range, uplo, n, JavaInterop.asTruffleObject(a),
-                                lda, vl, vu, il, iu, abstol, JavaInterop.asTruffleObject(m), JavaInterop.asTruffleObject(w), JavaInterop.asTruffleObject(z), ldz,
-                                JavaInterop.asTruffleObject(isuppz), JavaInterop.asTruffleObject(work), lwork, JavaInterop.asTruffleObject(iwork), liwork);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(jobz, range, uplo, n, a, lda, vl, vu, il, iu, abstol, m, w, z, ldz, isuppz, work, lwork, iwork, liwork);
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Misc.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Misc.java
index 80d3abefe87b4ae1527b354ed2a8d9a17b6e34eb..80503d78cb7dfafeda971a96041f4690fbb6981d 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Misc.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Misc.java
@@ -22,34 +22,19 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 
 public class TruffleNFI_Misc implements MiscRFFI {
 
-    private static class TruffleNFI_ExactSumNode extends ExactSumNode {
-        @Child private Node messageNode = Message.createExecute(4).createNode();
-        @CompilationFinal private TruffleObject function;
+    private static final class TruffleNFI_ExactSumNode extends TruffleNFI_DownCallNode implements ExactSumNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.exactSumFunc;
+        }
 
         @Override
         public double execute(double[] values, boolean hasNa, boolean naRm) {
-            if (function == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                function = TruffleNFI_Utils.lookupAndBind("exactSumFunc", "([double], sint32, sint32, sint32): double");
-            }
-            try {
-                return (double) ForeignAccess.sendExecute(messageNode, function, JavaInterop.asTruffleObject(values), values.length, hasNa ? 1 : 0, naRm ? 1 : 0);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (double) call(values, values.length, hasNa ? 1 : 0, naRm ? 1 : 0);
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PCRE.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PCRE.java
index 12aad365d6c63f3960e00c601d06c792cff27437..8a8326f9d4345d99e5111237c3dbc05eb12f75be 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PCRE.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PCRE.java
@@ -25,10 +25,7 @@ package com.oracle.truffle.r.ffi.impl.nfi;
 import java.nio.charset.StandardCharsets;
 
 import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.interop.pcre.CaptureNamesResult;
 import com.oracle.truffle.r.ffi.impl.interop.pcre.CompileResult;
 import com.oracle.truffle.r.runtime.RError;
@@ -37,84 +34,73 @@ import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 
 public class TruffleNFI_PCRE implements PCRERFFI {
 
-    private static class TruffleNFI_MaketablesNode extends MaketablesNode {
-        @Child private Node message = NFIFunction.maketables.createMessage();
+    private static class TruffleNFI_MaketablesNode extends TruffleNFI_DownCallNode implements MaketablesNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.maketables;
+        }
 
         @Override
         public long execute() {
-            try {
-                long result = (long) ForeignAccess.sendExecute(message, NFIFunction.maketables.getFunction());
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (long) call();
         }
     }
 
-    private static class TruffleNFI_GetCaptureCountNode extends GetCaptureCountNode {
-        @Child private Node message = NFIFunction.getcapturecount.createMessage();
+    private static class TruffleNFI_GetCaptureCountNode extends TruffleNFI_DownCallNode implements GetCaptureCountNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.getcapturecount;
+        }
 
         @Override
         public int execute(long code, long extra) {
-            try {
-                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.getcapturecount.getFunction(), code, extra);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            return (int) call(code, extra);
         }
     }
 
-    public static class TruffleNFI_GetCaptureNamesNode extends GetCaptureNamesNode {
-
-        @Child private Node message = NFIFunction.getcapturenames.createMessage();
+    private static class TruffleNFI_GetCaptureNamesNode extends TruffleNFI_DownCallNode implements GetCaptureNamesNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.getcapturenames;
+        }
 
         @Override
         public String[] execute(long code, long extra, int captureCount) {
-            try {
-                CaptureNamesResult data = new CaptureNamesResult(captureCount);
-                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.getcapturenames.getFunction(), data, code, extra);
-                if (result < 0) {
-                    CompilerDirectives.transferToInterpreter();
-                    throw RError.error(RError.NO_CALLER, RError.Message.WRONG_PCRE_INFO, result);
-                } else {
-                    return data.getCaptureNames();
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
+            CaptureNamesResult data = new CaptureNamesResult(captureCount);
+            int result = (int) call(data, code, extra);
+            if (result < 0) {
+                CompilerDirectives.transferToInterpreter();
+                throw RError.error(RError.NO_CALLER, RError.Message.WRONG_PCRE_INFO, result);
+            } else {
+                return data.getCaptureNames();
             }
         }
     }
 
-    public static class TruffleNFI_CompileNode extends CompileNode {
-
-        @Child private Node message = NFIFunction.compile.createMessage();
+    private static class TruffleNFI_CompileNode extends TruffleNFI_DownCallNode implements CompileNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.compile;
+        }
 
         @Override
         public Result execute(String pattern, int options, long tables) {
-            try {
-                CompileResult data = new CompileResult();
-                ForeignAccess.sendExecute(message, NFIFunction.compile.getFunction(), data, pattern, options, tables);
-                return data.getResult();
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            CompileResult data = new CompileResult();
+            call(data, pattern, options, tables);
+            return data.getResult();
         }
     }
 
-    private static class TruffleNFI_ExecNode extends ExecNode {
-        @Child private Node message = NFIFunction.exec.createMessage();
+    private static class TruffleNFI_ExecNode extends TruffleNFI_DownCallNode implements ExecNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.exec;
+        }
 
         @Override
         public int execute(long code, long extra, String subject, int offset, int options, int[] ovector) {
-            try {
-                byte[] subjectBytes = subject.getBytes(StandardCharsets.UTF_8);
-                return (int) ForeignAccess.sendExecute(message, NFIFunction.exec.getFunction(), code, extra,
-                                JavaInterop.asTruffleObject(subjectBytes), subjectBytes.length,
-                                offset, options, JavaInterop.asTruffleObject(ovector), ovector.length);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            byte[] subjectBytes = subject.getBytes(StandardCharsets.UTF_8);
+            return (int) call(code, extra, JavaInterop.asTruffleObject(subjectBytes), subjectBytes.length, offset, options, ovector, ovector.length);
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RAppl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RAppl.java
index 699b3542265fee1351140de3f89ca8b644693796..5210f3ec9d8fbdc869c7fa0d13bc0511e75aa26d 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RAppl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RAppl.java
@@ -22,157 +22,91 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 
 public class TruffleNFI_RAppl implements RApplRFFI {
 
-    private static class TruffleNFI_Dqrdc2Node extends Dqrdc2Node {
-        @Child private Node message = NFIFunction.dqrdc2.createMessage();
+    private static class TruffleNFI_Dqrdc2Node extends TruffleNFI_DownCallNode implements Dqrdc2Node {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dqrdc2;
+        }
 
         @Override
         public void execute(double[] x, int ldx, int n, int p, double tol, int[] rank, double[] qraux, int[] pivot, double[] work) {
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.dqrdc2.getFunction(),
-                                JavaInterop.asTruffleObject(x),
-                                ldx, n, p, tol,
-                                JavaInterop.asTruffleObject(rank),
-                                JavaInterop.asTruffleObject(qraux),
-                                JavaInterop.asTruffleObject(pivot),
-                                JavaInterop.asTruffleObject(work));
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(x, ldx, n, p, tol, rank, qraux, pivot, work);
         }
     }
 
-    private static class TruffleNFI_DqrcfNode extends DqrcfNode {
-        @Child private Node message = NFIFunction.dqrcf.createMessage();
+    private static class TruffleNFI_DqrcfNode extends TruffleNFI_DownCallNode implements DqrcfNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dqrcf;
+        }
 
         @Override
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] b, int[] info) {
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.dqrcf.getFunction(),
-                                JavaInterop.asTruffleObject(x),
-                                n, k,
-                                JavaInterop.asTruffleObject(qraux),
-                                JavaInterop.asTruffleObject(y),
-                                ny,
-                                JavaInterop.asTruffleObject(b),
-                                JavaInterop.asTruffleObject(info));
-
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(x, n, k, qraux, y, ny, b, info);
         }
     }
 
-    private static class TruffleNFI_DqrlsNode extends DqrlsNode {
-        @Child private Node message = NFIFunction.dqrls.createMessage();
+    private static class TruffleNFI_DqrlsNode extends TruffleNFI_DownCallNode implements DqrlsNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dqrls;
+        }
 
         @Override
         public void execute(double[] x, int n, int p, double[] y, int ny, double tol, double[] b, double[] rsd, double[] qty, int[] k, int[] jpvt, double[] qraux, double[] work) {
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.dqrls.getFunction(),
-                                JavaInterop.asTruffleObject(x),
-                                n, p,
-                                JavaInterop.asTruffleObject(y),
-                                ny, tol,
-                                JavaInterop.asTruffleObject(b),
-                                JavaInterop.asTruffleObject(rsd),
-                                JavaInterop.asTruffleObject(qty),
-                                JavaInterop.asTruffleObject(k),
-                                JavaInterop.asTruffleObject(jpvt),
-                                JavaInterop.asTruffleObject(qraux),
-                                JavaInterop.asTruffleObject(work));
-
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(x, n, p, y, ny, tol, b, rsd, qty, k, jpvt, qraux, work);
         }
     }
 
-    private static class TruffleNFI_DqrqtyNode extends DqrqtyNode {
-        @Child private Node message = NFIFunction.dqrqty.createMessage();
+    private static class TruffleNFI_DqrqtyNode extends TruffleNFI_DownCallNode implements DqrqtyNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dqrqty;
+        }
 
         @Override
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] qty) {
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.dqrqty.getFunction(),
-                                JavaInterop.asTruffleObject(x),
-                                n, k,
-                                JavaInterop.asTruffleObject(qraux),
-                                JavaInterop.asTruffleObject(y),
-                                ny,
-                                JavaInterop.asTruffleObject(qty));
-
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(x, n, k, qraux, y, ny, qty);
         }
     }
 
-    private static class TruffleNFI_DqrqyNode extends DqrqyNode {
-        @Child private Node message = NFIFunction.dqrqy.createMessage();
+    private static class TruffleNFI_DqrqyNode extends TruffleNFI_DownCallNode implements DqrqyNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dqrqy;
+        }
 
         @Override
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] qy) {
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.dqrqy.getFunction(),
-                                JavaInterop.asTruffleObject(x),
-                                n, k,
-                                JavaInterop.asTruffleObject(qraux),
-                                JavaInterop.asTruffleObject(y),
-                                ny,
-                                JavaInterop.asTruffleObject(qy));
-
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(x, n, k, qraux, y, ny, qy);
         }
     }
 
-    private static class TruffleNFI_DqrrsdNode extends DqrrsdNode {
-        @Child private Node message = NFIFunction.dqrrsd.createMessage();
+    private static class TruffleNFI_DqrrsdNode extends TruffleNFI_DownCallNode implements DqrrsdNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dqrrsd;
+        }
 
         @Override
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] rsd) {
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.dqrrsd.getFunction(),
-                                JavaInterop.asTruffleObject(x),
-                                n, k,
-                                JavaInterop.asTruffleObject(qraux),
-                                JavaInterop.asTruffleObject(y),
-                                ny,
-                                JavaInterop.asTruffleObject(rsd));
-
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(x, n, k, qraux, y, ny, rsd);
         }
     }
 
-    private static class TruffleNFI_DqrxbNode extends DqrxbNode {
-        @Child private Node message = NFIFunction.dqrxb.createMessage();
+    private static class TruffleNFI_DqrxbNode extends TruffleNFI_DownCallNode implements DqrxbNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.dqrxb;
+        }
 
         @Override
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] xb) {
-            try {
-                ForeignAccess.sendExecute(message, NFIFunction.dqrxb.getFunction(),
-                                JavaInterop.asTruffleObject(x),
-                                n, k,
-                                JavaInterop.asTruffleObject(qraux),
-                                JavaInterop.asTruffleObject(y),
-                                ny,
-                                JavaInterop.asTruffleObject(xb));
-
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            call(x, n, k, qraux, y, ny, xb);
         }
     }
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java
index d8d33862675cf107b21eeab6e84d6284c1165a19..fec06c845d5e687a51002a2588e7e6c70aa875c4 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java
@@ -30,13 +30,11 @@ public class TruffleNFI_REmbed implements REmbedRFFI {
     @Override
     public void suicide(String x) {
         throw RInternalError.unimplemented();
-
     }
 
     @Override
     public void cleanUp(int type, int x, int y) {
         throw RInternalError.unimplemented();
-
     }
 
     @Override
@@ -47,12 +45,10 @@ public class TruffleNFI_REmbed implements REmbedRFFI {
     @Override
     public void writeConsole(String x) {
         throw RInternalError.unimplemented();
-
     }
 
     @Override
     public void writeErrConsole(String x) {
         throw RInternalError.unimplemented();
-
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RFFIFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RFFIFactory.java
index 5f446ccc73e47cee94b957e611bd8b7493817da4..b141f8be4e4a0ea588c41fd4fe527e901e99170f 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RFFIFactory.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RFFIFactory.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
-import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.r.ffi.impl.common.LibPaths;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -44,7 +44,8 @@ import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
 import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
 import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
-public class TruffleNFI_RFFIFactory extends RFFIFactory implements RFFI {
+public class TruffleNFI_RFFIFactory extends RFFIFactory {
+
     private static class ContextStateImpl implements RContext.ContextState {
         @Override
         public ContextState initialize(RContext context) {
@@ -66,144 +67,152 @@ public class TruffleNFI_RFFIFactory extends RFFIFactory implements RFFI {
 
     @Override
     protected RFFI createRFFI() {
-        return this;
-    }
-
-    @CompilationFinal private CRFFI cRFFI;
-
-    @Override
-    public CRFFI getCRFFI() {
-        if (cRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            cRFFI = new TruffleNFI_C();
-        }
-        return cRFFI;
-    }
+        CompilerAsserts.neverPartOfCompilation();
+        return new RFFI() {
+
+            @CompilationFinal private CRFFI cRFFI;
+
+            @Override
+            public CRFFI getCRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (cRFFI == null) {
+                    cRFFI = new TruffleNFI_C();
+                }
+                return cRFFI;
+            }
 
-    @CompilationFinal private BaseRFFI baseRFFI;
+            @CompilationFinal private BaseRFFI baseRFFI;
 
-    @Override
-    public BaseRFFI getBaseRFFI() {
-        if (baseRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            baseRFFI = new TruffleNFI_Base();
-        }
-        return baseRFFI;
-    }
+            @Override
+            public BaseRFFI getBaseRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (baseRFFI == null) {
+                    baseRFFI = new TruffleNFI_Base();
+                }
+                return baseRFFI;
+            }
 
-    @CompilationFinal private CallRFFI callRFFI;
+            @CompilationFinal private CallRFFI callRFFI;
 
-    @Override
-    public CallRFFI getCallRFFI() {
-        if (callRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            callRFFI = new TruffleNFI_Call();
-        }
-        return callRFFI;
-    }
+            @Override
+            public CallRFFI getCallRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (callRFFI == null) {
+                    callRFFI = new TruffleNFI_Call();
+                }
+                return callRFFI;
+            }
 
-    @CompilationFinal private DLLRFFI dllRFFI;
+            @CompilationFinal private DLLRFFI dllRFFI;
 
-    @Override
-    public DLLRFFI getDLLRFFI() {
-        if (dllRFFI == null) {
-            dllRFFI = new TruffleNFI_DLL();
-        }
-        return dllRFFI;
-    }
+            @Override
+            public DLLRFFI getDLLRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                CompilerAsserts.neverPartOfCompilation();
+                if (dllRFFI == null) {
+                    dllRFFI = new TruffleNFI_DLL();
+                }
+                return dllRFFI;
+            }
 
-    @CompilationFinal private UserRngRFFI userRngRFFI;
+            @CompilationFinal private UserRngRFFI userRngRFFI;
 
-    @Override
-    public UserRngRFFI getUserRngRFFI() {
-        if (userRngRFFI == null) {
-            userRngRFFI = new TruffleNFI_UserRng();
-        }
-        return userRngRFFI;
-    }
+            @Override
+            public UserRngRFFI getUserRngRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (userRngRFFI == null) {
+                    userRngRFFI = new TruffleNFI_UserRng();
+                }
+                return userRngRFFI;
+            }
 
-    @CompilationFinal private ZipRFFI zipRFFI;
+            @CompilationFinal private ZipRFFI zipRFFI;
 
-    @Override
-    public ZipRFFI getZipRFFI() {
-        if (zipRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            zipRFFI = new TruffleNFI_Zip();
-        }
-        return zipRFFI;
-    }
+            @Override
+            public ZipRFFI getZipRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (zipRFFI == null) {
+                    zipRFFI = new TruffleNFI_Zip();
+                }
+                return zipRFFI;
+            }
 
-    @CompilationFinal private PCRERFFI pcreRFFI;
+            @CompilationFinal private PCRERFFI pcreRFFI;
 
-    @Override
-    public PCRERFFI getPCRERFFI() {
-        if (pcreRFFI == null) {
-            pcreRFFI = new TruffleNFI_PCRE();
-        }
-        return pcreRFFI;
-    }
+            @Override
+            public PCRERFFI getPCRERFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (pcreRFFI == null) {
+                    pcreRFFI = new TruffleNFI_PCRE();
+                }
+                return pcreRFFI;
+            }
 
-    @CompilationFinal private LapackRFFI lapackRFFI;
+            @CompilationFinal private LapackRFFI lapackRFFI;
 
-    @Override
-    public LapackRFFI getLapackRFFI() {
-        if (lapackRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            lapackRFFI = new TruffleNFI_Lapack();
-        }
-        return lapackRFFI;
-    }
+            @Override
+            public LapackRFFI getLapackRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (lapackRFFI == null) {
+                    lapackRFFI = new TruffleNFI_Lapack();
+                }
+                return lapackRFFI;
+            }
 
-    @CompilationFinal private RApplRFFI rApplRFFI;
+            @CompilationFinal private RApplRFFI rApplRFFI;
 
-    @Override
-    public RApplRFFI getRApplRFFI() {
-        if (rApplRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            rApplRFFI = new TruffleNFI_RAppl();
-        }
-        return rApplRFFI;
-    }
+            @Override
+            public RApplRFFI getRApplRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (rApplRFFI == null) {
+                    rApplRFFI = new TruffleNFI_RAppl();
+                }
+                return rApplRFFI;
+            }
 
-    @CompilationFinal private StatsRFFI statsRFFI;
+            @CompilationFinal private StatsRFFI statsRFFI;
 
-    @Override
-    public StatsRFFI getStatsRFFI() {
-        if (statsRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            statsRFFI = new TruffleNFI_Stats();
-        }
-        return statsRFFI;
-    }
+            @Override
+            public StatsRFFI getStatsRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (statsRFFI == null) {
+                    statsRFFI = new TruffleNFI_Stats();
+                }
+                return statsRFFI;
+            }
 
-    @CompilationFinal private ToolsRFFI toolsRFFI;
+            @CompilationFinal private ToolsRFFI toolsRFFI;
 
-    @Override
-    public ToolsRFFI getToolsRFFI() {
-        if (toolsRFFI == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            toolsRFFI = new TruffleNFI_Tools();
-        }
-        return toolsRFFI;
-    }
+            @Override
+            public ToolsRFFI getToolsRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (toolsRFFI == null) {
+                    toolsRFFI = new TruffleNFI_Tools();
+                }
+                return toolsRFFI;
+            }
 
-    private REmbedRFFI rEmbedRFFI;
+            private REmbedRFFI rEmbedRFFI;
 
-    @Override
-    public REmbedRFFI getREmbedRFFI() {
-        if (rEmbedRFFI == null) {
-            rEmbedRFFI = new TruffleNFI_REmbed();
-        }
-        return rEmbedRFFI;
-    }
+            @Override
+            public REmbedRFFI getREmbedRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (rEmbedRFFI == null) {
+                    rEmbedRFFI = new TruffleNFI_REmbed();
+                }
+                return rEmbedRFFI;
+            }
 
-    private MiscRFFI miscRFFI;
+            private MiscRFFI miscRFFI;
 
-    @Override
-    public MiscRFFI getMiscRFFI() {
-        if (miscRFFI == null) {
-            miscRFFI = new TruffleNFI_Misc();
-        }
-        return miscRFFI;
+            @Override
+            public MiscRFFI getMiscRFFI() {
+                CompilerAsserts.neverPartOfCompilation();
+                if (miscRFFI == null) {
+                    miscRFFI = new TruffleNFI_Misc();
+                }
+                return miscRFFI;
+            }
+        };
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Stats.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Stats.java
index 313faae9f3cddbebb6ab6ccade0797fe8e4b0db0..217fdeb5304e3057bd3f61462a89a66799c0c2b3 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Stats.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Stats.java
@@ -22,83 +22,34 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.runtime.RInternalError;
-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;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
 
 public class TruffleNFI_Stats implements StatsRFFI {
 
-    private static class TruffleNFI_FactorNode extends FactorNode {
-        private static final String FFT_FACTOR = "fft_factor";
-        private static final String FFT_FACTOR_SIGNATURE = "(sint32, [sint32], [sint32]): void";
-
-        @Child private Node factorMessage = Message.createExecute(3).createNode();
-        @Child private DLLRFFI.DLSymNode dlsymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
-
-        @CompilationFinal private TruffleObject fftFactorFunction;
+    private static class TruffleNFI_FactorNode extends TruffleNFI_DownCallNode implements FactorNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.fft_factor;
+        }
 
         @Override
         public void execute(int n, int[] pmaxf, int[] pmaxp) {
-            try {
-                if (fftFactorFunction == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    Node bind = Message.createInvoke(1).createNode();
-                    fftFactorFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, findSymbol(FFT_FACTOR, dlsymNode).asTruffleObject(), "bind", FFT_FACTOR_SIGNATURE);
-                }
-                ForeignAccess.sendExecute(factorMessage, fftFactorFunction, n, JavaInterop.asTruffleObject(pmaxf), JavaInterop.asTruffleObject(pmaxp));
-            } catch (InteropException t) {
-                throw RInternalError.shouldNotReachHere();
-            }
+            call(n, pmaxf, pmaxp);
         }
     }
 
-    private static class TruffleNFI_WorkNode extends WorkNode {
-        private static final String FFT_WORK = "fft_work";
-        private static final String FFT_WORK_SIGNATURE = "([double], sint32, sint32, sint32, sint32, [double], [sint32]): sint32";
-
-        @Child private DLLRFFI.DLSymNode dlsymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
-        @Child private Node workMessage = Message.createExecute(7).createNode();
-
-        @CompilationFinal private TruffleObject fftWorkFunction;
+    private static class TruffleNFI_WorkNode extends TruffleNFI_DownCallNode implements WorkNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.fft_work;
+        }
 
         @Override
         public int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
-            try {
-                if (fftWorkFunction == null) {
-                    CompilerDirectives.transferToInterpreterAndInvalidate();
-                    Node bind = Message.createInvoke(1).createNode();
-                    fftWorkFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, findSymbol(FFT_WORK, dlsymNode).asTruffleObject(), "bind", FFT_WORK_SIGNATURE);
-                }
-                return (int) ForeignAccess.sendExecute(workMessage, fftWorkFunction, JavaInterop.asTruffleObject(a), nseg, n, nspn, isn,
-                                JavaInterop.asTruffleObject(work), JavaInterop.asTruffleObject(iwork));
-            } catch (InteropException t) {
-                throw RInternalError.shouldNotReachHere();
-            }
+            return (int) call(a, nseg, n, nspn, isn, work, iwork);
         }
     }
 
-    private static SymbolHandle findSymbol(String symbol, DLLRFFI.DLSymNode dlsymNode) {
-        SymbolHandle fftAddress;
-        DLLInfo dllInfo = DLL.findLibrary("stats");
-        assert dllInfo != null;
-        // maybe DLL.findSymbol(symbol, dllInfo); ?
-        fftAddress = dlsymNode.execute(dllInfo.handle, symbol);
-        assert fftAddress != DLL.SYMBOL_NOT_FOUND;
-        return fftAddress;
-    }
-
     @Override
     public FactorNode createFactorNode() {
         return new TruffleNFI_FactorNode();
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UserRng.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UserRng.java
index b673b7ec201b0be0c7a194181f602a05b5e82923..e68c870b4a31a4e5e71819cc8f70b037a985727d 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UserRng.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UserRng.java
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
@@ -32,84 +34,79 @@ import com.oracle.truffle.r.runtime.rng.user.UserRNG.Function;
 
 public class TruffleNFI_UserRng implements UserRngRFFI {
 
-    private static class NFIUserRngRFFINode extends UserRngRFFINode {
-        Node initMessage;
-        Node randMessage;
-        Node nSeedMessage;
-        Node seedsMessage;
-        Node readPointerNode = Message.createExecute(1).createNode();
+    private abstract static class RNGNode extends Node {
 
-        TruffleObject initFunction;
-        TruffleObject nSeedFunction;
-        TruffleObject randFunction;
-        TruffleObject seedsFunction;
+        @CompilationFinal protected Node message;
+        @CompilationFinal protected Node readPointerNode = Message.createExecute(1).createNode();
+        @CompilationFinal protected TruffleObject targetFunction;
 
-        @Override
-        public void init(int seed) {
-            if (initMessage == null) {
-                initMessage = Message.createExecute(1).createNode();
+        protected void init(Function function, String signature) {
+            if (message == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                message = Message.createExecute(TruffleNFI_Utils.getArgCount(signature)).createNode();
             }
-            try {
-                if (initFunction == null) {
-                    Node bind = Message.createInvoke(1).createNode();
-                    initFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, Function.Init.getSymbolHandle().asTruffleObject(), "bind", "(sint32): void");
+            if (targetFunction == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                Node bind = Message.createInvoke(1).createNode();
+                try {
+                    targetFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, function.getSymbolHandle().asTruffleObject(), "bind", signature);
+                } catch (Throwable t) {
+                    throw RInternalError.shouldNotReachHere();
                 }
-                ForeignAccess.sendExecute(initMessage, initFunction, seed);
+            }
+        }
+    }
+
+    private static final class TruffleNFI_InitNode extends RNGNode implements InitNode {
+
+        @Override
+        public void execute(int seed) {
+            init(Function.Init, "(sint32): void");
+            try {
+                ForeignAccess.sendExecute(message, targetFunction, seed);
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere();
             }
         }
+    }
+
+    private static final class TruffleNFI_RandNode extends RNGNode implements RandNode {
 
         @Override
-        public double rand() {
-            if (randMessage == null) {
-                randMessage = Message.createExecute(0).createNode();
-            }
+        public double execute() {
+            init(Function.Rand, "(): pointer");
             try {
-                if (randFunction == null) {
-                    Node bind = Message.createInvoke(1).createNode();
-                    randFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, Function.Rand.getSymbolHandle().asTruffleObject(), "bind", "(): pointer");
-                }
-                Object address = ForeignAccess.sendExecute(randMessage, randFunction);
-                Object value = ForeignAccess.sendExecute(readPointerNode, TruffleNFI_CAccess.Function.READ_POINTER_DOUBLE.getSymbolFunction(), address);
-                return (double) value;
+                Object address = ForeignAccess.sendExecute(message, targetFunction);
+                return (double) ForeignAccess.sendExecute(readPointerNode, TruffleNFI_CAccess.Function.READ_POINTER_DOUBLE.getSymbolFunction(), address);
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere();
             }
         }
+    }
+
+    private static final class TruffleNFI_NSeedNode extends RNGNode implements NSeedNode {
 
         @Override
-        public int nSeed() {
-            if (nSeedMessage == null) {
-                nSeedMessage = Message.createExecute(0).createNode();
-            }
+        public int execute() {
+            init(Function.NSeed, "(): pointer");
             try {
-                if (nSeedFunction == null) {
-                    Node bind = Message.createInvoke(1).createNode();
-                    nSeedFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, Function.NSeed.getSymbolHandle().asTruffleObject(), "bind", "(): pointer");
-                }
-                Object address = ForeignAccess.sendExecute(nSeedMessage, nSeedFunction);
-                Object n = ForeignAccess.sendExecute(readPointerNode, TruffleNFI_CAccess.Function.READ_POINTER_INT.getSymbolFunction(), address);
-                return (int) n;
+                Object address = ForeignAccess.sendExecute(message, targetFunction);
+                return (int) ForeignAccess.sendExecute(readPointerNode, TruffleNFI_CAccess.Function.READ_POINTER_INT.getSymbolFunction(), address);
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere();
             }
         }
+    }
+
+    private static final class TruffleNFI_SeedsNode extends RNGNode implements SeedsNode {
 
         @Override
-        public void seeds(int[] n) {
-            if (seedsMessage == null) {
-                seedsMessage = Message.createExecute(0).createNode();
-            }
+        public void execute(int[] n) {
+            init(Function.Seedloc, "(): pointer");
             try {
-                if (seedsFunction == null) {
-                    Node bind = Message.createInvoke(1).createNode();
-                    seedsFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, Function.Seedloc.getSymbolHandle().asTruffleObject(), "bind", "(): pointer");
-                }
-                Object address = ForeignAccess.sendExecute(seedsMessage, seedsFunction);
+                Object address = ForeignAccess.sendExecute(message, targetFunction);
                 for (int i = 0; i < n.length; i++) {
-                    Object seed = ForeignAccess.sendExecute(readPointerNode, TruffleNFI_CAccess.Function.READ_ARRAY_INT.getSymbolFunction(), address, i);
-                    n[i] = (int) seed;
+                    n[i] = (int) ForeignAccess.sendExecute(readPointerNode, TruffleNFI_CAccess.Function.READ_ARRAY_INT.getSymbolFunction(), address, i);
                 }
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere();
@@ -118,7 +115,22 @@ public class TruffleNFI_UserRng implements UserRngRFFI {
     }
 
     @Override
-    public UserRngRFFINode createUserRngRFFINode() {
-        return new NFIUserRngRFFINode();
+    public InitNode createInitNode() {
+        return new TruffleNFI_InitNode();
+    }
+
+    @Override
+    public RandNode createRandNode() {
+        return new TruffleNFI_RandNode();
+    }
+
+    @Override
+    public NSeedNode createNSeedNode() {
+        return new TruffleNFI_NSeedNode();
+    }
+
+    @Override
+    public SeedsNode createSeedsNode() {
+        return new TruffleNFI_SeedsNode();
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java
index 2ab88086c95fd7fb7d8f5fb46bb24460ddd253d1..45fb1fd383423aac2755110063571d7e9d449bc6 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java
@@ -32,8 +32,11 @@ import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter;
+import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_DLL.NFIHandle;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLL.DLLInfo;
 
 public class TruffleNFI_Utils {
 
@@ -79,6 +82,16 @@ public class TruffleNFI_Utils {
         }
     }
 
+    static TruffleObject lookupAndBindStats(String name, String signature) {
+        DLLInfo dllInfo = DLL.findLibrary("stats");
+        try {
+            TruffleObject result = (TruffleObject) ForeignAccess.sendRead(Message.READ.createNode(), ((NFIHandle) dllInfo.handle).libHandle, name);
+            return (TruffleObject) ForeignAccess.sendInvoke(Message.createInvoke(1).createNode(), result, "bind", signature);
+        } catch (InteropException e) {
+            throw RInternalError.shouldNotReachHere(e);
+        }
+    }
+
     /**
      * Returns the number of arguments in an NFI signature.
      */
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Zip.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Zip.java
index a3ff3980e252e40e3123e583b96f212ea031ffff..fe6ff0492cdd9fad6c5a9e3d5cc043105f91be79 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Zip.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Zip.java
@@ -22,46 +22,36 @@
  */
 package com.oracle.truffle.r.ffi.impl.nfi;
 
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
 public class TruffleNFI_Zip implements ZipRFFI {
 
-    private static class TruffleNFI_CompressNode extends ZipRFFI.CompressNode {
-        @Child private Node message = NFIFunction.compress.createMessage();
+    private static class TruffleNFI_CompressNode extends TruffleNFI_DownCallNode implements ZipRFFI.CompressNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.compress;
+        }
 
         @Override
         public int execute(byte[] dest, byte[] source) {
             long[] destlen = new long[]{dest.length};
-            try {
-                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.compress.getFunction(),
-                                JavaInterop.asTruffleObject(dest), JavaInterop.asTruffleObject(destlen),
-                                JavaInterop.asTruffleObject(source), source.length);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            int result = (int) call(JavaInterop.asTruffleObject(dest), JavaInterop.asTruffleObject(destlen), JavaInterop.asTruffleObject(source), source.length);
+            return result;
         }
     }
 
-    private static class TruffleNFI_UncompressNode extends ZipRFFI.UncompressNode {
-        @Child private Node message = NFIFunction.uncompress.createMessage();
+    private static class TruffleNFI_UncompressNode extends TruffleNFI_DownCallNode implements ZipRFFI.UncompressNode {
+        @Override
+        protected NFIFunction getFunction() {
+            return NFIFunction.uncompress;
+        }
 
         @Override
         public int execute(byte[] dest, byte[] source) {
             long[] destlen = new long[]{dest.length};
-            try {
-                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.uncompress.getFunction(),
-                                JavaInterop.asTruffleObject(dest), JavaInterop.asTruffleObject(destlen),
-                                JavaInterop.asTruffleObject(source), source.length);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
+            int result = (int) call(JavaInterop.asTruffleObject(dest), JavaInterop.asTruffleObject(destlen), JavaInterop.asTruffleObject(source), source.length);
+            return result;
         }
     }
 
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 c3f83f62351031fb461fd8c64f836901b3bfc80d..b62147da84a016d7f4cd51f1ab5090db514cc0a9 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
@@ -44,7 +44,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 private ToolsRFFI.ParseRdNode parseRdNode = RFFIFactory.getRFFI().getToolsRFFI().createParseRdNode();
+    @Child private ToolsRFFI.ParseRdNode parseRdNode = RFFIFactory.getToolsRFFI().createParseRdNode();
 
     static {
         Casts casts = new Casts(C_ParseRd.class);
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 c8244e9b43e119641fcfe61092aaede81f6c5d26..868d59ad7321fbca04a61103f268912a5079f6cb 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
@@ -31,11 +31,12 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
+import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 @RBuiltin(name = "getwd", kind = INTERNAL, parameterNames = {}, behavior = IO)
 public abstract class Getwd extends RBuiltinNode.Arg0 {
 
-    @Child private BaseRFFI.GetwdNode getwdNode = BaseRFFI.GetwdNode.create();
+    @Child private BaseRFFI.GetwdNode getwdNode = RFFIFactory.getBaseRFFI().createGetwdNode();
 
     @Specialization
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java
index ccc5f3390a4957c3831ae29c75721005c5d54f9a..48237479d1eaa7a9477e52eff09ce9b8e4f403c9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java
@@ -123,8 +123,8 @@ public class GrepFunctions {
 
     @NodeInfo(cost = NodeCost.NONE)
     public static class CommonCodeNode extends RBaseNode {
-        @Child protected PCRERFFI.MaketablesNode maketablesNode = RFFIFactory.getRFFI().getPCRERFFI().createMaketablesNode();
-        @Child protected PCRERFFI.CompileNode compileNode = RFFIFactory.getRFFI().getPCRERFFI().createCompileNode();
+        @Child protected PCRERFFI.MaketablesNode maketablesNode = RFFIFactory.getPCRERFFI().createMaketablesNode();
+        @Child protected PCRERFFI.CompileNode compileNode = RFFIFactory.getPCRERFFI().createCompileNode();
 
         /**
          * Temporary method that handles the check for the arguments that are common to the majority
@@ -218,7 +218,7 @@ public class GrepFunctions {
     }
 
     protected static final class GrepCommonCodeNode extends CommonCodeNode {
-        @Child PCRERFFI.ExecNode execNode = RFFIFactory.getRFFI().getPCRERFFI().createExecNode();
+        @Child PCRERFFI.ExecNode execNode = RFFIFactory.getPCRERFFI().createExecNode();
 
         protected Object doGrep(String patternArg, RAbstractStringVector vector, boolean ignoreCase, boolean value, boolean perlPar, boolean fixed,
                         @SuppressWarnings("unused") boolean useBytes, boolean invert, boolean grepl) {
@@ -422,7 +422,7 @@ public class GrepFunctions {
     }
 
     protected static final class SubCommonCodeNode extends CommonCodeNode {
-        @Child PCRERFFI.ExecNode execNode = RFFIFactory.getRFFI().getPCRERFFI().createExecNode();
+        @Child PCRERFFI.ExecNode execNode = RFFIFactory.getPCRERFFI().createExecNode();
 
         protected RStringVector doSub(String patternArg, String replacementArg, RAbstractStringVector vector, boolean ignoreCase, boolean perlPar,
                         boolean fixedPar, @SuppressWarnings("unused") boolean useBytes, boolean gsub) {
@@ -745,9 +745,9 @@ public class GrepFunctions {
         @Child SetFixedAttributeNode setCaptureLengthAttrNode = SetFixedAttributeNode.create("capture.length");
         @Child SetFixedAttributeNode setCaptureNamesAttrNode = SetFixedAttributeNode.create("capture.names");
         @Child SetFixedAttributeNode setDimNamesAttrNode = SetFixedAttributeNode.createDimNames();
-        @Child PCRERFFI.ExecNode execNode = RFFIFactory.getRFFI().getPCRERFFI().createExecNode();
-        @Child PCRERFFI.GetCaptureNamesNode getCaptureNamesNode = RFFIFactory.getRFFI().getPCRERFFI().createGetCaptureNamesNode();
-        @Child PCRERFFI.GetCaptureCountNode getCaptureCountNode = RFFIFactory.getRFFI().getPCRERFFI().createGetCaptureCountNode();
+        @Child PCRERFFI.ExecNode execNode = RFFIFactory.getPCRERFFI().createExecNode();
+        @Child PCRERFFI.GetCaptureNamesNode getCaptureNamesNode = RFFIFactory.getPCRERFFI().createGetCaptureNamesNode();
+        @Child PCRERFFI.GetCaptureCountNode getCaptureCountNode = RFFIFactory.getPCRERFFI().createGetCaptureCountNode();
 
         static {
             Casts casts = new Casts(Regexpr.class);
@@ -1343,7 +1343,7 @@ public class GrepFunctions {
     @ImportStatic(GrepFunctions.class)
     @RBuiltin(name = "strsplit", kind = INTERNAL, parameterNames = {"x", "split", "fixed", "perl", "useBytes"}, behavior = PURE)
     public abstract static class Strsplit extends RBuiltinNode.Arg5 {
-        @Child PCRERFFI.ExecNode execNode = RFFIFactory.getRFFI().getPCRERFFI().createExecNode();
+        @Child PCRERFFI.ExecNode execNode = RFFIFactory.getPCRERFFI().createExecNode();
 
         static {
             Casts casts = new Casts(Strsplit.class);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
index 697043974ea64de7739edaa1e86e1836294ab5a0..48044327ef71962c683e810d0c5fffc8d4a0834e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
@@ -75,7 +75,7 @@ public class LaFunctions {
 
     @RBuiltin(name = "La_version", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class Version extends RBuiltinNode.Arg0 {
-        @Child LapackRFFI.IlaverNode ilaverNode = RFFIFactory.getRFFI().getLapackRFFI().createIlaverNode();
+        @Child LapackRFFI.IlaverNode ilaverNode = RFFIFactory.getLapackRFFI().createIlaverNode();
 
         @Specialization
         @TruffleBoundary
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 92ca9352eafb946d049bb19e5cbcd3defa9289bd..0965acbef02e3ea24c704d0bc2dead29d08211f4 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
@@ -30,13 +30,13 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
+import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 @RBuiltin(name = "setwd", visibility = OFF, kind = INTERNAL, parameterNames = "path", behavior = IO)
 public abstract class Setwd extends RBuiltinNode.Arg1 {
@@ -46,11 +46,12 @@ public abstract class Setwd extends RBuiltinNode.Arg1 {
         casts.arg("path").defaultError(CHAR_ARGUMENT).mustBe(stringValue()).asStringVector().mustBe(notEmpty()).findFirst();
     }
 
+    @Child private BaseRFFI.GetwdNode getwdNode = RFFIFactory.getBaseRFFI().createGetwdNode();
+    @Child private BaseRFFI.SetwdNode setwdNode = RFFIFactory.getBaseRFFI().createSetwdNode();
+
     @Specialization
     @TruffleBoundary
-    protected Object setwd(String path,
-                    @Cached("create()") BaseRFFI.GetwdNode getwdNode,
-                    @Cached("create()") BaseRFFI.SetwdNode setwdNode) {
+    protected Object setwd(String path) {
         String owd = getwdNode.execute();
         String nwd = Utils.tildeExpand(path);
         int rc = setwdNode.execute(nwd);
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 ad7d504a4ef5892e5c8d98091c3c6eee905515c6..2eb33a9b1299a8e003ef3c6a189b701c63599768 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
@@ -221,7 +221,7 @@ public class CallAndExternalFunctions {
     @RBuiltin(name = ".Call", kind = PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}, behavior = COMPLEX)
     public abstract static class DotCall extends LookupAdapter {
 
-        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
+        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getCallRFFI().createInvokeCallNode();
 
         static {
             Casts.noCasts(DotCall.class);
@@ -719,7 +719,7 @@ public class CallAndExternalFunctions {
     @RBuiltin(name = ".External", kind = PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}, behavior = COMPLEX)
     public abstract static class DotExternal extends LookupAdapter {
 
-        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
+        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getCallRFFI().createInvokeCallNode();
 
         static {
             Casts.noCasts(DotExternal.class);
@@ -832,7 +832,7 @@ public class CallAndExternalFunctions {
          */
         @CompilationFinal private Object op = null;
 
-        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
+        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getCallRFFI().createInvokeCallNode();
 
         static {
             Casts.noCasts(DotExternal2.class);
@@ -917,7 +917,7 @@ public class CallAndExternalFunctions {
     @RBuiltin(name = ".External.graphics", kind = PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}, behavior = COMPLEX)
     public abstract static class DotExternalGraphics extends LookupAdapter {
 
-        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
+        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getCallRFFI().createInvokeCallNode();
 
         static {
             Casts.noCasts(DotExternalGraphics.class);
@@ -971,7 +971,7 @@ public class CallAndExternalFunctions {
     @RBuiltin(name = ".Call.graphics", kind = PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}, behavior = COMPLEX)
     public abstract static class DotCallGraphics extends LookupAdapter {
 
-        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode();
+        @Child CallRFFI.InvokeCallNode callRFFINode = RFFIFactory.getCallRFFI().createInvokeCallNode();
 
         static {
             Casts.noCasts(DotCallGraphics.class);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
index 4f04292599ff7e04365ccd2a9dcbe7eb9dedf4ca..8a4d7c4ea7ef4c4c5e151f03cfa26cc40641b34e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
@@ -28,7 +28,7 @@ import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public abstract class Dqrcf extends RExternalBuiltinNode.Arg8 {
-    @Child private RApplRFFI.DqrcfNode dqcrfNode = RFFIFactory.getRFFI().getRApplRFFI().createDqrcfNode();
+    @Child private RApplRFFI.DqrcfNode dqcrfNode = RFFIFactory.getRApplRFFI().createDqrcfNode();
 
     private static final String E = RRuntime.NAMES_ATTR_EMPTY_VALUE;
     private static final RStringVector DQRCF_NAMES = RDataFactory.createStringVector(new String[]{E, E, E, E, E, E, "coef", "info"}, RDataFactory.COMPLETE_VECTOR);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
index b546af27357097d5925f084a10af04b9f5b85cd4..357bf832692615454238f5ee94cc264b4a1cf6a0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
@@ -26,7 +26,7 @@ import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public abstract class Dqrdc2 extends RExternalBuiltinNode.Arg9 {
-    @Child private RApplRFFI.Dqrdc2Node dqrdc2Node = RFFIFactory.getRFFI().getRApplRFFI().createDqrdc2Node();
+    @Child private RApplRFFI.Dqrdc2Node dqrdc2Node = RFFIFactory.getRApplRFFI().createDqrdc2Node();
 
     private static final String E = RRuntime.NAMES_ATTR_EMPTY_VALUE;
     private static final RStringVector DQRDC2_NAMES = RDataFactory.createStringVector(new String[]{"qr", E, E, E, E, "rank", "qraux", "pivot", E}, RDataFactory.COMPLETE_VECTOR);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrqty.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrqty.java
index e1926ed0d7e977e5b45aea7a5e354406b1780249..3a681d45226965b96f338db8851e8827676508a9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrqty.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrqty.java
@@ -36,7 +36,7 @@ import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public abstract class Dqrqty extends RExternalBuiltinNode.Arg7 {
-    @Child private RApplRFFI.DqrqtyNode dqrqtyNode = RFFIFactory.getRFFI().getRApplRFFI().createDqrqtyNode();
+    @Child private RApplRFFI.DqrqtyNode dqrqtyNode = RFFIFactory.getRApplRFFI().createDqrqtyNode();
 
     static {
         Casts casts = new Casts(Dqrqty.class);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrqy.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrqy.java
index a47e4a14a1c2fe2485c56abe1a4a047210739944..9ccdf43844cb8f5e3f46abe5716c1e5ad4b233a1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrqy.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrqy.java
@@ -36,7 +36,7 @@ import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public abstract class Dqrqy extends RExternalBuiltinNode.Arg7 {
-    @Child private RApplRFFI.DqrqyNode dqrqyNode = RFFIFactory.getRFFI().getRApplRFFI().createDqrqyNode();
+    @Child private RApplRFFI.DqrqyNode dqrqyNode = RFFIFactory.getRApplRFFI().createDqrqyNode();
 
     static {
         Casts casts = new Casts(Dqrqy.class);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrrsd.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrrsd.java
index f5b161299c9958ee59c2804d06d3b5f603f4fe25..23b66ab9b34d888b240ed044d08ee340e3615599 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrrsd.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrrsd.java
@@ -36,7 +36,7 @@ import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public abstract class Dqrrsd extends RExternalBuiltinNode.Arg7 {
-    @Child private RApplRFFI.DqrrsdNode dqrrsdNode = RFFIFactory.getRFFI().getRApplRFFI().createDqrrsdNode();
+    @Child private RApplRFFI.DqrrsdNode dqrrsdNode = RFFIFactory.getRApplRFFI().createDqrrsdNode();
 
     static {
         Casts casts = new Casts(Dqrrsd.class);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrxb.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrxb.java
index 5e4a1d742fcef1ce98f2824d206d353606e509df..6702b7633d6158245e202cb3dd79106e774b86a0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrxb.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrxb.java
@@ -36,7 +36,7 @@ import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public abstract class Dqrxb extends RExternalBuiltinNode.Arg7 {
-    @Child private RApplRFFI.DqrxbNode dqrrsdNode = RFFIFactory.getRFFI().getRApplRFFI().createDqrxbNode();
+    @Child private RApplRFFI.DqrxbNode dqrrsdNode = RFFIFactory.getRApplRFFI().createDqrxbNode();
 
     static {
         Casts casts = new Casts(Dqrxb.class);
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 050194c0e67ad37ddb1b7e0c5c399cc9d50b0d72..72a31dc4aee921dbd699201441ba380916047251 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
@@ -68,7 +68,7 @@ public class FortranAndCFunctions {
         private static final Charset charset = Charset.forName("US-ASCII");
 
         @Child protected ExtractNativeCallInfoNode extractSymbolInfo = ExtractNativeCallInfoNodeGen.create();
-        @Child private CRFFI.InvokeCNode invokeCNode = RFFIFactory.getRFFI().getCRFFI().createInvokeCNode();
+        @Child private CRFFI.InvokeCNode invokeCNode = RFFIFactory.getCRFFI().createInvokeCNode();
 
         @Override
         public Object[] getDefaultParameterValues() {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java
index 47f7e66ea42b3325d297d573edf5171ddbeff747..c5f6be6387c73d8e5482e3939a400905e4d9743f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java
@@ -37,7 +37,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
  */
 @RBuiltin(name = ".fastr.dqrls", visibility = OFF, kind = PRIMITIVE, parameterNames = {"x", "n", "p", "y", "ny", "tol", "coeff"}, behavior = PURE)
 public abstract class FastrDqrls extends RBuiltinNode.Arg7 {
-    @Child private RApplRFFI.DqrlsNode dqrlsNode = RFFIFactory.getRFFI().getRApplRFFI().createDqrlsNode();
+    @Child private RApplRFFI.DqrlsNode dqrlsNode = RFFIFactory.getRApplRFFI().createDqrlsNode();
 
     private static final String[] NAMES = new String[]{"qr", "coefficients", "residuals", "effects", "rank", "pivot", "qraux", "tol", "pivoted"};
     private static RStringVector namesVector = null;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
index da32bbc87d789bc01c315f9acc583553cb2c9f70..ff122d9ae283925310bf45f50762ab6ca98be07c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
@@ -30,7 +30,7 @@ public class RCleanUp {
 
     public static void cleanUp(SA_TYPE saveType, int status, boolean runLast) {
         if (RInterfaceCallbacks.R_CleanUp.isOverridden()) {
-            RFFIFactory.getRFFI().getREmbedRFFI().cleanUp(saveType.ordinal(), status, runLast ? 1 : 0);
+            RFFIFactory.getREmbedRFFI().cleanUp(saveType.ordinal(), status, runLast ? 1 : 0);
         } else {
             stdCleanUp(saveType, status, runLast);
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index 7906791e1f27ffa1f89e0dba2558fbe51c9b581d..03bbddee75cddc3aae06d32e9e661187d80debab 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -192,7 +192,7 @@ public final class Utils {
      */
     public static RuntimeException rSuicide(String msg) {
         if (RInterfaceCallbacks.R_Suicide.isOverridden()) {
-            RFFIFactory.getRFFI().getREmbedRFFI().suicide(msg);
+            RFFIFactory.getREmbedRFFI().suicide(msg);
         }
         throw rSuicideDefault(msg);
     }
@@ -200,7 +200,7 @@ public final class Utils {
     public static RuntimeException rSuicide(Throwable cause, String msg) {
         cause.printStackTrace();
         if (RInterfaceCallbacks.R_Suicide.isOverridden()) {
-            RFFIFactory.getRFFI().getREmbedRFFI().suicide(msg);
+            RFFIFactory.getREmbedRFFI().suicide(msg);
         }
         throw rSuicideDefault(msg);
     }
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 29afedc8a40c5c747d5969575a8bcff44d5706af..2a6ad6919008ef22802b97f96c276d9377bab21f 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
@@ -26,7 +26,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 
 /**
  * A statically typed interface to exactly those native functions required by the R {@code base}
@@ -34,50 +34,51 @@ import com.oracle.truffle.api.nodes.Node;
  * map 1-1 to a native function, they may involve the invocation of several native functions.
  */
 public interface BaseRFFI {
-    abstract class GetpidNode extends Node {
-        public abstract int execute();
 
-        public static GetpidNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createGetpidNode();
+    interface GetpidNode extends NodeInterface {
+        int execute();
+
+        static GetpidNode create() {
+            return RFFIFactory.getBaseRFFI().createGetpidNode();
         }
     }
 
-    abstract class GetwdNode extends Node {
+    interface GetwdNode extends NodeInterface {
         /**
          * Returns the current working directory, in the face of calls to {@code setwd}.
          */
-        public abstract String execute();
+        String execute();
 
-        public static GetwdNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createGetwdNode();
+        static GetwdNode create() {
+            return RFFIFactory.getBaseRFFI().createGetwdNode();
         }
     }
 
-    abstract class SetwdNode extends Node {
+    interface SetwdNode extends NodeInterface {
         /**
          * Sets the current working directory to {@code dir}. (cf. Unix {@code chdir}).
          *
          * @return 0 if successful.
          */
-        public abstract int execute(String dir);
+        int execute(String dir);
 
-        public static SetwdNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createSetwdNode();
+        static SetwdNode create() {
+            return RFFIFactory.getBaseRFFI().createSetwdNode();
         }
     }
 
-    abstract class MkdirNode extends Node {
+    interface MkdirNode extends NodeInterface {
         /**
-         * Create directory with given mode. Exception is thrown omn error.
+         * Create directory with given mode. Exception is thrown on error.
          */
-        public abstract void execute(String dir, int mode) throws IOException;
+        void execute(String dir, int mode) throws IOException;
 
-        public static MkdirNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createMkdirNode();
+        static MkdirNode create() {
+            return RFFIFactory.getBaseRFFI().createMkdirNode();
         }
     }
 
-    abstract class ReadlinkNode extends Node {
+    interface ReadlinkNode extends NodeInterface {
         /**
          * Try to convert a symbolic link to it's target.
          *
@@ -85,44 +86,44 @@ 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 execute(String path) throws IOException;
+        String execute(String path) throws IOException;
 
-        public static ReadlinkNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createReadlinkNode();
+        static ReadlinkNode create() {
+            return RFFIFactory.getBaseRFFI().createReadlinkNode();
         }
     }
 
-    abstract class MkdtempNode extends Node {
+    interface MkdtempNode extends NodeInterface {
         /**
          * Creates a temporary directory using {@code template} and return the resulting path or
          * {@code null} if error.
          */
-        public abstract String execute(String template);
+        String execute(String template);
 
-        public static MkdtempNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createMkdtempNode();
+        static MkdtempNode create() {
+            return RFFIFactory.getBaseRFFI().createMkdtempNode();
         }
     }
 
-    abstract class ChmodNode extends Node {
+    interface ChmodNode extends NodeInterface {
         /**
          * Change the file mode of {@code path}.
          */
-        public abstract int execute(String path, int mode);
+        int execute(String path, int mode);
 
-        public static ChmodNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createChmodNode();
+        static ChmodNode create() {
+            return RFFIFactory.getBaseRFFI().createChmodNode();
         }
     }
 
-    abstract class StrolNode extends Node {
+    interface StrolNode extends NodeInterface {
         /**
          * Convert string to long.
          */
-        public abstract long execute(String s, int base) throws IllegalArgumentException;
+        long execute(String s, int base) throws IllegalArgumentException;
 
-        public static StrolNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createStrolNode();
+        static StrolNode create() {
+            return RFFIFactory.getBaseRFFI().createStrolNode();
         }
     }
 
@@ -138,27 +139,27 @@ public interface BaseRFFI {
         String nodename();
     }
 
-    abstract class UnameNode extends Node {
+    interface UnameNode extends NodeInterface {
         /**
          * Return {@code utsname} info.
          */
-        public abstract UtsName execute();
+        UtsName execute();
 
-        public static UnameNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createUnameNode();
+        static UnameNode create() {
+            return RFFIFactory.getBaseRFFI().createUnameNode();
         }
     }
 
-    abstract class GlobNode extends Node {
+    interface GlobNode extends NodeInterface {
         /**
          * Returns an array of pathnames that match {@code pattern} using the OS glob function. This
          * is done in native code because it is very hard to write in Java in the face of
          * {@code setwd}.
          */
-        public abstract ArrayList<String> glob(String pattern);
+        ArrayList<String> glob(String pattern);
 
-        public static GlobNode create() {
-            return RFFIFactory.getRFFI().getBaseRFFI().createGlobNode();
+        static GlobNode create() {
+            return RFFIFactory.getBaseRFFI().createGlobNode();
         }
     }
 
@@ -196,7 +197,7 @@ public interface BaseRFFI {
         private static GetpidRootNode getpidRootNode;
 
         private GetpidRootNode() {
-            super(RFFIFactory.getRFFI().getBaseRFFI().createGetpidNode());
+            super(RFFIFactory.getBaseRFFI().createGetpidNode());
         }
 
         @Override
@@ -216,7 +217,7 @@ public interface BaseRFFI {
         private static GetwdRootNode getwdRootNode;
 
         private GetwdRootNode() {
-            super(RFFIFactory.getRFFI().getBaseRFFI().createGetwdNode());
+            super(RFFIFactory.getBaseRFFI().createGetwdNode());
         }
 
         @Override
@@ -236,7 +237,7 @@ public interface BaseRFFI {
         private static MkdtempRootNode mkdtempRootNode;
 
         private MkdtempRootNode() {
-            super(RFFIFactory.getRFFI().getBaseRFFI().createMkdtempNode());
+            super(RFFIFactory.getBaseRFFI().createMkdtempNode());
         }
 
         @Override
@@ -257,7 +258,7 @@ public interface BaseRFFI {
         private static UnameRootNode unameRootNode;
 
         private UnameRootNode() {
-            super(RFFIFactory.getRFFI().getBaseRFFI().createUnameNode());
+            super(RFFIFactory.getBaseRFFI().createUnameNode());
         }
 
         @Override
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 c914be958fcfedee11a6b50ff5a2e80c540e0d2f..070fb25fb0d4ad831bc43084f25de50b58cef01c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFI.java
@@ -22,13 +22,13 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 
 /**
  * Support for the {.C} and {.Fortran} calls.
  */
 public interface CRFFI {
-    abstract class InvokeCNode extends Node {
+    interface InvokeCNode extends NodeInterface {
         /**
          * 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[]}
@@ -38,7 +38,7 @@ public interface CRFFI {
          *            values of type {@code byte[][]}, which represent arrays of strings in ASCII
          *            encoding.
          */
-        public abstract void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings);
+        void execute(NativeCallInfo nativeCallInfo, Object[] args, boolean hasStrings);
     }
 
     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 d797005418390c6db346aee0ea4645b05fcdae5d..19f20718cee563658ec4faf3b1e65b009706c34e 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
@@ -23,28 +23,27 @@
 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.api.nodes.NodeInterface;
 import com.oracle.truffle.r.runtime.data.RNull;
 
 /**
  * Support for the {.Call} and {.External} calls.
  */
 public interface CallRFFI {
-    abstract class InvokeCallNode extends Node {
+    interface InvokeCallNode extends NodeInterface {
         /**
          * 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 execute(NativeCallInfo nativeCallInfo, Object[] args);
+        Object execute(NativeCallInfo nativeCallInfo, Object[] args);
     }
 
-    abstract class InvokeVoidCallNode extends Node {
+    interface InvokeVoidCallNode extends NodeInterface {
         /**
          * Variant that does not return a result (primarily for library "init" methods).
          */
-        public abstract void execute(NativeCallInfo nativeCallInfo, Object[] args);
-
+        void execute(NativeCallInfo nativeCallInfo, Object[] args);
     }
 
     InvokeCallNode createInvokeCallNode();
@@ -55,7 +54,7 @@ public interface CallRFFI {
         private static InvokeCallRootNode invokeCallRootNode;
 
         private InvokeCallRootNode() {
-            super(RFFIFactory.getRFFI().getCallRFFI().createInvokeCallNode());
+            super(RFFIFactory.getCallRFFI().createInvokeCallNode());
         }
 
         @Override
@@ -76,7 +75,7 @@ public interface CallRFFI {
         private static InvokeVoidCallRootNode InvokeVoidCallRootNode;
 
         private InvokeVoidCallRootNode() {
-            super(RFFIFactory.getRFFI().getCallRFFI().createInvokeVoidCallNode());
+            super(RFFIFactory.getCallRFFI().createInvokeVoidCallNode());
         }
 
         @Override
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 977709d6d24206e49c0191ae4282a8a63ed36db3..74fde8799285b90d7bd9015a852b9df7da0f772a 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
@@ -433,8 +433,8 @@ public class DLL {
 
     public static class LoadPackageDLLNode extends Node {
         @Child private InvokeVoidCallNode invokeVoidCallNode;
-        @Child private DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
-        @Child private DLLRFFI.DLOpenNode dlOpenNode = RFFIFactory.getRFFI().getDLLRFFI().createDLOpenNode();
+        @Child private DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getDLLRFFI().createDLSymNode();
+        @Child private DLLRFFI.DLOpenNode dlOpenNode = RFFIFactory.getDLLRFFI().createDLOpenNode();
 
         public static LoadPackageDLLNode create() {
             return new LoadPackageDLLNode();
@@ -459,7 +459,7 @@ public class DLL {
                 try {
                     if (invokeVoidCallNode == null) {
                         CompilerDirectives.transferToInterpreterAndInvalidate();
-                        invokeVoidCallNode = insert(RFFIFactory.getRFFI().getCallRFFI().createInvokeVoidCallNode());
+                        invokeVoidCallNode = (InvokeVoidCallNode) insert((Node) RFFIFactory.getCallRFFI().createInvokeVoidCallNode());
                     }
                     invokeVoidCallNode.execute(new NativeCallInfo(pkgInit, initFunc, dllInfo), new Object[]{dllInfo});
                 } catch (ReturnException ex) {
@@ -503,7 +503,7 @@ public class DLL {
     }
 
     public static class UnloadNode extends Node {
-        @Child private DLLRFFI.DLCloseNode dlCloseNode = RFFIFactory.getRFFI().getDLLRFFI().createDLCloseNode();
+        @Child private DLLRFFI.DLCloseNode dlCloseNode = RFFIFactory.getDLLRFFI().createDLCloseNode();
 
         @TruffleBoundary
         public void execute(String path) throws DLLException {
@@ -644,7 +644,7 @@ public class DLL {
     }
 
     public static final class RdlsymNode extends Node {
-        @Child DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getDLLRFFI().createDLSymNode();
 
         /**
          * Directly analogous to the GnuR function {@code R_dlsym}. Checks first for a
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 d4f90599b29133131174d1048de7690b854ef7be..5b9d61239f89ebe117cf3b8fafef84b7e023415f 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
@@ -24,46 +24,46 @@ package com.oracle.truffle.r.runtime.ffi;
 
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 
 public interface DLLRFFI {
-    abstract class DLOpenNode extends Node {
+    interface DLOpenNode extends NodeInterface {
         /**
          * Open a DLL.
          *
          * @return {@code null} on error, opaque handle for following calls otherwise.
          */
-        public abstract Object execute(String path, boolean local, boolean now) throws UnsatisfiedLinkError;
+        Object execute(String path, boolean local, boolean now) throws UnsatisfiedLinkError;
 
-        public static DLOpenNode create() {
-            return RFFIFactory.getRFFI().getDLLRFFI().createDLOpenNode();
+        static DLOpenNode create() {
+            return RFFIFactory.getDLLRFFI().createDLOpenNode();
         }
     }
 
-    abstract class DLSymNode extends Node {
+    interface DLSymNode extends NodeInterface {
         /**
          * 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 execute(Object handle, String symbol) throws UnsatisfiedLinkError;
+        SymbolHandle execute(Object handle, String symbol) throws UnsatisfiedLinkError;
 
-        public static DLSymNode create() {
-            return RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        static DLSymNode create() {
+            return RFFIFactory.getDLLRFFI().createDLSymNode();
         }
     }
 
-    abstract class DLCloseNode extends Node {
+    interface DLCloseNode extends NodeInterface {
         /**
          * Close DLL specified by {@code handle}.
          */
-        public abstract int execute(Object handle);
+        int execute(Object handle);
 
-        public static DLCloseNode create() {
-            return RFFIFactory.getRFFI().getDLLRFFI().createDLCloseNode();
+        static DLCloseNode create() {
+            return RFFIFactory.getDLLRFFI().createDLCloseNode();
         }
     }
 
@@ -77,7 +77,7 @@ public interface DLLRFFI {
 
     final class DLOpenRootNode extends RFFIRootNode<DLOpenNode> {
         private DLOpenRootNode() {
-            super(RFFIFactory.getRFFI().getDLLRFFI().createDLOpenNode());
+            super(RFFIFactory.getDLLRFFI().createDLOpenNode());
         }
 
         @Override
@@ -95,7 +95,7 @@ public interface DLLRFFI {
         private static DLSymRootNode dlSymRootNode;
 
         private DLSymRootNode() {
-            super(RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode());
+            super(RFFIFactory.getDLLRFFI().createDLSymNode());
         }
 
         @Override
@@ -116,7 +116,7 @@ public interface DLLRFFI {
         private static DLCloseRootNode dlCloseRootNode;
 
         private DLCloseRootNode() {
-            super(RFFIFactory.getRFFI().getDLLRFFI().createDLCloseNode());
+            super(RFFIFactory.getDLLRFFI().createDLCloseNode());
         }
 
         @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/LapackRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/LapackRFFI.java
index 30632fc5d3d2457910993d77303c3e236f829171..d995028b428278380d452486f8a8faab5237ae56 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/LapackRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/LapackRFFI.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 
 /**
  * Collection of statically typed Lapack methods that are used in the {@code base} package. The
@@ -30,151 +30,151 @@ import com.oracle.truffle.api.nodes.Node;
  * the result of the call.
  */
 public interface LapackRFFI {
-    abstract class IlaverNode extends Node {
+    interface IlaverNode extends NodeInterface {
         /**
          * Return version info, mjor, minor, patch, in {@code version}.
          */
-        public abstract void execute(int[] version);
+        void execute(int[] version);
 
-        public static IlaverNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createIlaverNode();
+        static IlaverNode create() {
+            return RFFIFactory.getLapackRFFI().createIlaverNode();
         }
     }
 
-    abstract class DgeevNode extends Node {
+    interface DgeevNode extends NodeInterface {
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/d9/d28/dgeev_8f.html">spec</a>.
          */
-        public abstract int execute(char jobVL, char jobVR, int n, double[] a, int lda, double[] wr, double[] wi, double[] vl, int ldvl, double[] vr, int ldvr, double[] work, int lwork);
+        int execute(char jobVL, char jobVR, int n, double[] a, int lda, double[] wr, double[] wi, double[] vl, int ldvl, double[] vr, int ldvr, double[] work, int lwork);
 
-        public static DgeevNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDgeevNode();
+        static DgeevNode create() {
+            return RFFIFactory.getLapackRFFI().createDgeevNode();
         }
     }
 
-    abstract class Dgeqp3Node extends Node {
+    interface Dgeqp3Node extends NodeInterface {
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/db/de5/dgeqp3_8f.html">spec</a>.
          */
-        public abstract int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork);
+        int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork);
 
-        public static Dgeqp3Node create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDgeqp3Node();
+        static Dgeqp3Node create() {
+            return RFFIFactory.getLapackRFFI().createDgeqp3Node();
         }
     }
 
-    abstract class DormqrNode extends Node {
+    interface DormqrNode extends NodeInterface {
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/da/d82/dormqr_8f.html">spec</a>.
          */
-        public abstract int execute(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork);
+        int execute(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork);
 
-        public static DormqrNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDormqrNode();
+        static DormqrNode create() {
+            return RFFIFactory.getLapackRFFI().createDormqrNode();
         }
     }
 
-    abstract class DtrtrsNode extends Node {
+    interface DtrtrsNode extends NodeInterface {
 
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/d6/d6f/dtrtrs_8f.html">spec</a>.
          */
-        public abstract int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb);
+        int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb);
 
-        public static DtrtrsNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDtrtrsNode();
+        static DtrtrsNode create() {
+            return RFFIFactory.getLapackRFFI().createDtrtrsNode();
         }
     }
 
-    abstract class DgetrfNode extends Node {
+    interface DgetrfNode extends NodeInterface {
 
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/d3/d6a/dgetrf_8f.html">spec</a>.
          */
-        public abstract int execute(int m, int n, double[] a, int lda, int[] ipiv);
+        int execute(int m, int n, double[] a, int lda, int[] ipiv);
 
-        public static DgetrfNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDgetrfNode();
+        static DgetrfNode create() {
+            return RFFIFactory.getLapackRFFI().createDgetrfNode();
         }
     }
 
-    abstract class DpotrfNode extends Node {
+    interface DpotrfNode extends NodeInterface {
 
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/d0/d8a/dpotrf_8f.html">spec</a>.
          */
-        public abstract int execute(char uplo, int n, double[] a, int lda);
+        int execute(char uplo, int n, double[] a, int lda);
 
-        public static DpotrfNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDpotrfNode();
+        static DpotrfNode create() {
+            return RFFIFactory.getLapackRFFI().createDpotrfNode();
         }
     }
 
-    abstract class DpotriNode extends Node {
+    interface DpotriNode extends NodeInterface {
 
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/d0/d8a/dpotri_8f.html">spec</a>.
          */
-        public abstract int execute(char uplo, int n, double[] a, int lda);
+        int execute(char uplo, int n, double[] a, int lda);
 
-        public static DpotriNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDpotriNode();
+        static DpotriNode create() {
+            return RFFIFactory.getLapackRFFI().createDpotriNode();
         }
     }
 
-    abstract class DpstrfNode extends Node {
+    interface DpstrfNode extends NodeInterface {
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/dd/dad/dpstrf_8f.html">spec</a>.
          */
-        public abstract int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work);
+        int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work);
 
-        public static DpstrfNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDpstrfNode();
+        static DpstrfNode create() {
+            return RFFIFactory.getLapackRFFI().createDpstrfNode();
         }
     }
 
-    abstract class DgesvNode extends Node {
+    interface DgesvNode extends NodeInterface {
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/d8/d72/dgesv_8f.html">spec</a>.
          */
-        public abstract int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb);
+        int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb);
 
-        public static DgesvNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDgesvNode();
+        static DgesvNode create() {
+            return RFFIFactory.getLapackRFFI().createDgesvNode();
         }
     }
 
-    abstract class DlangeNode extends Node {
+    interface DlangeNode extends NodeInterface {
 
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/dc/d09/dlange_8f.html">spec</a>.
          */
-        public abstract double execute(char norm, int m, int n, double[] a, int lda, double[] work);
+        double execute(char norm, int m, int n, double[] a, int lda, double[] work);
 
-        public static DlangeNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDlangeNode();
+        static DlangeNode create() {
+            return RFFIFactory.getLapackRFFI().createDlangeNode();
         }
     }
 
-    abstract class DgeconNode extends Node {
+    interface DgeconNode extends NodeInterface {
 
         /**
          * See <a href="http://www.netlib.org/lapack/explore-html/db/de4/dgecon_8f.html">spec</a>.
          */
-        public abstract int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork);
+        int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork);
 
-        public static DgeconNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDgeconNode();
+        static DgeconNode create() {
+            return RFFIFactory.getLapackRFFI().createDgeconNode();
         }
     }
 
-    abstract class DsyevrNode extends Node {
+    interface DsyevrNode extends NodeInterface {
 
-        public abstract int execute(char jobz, char range, char uplo, int n, double[] a, int lda, double vl, double vu, int il, int iu, double abstol, int[] m, double[] w,
-                        double[] z, int ldz, int[] isuppz, double[] work, int lwork, int[] iwork, int liwork);
+        int execute(char jobz, char range, char uplo, int n, double[] a, int lda, double vl, double vu, int il, int iu, double abstol, int[] m, double[] w, double[] z, int ldz, int[] isuppz,
+                        double[] work, int lwork, int[] iwork, int liwork);
 
-        public static DsyevrNode create() {
-            return RFFIFactory.getRFFI().getLapackRFFI().createDsyevrNode();
+        static DsyevrNode create() {
+            return RFFIFactory.getLapackRFFI().createDsyevrNode();
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/MiscRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/MiscRFFI.java
index a401f6b1b84c35c807d0f3051392adfbb77692e4..7f20d3f638ce6e30adf34948f1231f4e42315bd7 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/MiscRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/MiscRFFI.java
@@ -22,18 +22,18 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 
 /**
  * Miscellaneous methods implemented in native code.
  *
  */
 public interface MiscRFFI {
-    abstract class ExactSumNode extends Node {
-        public abstract double execute(double[] values, boolean hasNa, boolean naRm);
+    interface ExactSumNode extends NodeInterface {
+        double execute(double[] values, boolean hasNa, boolean naRm);
 
-        public static ExactSumNode create() {
-            return RFFIFactory.getRFFI().getMiscRFFI().createExactSumNode();
+        static ExactSumNode create() {
+            return RFFIFactory.getMiscRFFI().createExactSumNode();
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/PCRERFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/PCRERFFI.java
index c963ab2ec8ff7385eb2d5eb97d09196fab53f23b..90489b5ffc5ac02677c3454379bb3b45015dac57 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/PCRERFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/PCRERFFI.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 
 /**
  * An interface to the <a href="http://www.pcre.org/original/doc/html/index.html">PCRE</a> library
@@ -49,54 +49,51 @@ public interface PCRERFFI {
         }
     }
 
-    abstract class MaketablesNode extends Node {
+    interface MaketablesNode extends NodeInterface {
+        long execute();
 
-        public abstract long execute();
-
-        public static MaketablesNode create() {
-            return RFFIFactory.getRFFI().getPCRERFFI().createMaketablesNode();
+        static MaketablesNode create() {
+            return RFFIFactory.getPCRERFFI().createMaketablesNode();
         }
     }
 
-    abstract class CompileNode extends Node {
-
-        public abstract Result execute(String pattern, int options, long tables);
+    interface CompileNode extends NodeInterface {
+        Result execute(String pattern, int options, long tables);
 
-        public static CompileNode create() {
-            return RFFIFactory.getRFFI().getPCRERFFI().createCompileNode();
+        static CompileNode create() {
+            return RFFIFactory.getPCRERFFI().createCompileNode();
         }
     }
 
-    abstract class GetCaptureCountNode extends Node {
-
-        public abstract int execute(long code, long extra);
+    interface GetCaptureCountNode extends NodeInterface {
+        int execute(long code, long extra);
 
-        public static GetCaptureCountNode create() {
-            return RFFIFactory.getRFFI().getPCRERFFI().createGetCaptureCountNode();
+        static GetCaptureCountNode create() {
+            return RFFIFactory.getPCRERFFI().createGetCaptureCountNode();
         }
     }
 
-    abstract class GetCaptureNamesNode extends Node {
-        public abstract String[] execute(long code, long extra, int captureCount);
+    interface GetCaptureNamesNode extends NodeInterface {
+        String[] execute(long code, long extra, int captureCount);
 
-        public static GetCaptureNamesNode create() {
-            return RFFIFactory.getRFFI().getPCRERFFI().createGetCaptureNamesNode();
+        static GetCaptureNamesNode create() {
+            return RFFIFactory.getPCRERFFI().createGetCaptureNamesNode();
         }
     }
 
-    abstract class StudyNode extends Node {
-        public abstract Result execute(long code, int options);
+    interface StudyNode extends NodeInterface {
+        Result execute(long code, int options);
 
-        public static StudyNode create() {
-            return RFFIFactory.getRFFI().getPCRERFFI().createStudyNode();
+        static StudyNode create() {
+            return RFFIFactory.getPCRERFFI().createStudyNode();
         }
     }
 
-    abstract class ExecNode extends Node {
-        public abstract int execute(long code, long extra, String subject, int offset, int options, int[] ovector);
+    interface ExecNode extends NodeInterface {
+        int execute(long code, long extra, String subject, int offset, int options, int[] ovector);
 
-        public static ExecNode create() {
-            return RFFIFactory.getRFFI().getPCRERFFI().createExecNode();
+        static ExecNode create() {
+            return RFFIFactory.getPCRERFFI().createExecNode();
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RApplRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RApplRFFI.java
index d24fa19c95f553d1a6bfa1b3da03c0b45942a377..1134a3c705374c907bed9a82be01a8c04bdb589f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RApplRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RApplRFFI.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 
 /**
  * Collection of statically typed methods (from Linpack and elsewhere) that are built in to a GnuR
@@ -30,60 +30,60 @@ import com.oracle.truffle.api.nodes.Node;
  * {@code libappl} library in GnuR.
  */
 public interface RApplRFFI {
-    abstract class Dqrdc2Node extends Node {
-        public abstract void execute(double[] x, int ldx, int n, int p, double tol, int[] rank, double[] qraux, int[] pivot, double[] work);
+    interface Dqrdc2Node extends NodeInterface {
+        void execute(double[] x, int ldx, int n, int p, double tol, int[] rank, double[] qraux, int[] pivot, double[] work);
 
-        public static Dqrdc2Node create() {
-            return RFFIFactory.getRFFI().getRApplRFFI().createDqrdc2Node();
+        static Dqrdc2Node create() {
+            return RFFIFactory.getRApplRFFI().createDqrdc2Node();
         }
     }
 
-    abstract class DqrcfNode extends Node {
-        public abstract void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] b, int[] info);
+    interface DqrcfNode extends NodeInterface {
+        void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] b, int[] info);
 
-        public static Dqrdc2Node create() {
-            return RFFIFactory.getRFFI().getRApplRFFI().createDqrdc2Node();
+        static Dqrdc2Node create() {
+            return RFFIFactory.getRApplRFFI().createDqrdc2Node();
         }
     }
 
-    abstract class DqrlsNode extends Node {
-        public abstract void execute(double[] x, int n, int p, double[] y, int ny, double tol, double[] b, double[] rsd, double[] qty, int[] k, int[] jpvt, double[] qraux, double[] work);
+    interface DqrlsNode extends NodeInterface {
+        void execute(double[] x, int n, int p, double[] y, int ny, double tol, double[] b, double[] rsd, double[] qty, int[] k, int[] jpvt, double[] qraux, double[] work);
 
-        public static DqrlsNode create() {
-            return RFFIFactory.getRFFI().getRApplRFFI().createDqrlsNode();
+        static DqrlsNode create() {
+            return RFFIFactory.getRApplRFFI().createDqrlsNode();
         }
     }
 
-    abstract class DqrqtyNode extends Node {
-        public abstract void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] qty);
+    interface DqrqtyNode extends NodeInterface {
+        void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] qty);
 
-        public static DqrqtyNode create() {
+        static DqrqtyNode create() {
 
-            return RFFIFactory.getRFFI().getRApplRFFI().createDqrqtyNode();
+            return RFFIFactory.getRApplRFFI().createDqrqtyNode();
         }
     }
 
-    abstract class DqrqyNode extends Node {
-        public abstract void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] qy);
+    interface DqrqyNode extends NodeInterface {
+        void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] qy);
 
-        public static DqrqyNode create() {
-            return RFFIFactory.getRFFI().getRApplRFFI().createDqrqyNode();
+        static DqrqyNode create() {
+            return RFFIFactory.getRApplRFFI().createDqrqyNode();
         }
     }
 
-    abstract class DqrrsdNode extends Node {
-        public abstract void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] rsd);
+    interface DqrrsdNode extends NodeInterface {
+        void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] rsd);
 
-        public static DqrrsdNode create() {
-            return RFFIFactory.getRFFI().getRApplRFFI().createDqrrsdNode();
+        static DqrrsdNode create() {
+            return RFFIFactory.getRApplRFFI().createDqrrsdNode();
         }
     }
 
-    abstract class DqrxbNode extends Node {
-        public abstract void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] xb);
+    interface DqrxbNode extends NodeInterface {
+        void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] xb);
 
-        public static DqrxbNode create() {
-            return RFFIFactory.getRFFI().getRApplRFFI().createDqrxbNode();
+        static DqrxbNode create() {
+            return RFFIFactory.getRApplRFFI().createDqrxbNode();
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
index 84f11ac9385b578def470a05a5c7a77a008556ca..daf3e5a28c864d6f524bfec8e7a684ba68c7f9cb 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
@@ -105,11 +105,67 @@ public abstract class RFFIFactory {
         return instance;
     }
 
-    public static RFFI getRFFI() {
+    private static RFFI getRFFI() {
         assert theRFFI != null : "RFFI factory is not initialized!";
         return theRFFI;
     }
 
+    /*
+     * Some shortcuts to the specific RFFI interfaces:
+     */
+
+    public static BaseRFFI getBaseRFFI() {
+        return getRFFI().getBaseRFFI();
+    }
+
+    public static LapackRFFI getLapackRFFI() {
+        return getRFFI().getLapackRFFI();
+    }
+
+    public static RApplRFFI getRApplRFFI() {
+        return getRFFI().getRApplRFFI();
+    }
+
+    public static StatsRFFI getStatsRFFI() {
+        return getRFFI().getStatsRFFI();
+    }
+
+    public static ToolsRFFI getToolsRFFI() {
+        return getRFFI().getToolsRFFI();
+    }
+
+    public static CRFFI getCRFFI() {
+        return getRFFI().getCRFFI();
+    }
+
+    public static CallRFFI getCallRFFI() {
+        return getRFFI().getCallRFFI();
+    }
+
+    public static UserRngRFFI getUserRngRFFI() {
+        return getRFFI().getUserRngRFFI();
+    }
+
+    public static PCRERFFI getPCRERFFI() {
+        return getRFFI().getPCRERFFI();
+    }
+
+    public static ZipRFFI getZipRFFI() {
+        return getRFFI().getZipRFFI();
+    }
+
+    public static DLLRFFI getDLLRFFI() {
+        return getRFFI().getDLLRFFI();
+    }
+
+    public static REmbedRFFI getREmbedRFFI() {
+        return getRFFI().getREmbedRFFI();
+    }
+
+    public static MiscRFFI getMiscRFFI() {
+        return getRFFI().getMiscRFFI();
+    }
+
     public static Type getType() {
         return type;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java
index bfc56792a3d8aa702c76c3074abc78fd82d8e521..d7ddf499f6679be776b5ce12d7a281da6f3f9f47 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java
@@ -23,13 +23,13 @@
 package com.oracle.truffle.r.runtime.ffi;
 
 import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-public abstract class RFFIRootNode<T extends Node> extends RootNode {
+public abstract class RFFIRootNode<T extends NodeInterface> extends RootNode {
     @Child protected T rffiNode;
 
     protected RFFIRootNode(T baseRFFINode) {
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 d1dcd13fc0be31779570e816926bfa3cf6b1d3e3..5fc727da587d79245183abee162095104739d644 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
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 
 /**
  * Interface to native (C) methods provided by the {@code stats} package that are used to implement
@@ -30,19 +30,19 @@ 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 FactorNode extends Node {
-        public abstract void execute(int n, int[] pmaxf, int[] pmaxp);
+    interface FactorNode extends NodeInterface {
+        void execute(int n, int[] pmaxf, int[] pmaxp);
 
-        public static FactorNode create() {
-            return RFFIFactory.getRFFI().getStatsRFFI().createFactorNode();
+        static FactorNode create() {
+            return RFFIFactory.getStatsRFFI().createFactorNode();
         }
     }
 
-    abstract class WorkNode extends Node {
-        public abstract int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork);
+    interface WorkNode extends NodeInterface {
+        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();
+        static WorkNode create() {
+            return RFFIFactory.getStatsRFFI().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 7d797e62f112dfb5f13ca953d802bd2c0f3086d2..629bc1a5889deaf3bfc9beaa9784423f32ae5e16 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
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -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 ParseRdNode extends Node {
+    interface ParseRdNode extends NodeInterface {
         /**
          * 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,7 +41,7 @@ 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 execute(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls, Object macros,
+        Object execute(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls, Object macros,
                         RLogicalVector warndups);
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UserRngRFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UserRngRFFI.java
index 64059decbdafae4071c9165e91a345da6c2f313d..d2be2ee0cfb6bb86ffe7a2ea8d6b9c87d726b844 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UserRngRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UserRngRFFI.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,23 +22,37 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeInterface;
 
 /**
  * Explicit statically typed interface to user-supplied random number generators.
  */
 public interface UserRngRFFI {
-    abstract class UserRngRFFINode extends Node {
+    interface InitNode extends NodeInterface {
 
-        public abstract void init(int seed);
+        void execute(int seed);
+    }
+
+    interface RandNode extends NodeInterface {
+
+        double execute();
+    }
 
-        public abstract double rand();
+    interface NSeedNode extends NodeInterface {
 
-        public abstract int nSeed();
+        int execute();
+    }
+
+    interface SeedsNode extends NodeInterface {
 
-        public abstract void seeds(int[] n);
+        void execute(int[] n);
     }
 
-    UserRngRFFINode createUserRngRFFINode();
+    InitNode createInitNode();
+
+    RandNode createRandNode();
+
+    NSeedNode createNSeedNode();
 
+    SeedsNode createSeedsNode();
 }
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 04da4dd3e9f7fcd9ae46720cc120b2060e8b2f3f..d41187a6c10cabb41638ca7e7fd5a215884fd667 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
@@ -23,33 +23,33 @@
 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.api.nodes.NodeInterface;
 
 /**
  * zip compression/uncompression.
  */
 public interface ZipRFFI {
 
-    abstract class CompressNode extends Node {
+    interface CompressNode extends NodeInterface {
         /**
          * compress {@code source} into {@code dest}.
          *
          * @return standard return code (0 ok)
          */
-        public abstract int execute(byte[] dest, byte[] source);
+        int execute(byte[] dest, byte[] source);
 
-        public static CompressNode create() {
-            return RFFIFactory.getRFFI().getZipRFFI().createCompressNode();
+        static CompressNode create() {
+            return RFFIFactory.getZipRFFI().createCompressNode();
         }
     }
 
-    abstract class UncompressNode extends Node {
+    interface UncompressNode extends NodeInterface {
         /**
          * uncompress {@code source} into {@code dest}.
          *
          * @return standard return code (0 ok)
          */
-        public abstract int execute(byte[] dest, byte[] source);
+        int execute(byte[] dest, byte[] source);
     }
 
     CompressNode createCompressNode();
@@ -62,7 +62,7 @@ public interface ZipRFFI {
         private static CompressRootNode compressRootNode;
 
         private CompressRootNode() {
-            super(RFFIFactory.getRFFI().getZipRFFI().createCompressNode());
+            super(RFFIFactory.getZipRFFI().createCompressNode());
         }
 
         @Override
@@ -83,7 +83,7 @@ public interface ZipRFFI {
         private static UncompressRootNode uncompressRootNode;
 
         private UncompressRootNode() {
-            super(RFFIFactory.getRFFI().getZipRFFI().createUncompressNode());
+            super(RFFIFactory.getZipRFFI().createUncompressNode());
         }
 
         @Override
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java
index b85827433bf0fa9ea0c336bdbd03450b3376f693..3e0cbf8fc85f520efcc9df5b8a3ac0da82f7036c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/user/UserRNG.java
@@ -77,7 +77,10 @@ public final class UserRNG implements RandomNumberGenerator {
     private int nSeeds = 0;
 
     private abstract static class UserRNGRootNodeAdapter extends RootNode {
-        @Child protected UserRngRFFI.UserRngRFFINode userRngRFFINode = RFFIFactory.getRFFI().getUserRngRFFI().createUserRngRFFINode();
+        @Child protected UserRngRFFI.InitNode initNode = RFFIFactory.getUserRngRFFI().createInitNode();
+        @Child protected UserRngRFFI.RandNode randNode = RFFIFactory.getUserRngRFFI().createRandNode();
+        @Child protected UserRngRFFI.NSeedNode nSeedNode = RFFIFactory.getUserRngRFFI().createNSeedNode();
+        @Child protected UserRngRFFI.SeedsNode seedsNode = RFFIFactory.getUserRngRFFI().createSeedsNode();
 
         protected UserRNGRootNodeAdapter() {
             /*
@@ -101,12 +104,12 @@ public final class UserRNG implements RandomNumberGenerator {
             Function function = (Function) args[0];
             switch (function) {
                 case Init:
-                    userRngRFFINode.init((int) args[1]);
+                    initNode.execute((int) args[1]);
                     return RNull.instance;
                 case NSeed:
-                    return userRngRFFINode.nSeed();
+                    return nSeedNode.execute();
                 case Seedloc:
-                    userRngRFFINode.seeds((int[]) args[1]);
+                    seedsNode.execute((int[]) args[1]);
                     return RNull.instance;
                 default:
                     throw RInternalError.shouldNotReachHere();
@@ -118,7 +121,7 @@ public final class UserRNG implements RandomNumberGenerator {
 
         @Override
         public Object execute(VirtualFrame frame) {
-            return userRngRFFINode.rand();
+            return randNode.execute();
         }
     }