From 1faa99ad19cfbe6507702732f6432f9cbfc04719 Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Mon, 19 Feb 2018 14:37:34 +0100
Subject: [PATCH] Unify NativeFunction invocations for NFI and LLVM

---
 .../truffle/r/engine/interop/RNullMR.java     |   1 -
 .../r/ffi/impl/common/CallToNativeNode.java   | 164 --------
 .../r/ffi/impl/common/DownCallNode.java       |  66 ----
 .../r/ffi/impl/interop/CharSXPWrapperMR.java  |   2 +-
 .../impl/interop/NativeIntegerArrayMR.java    |   3 +-
 .../r/ffi/impl/llvm/TruffleLLVM_Base.java     | 299 ---------------
 .../r/ffi/impl/llvm/TruffleLLVM_C.java        |   2 +-
 .../r/ffi/impl/llvm/TruffleLLVM_Context.java  |  21 +-
 .../impl/llvm/TruffleLLVM_DownCallNode.java   |  67 ----
 .../llvm/TruffleLLVM_DownCallNodeFactory.java | 103 +++++
 .../r/ffi/impl/llvm/TruffleLLVM_Lapack.java   | 316 +---------------
 .../r/ffi/impl/llvm/TruffleLLVM_Misc.java     |  66 ----
 .../ffi/impl/llvm/TruffleLLVM_NativeDLL.java  |   2 +-
 .../r/ffi/impl/llvm/TruffleLLVM_PCRE.java     | 121 +-----
 .../r/ffi/impl/llvm/TruffleLLVM_Stats.java    | 192 ----------
 .../llvm/TruffleLLVM_UpCallsRFFIImpl.java     |   2 +-
 .../r/ffi/impl/llvm/TruffleLLVM_Zip.java      |  57 +--
 .../r/ffi/impl/managed/FilesystemUtils.java   |  41 --
 .../r/ffi/impl/managed/Managed_Base.java      | 208 ----------
 .../managed/Managed_DownCallNodeFactory.java  |  72 ++++
 .../ffi/impl/managed/Managed_LapackRFFI.java  | 114 ------
 .../r/ffi/impl/managed/Managed_PCRERFFI.java  |  59 ---
 .../ffi/impl/managed/Managed_RFFIFactory.java |  48 +--
 .../nfi/HandleNFIUpCallExceptionNode.java     |  18 +-
 .../r/ffi/impl/nfi/TruffleNFI_Base.java       | 290 --------------
 .../r/ffi/impl/nfi/TruffleNFI_Context.java    |  11 +-
 .../ffi/impl/nfi/TruffleNFI_DownCallNode.java |  64 ----
 .../nfi/TruffleNFI_DownCallNodeFactory.java   |  96 +++++
 .../r/ffi/impl/nfi/TruffleNFI_Lapack.java     | 323 ----------------
 .../r/ffi/impl/nfi/TruffleNFI_Misc.java       |  64 ----
 .../r/ffi/impl/nfi/TruffleNFI_PCRE.java       | 137 -------
 .../r/ffi/impl/nfi/TruffleNFI_Stats.java      |  80 ----
 .../r/ffi/impl/nfi/TruffleNFI_Zip.java        |  64 ----
 .../truffle/r/runtime/ffi/BaseRFFI.java       | 262 ++++++++++---
 .../r/runtime/ffi/DownCallNodeFactory.java    | 104 +++++
 .../truffle/r/runtime/ffi/LapackRFFI.java     | 357 ++++++++++++------
 .../truffle/r/runtime/ffi/MiscRFFI.java       |  44 ++-
 .../truffle/r/runtime/ffi/NativeCallNode.java |  23 +-
 .../truffle/r/runtime/ffi/PCRERFFI.java       | 159 ++++++--
 .../truffle/r/runtime/ffi/StatsRFFI.java      |  62 ++-
 .../oracle/truffle/r/runtime/ffi/ZipRFFI.java |  56 ++-
 .../runtime/ffi}/base/ESoftVersionResult.java |   2 +-
 .../ffi}/base/ESoftVersionResultMR.java       |   2 +-
 .../r/runtime/ffi}/base/GlobResult.java       |   4 +-
 .../r/runtime/ffi}/base/GlobResultMR.java     |   4 +-
 .../r/runtime/ffi}/base/ReadlinkResult.java   |   4 +-
 .../r/runtime/ffi}/base/ReadlinkResultMR.java |   2 +-
 .../r/runtime/ffi}/base/StrtolResult.java     |   4 +-
 .../r/runtime/ffi}/base/StrtolResultMR.java   |   4 +-
 .../r/runtime/ffi}/base/UnameResult.java      |   8 +-
 .../r/runtime/ffi}/base/UnameResultMR.java    |   4 +-
 .../runtime/ffi/interop/NativeCharArray.java  |  80 ++++
 .../ffi}/interop/NativeCharArrayMR.java       |   2 +-
 .../r/runtime/ffi}/interop/NativePointer.java |  15 +-
 .../runtime/ffi}/interop/NativePointerMR.java |   4 +-
 .../runtime/ffi}/interop/NativeRawArray.java  |   4 +-
 .../ffi}/interop/NativeRawArrayMR.java        |   4 +-
 .../ffi}/interop/NativeUInt8Array.java        |  36 +-
 .../ffi}/interop/pcre/CaptureNamesResult.java |   4 +-
 .../interop/pcre/CaptureNamesResultMR.java    |   4 +-
 .../ffi}/interop/pcre/CompileResult.java      |   7 +-
 .../ffi}/interop/pcre/CompileResultMR.java    |   4 +-
 62 files changed, 1264 insertions(+), 3178 deletions(-)
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/CallToNativeNode.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/DownCallNode.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Base.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNode.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNodeFactory.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Misc.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Stats.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/FilesystemUtils.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_Base.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_DownCallNodeFactory.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_LapackRFFI.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_PCRERFFI.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Base.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNode.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNodeFactory.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Lapack.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Misc.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PCRE.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Stats.java
 delete mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Zip.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DownCallNodeFactory.java
 rename com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArray.java => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeCallNode.java (62%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/ESoftVersionResult.java (96%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/ESoftVersionResultMR.java (97%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/GlobResult.java (92%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/GlobResultMR.java (94%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/ReadlinkResult.java (92%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/ReadlinkResultMR.java (98%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/StrtolResult.java (92%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/StrtolResultMR.java (94%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/UnameResult.java (88%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/base/UnameResultMR.java (94%)
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArray.java
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/NativeCharArrayMR.java (98%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/NativePointer.java (87%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/NativePointerMR.java (93%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/NativeRawArray.java (91%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/NativeRawArrayMR.java (96%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/NativeUInt8Array.java (77%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/pcre/CaptureNamesResult.java (92%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/pcre/CaptureNamesResultMR.java (95%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/pcre/CompileResult.java (85%)
 rename {com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl => com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi}/interop/pcre/CompileResultMR.java (96%)

diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RNullMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RNullMR.java
index d8522ba773..ff17f75a31 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RNullMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RNullMR.java
@@ -27,7 +27,6 @@ import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.interop.RNullMRContextState;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/CallToNativeNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/CallToNativeNode.java
deleted file mode 100644
index ff28a7c795..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/CallToNativeNode.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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.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.ExplodeLoop;
-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.ffi.impl.interop.NativeNACheck;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.ffi.DLL;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
-
-public abstract class CallToNativeNode extends Node {
-
-    @Child private Node message;
-    protected final NativeFunction function;
-
-    private CallToNativeNode(NativeFunction function) {
-        this.function = function;
-    }
-
-    public CallToNativeNode create(NativeFunction f) {
-        switch (RFFIFactory.getFactoryType()) {
-            case LLVM:
-                return new NFI(f);
-            case NFI:
-                return new LLVM(f);
-            default:
-                throw RInternalError.shouldNotReachHere();
-        }
-    }
-
-    protected abstract TruffleObject getTarget();
-
-    protected final Object call(Object... args) {
-        try {
-            if (message == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                message = insert(Message.createExecute(function.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);
-
-    private final class NFI extends CallToNativeNode {
-
-        @CompilationFinal private TruffleObject target;
-
-        private NFI(NativeFunction function) {
-            super(function);
-        }
-
-        @Override
-        protected TruffleObject getTarget() {
-            throw RInternalError.unimplemented("unused implementation");
-            // return function.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();
-                }
-            }
-        }
-    }
-
-    private final class LLVM extends CallToNativeNode {
-
-        @CompilationFinal private TruffleObject target;
-
-        private LLVM(NativeFunction function) {
-            super(function);
-        }
-
-        @Override
-        protected TruffleObject getTarget() {
-            if (target == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                target = DLL.findSymbol(function.getCallName(), null).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/common/DownCallNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/DownCallNode.java
deleted file mode 100644
index a554e2dd93..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/DownCallNode.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.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.nodes.Node;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-
-public abstract class DownCallNode extends Node {
-
-    @Child private Node message;
-    @CompilationFinal private TruffleObject target;
-
-    protected abstract TruffleObject getTarget();
-
-    protected abstract NativeFunction getFunction();
-
-    protected final Object call(Object... args) {
-        try {
-            if (message == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                message = insert(Message.createExecute(getFunction().getArgumentCount()).createNode());
-            }
-            if (target == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                target = getTarget();
-            }
-            wrapArguments(args);
-            return ForeignAccess.sendExecute(message, target, 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/interop/CharSXPWrapperMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/CharSXPWrapperMR.java
index 95c06bb5e6..f632643a7b 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/CharSXPWrapperMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/CharSXPWrapperMR.java
@@ -52,7 +52,7 @@ public class CharSXPWrapperMR {
 
     @Resolve(message = "HAS_SIZE")
     public abstract static class NCAHasSizeNode extends Node {
-        protected boolean access(@SuppressWarnings("unused") NativeCharArray receiver) {
+        protected boolean access(@SuppressWarnings("unused") Object receiver) {
             return true;
         }
     }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArrayMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArrayMR.java
index d92725c8fd..aa6edfb67d 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArrayMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArrayMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@ import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.ffi.interop.NativePointer;
 
 @MessageResolution(receiverType = NativeIntegerArray.class)
 public class NativeIntegerArrayMR {
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
deleted file mode 100644
index 0f61b48a67..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Base.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (c) 2014, 2018, 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 java.io.IOException;
-import java.util.ArrayList;
-import java.util.Map;
-
-import com.oracle.truffle.r.ffi.impl.interop.NativeCharArray;
-import com.oracle.truffle.r.ffi.impl.interop.base.ESoftVersionResult;
-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.ffi.BaseRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-
-public class TruffleLLVM_Base implements BaseRFFI {
-    private static final class TruffleLLVM_GetpidNode extends TruffleLLVM_DownCallNode implements GetpidNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.getpid;
-        }
-
-        @Override
-        public int execute() {
-            return (int) call();
-        }
-    }
-
-    private static class TruffleLLVM_GetwdNode extends TruffleLLVM_DownCallNode implements GetwdNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.getcwd;
-        }
-
-        @Override
-        public String execute() {
-            // TODO: pass buf to "call" and do the wrapping inside wrapArguments
-            byte[] buf = new byte[4096];
-            NativeCharArray nativeBuf = new NativeCharArray(buf);
-            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++;
-                }
-                return new String(mbuf, 0, i);
-            }
-        }
-    }
-
-    private static class TruffleLLVM_SetwdNode extends TruffleLLVM_DownCallNode implements SetwdNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.chdir;
-        }
-
-        @Override
-        public int execute(String dir) {
-            NativeCharArray nativeBuf = new NativeCharArray(dir.getBytes());
-            return (int) call(nativeBuf);
-        }
-    }
-
-    private static class TruffleLLVM_MkdirNode extends TruffleLLVM_DownCallNode implements MkdirNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.mkdir;
-        }
-
-        @Override
-        public void execute(String dir, int mode) throws IOException {
-            NativeCharArray nativeBuf = new NativeCharArray(dir.getBytes());
-            int result = (int) call(nativeBuf, mode);
-            if (result != 0) {
-                throw new IOException("mkdir " + dir + " failed");
-            }
-        }
-    }
-
-    private static class TruffleLLVM_ReadlinkNode extends TruffleLLVM_DownCallNode implements ReadlinkNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.readlink;
-        }
-
-        private static final int EINVAL = 22;
-
-        @Override
-        public String execute(String path) throws IOException {
-            NativeCharArray nativePath = new NativeCharArray(path.getBytes());
-            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;
-        }
-    }
-
-    private static class TruffleLLVM_MkdtempNode extends TruffleLLVM_DownCallNode implements MkdtempNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.mkdtemp;
-        }
-
-        @Override
-        public String execute(String template) {
-            /*
-             * Not only must the (C) string end in XXXXXX it must also be null-terminated. Since it
-             * is modified by mkdtemp we must make a copy.
-             */
-            byte[] bytes = template.getBytes();
-            byte[] ztbytes = new byte[bytes.length + 1];
-            System.arraycopy(bytes, 0, ztbytes, 0, bytes.length);
-            ztbytes[bytes.length] = 0;
-            NativeCharArray nativeZtbytes = new NativeCharArray(ztbytes);
-            int result = (int) call(nativeZtbytes);
-            if (result == 0) {
-                return null;
-            } else {
-                byte[] mztBytes = nativeZtbytes.getValue();
-                return new String(mztBytes, 0, bytes.length);
-            }
-        }
-    }
-
-    private static class TruffleLLVM_ChmodNode extends TruffleLLVM_DownCallNode implements ChmodNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.chmod;
-        }
-
-        @Override
-        public int execute(String path, int mode) {
-            NativeCharArray nativePath = new NativeCharArray(path.getBytes());
-            return (int) call(nativePath, mode);
-        }
-    }
-
-    private static class TruffleLLVM_StrtolNode extends TruffleLLVM_DownCallNode implements StrtolNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.strtol;
-        }
-
-        @Override
-        public long execute(String s, int base) throws IllegalArgumentException {
-            NativeCharArray nativeString = new NativeCharArray(s.getBytes());
-            StrtolResult callback = new StrtolResult();
-            call(callback, nativeString, base);
-            if (callback.getErrno() != 0) {
-                throw new IllegalArgumentException("strtol failure");
-            } else {
-                return callback.getResult();
-            }
-        }
-    }
-
-    private static class TruffleLLVM_UnameNode extends TruffleLLVM_DownCallNode implements UnameNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.uname;
-        }
-
-        @Override
-        public UtsName execute() {
-            UnameResult baseUnameResultCallback = new UnameResult();
-            call(baseUnameResultCallback);
-            return baseUnameResultCallback;
-        }
-    }
-
-    private static class TruffleLLVM_GlobNode extends TruffleLLVM_DownCallNode implements GlobNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.glob;
-        }
-
-        @Override
-        public ArrayList<String> glob(String pattern) {
-            NativeCharArray nativePattern = new NativeCharArray(pattern.getBytes());
-            GlobResult baseGlobResultCallback = new GlobResult();
-            call(baseGlobResultCallback, nativePattern);
-            return baseGlobResultCallback.getPaths();
-        }
-    }
-
-    private static class TruffleLLVM_ESoftVersionNode extends TruffleLLVM_DownCallNode implements ESoftVersionNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.eSoftVersion;
-        }
-
-        @Override
-        public Map<String, String> eSoftVersion() {
-            ESoftVersionResult result = new ESoftVersionResult();
-            call(result);
-            return result.getVersions();
-        }
-
-    }
-
-    @Override
-    public GetpidNode createGetpidNode() {
-        return new TruffleLLVM_GetpidNode();
-    }
-
-    @Override
-    public GetwdNode createGetwdNode() {
-        return new TruffleLLVM_GetwdNode();
-    }
-
-    @Override
-    public SetwdNode createSetwdNode() {
-        return new TruffleLLVM_SetwdNode();
-    }
-
-    @Override
-    public MkdirNode createMkdirNode() {
-        return new TruffleLLVM_MkdirNode();
-    }
-
-    @Override
-    public ReadlinkNode createReadlinkNode() {
-        return new TruffleLLVM_ReadlinkNode();
-    }
-
-    @Override
-    public MkdtempNode createMkdtempNode() {
-        return new TruffleLLVM_MkdtempNode();
-    }
-
-    @Override
-    public ChmodNode createChmodNode() {
-        return new TruffleLLVM_ChmodNode();
-    }
-
-    @Override
-    public StrtolNode createStrtolNode() {
-        return new TruffleLLVM_StrtolNode();
-    }
-
-    @Override
-    public UnameNode createUnameNode() {
-        return new TruffleLLVM_UnameNode();
-    }
-
-    @Override
-    public GlobNode createGlobNode() {
-        return new TruffleLLVM_GlobNode();
-    }
-
-    @Override
-    public ESoftVersionNode createESoftVersionNode() {
-        return new TruffleLLVM_ESoftVersionNode();
-    }
-}
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 e9792234bc..496e75c667 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
@@ -30,10 +30,10 @@ import com.oracle.truffle.api.interop.TruffleObject;
 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.ffi.impl.interop.NativeRawArray;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
+import com.oracle.truffle.r.runtime.ffi.interop.NativeRawArray;
 
 class TruffleLLVM_C implements CRFFI {
     private static class TruffleLLVM_InvokeCNode extends InvokeCNode {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java
index c9365ee397..7efafe94f6 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java
@@ -26,9 +26,15 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.r.ffi.impl.common.LibPaths;
 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;
 import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
+import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeFunction;
+import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIContext;
+import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
+import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
 /**
  * A facade for the context state for the Truffle LLVM factory. Delegates to the various
@@ -36,13 +42,14 @@ import com.oracle.truffle.r.runtime.ffi.RFFIContext;
  */
 final class TruffleLLVM_Context extends RFFIContext {
 
-    final TruffleLLVM_DLL.ContextStateImpl dllState = new TruffleLLVM_DLL.ContextStateImpl();
+    private final TruffleLLVM_DLL.ContextStateImpl dllState = new TruffleLLVM_DLL.ContextStateImpl();
     final TruffleLLVM_Call.ContextStateImpl callState = new TruffleLLVM_Call.ContextStateImpl();
 
     TruffleLLVM_Context() {
-        super(new TruffleLLVM_C(), new TruffleLLVM_Base(), new TruffleLLVM_Call(), new TruffleLLVM_DLL(), new TruffleLLVM_UserRng(), new TruffleLLVM_Zip(), new TruffleLLVM_PCRE(),
-                        new TruffleLLVM_Lapack(), new TruffleLLVM_Stats(),
-                        new TruffleLLVM_Tools(), new TruffleLLVM_REmbed(), new TruffleLLVM_Misc());
+        super(new TruffleLLVM_C(), new BaseRFFI(TruffleLLVM_DownCallNodeFactory.INSTANCE), new TruffleLLVM_Call(), new TruffleLLVM_DLL(), new TruffleLLVM_UserRng(),
+                        new ZipRFFI(TruffleLLVM_DownCallNodeFactory.INSTANCE), new PCRERFFI(TruffleLLVM_DownCallNodeFactory.INSTANCE),
+                        new LapackRFFI(TruffleLLVM_DownCallNodeFactory.INSTANCE), new StatsRFFI(TruffleLLVM_DownCallNodeFactory.INSTANCE),
+                        new TruffleLLVM_Tools(), new TruffleLLVM_REmbed(), new MiscRFFI(TruffleLLVM_DownCallNodeFactory.INSTANCE));
     }
 
     static TruffleLLVM_Context getContextState() {
@@ -69,9 +76,9 @@ final class TruffleLLVM_Context extends RFFIContext {
         super.initializeVariables(context);
 
         callState.initializeVariables();
-        ((TruffleLLVM_PCRE) pcreRFFI).initialize();
-        ((TruffleLLVM_Lapack) lapackRFFI).initialize();
-        ((TruffleLLVM_Zip) zipRFFI).initialize();
+        TruffleLLVM_PCRE.initialize();
+        TruffleLLVM_Lapack.initialize();
+        TruffleLLVM_Zip.initialize();
     }
 
     @Override
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
deleted file mode 100644
index 9bdf1ad1c0..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNode.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2017, 2018, 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.CompilerAsserts;
-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;
-import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
-import com.oracle.truffle.r.runtime.ffi.DLL;
-
-public abstract class TruffleLLVM_DownCallNode extends DownCallNode {
-
-    @Override
-    protected final TruffleObject getTarget() {
-        CompilerAsserts.neverPartOfCompilation();
-        return DLL.findSymbol(getFunction().getCallName(), null).asTruffleObject();
-    }
-
-    @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] = NativePointer.NULL_NATIVEPOINTER;
-            }
-        }
-    }
-
-    @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_DownCallNodeFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNodeFactory.java
new file mode 100644
index 0000000000..348720b931
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNodeFactory.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018, 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 static com.oracle.truffle.r.runtime.ffi.NativeFunction.anyLibrary;
+
+import java.nio.charset.StandardCharsets;
+
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.ExplodeLoop;
+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;
+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.DownCallNodeFactory;
+import com.oracle.truffle.r.runtime.ffi.NativeFunction;
+import com.oracle.truffle.r.runtime.ffi.interop.NativeCharArray;
+import com.oracle.truffle.r.runtime.ffi.interop.NativePointer;
+
+final class TruffleLLVM_DownCallNodeFactory extends DownCallNodeFactory {
+
+    static final TruffleLLVM_DownCallNodeFactory INSTANCE = new TruffleLLVM_DownCallNodeFactory();
+
+    private TruffleLLVM_DownCallNodeFactory() {
+    }
+
+    @Override
+    public DownCallNode createDownCallNode(NativeFunction function) {
+        return new DownCallNode(function) {
+            @Override
+            protected TruffleObject getTarget(NativeFunction function) {
+                CompilerAsserts.neverPartOfCompilation();
+                String library = function.getLibrary();
+                DLLInfo dllInfo = null;
+                if (library != anyLibrary()) {
+                    dllInfo = DLL.findLibrary(library);
+                }
+                SymbolHandle result = DLL.findSymbol(function.getCallName(), dllInfo);
+                if (result == DLL.SYMBOL_NOT_FOUND) {
+                    throw RInternalError.shouldNotReachHere("Could not find function " + function.getCallName() + " in library " + library);
+                }
+                return result.asTruffleObject();
+            }
+
+            @Override
+            @ExplodeLoop
+            protected void wrapArguments(TruffleObject function, 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] = NativePointer.NULL_NATIVEPOINTER;
+                    } else if (obj instanceof String) {
+                        args[i] = new NativeCharArray(getStringBytes((String) obj));
+                    }
+                }
+            }
+
+            @TruffleBoundary
+            private byte[] getStringBytes(String obj) {
+                return obj.getBytes(StandardCharsets.UTF_8);
+            }
+
+            @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 8b0dd78755..8d38e10a3b 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
@@ -26,8 +26,6 @@ import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.r.ffi.impl.common.LibPaths;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
-import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
 
 /**
  * When the embedded GNU R is built, LLVM is created for the components of the {@code libRblas} and
@@ -43,9 +41,9 @@ import com.oracle.truffle.r.runtime.ffi.NativeFunction;
  * when this API is requested.
  *
  */
-public class TruffleLLVM_Lapack implements LapackRFFI {
+final class TruffleLLVM_Lapack {
 
-    void initialize() {
+    static void initialize() {
         /*
          * This is a workaround for bad LLVM generated by DragonEgg for (some) of the Lapack
          * functions; additional spurious arguments. Unfortunately for this to be portable we would
@@ -78,314 +76,4 @@ public class TruffleLLVM_Lapack implements LapackRFFI {
         TruffleLLVM_NativeDLL.NativeDLOpenRootNode rootNode = TruffleLLVM_NativeDLL.NativeDLOpenRootNode.create();
         return rootNode.getCallTarget();
     }
-
-    private static final class TruffleLLVM_IlaverNode extends TruffleLLVM_DownCallNode implements IlaverNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.ilaver;
-        }
-
-        @Override
-        public void execute(int[] version) {
-            call(version);
-        }
-    }
-
-    private static final class TruffleLLVM_DgeevNode extends TruffleLLVM_DownCallNode implements DgeevNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.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) {
-            return (int) call(jobVL, jobVR, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork);
-        }
-    }
-
-    private static final class TruffleLLVM_Dgeqp3Node extends TruffleLLVM_DownCallNode implements Dgeqp3Node {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgeqp3;
-        }
-
-        @Override
-        public int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork) {
-            return (int) call(m, n, a, lda, jpvt, tau, work, lwork);
-        }
-    }
-
-    private static final class TruffleLLVM_DormqrNode extends TruffleLLVM_DownCallNode implements DormqrNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.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) {
-            return (int) call(side, trans, m, n, k, a, lda, tau, c, ldc, work, lwork);
-        }
-    }
-
-    private static final class TruffleLLVM_DtrtrsNode extends TruffleLLVM_DownCallNode implements DtrtrsNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dtrtrs;
-        }
-
-        @Override
-        public int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
-            return (int) call(uplo, trans, diag, n, nrhs, a, lda, b, ldb);
-        }
-    }
-
-    private static final class TruffleLLVM_DgetrfNode extends TruffleLLVM_DownCallNode implements DgetrfNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgetrf;
-        }
-
-        @Override
-        public int execute(int m, int n, double[] a, int lda, int[] ipiv) {
-            return (int) call(m, n, a, lda, ipiv);
-        }
-    }
-
-    private static final class TruffleLLVM_DpotrfNode extends TruffleLLVM_DownCallNode implements DpotrfNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dpotrf;
-        }
-
-        @Override
-        public int execute(char uplo, int n, double[] a, int lda) {
-            return (int) call(uplo, n, a, lda);
-        }
-    }
-
-    private static final class TruffleLLVM_DpotriNode extends TruffleLLVM_DownCallNode implements DpotriNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dpotri;
-        }
-
-        @Override
-        public int execute(char uplo, int n, double[] a, int lda) {
-            return (int) call(uplo, n, a, lda);
-        }
-    }
-
-    private static final class TruffleLLVM_DpstrfNode extends TruffleLLVM_DownCallNode implements DpstrfNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dpstrf;
-        }
-
-        @Override
-        public int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work) {
-            return (int) call(uplo, n, a, lda, piv, rank, tol, work);
-        }
-    }
-
-    private static final class TruffleLLVM_DgesvNode extends TruffleLLVM_DownCallNode implements DgesvNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgesv;
-        }
-
-        @Override
-        public int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb) {
-            return (int) call(n, nrhs, a, lda, ipiv, b, ldb);
-        }
-    }
-
-    private static final class TruffleLLVM_DgesddNode extends TruffleLLVM_DownCallNode implements DgesddNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgesdd;
-        }
-
-        @Override
-        public int execute(char jobz, int m, int n, double[] a, int lda, double[] s, double[] u, int ldu, double[] vt, int ldtv, double[] work, int lwork, int[] iwork) {
-            return (int) call(jobz, m, n, a, lda, s, u, ldu, vt, ldtv, work, lwork, iwork);
-        }
-    }
-
-    private static final class TruffleLLVM_DlangeNode extends TruffleLLVM_DownCallNode implements DlangeNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dlange;
-        }
-
-        @Override
-        public double execute(char norm, int m, int n, double[] a, int lda, double[] work) {
-            return (double) call(norm, m, n, a, lda, work);
-        }
-    }
-
-    private static final class TruffleLLVM_DgeconNode extends TruffleLLVM_DownCallNode implements DgeconNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgecon;
-        }
-
-        @Override
-        public int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork) {
-            return (int) call(norm, n, a, lda, anorm, rcond, work, iwork);
-        }
-    }
-
-    private static final class TruffleLLVM_DsyevrNode extends TruffleLLVM_DownCallNode implements DsyevrNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.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) {
-            return (int) call(jobz, range, uplo, n, a, lda, vl, vu, il, iu, abstol, m, w, z, ldz, isuppz, work, lwork, iwork, liwork);
-        }
-    }
-
-    private static final class TruffleLLVM_ZunmqrNode extends TruffleLLVM_DownCallNode implements ZunmqrNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.zunmqr;
-        }
-
-        @Override
-        public int execute(String side, String trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork) {
-            return (int) call(side, trans, m, n, k, a, lda, tau, c, ldc, work, lwork);
-        }
-
-    }
-
-    private static final class TruffleLLVM_ZtrtrsNode extends TruffleLLVM_DownCallNode implements ZtrtrsNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.ztrtrs;
-        }
-
-        @Override
-        public int execute(String uplo, String trans, String diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
-            return (int) call(uplo, trans, diag, n, nrhs, a, lda, b, ldb);
-        }
-
-    }
-
-    private static final class TruffleLLVM_DtrsmNode extends TruffleLLVM_DownCallNode implements DtrsmNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dtrsm;
-        }
-
-        @Override
-        public void execute(String side, String uplo, String transa, String diag, int m, int n, double alpha, double[] a, int lda, double[] b, int ldb) {
-            call(side, uplo, transa, diag, m, n, alpha, a, lda, b, ldb);
-        }
-
-    }
-
-    @Override
-    public IlaverNode createIlaverNode() {
-        return new TruffleLLVM_IlaverNode();
-    }
-
-    @Override
-    public DgeevNode createDgeevNode() {
-        return new TruffleLLVM_DgeevNode();
-    }
-
-    @Override
-    public Dgeqp3Node createDgeqp3Node() {
-        return new TruffleLLVM_Dgeqp3Node();
-    }
-
-    @Override
-    public DormqrNode createDormqrNode() {
-        return new TruffleLLVM_DormqrNode();
-    }
-
-    @Override
-    public DtrtrsNode createDtrtrsNode() {
-        return new TruffleLLVM_DtrtrsNode();
-    }
-
-    @Override
-    public DgetrfNode createDgetrfNode() {
-        return new TruffleLLVM_DgetrfNode();
-    }
-
-    @Override
-    public DpotrfNode createDpotrfNode() {
-        return new TruffleLLVM_DpotrfNode();
-    }
-
-    @Override
-    public DpotriNode createDpotriNode() {
-        return new TruffleLLVM_DpotriNode();
-    }
-
-    @Override
-    public DpstrfNode createDpstrfNode() {
-        return new TruffleLLVM_DpstrfNode();
-    }
-
-    @Override
-    public DgesvNode createDgesvNode() {
-        return new TruffleLLVM_DgesvNode();
-    }
-
-    @Override
-    public DgesddNode createDgesddNode() {
-        return new TruffleLLVM_DgesddNode();
-    }
-
-    @Override
-    public DlangeNode createDlangeNode() {
-        return new TruffleLLVM_DlangeNode();
-    }
-
-    @Override
-    public DgeconNode createDgeconNode() {
-        return new TruffleLLVM_DgeconNode();
-    }
-
-    @Override
-    public DsyevrNode createDsyevrNode() {
-        return new TruffleLLVM_DsyevrNode();
-    }
-
-    @Override
-    public ZunmqrNode createZunmqrNode() {
-        return new TruffleLLVM_ZunmqrNode();
-    }
-
-    @Override
-    public ZtrtrsNode createZtrtrsNode() {
-        return new TruffleLLVM_ZtrtrsNode();
-    }
-
-    @Override
-    public DtrsmNode createDtrsmNode() {
-        return new TruffleLLVM_DtrsmNode();
-    }
 }
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
deleted file mode 100644
index f30eb295ef..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Misc.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.ffi.impl.llvm;
-
-import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-
-public class TruffleLLVM_Misc implements MiscRFFI {
-
-    private static class TruffleLLVM_ExactSumNode extends TruffleLLVM_DownCallNode implements ExactSumNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.exactSumFunc;
-        }
-
-        @Override
-        public double execute(double[] values, boolean hasNa, boolean naRm) {
-            return (double) call(values, values.length, hasNa ? 1 : 0, naRm ? 1 : 0);
-        }
-    }
-
-    private static final class TruffleLLVM_DqrlsNode extends TruffleLLVM_DownCallNode implements DqrlsNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.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) {
-            call(x, n, p, y, ny, tol, b, rsd, qty, k, jpvt, qraux, work);
-        }
-    }
-
-    @Override
-    public ExactSumNode createExactSumNode() {
-        return new TruffleLLVM_ExactSumNode();
-    }
-
-    @Override
-    public DqrlsNode createDqrlsNode() {
-        return new TruffleLLVM_DqrlsNode();
-    }
-
-}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_NativeDLL.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_NativeDLL.java
index 0bf28ec578..433b1e9c17 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_NativeDLL.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_NativeDLL.java
@@ -33,12 +33,12 @@ import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.ffi.impl.interop.NativeCharArray;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.RFFIRootNode;
+import com.oracle.truffle.r.runtime.ffi.interop.NativeCharArray;
 
 /**
  * Direct access to native {@code dlopen} for libraries for which no LLVM code is available.
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 ce5be6d555..31b6439aa3 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
@@ -22,128 +22,15 @@
  */
 package com.oracle.truffle.r.ffi.impl.llvm;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.interop.TruffleObject;
 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.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.NativeFunction;
-import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 
-public class TruffleLLVM_PCRE implements PCRERFFI {
+final class TruffleLLVM_PCRE {
+    private TruffleLLVM_PCRE() {
+    }
 
-    void initialize() {
+    static void initialize() {
         // Need to ensure that the native pcre library is loaded
         String pcrePath = LibPaths.getBuiltinLibPath("pcre");
         TruffleLLVM_NativeDLL.NativeDLOpenRootNode.create().getCallTarget().call(pcrePath, false, true);
     }
-
-    private static class TruffleLLVM_MaketablesNode extends TruffleLLVM_DownCallNode implements MaketablesNode {
-
-        @Child private AsPointerNode asPointer = new AsPointerNode();
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.maketables;
-        }
-
-        @Override
-        public long execute() {
-            return asPointer.execute((TruffleObject) call());
-        }
-    }
-
-    private static class TruffleLLVM_GetCaptureCountNode extends TruffleLLVM_DownCallNode implements GetCaptureCountNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.getcapturecount;
-        }
-
-        @Override
-        public int execute(long code, long extra) {
-            return (int) call(code, extra);
-        }
-    }
-
-    private static class TruffleLLVM_GetCaptureNamesNode extends TruffleLLVM_DownCallNode implements GetCaptureNamesNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.getcapturenames;
-        }
-
-        @Override
-        public String[] execute(long code, long extra, int captureCount) {
-            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();
-            }
-        }
-    }
-
-    private static class TruffleLLVM_CompileNode extends TruffleLLVM_DownCallNode implements CompileNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.compile;
-        }
-
-        @Override
-        public Result execute(String pattern, int options, long tables) {
-            NativeCharArray pattenChars = new NativeCharArray(pattern.getBytes());
-            CompileResult data = new CompileResult();
-            call(data, pattenChars, options, tables);
-            return data.getResult();
-        }
-    }
-
-    private static class TruffleLLVM_ExecNode extends TruffleLLVM_DownCallNode implements ExecNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.exec;
-        }
-
-        @Override
-        public int execute(long code, long extra, String subject, int offset, int options, int[] ovector) {
-            byte[] subjectBytes = subject.getBytes();
-            NativeCharArray subjectChars = new NativeCharArray(subjectBytes);
-            return (int) call(code, extra, subjectChars, subjectBytes.length, offset, options, ovector, ovector.length);
-        }
-    }
-
-    @Override
-    public MaketablesNode createMaketablesNode() {
-        return new TruffleLLVM_MaketablesNode();
-    }
-
-    @Override
-    public CompileNode createCompileNode() {
-        return new TruffleLLVM_CompileNode();
-    }
-
-    @Override
-    public GetCaptureCountNode createGetCaptureCountNode() {
-        return new TruffleLLVM_GetCaptureCountNode();
-    }
-
-    @Override
-    public GetCaptureNamesNode createGetCaptureNamesNode() {
-        return new TruffleLLVM_GetCaptureNamesNode();
-    }
-
-    @Override
-    public StudyNode createStudyNode() {
-        throw RInternalError.unimplemented();
-    }
-
-    @Override
-    public ExecNode createExecNode() {
-        return new TruffleLLVM_ExecNode();
-    }
 }
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
deleted file mode 100644
index e2c6e30f11..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Stats.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (c) 2014, 2018, 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.dsl.Cached;
-import com.oracle.truffle.api.dsl.ImportStatic;
-import com.oracle.truffle.api.dsl.Specialization;
-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.ffi.impl.interop.NativeIntegerArray;
-import com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_StatsFactory.ExecuteFactorNodeGen;
-import com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_StatsFactory.ExecuteWorkNodeGen;
-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;
-import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
-import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
-import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
-
-public class TruffleLLVM_Stats implements StatsRFFI {
-
-    public enum FFT_FUN {
-        fft_work,
-        fft_factor;
-    }
-
-    public abstract static class LookupAdapter extends Node {
-        @Child private DLLRFFI.DLSymNode dllSymNode = RFFIFactory.getDLLRFFI().createDLSymNode();
-
-        public SymbolHandle lookup(String name) {
-            DLLInfo dllInfo = DLL.findLibrary("stats");
-            // cannot go through DLL because stats does not allow dynamic lookup
-            // and these symbols are not registered (only fft)
-            SymbolHandle result = dllSymNode.execute(dllInfo.handle, name);
-            if (result == DLL.SYMBOL_NOT_FOUND) {
-                throw RInternalError.shouldNotReachHere();
-            }
-            return result;
-        }
-    }
-
-    @ImportStatic({RContext.class})
-    public abstract static class ExecuteWork extends LookupAdapter {
-        public abstract int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork, RContext context);
-
-        @Specialization(guards = "context == cachedContext")
-        protected int executeWorkCached(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork, @SuppressWarnings("unused") RContext context,
-                        @SuppressWarnings("unused") @Cached("getInstance()") RContext cachedContext,
-                        @Cached("createMessageNode()") Node messageNode,
-                        @Cached("lookupWork()") SymbolHandle fftWork) {
-            return doWork(a, nseg, n, nspn, isn, work, iwork, messageNode, fftWork);
-        }
-
-        @Specialization(replaces = "executeWorkCached")
-        protected int executeWorkNormal(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork, @SuppressWarnings("unused") RContext context) {
-            return doWork(a, nseg, n, nspn, isn, work, iwork, createMessageNode(), lookup("fft_work"));
-        }
-
-        private static int doWork(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork, Node messageNode, SymbolHandle fftWork) {
-            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 (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
-        }
-
-        public static Node createMessageNode() {
-            return Message.createExecute(7).createNode();
-        }
-
-        public static ExecuteWork create() {
-            return ExecuteWorkNodeGen.create();
-        }
-
-        public SymbolHandle lookupWork() {
-            return lookup("fft_work");
-        }
-    }
-
-    @ImportStatic({RContext.class})
-    public abstract static class ExecuteFactor extends LookupAdapter {
-        protected abstract void execute(int n, int[] pmaxf, int[] pmaxp, RContext context);
-
-        @Specialization(guards = "context == cachedContext")
-        protected void executeFactorCached(int n, int[] pmaxf, int[] pmaxp, @SuppressWarnings("unused") RContext context,
-                        @SuppressWarnings("unused") @Cached("getInstance()") RContext cachedContext,
-                        @Cached("createMessageNode()") Node messageNode,
-                        @Cached("lookupFactor()") SymbolHandle fftFactor) {
-            doFactor(n, pmaxf, pmaxp, messageNode, fftFactor);
-        }
-
-        @Specialization(replaces = "executeFactorCached")
-        protected void executeFactorNormal(int n, int[] pmaxf, int[] pmaxp, @SuppressWarnings("unused") RContext context) {
-            doFactor(n, pmaxf, pmaxp, createMessageNode(), lookup("fft_factor"));
-        }
-
-        private static void doFactor(int n, int[] pmaxf, int[] pmaxp, Node messageNode, SymbolHandle fftFactor) {
-            try (NativeIntegerArray npmaxf = new NativeIntegerArray(pmaxf);
-                            NativeIntegerArray npmaxp = new NativeIntegerArray(pmaxp)) {
-                ForeignAccess.sendExecute(messageNode, fftFactor.asTruffleObject(), n, npmaxf, npmaxp);
-            } catch (InteropException ex) {
-                throw RInternalError.shouldNotReachHere(ex);
-            }
-        }
-
-        public static Node createMessageNode() {
-            return Message.createExecute(3).createNode();
-        }
-
-        public static ExecuteFactor create() {
-            return ExecuteFactorNodeGen.create();
-        }
-
-        public SymbolHandle lookupFactor() {
-            return lookup("fft_factor");
-        }
-    }
-
-    private static class Truffle_FactorNode extends Node implements FactorNode {
-        @Child private ExecuteFactor executeFactor = ExecuteFactor.create();
-
-        @Override
-        public void execute(int n, int[] pmaxf, int[] pmaxp) {
-            executeFactor.execute(n, pmaxf, pmaxp, RContext.getInstance());
-        }
-    }
-
-    private static class Truffle_WorkNode extends Node implements WorkNode {
-        @Child private ExecuteWork executeWork = ExecuteWork.create();
-
-        @Override
-        public int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
-            return executeWork.execute(a, nseg, n, nspn, isn, work, iwork, RContext.getInstance());
-        }
-    }
-
-    private static class TruffleLLVM_LminflNode extends TruffleLLVM_DownCallNode implements LminflNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.lminfl;
-        }
-
-        @Override
-        public void execute(double[] x, int ldx, int n, int k, int docoef, double[] qraux, double[] resid, double[] hat, double[] coef, double[] sigma, double tol) {
-            call(x, ldx, n, k, docoef, qraux, resid, hat, coef, sigma, tol);
-        }
-    }
-
-    @Override
-    public FactorNode createFactorNode() {
-        return new Truffle_FactorNode();
-    }
-
-    @Override
-    public WorkNode createWorkNode() {
-        return new Truffle_WorkNode();
-    }
-
-    @Override
-    public LminflNode createLminflNode() {
-        return new TruffleLLVM_LminflNode();
-    }
-}
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 e1d6e40f1c..d6a1f4b67f 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
@@ -29,7 +29,6 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.common.JavaUpCallsRFFIImpl;
 import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
-import com.oracle.truffle.r.ffi.impl.interop.NativeCharArray;
 import com.oracle.truffle.r.ffi.impl.upcalls.Callbacks;
 import com.oracle.truffle.r.ffi.impl.upcalls.FFIUnwrapNode;
 import com.oracle.truffle.r.runtime.REnvVars;
@@ -46,6 +45,7 @@ import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLL.CEntry;
 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.interop.NativeCharArray;
 
 /**
  * (Incomplete) Variant of {@link JavaUpCallsRFFIImpl} for Truffle LLVM.
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 b6922d7ec1..eb4fcd3775 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
@@ -23,63 +23,14 @@
 package com.oracle.truffle.r.ffi.impl.llvm;
 
 import com.oracle.truffle.r.ffi.impl.common.LibPaths;
-import com.oracle.truffle.r.ffi.impl.interop.NativeRawArray;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
-public class TruffleLLVM_Zip implements ZipRFFI {
+final class TruffleLLVM_Zip {
+    private TruffleLLVM_Zip() {
+    }
 
-    void initialize() {
+    static void initialize() {
         // Need to ensure that the native libz library is loaded
         String libzPath = LibPaths.getBuiltinLibPath("z");
         TruffleLLVM_NativeDLL.NativeDLOpenRootNode.create().getCallTarget().call(libzPath, false, true);
     }
-
-    private static class TruffleLLVM_CompressNode extends TruffleLLVM_DownCallNode implements CompressNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.compress;
-        }
-
-        @Override
-        public int execute(byte[] dest, byte[] source) {
-            NativeRawArray nativeDest = new NativeRawArray(dest);
-            NativeRawArray nativeSource = new NativeRawArray(source);
-            try {
-                return (int) call(nativeDest, dest.length, nativeSource, source.length);
-            } finally {
-                nativeDest.getValue();
-            }
-        }
-    }
-
-    private static class TruffleLLVM_UncompressNode extends TruffleLLVM_DownCallNode implements UncompressNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.uncompress;
-        }
-
-        @Override
-        public int execute(byte[] dest, byte[] source) {
-            NativeRawArray nativeDest = new NativeRawArray(dest);
-            NativeRawArray nativeSource = new NativeRawArray(source);
-            try {
-                return (int) call(nativeDest, dest.length, nativeSource, source.length);
-            } finally {
-                nativeDest.getValue();
-            }
-        }
-    }
-
-    @Override
-    public CompressNode createCompressNode() {
-        return new TruffleLLVM_CompressNode();
-    }
-
-    @Override
-    public UncompressNode createUncompressNode() {
-        return new TruffleLLVM_UncompressNode();
-    }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/FilesystemUtils.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/FilesystemUtils.java
deleted file mode 100644
index 5a4a822f7a..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/FilesystemUtils.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.managed;
-
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.EnumSet;
-import java.util.Set;
-
-class FilesystemUtils {
-    private static PosixFilePermission[] permissionValues = PosixFilePermission.values();
-
-    static Set<PosixFilePermission> permissionsFromMode(int mode) {
-        Set<PosixFilePermission> permissions = EnumSet.noneOf(PosixFilePermission.class);
-        for (int i = 0; i < permissionValues.length; i++) {
-            if ((mode & (1 << (permissionValues.length - i - 1))) != 0) {
-                permissions.add(permissionValues[i]);
-            }
-        }
-        return permissions;
-    }
-}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_Base.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_Base.java
deleted file mode 100644
index a1280ebd9b..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_Base.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (c) 2017, 2018, 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.managed;
-
-import static com.oracle.truffle.r.ffi.impl.managed.FilesystemUtils.permissionsFromMode;
-import static com.oracle.truffle.r.ffi.impl.managed.Managed_RFFIFactory.unsupported;
-
-import java.io.IOException;
-import java.nio.file.FileAlreadyExistsException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.attribute.PosixFilePermission;
-import java.nio.file.attribute.PosixFilePermissions;
-import java.util.Set;
-
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RError.Message;
-import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
-
-public class Managed_Base implements BaseRFFI {
-
-    private static final class ManagedGetpidNode extends Node implements GetpidNode {
-        private int fakePid = (int) System.currentTimeMillis();
-
-        @Override
-        public int execute() {
-            return fakePid;
-        }
-    }
-
-    /**
-     * Process id is used as seed for random number generator. We return another "random" number.
-     */
-    @Override
-    public GetpidNode createGetpidNode() {
-        return new ManagedGetpidNode();
-    }
-
-    private static final class ManagedGetwdNode extends Node implements GetwdNode {
-        @Override
-        @TruffleBoundary
-        public String execute() {
-            return Paths.get(".").toAbsolutePath().normalize().toString();
-        }
-    }
-
-    @Override
-    public GetwdNode createGetwdNode() {
-        return new ManagedGetwdNode();
-    }
-
-    private static final class ManagedSetwdNode extends Node implements SetwdNode {
-        @Override
-        public int execute(String dir) {
-            throw unsupported("setwd");
-        }
-    }
-
-    @Override
-    public SetwdNode createSetwdNode() {
-        return new ManagedSetwdNode();
-    }
-
-    private static final class ManagedMkdirNode extends Node implements MkdirNode {
-        @Override
-        @TruffleBoundary
-        public void execute(String dir, int mode) throws IOException {
-            Set<PosixFilePermission> permissions = permissionsFromMode(mode);
-            Files.createDirectories(Paths.get(dir), PosixFilePermissions.asFileAttribute(permissions));
-        }
-    }
-
-    @Override
-    public MkdirNode createMkdirNode() {
-        return new ManagedMkdirNode();
-    }
-
-    private static final class ManagedReadLinkNode extends Node implements ReadlinkNode {
-        @Override
-        public String execute(String path) throws IOException {
-            throw unsupported("linknode");
-        }
-    }
-
-    @Override
-    public ReadlinkNode createReadlinkNode() {
-        return new ManagedReadLinkNode();
-    }
-
-    private static final class ManagedMkdtempNode extends Node implements MkdtempNode {
-        @Override
-        @TruffleBoundary
-        public String execute(String template) {
-            Path path = null;
-            boolean done = false;
-            while (!done) {
-                try {
-                    path = Paths.get(template);
-                    Files.createDirectories(path);
-                    done = true;
-                } catch (FileAlreadyExistsException e) {
-                    // nop
-                } catch (IOException e) {
-                    throw RError.error(RError.NO_CALLER, Message.GENERIC, "Cannot create temp directories.");
-                }
-            }
-            return path.toString();
-        }
-    }
-
-    @Override
-    public MkdtempNode createMkdtempNode() {
-        return new ManagedMkdtempNode();
-    }
-
-    private static final class ManagedChmodNode extends Node implements ChmodNode {
-        @Override
-        @TruffleBoundary
-        public int execute(String path, int mode) {
-            try {
-                Files.setPosixFilePermissions(Paths.get(path), permissionsFromMode(mode));
-                return mode;
-            } catch (IOException e) {
-                throw RError.error(RError.NO_CALLER, Message.GENERIC, "Cannot change file permissions.");
-            }
-        }
-    }
-
-    @Override
-    public ChmodNode createChmodNode() {
-        return new ManagedChmodNode();
-    }
-
-    @Override
-    public StrtolNode createStrtolNode() {
-        return null;
-    }
-
-    private static final class ManagedUnameNode extends Node implements UnameNode {
-        @Override
-        public UtsName execute() {
-            return new UtsName() {
-                @Override
-                public String sysname() {
-                    return System.getProperty("os.name");
-                }
-
-                @Override
-                public String release() {
-                    return "";
-                }
-
-                @Override
-                public String version() {
-                    return System.getProperty("os.version");
-                }
-
-                @Override
-                public String machine() {
-                    return System.getProperty("os.arch");
-                }
-
-                @Override
-                public String nodename() {
-                    return "";
-                }
-            };
-        }
-    }
-
-    @Override
-    public UnameNode createUnameNode() {
-        return new ManagedUnameNode();
-    }
-
-    @Override
-    public GlobNode createGlobNode() {
-        return null;
-    }
-
-    @Override
-    public ESoftVersionNode createESoftVersionNode() {
-        return null;
-    }
-}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_DownCallNodeFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_DownCallNodeFactory.java
new file mode 100644
index 0000000000..6d2a509f84
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_DownCallNodeFactory.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018, 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.managed;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.DownCallNodeFactory;
+import com.oracle.truffle.r.runtime.ffi.NativeFunction;
+
+public final class Managed_DownCallNodeFactory extends DownCallNodeFactory {
+
+    static final Managed_DownCallNodeFactory INSTANCE = new Managed_DownCallNodeFactory();
+
+    private Managed_DownCallNodeFactory() {
+    }
+
+    @Override
+    public DownCallNode createDownCallNode(NativeFunction function) {
+        return new DownCallNode(function) {
+            @Override
+            protected TruffleObject getTarget(NativeFunction function) {
+                return new DummyFunctionObject(function);
+            }
+
+            @Override
+            protected void wrapArguments(TruffleObject function, Object[] args) {
+                // Report unsupported functions at invocation time
+                assert function instanceof DummyFunctionObject;
+                throw Managed_RFFIFactory.unsupported(((DummyFunctionObject) function).function.getCallName());
+            }
+
+            @Override
+            protected void finishArguments(Object[] args) {
+                throw RInternalError.shouldNotReachHere();
+            }
+        };
+    }
+
+    private static final class DummyFunctionObject implements TruffleObject {
+        final NativeFunction function;
+
+        private DummyFunctionObject(NativeFunction function) {
+            this.function = function;
+        }
+
+        @Override
+        public ForeignAccess getForeignAccess() {
+            return null;
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_LapackRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_LapackRFFI.java
deleted file mode 100644
index bc1cb7872e..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_LapackRFFI.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (c) 2017, 2018, 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.managed;
-
-import static com.oracle.truffle.r.ffi.impl.managed.Managed_RFFIFactory.unsupported;
-
-import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
-
-public class Managed_LapackRFFI implements LapackRFFI {
-    @Override
-    public IlaverNode createIlaverNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DgeevNode createDgeevNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public Dgeqp3Node createDgeqp3Node() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DormqrNode createDormqrNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DtrtrsNode createDtrtrsNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DgetrfNode createDgetrfNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DpotrfNode createDpotrfNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DpotriNode createDpotriNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DpstrfNode createDpstrfNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DgesvNode createDgesvNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DgesddNode createDgesddNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DlangeNode createDlangeNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DgeconNode createDgeconNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DsyevrNode createDsyevrNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public ZunmqrNode createZunmqrNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public ZtrtrsNode createZtrtrsNode() {
-        throw unsupported("lapack");
-    }
-
-    @Override
-    public DtrsmNode createDtrsmNode() {
-        throw unsupported("lapack");
-    }
-}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_PCRERFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_PCRERFFI.java
deleted file mode 100644
index 7fe533a23f..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_PCRERFFI.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.managed;
-
-import static com.oracle.truffle.r.ffi.impl.managed.Managed_RFFIFactory.unsupported;
-
-import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
-
-public class Managed_PCRERFFI implements PCRERFFI {
-    @Override
-    public MaketablesNode createMaketablesNode() {
-        throw unsupported("PCRE");
-    }
-
-    @Override
-    public CompileNode createCompileNode() {
-        throw unsupported("PCRE");
-    }
-
-    @Override
-    public GetCaptureCountNode createGetCaptureCountNode() {
-        throw unsupported("PCRE");
-    }
-
-    @Override
-    public GetCaptureNamesNode createGetCaptureNamesNode() {
-        throw unsupported("PCRE");
-    }
-
-    @Override
-    public StudyNode createStudyNode() {
-        throw unsupported("PCRE");
-    }
-
-    @Override
-    public ExecNode createExecNode() {
-        throw unsupported("PCRE");
-    }
-}
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 2e4f6ad6a4..5807ab6fe9 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
@@ -28,12 +28,15 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI.HandleUpCallExceptionNode;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
+import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
 import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeFunction;
+import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIContext;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
@@ -54,7 +57,7 @@ public final class Managed_RFFIFactory extends RFFIFactory {
                 public InvokeCNode createInvokeCNode() {
                     throw unsupported("invoke");
                 }
-            }, new Managed_Base(), new CallRFFI() {
+            }, new BaseRFFI(Managed_DownCallNodeFactory.INSTANCE), new CallRFFI() {
                 @Override
                 public InvokeCallNode createInvokeCallNode() {
                     throw unsupported("native code invocation");
@@ -104,51 +107,16 @@ public final class Managed_RFFIFactory extends RFFIFactory {
                 public SeedsNode createSeedsNode() {
                     throw unsupported("user defined RNG");
                 }
-            }, new ZipRFFI() {
-                @Override
-                public CompressNode createCompressNode() {
-                    throw unsupported("zip compression");
-                }
-
-                @Override
-                public UncompressNode createUncompressNode() {
-                    throw unsupported("zip decompression");
-                }
-            }, new Managed_PCRERFFI(), new Managed_LapackRFFI(),
-                            new StatsRFFI() {
-                                @Override
-                                public FactorNode createFactorNode() {
-                                    throw unsupported("factor");
-                                }
-
-                                @Override
-                                public WorkNode createWorkNode() {
-                                    throw unsupported("work");
-                                }
-
-                                @Override
-                                public LminflNode createLminflNode() {
-                                    throw unsupported("lminfl");
-                                }
-                            }, new ToolsRFFI() {
+            }, new ZipRFFI(Managed_DownCallNodeFactory.INSTANCE), new PCRERFFI(Managed_DownCallNodeFactory.INSTANCE), new LapackRFFI(Managed_DownCallNodeFactory.INSTANCE),
+                            new StatsRFFI(Managed_DownCallNodeFactory.INSTANCE), new ToolsRFFI() {
                                 @Override
                                 public ParseRdNode createParseRdNode() {
                                     throw unsupported("parseRD");
                                 }
-                            }, new Managed_REmbedRFFI(), new MiscRFFI() {
-                                @Override
-                                public ExactSumNode createExactSumNode() {
-                                    throw unsupported("exactsum");
-                                }
-
-                                @Override
-                                public DqrlsNode createDqrlsNode() {
-                                    throw unsupported("dqrls");
-                                }
-                            });
+                            }, new Managed_REmbedRFFI(), new MiscRFFI(Managed_DownCallNodeFactory.INSTANCE));
         }
 
-        static class IgnoreUpCallExceptionNode extends Node implements HandleUpCallExceptionNode {
+        private static class IgnoreUpCallExceptionNode extends Node implements HandleUpCallExceptionNode {
             @Override
             public void execute(Throwable ex) {
                 // nop
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/HandleNFIUpCallExceptionNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/HandleNFIUpCallExceptionNode.java
index b1620ab092..4c3835c1d6 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/HandleNFIUpCallExceptionNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/HandleNFIUpCallExceptionNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,15 +25,16 @@ package com.oracle.truffle.r.ffi.impl.nfi;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI.HandleUpCallExceptionNode;
+import com.oracle.truffle.r.runtime.ffi.DownCallNodeFactory.DownCallNode;
 import com.oracle.truffle.r.runtime.ffi.NativeFunction;
 
 public class HandleNFIUpCallExceptionNode extends Node implements HandleUpCallExceptionNode {
-    @Child private SetFlagNode setFlagNode = new SetFlagNode();
+    @Child private DownCallNode setFlagNode = TruffleNFI_DownCallNodeFactory.INSTANCE.createDownCallNode(NativeFunction.set_exception_flag);
 
     @Override
     @TruffleBoundary
     public void execute(Throwable originalEx) {
-        setFlagNode.execute();
+        setFlagNode.call();
         RuntimeException ex;
         if (originalEx instanceof RuntimeException) {
             ex = (RuntimeException) originalEx;
@@ -42,15 +43,4 @@ public class HandleNFIUpCallExceptionNode extends Node implements HandleUpCallEx
         }
         TruffleNFI_Context.getInstance().setLastUpCallException(ex);
     }
-
-    private static final class SetFlagNode extends TruffleNFI_DownCallNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.set_exception_flag;
-        }
-
-        public void execute() {
-            call();
-        }
-    }
 }
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
deleted file mode 100644
index 7d8f8a9a8a..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Base.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (c) 2014, 2018, 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 java.io.IOException;
-import java.util.ArrayList;
-import java.util.Map;
-
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.r.ffi.impl.interop.base.ESoftVersionResult;
-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.ffi.BaseRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-
-public class TruffleNFI_Base implements BaseRFFI {
-
-    private static class TruffleNFI_GetpidNode extends TruffleNFI_DownCallNode implements GetpidNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.getpid;
-        }
-
-        @Override
-        public int execute() {
-            return (int) call();
-        }
-    }
-
-    private static final class TruffleNFI_GetwdNode extends TruffleNFI_DownCallNode implements GetwdNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.getcwd;
-        }
-
-        @TruffleBoundary
-        @Override
-        public String execute() {
-            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++;
-                }
-                return new String(buf, 0, i);
-            }
-        }
-    }
-
-    private static class TruffleNFI_SetwdNode extends TruffleNFI_DownCallNode implements SetwdNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.chdir;
-        }
-
-        @Override
-        public int execute(String dir) {
-            return (int) call(dir);
-        }
-    }
-
-    private static class TruffleNFI_MkdirNode extends TruffleNFI_DownCallNode implements MkdirNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.mkdir;
-        }
-
-        @Override
-        public void execute(String dir, int mode) throws IOException {
-            if ((int) call(dir, mode) != 0) {
-                throw new IOException("mkdir " + dir + " failed");
-            }
-        }
-    }
-
-    private static class TruffleNFI_ReadlinkNode extends TruffleNFI_DownCallNode implements ReadlinkNode {
-        private static final int EINVAL = 22;
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.readlink;
-        }
-
-        @Override
-        public String execute(String path) throws IOException {
-            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();
-        }
-    }
-
-    private static class TruffleNFI_MkdtempNode extends TruffleNFI_DownCallNode implements MkdtempNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.mkdtemp;
-        }
-
-        @TruffleBoundary
-        @Override
-        public String execute(String template) {
-            /*
-             * Not only must the (C) string end in XXXXXX it must also be null-terminated. Since it
-             * is modified by mkdtemp we must make a copy.
-             */
-            byte[] bytes = template.getBytes();
-            byte[] ztbytes = new byte[bytes.length + 1];
-            System.arraycopy(bytes, 0, ztbytes, 0, bytes.length);
-            ztbytes[bytes.length] = 0;
-            int result = (int) call(JavaInterop.asTruffleObject(ztbytes));
-            if (result == 0) {
-                return null;
-            } else {
-                return new String(ztbytes, 0, bytes.length);
-            }
-        }
-    }
-
-    private static class TruffleNFI_ChmodNode extends TruffleNFI_DownCallNode implements ChmodNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.chmod;
-        }
-
-        @Override
-        public int execute(String path, int mode) {
-            return (int) call(path, mode);
-        }
-    }
-
-    private static class TruffleNFI_StrtolNode extends TruffleNFI_DownCallNode implements StrtolNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.strtol;
-        }
-
-        @Override
-        public long execute(String s, int base) throws IllegalArgumentException {
-            StrtolResult data = new StrtolResult();
-            call(data, s, base);
-            if (data.getErrno() != 0) {
-                throw new IllegalArgumentException("strtol failure");
-            } else {
-                return data.getResult();
-            }
-        }
-    }
-
-    private static class TruffleNFI_UnameNode extends TruffleNFI_DownCallNode implements UnameNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.uname;
-        }
-
-        @Override
-        public UtsName execute() {
-            UnameResult data = new UnameResult();
-            call(data);
-            return data;
-        }
-    }
-
-    private static class TruffleNFI_GlobNode extends TruffleNFI_DownCallNode implements GlobNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.glob;
-        }
-
-        @Override
-        public ArrayList<String> glob(String pattern) {
-            GlobResult data = new GlobResult();
-            call(data, pattern);
-            return data.getPaths();
-        }
-    }
-
-    private static class TruffleNFI_ESoftVersionNode extends TruffleNFI_DownCallNode implements ESoftVersionNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.eSoftVersion;
-        }
-
-        @Override
-        public Map<String, String> eSoftVersion() {
-            ESoftVersionResult result = new ESoftVersionResult();
-            call(result);
-            return result.getVersions();
-        }
-
-    }
-
-    @Override
-    public GetpidNode createGetpidNode() {
-        return new TruffleNFI_GetpidNode();
-    }
-
-    @Override
-    public GetwdNode createGetwdNode() {
-        return new TruffleNFI_GetwdNode();
-    }
-
-    @Override
-    public SetwdNode createSetwdNode() {
-        return new TruffleNFI_SetwdNode();
-    }
-
-    @Override
-    public MkdirNode createMkdirNode() {
-        return new TruffleNFI_MkdirNode();
-    }
-
-    @Override
-    public ReadlinkNode createReadlinkNode() {
-        return new TruffleNFI_ReadlinkNode();
-    }
-
-    @Override
-    public MkdtempNode createMkdtempNode() {
-        return new TruffleNFI_MkdtempNode();
-    }
-
-    @Override
-    public ChmodNode createChmodNode() {
-        return new TruffleNFI_ChmodNode();
-    }
-
-    @Override
-    public StrtolNode createStrtolNode() {
-        return new TruffleNFI_StrtolNode();
-    }
-
-    @Override
-    public UnameNode createUnameNode() {
-        return new TruffleNFI_UnameNode();
-    }
-
-    @Override
-    public GlobNode createGlobNode() {
-        return new TruffleNFI_GlobNode();
-    }
-
-    @Override
-    public ESoftVersionNode createESoftVersionNode() {
-        return new TruffleNFI_ESoftVersionNode();
-    }
-
-}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java
index affec6643a..c620ea7996 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java
@@ -50,13 +50,19 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextKind;
 import com.oracle.truffle.r.runtime.context.RContext.ContextState;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
 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.LapackRFFI;
+import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeFunction;
+import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 import com.oracle.truffle.r.runtime.ffi.RFFIContext;
 import com.oracle.truffle.r.runtime.ffi.RFFIVariables;
+import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
+import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
 import sun.misc.Unsafe;
 
@@ -84,8 +90,9 @@ final class TruffleNFI_Context extends RFFIContext {
     private static ReentrantLock accessLock;
 
     TruffleNFI_Context() {
-        super(new TruffleNFI_C(), new TruffleNFI_Base(), new TruffleNFI_Call(), new TruffleNFI_DLL(), new TruffleNFI_UserRng(), new TruffleNFI_Zip(), new TruffleNFI_PCRE(), new TruffleNFI_Lapack(),
-                        new TruffleNFI_Stats(), new TruffleNFI_Tools(), new TruffleNFI_REmbed(), new TruffleNFI_Misc());
+        super(new TruffleNFI_C(), new BaseRFFI(TruffleNFI_DownCallNodeFactory.INSTANCE), new TruffleNFI_Call(), new TruffleNFI_DLL(), new TruffleNFI_UserRng(),
+                        new ZipRFFI(TruffleNFI_DownCallNodeFactory.INSTANCE), new PCRERFFI(TruffleNFI_DownCallNodeFactory.INSTANCE), new LapackRFFI(TruffleNFI_DownCallNodeFactory.INSTANCE),
+                        new StatsRFFI(TruffleNFI_DownCallNodeFactory.INSTANCE), new TruffleNFI_Tools(), new TruffleNFI_REmbed(), new MiscRFFI(TruffleNFI_DownCallNodeFactory.INSTANCE));
         // forward constructor
     }
 
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
deleted file mode 100644
index 3f0177ab38..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNode.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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 {
-
-    @Override
-    protected final TruffleObject getTarget() {
-        // TODO: this lookupNativeFunction function can exist in all FFI Contexts
-        return TruffleNFI_Context.getInstance().lookupNativeFunction(getFunction());
-    }
-
-    @SuppressWarnings("cast")
-    @Override
-    @ExplodeLoop
-    protected void wrapArguments(Object[] args) {
-        for (int i = 0; i < args.length; i++) {
-            Object obj = args[i];
-            // TODO: this could use the wrappers from LLVM
-            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) {
-            // TODO: can this ever happen in NFI?
-            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_DownCallNodeFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNodeFactory.java
new file mode 100644
index 0000000000..efa57394f1
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNodeFactory.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018, 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.CompilerDirectives.TruffleBoundary;
+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.interop.NativeNACheck;
+import com.oracle.truffle.r.runtime.ffi.DownCallNodeFactory;
+import com.oracle.truffle.r.runtime.ffi.NativeFunction;
+import com.oracle.truffle.r.runtime.ffi.interop.NativeUInt8Array;
+
+public final class TruffleNFI_DownCallNodeFactory extends DownCallNodeFactory {
+    public static final TruffleNFI_DownCallNodeFactory INSTANCE = new TruffleNFI_DownCallNodeFactory();
+
+    private TruffleNFI_DownCallNodeFactory() {
+    }
+
+    @Override
+    public DownCallNode createDownCallNode(NativeFunction function) {
+        return new DownCallNode(function) {
+            @Override
+            protected TruffleObject getTarget(NativeFunction function) {
+                // TODO: this lookupNativeFunction function can exist in all FFI Contexts
+                return TruffleNFI_Context.getInstance().lookupNativeFunction(function);
+            }
+
+            @SuppressWarnings("cast")
+            @Override
+            @ExplodeLoop
+            protected void wrapArguments(TruffleObject function, Object[] args) {
+                for (int i = 0; i < args.length; i++) {
+                    Object obj = args[i];
+                    if (obj instanceof double[]) {
+                        args[i] = JavaInterop.asTruffleObject(obj);
+                    } else if (obj instanceof int[] || obj == null) {
+                        args[i] = JavaInterop.asTruffleObject(obj);
+                    } else if (obj instanceof NativeUInt8Array) {
+                        // accounts for NativeCharArray and NativeRawArray
+                        // the assumption is that getValue() gives us the actual backing array and
+                        // NFI will transfer any changes back to this array
+                        NativeUInt8Array nativeArr = (NativeUInt8Array) obj;
+                        byte[] data;
+                        if (nativeArr.fakesNullTermination()) {
+                            data = getNullTerminatedBytes(nativeArr.getValue());
+                            nativeArr.setValue(data, false);
+                        } else {
+                            data = nativeArr.getValue();
+                        }
+                        args[i] = JavaInterop.asTruffleObject(data);
+                    }
+                }
+            }
+
+            @TruffleBoundary
+            private byte[] getNullTerminatedBytes(byte[] data) {
+                byte[] newData = new byte[data.length + 1];
+                System.arraycopy(data, 0, newData, 0, data.length);
+                newData[data.length] = 0;
+                return newData;
+            }
+
+            @Override
+            @ExplodeLoop
+            protected void finishArguments(Object[] args) {
+                for (Object obj : args) {
+                    // TODO: can this ever happen in NFI?
+                    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
deleted file mode 100644
index 6138264648..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Lapack.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (c) 2014, 2018, 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.r.runtime.ffi.LapackRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-
-public class TruffleNFI_Lapack implements LapackRFFI {
-
-    private static class TruffleNFI_IlaverNode extends TruffleNFI_DownCallNode implements IlaverNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.ilaver;
-        }
-
-        @Override
-        public void execute(int[] version) {
-            call(version);
-        }
-    }
-
-    private static class TruffleNFI_DgeevNode extends TruffleNFI_DownCallNode implements DgeevNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.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) {
-            return (int) call(jobVL, jobVR, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork);
-        }
-    }
-
-    private static class TruffleNFI_Dgeqp3Node extends TruffleNFI_DownCallNode implements Dgeqp3Node {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgeqp3;
-        }
-
-        @Override
-        public int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork) {
-            return (int) call(m, n, a, lda, jpvt, tau, work, lwork);
-        }
-    }
-
-    private static class TruffleNFI_DormqrNode extends TruffleNFI_DownCallNode implements DormqrNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.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) {
-            return (int) call(side, trans, m, n, k, a, lda, tau, c, ldc, work, lwork);
-        }
-    }
-
-    private static class TruffleNFI_DtrtrsNode extends TruffleNFI_DownCallNode implements DtrtrsNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dtrtrs;
-        }
-
-        @Override
-        public int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
-            return (int) call(uplo, trans, diag, n, nrhs, a, lda, b, ldb);
-        }
-    }
-
-    private static class TruffleNFI_DgetrfNode extends TruffleNFI_DownCallNode implements DgetrfNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgetrf;
-        }
-
-        @Override
-        public int execute(int m, int n, double[] a, int lda, int[] ipiv) {
-            return (int) call(m, n, a, lda, ipiv);
-        }
-    }
-
-    private static class TruffleNFI_DpotrfNode extends TruffleNFI_DownCallNode implements DpotrfNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dpotrf;
-        }
-
-        @Override
-        public int execute(char uplo, int n, double[] a, int lda) {
-            return (int) call(uplo, n, a, lda);
-        }
-    }
-
-    private static class TruffleNFI_DpotriNode extends TruffleNFI_DownCallNode implements DpotriNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dpotri;
-        }
-
-        @Override
-        public int execute(char uplo, int n, double[] a, int lda) {
-            return (int) call(uplo, n, a, lda);
-        }
-    }
-
-    private static class TruffleNFI_DpstrfNode extends TruffleNFI_DownCallNode implements DpstrfNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dpstrf;
-        }
-
-        @Override
-        public int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work) {
-            return (int) call(uplo, n, a, lda, piv, rank, tol, work);
-        }
-    }
-
-    private static class TruffleNFI_DgesvNode extends TruffleNFI_DownCallNode implements DgesvNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgesv;
-        }
-
-        @Override
-        public int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb) {
-            return (int) call(n, nrhs, a, lda, ipiv, b, ldb);
-        }
-    }
-
-    private static class TruffleNFI_DgesddNode extends TruffleNFI_DownCallNode implements DgesddNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgesdd;
-        }
-
-        @Override
-        public int execute(char jobz, int m, int n, double[] a, int lda, double[] s, double[] u, int ldu, double[] vt, int ldtv, double[] work, int lwork, int[] iwork) {
-            return (int) call(jobz, m, n, a, lda, s, u, ldu, vt, ldtv, work, lwork, iwork);
-        }
-    }
-
-    private static class TruffleNFI_DlangeNode extends TruffleNFI_DownCallNode implements DlangeNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dlange;
-        }
-
-        @Override
-        public double execute(char norm, int m, int n, double[] a, int lda, double[] work) {
-            return (double) call(norm, m, n, a, lda, work);
-        }
-    }
-
-    private static class TruffleNFI_DgeconNode extends TruffleNFI_DownCallNode implements DgeconNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dgecon;
-        }
-
-        @Override
-        public int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork) {
-            return (int) call(norm, n, a, lda, anorm, rcond, work, iwork);
-        }
-    }
-
-    private static class TruffleNFI_DsyevrNode extends TruffleNFI_DownCallNode implements DsyevrNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.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) {
-            return (int) call(jobz, range, uplo, n, a, lda, vl, vu, il, iu, abstol, m, w, z, ldz, isuppz, work, lwork, iwork, liwork);
-        }
-    }
-
-    private static class TruffleNFI_ZunmqrNode extends TruffleNFI_DownCallNode implements ZunmqrNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.zunmqr;
-        }
-
-        @Override
-        public int execute(String side, String trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork) {
-            return (int) call(side, trans, m, n, k, a, lda, tau, c, ldc, work, lwork);
-        }
-
-    }
-
-    private static final class TruffleNFI_ZtrtrsNode extends TruffleNFI_DownCallNode implements ZtrtrsNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.ztrtrs;
-        }
-
-        @Override
-        public int execute(String uplo, String trans, String diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
-            return (int) call(uplo, trans, diag, n, nrhs, a, lda, b, ldb);
-        }
-    }
-
-    private static class TruffleNFI_DtrsmNode extends TruffleNFI_DownCallNode implements DtrsmNode {
-
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.dtrsm;
-        }
-
-        @Override
-        public void execute(String side, String uplo, String transa, String diag, int m, int n, double alpha, double[] a, int lda, double[] b, int ldb) {
-            call(side, uplo, transa, diag, m, n, alpha, a, lda, b, ldb);
-        }
-
-    }
-
-    @Override
-    public IlaverNode createIlaverNode() {
-        return new TruffleNFI_IlaverNode();
-    }
-
-    @Override
-    public DgeevNode createDgeevNode() {
-        return new TruffleNFI_DgeevNode();
-    }
-
-    @Override
-    public Dgeqp3Node createDgeqp3Node() {
-        return new TruffleNFI_Dgeqp3Node();
-    }
-
-    @Override
-    public DormqrNode createDormqrNode() {
-        return new TruffleNFI_DormqrNode();
-    }
-
-    @Override
-    public DtrtrsNode createDtrtrsNode() {
-        return new TruffleNFI_DtrtrsNode();
-    }
-
-    @Override
-    public DgetrfNode createDgetrfNode() {
-        return new TruffleNFI_DgetrfNode();
-    }
-
-    @Override
-    public DpotrfNode createDpotrfNode() {
-        return new TruffleNFI_DpotrfNode();
-    }
-
-    @Override
-    public DpotriNode createDpotriNode() {
-        return new TruffleNFI_DpotriNode();
-    }
-
-    @Override
-    public DpstrfNode createDpstrfNode() {
-        return new TruffleNFI_DpstrfNode();
-    }
-
-    @Override
-    public DgesvNode createDgesvNode() {
-        return new TruffleNFI_DgesvNode();
-    }
-
-    @Override
-    public DgesddNode createDgesddNode() {
-        return new TruffleNFI_DgesddNode();
-    }
-
-    @Override
-    public DlangeNode createDlangeNode() {
-        return new TruffleNFI_DlangeNode();
-    }
-
-    @Override
-    public DgeconNode createDgeconNode() {
-        return new TruffleNFI_DgeconNode();
-    }
-
-    @Override
-    public DsyevrNode createDsyevrNode() {
-        return new TruffleNFI_DsyevrNode();
-    }
-
-    @Override
-    public ZunmqrNode createZunmqrNode() {
-        return new TruffleNFI_ZunmqrNode();
-    }
-
-    @Override
-    public ZtrtrsNode createZtrtrsNode() {
-        return new TruffleNFI_ZtrtrsNode();
-    }
-
-    @Override
-    public DtrsmNode createDtrsmNode() {
-        return new TruffleNFI_DtrsmNode();
-    }
-}
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
deleted file mode 100644
index e94bcccd81..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Misc.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.nfi;
-
-import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-
-public class TruffleNFI_Misc implements MiscRFFI {
-
-    private static final class TruffleNFI_ExactSumNode extends TruffleNFI_DownCallNode implements ExactSumNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.exactSumFunc;
-        }
-
-        @Override
-        public double execute(double[] values, boolean hasNa, boolean naRm) {
-            return (double) call(values, values.length, hasNa ? 1 : 0, naRm ? 1 : 0);
-        }
-    }
-
-    private static class TruffleNFI_DqrlsNode extends TruffleNFI_DownCallNode implements DqrlsNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.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) {
-            call(x, n, p, y, ny, tol, b, rsd, qty, k, jpvt, qraux, work);
-        }
-    }
-
-    @Override
-    public ExactSumNode createExactSumNode() {
-        return new TruffleNFI_ExactSumNode();
-    }
-
-    @Override
-    public DqrlsNode createDqrlsNode() {
-        return new TruffleNFI_DqrlsNode();
-    }
-
-}
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
deleted file mode 100644
index 0be4447c54..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PCRE.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.ffi.impl.nfi;
-
-import java.nio.charset.StandardCharsets;
-
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.interop.java.JavaInterop;
-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;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
-
-public class TruffleNFI_PCRE implements PCRERFFI {
-
-    private static class TruffleNFI_MaketablesNode extends TruffleNFI_DownCallNode implements MaketablesNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.maketables;
-        }
-
-        @Override
-        public long execute() {
-            return (long) call();
-        }
-    }
-
-    private static class TruffleNFI_GetCaptureCountNode extends TruffleNFI_DownCallNode implements GetCaptureCountNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.getcapturecount;
-        }
-
-        @Override
-        public int execute(long code, long extra) {
-            return (int) call(code, extra);
-        }
-    }
-
-    private static class TruffleNFI_GetCaptureNamesNode extends TruffleNFI_DownCallNode implements GetCaptureNamesNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.getcapturenames;
-        }
-
-        @Override
-        public String[] execute(long code, long extra, int captureCount) {
-            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();
-            }
-        }
-    }
-
-    private static class TruffleNFI_CompileNode extends TruffleNFI_DownCallNode implements CompileNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.compile;
-        }
-
-        @Override
-        public Result execute(String pattern, int options, long tables) {
-            CompileResult data = new CompileResult();
-            call(data, pattern, options, tables);
-            return data.getResult();
-        }
-    }
-
-    private static class TruffleNFI_ExecNode extends TruffleNFI_DownCallNode implements ExecNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.exec;
-        }
-
-        @Override
-        public int execute(long code, long extra, String subject, int offset, int options, int[] ovector) {
-            byte[] subjectBytes = subject.getBytes(StandardCharsets.UTF_8);
-            return (int) call(code, extra, JavaInterop.asTruffleObject(subjectBytes), subjectBytes.length, offset, options, ovector, ovector.length);
-        }
-    }
-
-    @Override
-    public MaketablesNode createMaketablesNode() {
-        return new TruffleNFI_MaketablesNode();
-    }
-
-    @Override
-    public CompileNode createCompileNode() {
-        return new TruffleNFI_CompileNode();
-    }
-
-    @Override
-    public GetCaptureCountNode createGetCaptureCountNode() {
-        return new TruffleNFI_GetCaptureCountNode();
-    }
-
-    @Override
-    public GetCaptureNamesNode createGetCaptureNamesNode() {
-        return new TruffleNFI_GetCaptureNamesNode();
-    }
-
-    @Override
-    public StudyNode createStudyNode() {
-        throw RInternalError.unimplemented();
-    }
-
-    @Override
-    public ExecNode createExecNode() {
-        return new TruffleNFI_ExecNode();
-    }
-}
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
deleted file mode 100644
index 86a8ce5bf3..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Stats.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.ffi.impl.nfi;
-
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
-
-public class TruffleNFI_Stats implements StatsRFFI {
-
-    private static class TruffleNFI_FactorNode extends TruffleNFI_DownCallNode implements FactorNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.fft_factor;
-        }
-
-        @Override
-        public void execute(int n, int[] pmaxf, int[] pmaxp) {
-            call(n, pmaxf, pmaxp);
-        }
-    }
-
-    private static class TruffleNFI_WorkNode extends TruffleNFI_DownCallNode implements WorkNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.fft_work;
-        }
-
-        @Override
-        public int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
-            return (int) call(a, nseg, n, nspn, isn, work, iwork);
-        }
-    }
-
-    private static class TruffleNFI_LminflNode extends TruffleNFI_DownCallNode implements LminflNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.lminfl;
-        }
-
-        @Override
-        public void execute(double[] x, int ldx, int n, int k, int docoef, double[] qraux, double[] resid, double[] hat, double[] coef, double[] sigma, double tol) {
-            call(x, ldx, n, k, docoef, qraux, resid, hat, coef, sigma, tol);
-        }
-    }
-
-    @Override
-    public FactorNode createFactorNode() {
-        return new TruffleNFI_FactorNode();
-    }
-
-    @Override
-    public WorkNode createWorkNode() {
-        return new TruffleNFI_WorkNode();
-    }
-
-    @Override
-    public LminflNode createLminflNode() {
-        return new TruffleNFI_LminflNode();
-    }
-}
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
deleted file mode 100644
index c3340a66c9..0000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Zip.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.ffi.impl.nfi;
-
-import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.r.runtime.ffi.NativeFunction;
-import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
-
-public class TruffleNFI_Zip implements ZipRFFI {
-
-    private static class TruffleNFI_CompressNode extends TruffleNFI_DownCallNode implements ZipRFFI.CompressNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.compress;
-        }
-
-        @Override
-        public int execute(byte[] dest, byte[] source) {
-            return (int) call(JavaInterop.asTruffleObject(dest), (long) dest.length, JavaInterop.asTruffleObject(source), source.length);
-        }
-    }
-
-    private static class TruffleNFI_UncompressNode extends TruffleNFI_DownCallNode implements ZipRFFI.UncompressNode {
-        @Override
-        protected NativeFunction getFunction() {
-            return NativeFunction.uncompress;
-        }
-
-        @Override
-        public int execute(byte[] dest, byte[] source) {
-            return (int) call(JavaInterop.asTruffleObject(dest), (long) dest.length, JavaInterop.asTruffleObject(source), source.length);
-        }
-    }
-
-    @Override
-    public CompressNode createCompressNode() {
-        return new TruffleNFI_CompressNode();
-    }
-
-    @Override
-    public UncompressNode createUncompressNode() {
-        return new TruffleNFI_UncompressNode();
-    }
-}
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 4dd512a84a..297d4c7b6d 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,60 +26,116 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Map;
 
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.NodeInterface;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.r.runtime.ffi.base.ESoftVersionResult;
+import com.oracle.truffle.r.runtime.ffi.base.GlobResult;
+import com.oracle.truffle.r.runtime.ffi.base.ReadlinkResult;
+import com.oracle.truffle.r.runtime.ffi.base.StrtolResult;
+import com.oracle.truffle.r.runtime.ffi.base.UnameResult;
+import com.oracle.truffle.r.runtime.ffi.interop.NativeCharArray;
 
 /**
  * A statically typed interface to exactly those native functions required by the R {@code base}
  * package, because the functionality is not provided by the JDK. These methods do not necessarily
  * map 1-1 to a native function, they may involve the invocation of several native functions.
  */
-public interface BaseRFFI {
+public final class BaseRFFI {
 
-    interface GetpidNode extends NodeInterface {
-        int execute();
+    private final DownCallNodeFactory downCallNodeFactory;
 
-        static GetpidNode create() {
+    public BaseRFFI(DownCallNodeFactory downCallNodeFactory) {
+        this.downCallNodeFactory = downCallNodeFactory;
+    }
+
+    public static final class GetpidNode extends NativeCallNode {
+
+        private GetpidNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.getpid));
+        }
+
+        public int execute() {
+            return (int) call();
+        }
+
+        public static GetpidNode create() {
             return RFFIFactory.getBaseRFFI().createGetpidNode();
         }
     }
 
-    interface GetwdNode extends NodeInterface {
+    public static final class GetwdNode extends NativeCallNode {
+        private static final int BUFFER_LEN = 4096;
+
+        private GetwdNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.getcwd));
+        }
+
         /**
          * Returns the current working directory, in the face of calls to {@code setwd}.
          */
-        String execute();
+        public String execute() {
+            NativeCharArray nativeBuf = NativeCharArray.crateOutputBuffer(BUFFER_LEN);
+            int result = (int) call(nativeBuf, BUFFER_LEN);
+            if (result == 0) {
+                return null;
+            } else {
+                return nativeBuf.getStringFromOutputBuffer();
+            }
+        }
 
-        static GetwdNode create() {
+        public static GetwdNode create() {
             return RFFIFactory.getBaseRFFI().createGetwdNode();
         }
     }
 
-    interface SetwdNode extends NodeInterface {
+    public static final class SetwdNode extends NativeCallNode {
+
+        private SetwdNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.chdir));
+        }
+
         /**
          * Sets the current working directory to {@code dir}. (cf. Unix {@code chdir}).
          *
          * @return 0 if successful.
          */
-        int execute(String dir);
+        public int execute(String dir) {
+            return (int) call(dir);
+        }
 
-        static SetwdNode create() {
+        public static SetwdNode create() {
             return RFFIFactory.getBaseRFFI().createSetwdNode();
         }
     }
 
-    interface MkdirNode extends NodeInterface {
+    public static final class MkdirNode extends NativeCallNode {
+
+        private MkdirNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.mkdir));
+        }
+
         /**
          * Create directory with given mode. Exception is thrown on error.
          */
-        void execute(String dir, int mode) throws IOException;
+        public void execute(String dir, int mode) throws IOException {
+            if ((int) call(dir, mode) != 0) {
+                throw new IOException("mkdir " + dir + " failed");
+            }
+        }
 
-        static MkdirNode create() {
+        public static MkdirNode create() {
             return RFFIFactory.getBaseRFFI().createMkdirNode();
         }
     }
 
-    interface ReadlinkNode extends NodeInterface {
+    public static final class ReadlinkNode extends NativeCallNode {
+        private static final int EINVAL = 22;
+
+        private ReadlinkNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.readlink));
+        }
+
         /**
          * Try to convert a symbolic link to it's target.
          *
@@ -87,43 +143,93 @@ 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"
          */
-        String execute(String path) throws IOException;
+        public String execute(String path) throws IOException {
+            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();
+        }
 
-        static ReadlinkNode create() {
+        public static ReadlinkNode create() {
             return RFFIFactory.getBaseRFFI().createReadlinkNode();
         }
     }
 
-    interface MkdtempNode extends NodeInterface {
+    public static final class MkdtempNode extends NativeCallNode {
+
+        private MkdtempNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.mkdtemp));
+        }
+
         /**
          * Creates a temporary directory using {@code template} and return the resulting path or
          * {@code null} if error.
          */
-        String execute(String template);
+        @TruffleBoundary
+        public String execute(String template) {
+            /*
+             * Not only must the (C) string end in XXXXXX it must also be null-terminated. Since it
+             * is modified by mkdtemp we must make a copy.
+             */
+            NativeCharArray nativeZtbytes = new NativeCharArray(template);
+            int result = (int) call(nativeZtbytes);
+            if (result == 0) {
+                return null;
+            } else {
+                return nativeZtbytes.getString();
+            }
+        }
 
-        static MkdtempNode create() {
+        public static MkdtempNode create() {
             return RFFIFactory.getBaseRFFI().createMkdtempNode();
         }
     }
 
-    interface ChmodNode extends NodeInterface {
+    public static final class ChmodNode extends NativeCallNode {
+
+        private ChmodNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.chmod));
+        }
+
         /**
          * Change the file mode of {@code path}.
          */
-        int execute(String path, int mode);
+        public int execute(String path, int mode) {
+            return (int) call(path, mode);
+        }
 
-        static ChmodNode create() {
+        public static ChmodNode create() {
             return RFFIFactory.getBaseRFFI().createChmodNode();
         }
     }
 
-    interface StrtolNode extends NodeInterface {
+    public static final class StrtolNode extends NativeCallNode {
+
+        private StrtolNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.strtol));
+        }
+
         /**
          * Convert string to long.
          */
-        long execute(String s, int base) throws IllegalArgumentException;
+        public long execute(String s, int base) throws IllegalArgumentException {
+            StrtolResult data = new StrtolResult();
+            call(data, s, base);
+            if (data.getErrno() != 0) {
+                throw new IllegalArgumentException("strtol failure");
+            } else {
+                return data.getResult();
+            }
+        }
 
-        static StrtolNode create() {
+        public static StrtolNode create() {
             return RFFIFactory.getBaseRFFI().createStrtolNode();
         }
     }
@@ -140,71 +246,113 @@ public interface BaseRFFI {
         String nodename();
     }
 
-    interface UnameNode extends NodeInterface {
+    public static final class UnameNode extends NativeCallNode {
+
+        private UnameNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.uname));
+        }
+
         /**
          * Return {@code utsname} info.
          */
-        UtsName execute();
+        public UtsName execute() {
+            UnameResult data = new UnameResult();
+            call(data);
+            return data;
+        }
 
-        static UnameNode create() {
+        public static UnameNode create() {
             return RFFIFactory.getBaseRFFI().createUnameNode();
         }
     }
 
-    interface GlobNode extends NodeInterface {
+    public static final class GlobNode extends NativeCallNode {
+
+        private GlobNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.glob));
+        }
+
         /**
          * 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}.
          */
-        ArrayList<String> glob(String pattern);
+        public ArrayList<String> glob(String pattern) {
+            GlobResult data = new GlobResult();
+            call(data, pattern);
+            return data.getPaths();
+        }
 
-        static GlobNode create() {
+        public static GlobNode create() {
             return RFFIFactory.getBaseRFFI().createGlobNode();
         }
     }
 
-    interface ESoftVersionNode extends NodeInterface {
-        Map<String, String> eSoftVersion();
+    public static final class ESoftVersionNode extends NativeCallNode {
+
+        private ESoftVersionNode(DownCallNodeFactory parent) {
+            super(parent.createDownCallNode(NativeFunction.eSoftVersion));
+        }
+
+        public Map<String, String> eSoftVersion() {
+            ESoftVersionResult result = new ESoftVersionResult();
+            call(result);
+            return result.getVersions();
+        }
 
-        static ESoftVersionNode create() {
+        public static ESoftVersionNode create() {
             return RFFIFactory.getBaseRFFI().createESoftVersionNode();
         }
     }
 
-    /*
-     * The RFFI implementation influences exactly what subclass of the above nodes is created. Each
-     * implementation must therefore, implement these methods that are called by the associated
-     * "public static create()" methods above.
-     */
-
-    GetpidNode createGetpidNode();
+    public GetpidNode createGetpidNode() {
+        return new GetpidNode(downCallNodeFactory);
+    }
 
-    GetwdNode createGetwdNode();
+    public GetwdNode createGetwdNode() {
+        return new GetwdNode(downCallNodeFactory);
+    }
 
-    SetwdNode createSetwdNode();
+    public SetwdNode createSetwdNode() {
+        return new SetwdNode(downCallNodeFactory);
+    }
 
-    MkdirNode createMkdirNode();
+    public MkdirNode createMkdirNode() {
+        return new MkdirNode(downCallNodeFactory);
+    }
 
-    ReadlinkNode createReadlinkNode();
+    public ReadlinkNode createReadlinkNode() {
+        return new ReadlinkNode(downCallNodeFactory);
+    }
 
-    MkdtempNode createMkdtempNode();
+    public MkdtempNode createMkdtempNode() {
+        return new MkdtempNode(downCallNodeFactory);
+    }
 
-    ChmodNode createChmodNode();
+    public ChmodNode createChmodNode() {
+        return new ChmodNode(downCallNodeFactory);
+    }
 
-    StrtolNode createStrtolNode();
+    public StrtolNode createStrtolNode() {
+        return new StrtolNode(downCallNodeFactory);
+    }
 
-    UnameNode createUnameNode();
+    public UnameNode createUnameNode() {
+        return new UnameNode(downCallNodeFactory);
+    }
 
-    GlobNode createGlobNode();
+    public GlobNode createGlobNode() {
+        return new GlobNode(downCallNodeFactory);
+    }
 
-    ESoftVersionNode createESoftVersionNode();
+    public ESoftVersionNode createESoftVersionNode() {
+        return new ESoftVersionNode(downCallNodeFactory);
+    }
 
     /*
      * Some functions are called from non-Truffle contexts, which requires a RootNode
      */
-
-    final class GetpidRootNode extends RFFIRootNode<GetpidNode> {
+    public static final class GetpidRootNode extends RFFIRootNode<GetpidNode> {
 
         private GetpidRootNode() {
             super(RFFIFactory.getBaseRFFI().createGetpidNode());
@@ -220,7 +368,7 @@ public interface BaseRFFI {
         }
     }
 
-    final class GetwdRootNode extends RFFIRootNode<GetwdNode> {
+    public static final class GetwdRootNode extends RFFIRootNode<GetwdNode> {
 
         private GetwdRootNode() {
             super(RFFIFactory.getBaseRFFI().createGetwdNode());
@@ -236,7 +384,7 @@ public interface BaseRFFI {
         }
     }
 
-    final class MkdtempRootNode extends RFFIRootNode<MkdtempNode> {
+    public static final class MkdtempRootNode extends RFFIRootNode<MkdtempNode> {
 
         private MkdtempRootNode() {
             super(RFFIFactory.getBaseRFFI().createMkdtempNode());
@@ -253,7 +401,7 @@ public interface BaseRFFI {
         }
     }
 
-    final class UnameRootNode extends RFFIRootNode<UnameNode> {
+    public static final class UnameRootNode extends RFFIRootNode<UnameNode> {
 
         private UnameRootNode() {
             super(RFFIFactory.getBaseRFFI().createUnameNode());
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DownCallNodeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DownCallNodeFactory.java
new file mode 100644
index 0000000000..ad69b9cfe7
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DownCallNodeFactory.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.ffi;
+
+import 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.nodes.Node;
+import com.oracle.truffle.r.runtime.RInternalError;
+
+/**
+ * Factory for RFFI implementation specific {@link DownCallNode} which is responsible for
+ * implementing the invocation of functions from {@link NativeFunction}.
+ */
+public abstract class DownCallNodeFactory {
+    public abstract DownCallNode createDownCallNode(NativeFunction function);
+
+    /**
+     * This node has RFFI backend (LLVM/NFI) specific implementation and its purpose is to provide
+     * functionality to invoke functions from {@link NativeFunction}.
+     */
+    public abstract static class DownCallNode extends Node {
+
+        private final NativeFunction function;
+        @Child private Node message;
+        // TODO: can this be really shared across contexts?
+        @CompilationFinal private TruffleObject target;
+
+        protected DownCallNode(NativeFunction function) {
+            assert function != null;
+            this.function = function;
+        }
+
+        /**
+         * The arguments may contain primitive java types, Strings, arrays of any primitive Java
+         * types, {@link TruffleObject}s,
+         * {@link com.oracle.truffle.r.runtime.ffi.interop.NativeCharArray}s and
+         * {@link com.oracle.truffle.r.runtime.ffi.interop.NativeRawArray}s. {@link TruffleObject}
+         * should be passed to LLVM/NFI as is.
+         * {@link com.oracle.truffle.r.runtime.ffi.interop.NativeRawArray} and
+         * {@link com.oracle.truffle.r.runtime.ffi.interop.NativeCharArray} have special handling in
+         * NFI where the array should be passed as Java array, not as Truffle Object.
+         */
+        public final Object call(Object... args) {
+            try {
+                if (message == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    message = insert(Message.createExecute(function.getArgumentCount()).createNode());
+                }
+                if (target == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    target = getTarget(function);
+                }
+                wrapArguments(target, args);
+                return ForeignAccess.sendExecute(message, target, args);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                finishArguments(args);
+            }
+        }
+
+        /**
+         * Should return a {@link TruffleObject} that will invoke the given function upon the
+         * {@code EXECUTE} message.
+         */
+        protected abstract TruffleObject getTarget(NativeFunction function);
+
+        /**
+         * Allows to transform the arguments before the execute message is sent to the result of
+         * {@link #getTarget(NativeFunction)}. Allways invoked even if {@code args.length == 0}.
+         */
+        protected abstract void wrapArguments(TruffleObject function, Object[] args);
+
+        /**
+         * Allows to post-process the arguments after the execute message was sent to the result of
+         * {@link #getTarget(NativeFunction)}.
+         */
+        protected abstract void finishArguments(Object[] args);
+    }
+}
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 eaff8035fc..ff82db0faf 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,232 +22,343 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.NodeInterface;
-
 /**
  * Collection of statically typed Lapack methods that are used in the {@code base} package. The
  * signatures match the Fortran definition with the exception that the "info" value is returned as
  * the result of the call.
+ * 
+ * The documentation for individual functions can be found in the
+ * <a href="http://www.netlib.org/lapack/explore-html">spec</a>.
  */
-public interface LapackRFFI {
-    interface IlaverNode extends NodeInterface {
-        /**
-         * Return version info, mjor, minor, patch, in {@code version}.
-         */
-        void execute(int[] version);
-
-        static IlaverNode create() {
+public final class LapackRFFI {
+    private final DownCallNodeFactory downCallNodeFactory;
+
+    public LapackRFFI(DownCallNodeFactory downCallNodeFactory) {
+        this.downCallNodeFactory = downCallNodeFactory;
+    }
+
+    public static final class IlaverNode extends NativeCallNode {
+        public static IlaverNode create() {
             return RFFIFactory.getLapackRFFI().createIlaverNode();
         }
+
+        private IlaverNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.ilaver));
+        }
+
+        public void execute(int[] version) {
+            call((Object) version);
+        }
     }
 
-    interface DgeevNode extends NodeInterface {
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/d9/d28/dgeev_8f.html">spec</a>.
-         */
-        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 final class DgeevNode extends NativeCallNode {
 
-        static DgeevNode create() {
+        public static DgeevNode create() {
             return RFFIFactory.getLapackRFFI().createDgeevNode();
         }
+
+        private DgeevNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dgeev));
+        }
+
+        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) {
+            return (int) call(jobVL, jobVR, n, a, lda, wr, wi, vl, ldvl, vr, ldvr, work, lwork);
+        }
     }
 
-    interface Dgeqp3Node extends NodeInterface {
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/db/de5/dgeqp3_8f.html">spec</a>.
-         */
-        int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork);
+    public static final class Dgeqp3Node extends NativeCallNode {
 
-        static Dgeqp3Node create() {
+        public static Dgeqp3Node create() {
             return RFFIFactory.getLapackRFFI().createDgeqp3Node();
         }
+
+        private Dgeqp3Node(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dgeqp3));
+        }
+
+        public int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork) {
+            return (int) call(m, n, a, lda, jpvt, tau, work, lwork);
+        }
     }
 
-    interface DormqrNode extends NodeInterface {
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/da/d82/dormqr_8f.html">spec</a>.
-         */
-        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 final class DormqrNode extends NativeCallNode {
 
-        static DormqrNode create() {
+        public static DormqrNode create() {
             return RFFIFactory.getLapackRFFI().createDormqrNode();
         }
-    }
 
-    interface DtrtrsNode extends NodeInterface {
+        private DormqrNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dormq));
+        }
 
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/d6/d6f/dtrtrs_8f.html">spec</a>.
-         */
-        int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb);
+        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) {
+            return (int) call(side, trans, m, n, k, a, lda, tau, c, ldc, work, lwork);
+        }
+    }
 
-        static DtrtrsNode create() {
+    public static final class DtrtrsNode extends NativeCallNode {
+
+        public static DtrtrsNode create() {
             return RFFIFactory.getLapackRFFI().createDtrtrsNode();
         }
-    }
 
-    interface DgetrfNode extends NodeInterface {
+        private DtrtrsNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dtrtrs));
+        }
+
+        public int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
+            return (int) call(uplo, trans, diag, n, nrhs, a, lda, b, ldb);
+        }
+    }
 
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/d3/d6a/dgetrf_8f.html">spec</a>.
-         */
-        int execute(int m, int n, double[] a, int lda, int[] ipiv);
+    public static final class DgetrfNode extends NativeCallNode {
 
-        static DgetrfNode create() {
+        public static DgetrfNode create() {
             return RFFIFactory.getLapackRFFI().createDgetrfNode();
         }
-    }
 
-    interface DpotrfNode extends NodeInterface {
+        private DgetrfNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dgetrf));
+        }
 
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/d0/d8a/dpotrf_8f.html">spec</a>.
-         */
-        int execute(char uplo, int n, double[] a, int lda);
+        public int execute(int m, int n, double[] a, int lda, int[] ipiv) {
+            return (int) call(m, n, a, lda, ipiv);
+        }
+    }
 
-        static DpotrfNode create() {
+    public static final class DpotrfNode extends NativeCallNode {
+
+        public static DpotrfNode create() {
             return RFFIFactory.getLapackRFFI().createDpotrfNode();
         }
-    }
 
-    interface DpotriNode extends NodeInterface {
+        private DpotrfNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dpotrf));
+        }
 
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/d0/d8a/dpotri_8f.html">spec</a>.
-         */
-        int execute(char uplo, int n, double[] a, int lda);
+        public int execute(char uplo, int n, double[] a, int lda) {
+            return (int) call(uplo, n, a, lda);
+        }
+    }
+
+    public static final class DpotriNode extends NativeCallNode {
 
-        static DpotriNode create() {
+        public static DpotriNode create() {
             return RFFIFactory.getLapackRFFI().createDpotriNode();
         }
+
+        private DpotriNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dpotri));
+        }
+
+        public int execute(char uplo, int n, double[] a, int lda) {
+            return (int) call(uplo, n, a, lda);
+        }
     }
 
-    interface DpstrfNode extends NodeInterface {
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/dd/dad/dpstrf_8f.html">spec</a>.
-         */
-        int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work);
+    public static final class DpstrfNode extends NativeCallNode {
 
-        static DpstrfNode create() {
+        public static DpstrfNode create() {
             return RFFIFactory.getLapackRFFI().createDpstrfNode();
         }
+
+        private DpstrfNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dpstrf));
+        }
+
+        public int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work) {
+            return (int) call(uplo, n, a, lda, piv, rank, tol, work);
+        }
     }
 
-    interface DgesvNode extends NodeInterface {
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/d8/d72/dgesv_8f.html">spec</a>.
-         */
-        int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb);
+    public static final class DgesvNode extends NativeCallNode {
 
-        static DgesvNode create() {
+        public static DgesvNode create() {
             return RFFIFactory.getLapackRFFI().createDgesvNode();
         }
+
+        private DgesvNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dgesv));
+        }
+
+        public int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb) {
+            return (int) call(n, nrhs, a, lda, ipiv, b, ldb);
+        }
     }
 
-    interface DgesddNode extends NodeInterface {
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/db/db4/dgesdd_8f.html">spec</a>.
-         */
-        int execute(char jobz, int m, int n, double[] a, int lda, double[] s, double[] u, int ldu, double[] vt, int ldtv, double[] work, int lwork, int[] iwork);
+    public static final class DgesddNode extends NativeCallNode {
 
-        static DgesddNode create() {
+        public static DgesddNode create() {
             return RFFIFactory.getLapackRFFI().createDgesddNode();
         }
-    }
 
-    interface DlangeNode extends NodeInterface {
+        private DgesddNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dgesdd));
+        }
+
+        public int execute(char jobz, int m, int n, double[] a, int lda, double[] s, double[] u, int ldu, double[] vt, int ldtv, double[] work, int lwork, int[] iwork) {
+            return (int) call(jobz, m, n, a, lda, s, u, ldu, vt, ldtv, work, lwork, iwork);
+        }
+    }
 
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/dc/d09/dlange_8f.html">spec</a>.
-         */
-        double execute(char norm, int m, int n, double[] a, int lda, double[] work);
+    public static final class DlangeNode extends NativeCallNode {
 
-        static DlangeNode create() {
+        public static DlangeNode create() {
             return RFFIFactory.getLapackRFFI().createDlangeNode();
         }
-    }
 
-    interface DgeconNode extends NodeInterface {
+        private DlangeNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dlange));
+        }
+
+        public double execute(char norm, int m, int n, double[] a, int lda, double[] work) {
+            return (double) call(norm, m, n, a, lda, work);
+        }
+    }
 
-        /**
-         * See <a href="http://www.netlib.org/lapack/explore-html/db/de4/dgecon_8f.html">spec</a>.
-         */
-        int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork);
+    public static final class DgeconNode extends NativeCallNode {
 
-        static DgeconNode create() {
+        public static DgeconNode create() {
             return RFFIFactory.getLapackRFFI().createDgeconNode();
         }
-    }
 
-    interface DsyevrNode extends NodeInterface {
+        private DgeconNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dgecon));
+        }
+
+        public int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork) {
+            return (int) call(norm, n, a, lda, anorm, rcond, work, iwork);
+        }
+    }
 
-        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 final class DsyevrNode extends NativeCallNode {
 
-        static DsyevrNode create() {
+        public static DsyevrNode create() {
             return RFFIFactory.getLapackRFFI().createDsyevrNode();
         }
-    }
 
-    interface ZunmqrNode extends NodeInterface {
+        private DsyevrNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dsyevr));
+        }
+
+        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) {
+            return (int) call(jobz, range, uplo, n, a, lda, vl, vu, il, iu, abstol, m, w, z, ldz, isuppz, work, lwork, iwork, liwork);
+        }
+    }
 
-        int execute(String side, String trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork);
+    public static final class ZunmqrNode extends NativeCallNode {
 
-        static ZunmqrNode create() {
+        public static ZunmqrNode create() {
             return RFFIFactory.getLapackRFFI().createZunmqrNode();
         }
-    }
 
-    interface ZtrtrsNode extends NodeInterface {
+        private ZunmqrNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.zunmqr));
+        }
 
-        int execute(String uplo, String trans, String diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb);
+        public int execute(String side, String trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork) {
+            return (int) call(side, trans, m, n, k, a, lda, tau, c, ldc, work, lwork);
+        }
 
-        static ZtrtrsNode create() {
+    }
+
+    public static final class ZtrtrsNode extends NativeCallNode {
+
+        public static ZtrtrsNode create() {
             return RFFIFactory.getLapackRFFI().createZtrtrsNode();
         }
-    }
 
-    interface DtrsmNode extends NodeInterface {
+        private ZtrtrsNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.ztrtrs));
+        }
 
-        void execute(String side, String uplo, String transa, String diag, int m, int n, double alpha, double[] a, int lda, double[] b, int ldb);
+        public int execute(String uplo, String trans, String diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
+            return (int) call(uplo, trans, diag, n, nrhs, a, lda, b, ldb);
+        }
+    }
+
+    public static final class DtrsmNode extends NativeCallNode {
 
-        static DtrsmNode create() {
+        public static DtrsmNode create() {
             return RFFIFactory.getLapackRFFI().createDtrsmNode();
         }
-    }
 
-    IlaverNode createIlaverNode();
+        private DtrsmNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dtrsm));
+        }
 
-    DgeevNode createDgeevNode();
+        public void execute(String side, String uplo, String transa, String diag, int m, int n, double alpha, double[] a, int lda, double[] b, int ldb) {
+            call(side, uplo, transa, diag, m, n, alpha, a, lda, b, ldb);
+        }
 
-    Dgeqp3Node createDgeqp3Node();
+    }
 
-    DormqrNode createDormqrNode();
+    public IlaverNode createIlaverNode() {
+        return new IlaverNode(downCallNodeFactory);
+    }
 
-    DtrtrsNode createDtrtrsNode();
+    public DgeevNode createDgeevNode() {
+        return new DgeevNode(downCallNodeFactory);
+    }
 
-    DgetrfNode createDgetrfNode();
+    public Dgeqp3Node createDgeqp3Node() {
+        return new Dgeqp3Node(downCallNodeFactory);
+    }
 
-    DpotrfNode createDpotrfNode();
+    public DormqrNode createDormqrNode() {
+        return new DormqrNode(downCallNodeFactory);
+    }
 
-    DpotriNode createDpotriNode();
+    public DtrtrsNode createDtrtrsNode() {
+        return new DtrtrsNode(downCallNodeFactory);
+    }
 
-    DpstrfNode createDpstrfNode();
+    public DgetrfNode createDgetrfNode() {
+        return new DgetrfNode(downCallNodeFactory);
+    }
 
-    DgesvNode createDgesvNode();
+    public DpotrfNode createDpotrfNode() {
+        return new DpotrfNode(downCallNodeFactory);
+    }
 
-    DgesddNode createDgesddNode();
+    public DpotriNode createDpotriNode() {
+        return new DpotriNode(downCallNodeFactory);
+    }
+
+    public DpstrfNode createDpstrfNode() {
+        return new DpstrfNode(downCallNodeFactory);
+    }
 
-    DlangeNode createDlangeNode();
+    public DgesvNode createDgesvNode() {
+        return new DgesvNode(downCallNodeFactory);
+    }
+
+    public DgesddNode createDgesddNode() {
+        return new DgesddNode(downCallNodeFactory);
+    }
 
-    DgeconNode createDgeconNode();
+    public DlangeNode createDlangeNode() {
+        return new DlangeNode(downCallNodeFactory);
+    }
 
-    DsyevrNode createDsyevrNode();
+    public DgeconNode createDgeconNode() {
+        return new DgeconNode(downCallNodeFactory);
+    }
 
-    ZunmqrNode createZunmqrNode();
+    public DsyevrNode createDsyevrNode() {
+        return new DsyevrNode(downCallNodeFactory);
+    }
 
-    ZtrtrsNode createZtrtrsNode();
+    public ZunmqrNode createZunmqrNode() {
+        return new ZunmqrNode(downCallNodeFactory);
+    }
 
-    DtrsmNode createDtrsmNode();
+    public ZtrtrsNode createZtrtrsNode() {
+        return new ZtrtrsNode(downCallNodeFactory);
+    }
 
+    public DtrsmNode createDtrsmNode() {
+        return new DtrsmNode(downCallNodeFactory);
+    }
 }
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 9fb02a1e8e..12d634f4f6 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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,30 +22,50 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.NodeInterface;
-
 /**
  * Miscellaneous methods implemented in native code.
  *
  */
-public interface MiscRFFI {
-    interface ExactSumNode extends NodeInterface {
-        double execute(double[] values, boolean hasNa, boolean naRm);
+public final class MiscRFFI {
+    private final DownCallNodeFactory downCallNodeFactory;
+
+    public MiscRFFI(DownCallNodeFactory downCallNodeFactory) {
+        this.downCallNodeFactory = downCallNodeFactory;
+    }
+
+    public static final class ExactSumNode extends NativeCallNode {
+        private ExactSumNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.exactSumFunc));
+        }
+
+        public double execute(double[] values, boolean hasNa, boolean naRm) {
+            return (double) call(values, values.length, hasNa ? 1 : 0, naRm ? 1 : 0);
+        }
 
-        static ExactSumNode create() {
+        public static ExactSumNode create() {
             return RFFIFactory.getMiscRFFI().createExactSumNode();
         }
     }
 
-    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 final class DqrlsNode extends NativeCallNode {
+        private DqrlsNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.dqrls));
+        }
+
+        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) {
+            call(x, n, p, y, ny, tol, b, rsd, qty, k, jpvt, qraux, work);
+        }
 
-        static DqrlsNode create() {
+        public static DqrlsNode create() {
             return RFFIFactory.getMiscRFFI().createDqrlsNode();
         }
     }
 
-    ExactSumNode createExactSumNode();
+    public ExactSumNode createExactSumNode() {
+        return new ExactSumNode(downCallNodeFactory);
+    }
 
-    DqrlsNode createDqrlsNode();
+    public DqrlsNode createDqrlsNode() {
+        return new DqrlsNode(downCallNodeFactory);
+    }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArray.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeCallNode.java
similarity index 62%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArray.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeCallNode.java
index cbba5538b3..c192214100 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArray.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeCallNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -20,23 +20,22 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop;
+package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
 
 /**
- * A {@link TruffleObject} that represents an array of {@code unsigned char} values, that is
- * {@code NULL} terminated in the C domain.
+ * Convenient base class for nodes invoking
+ * {@link com.oracle.truffle.r.runtime.ffi.DownCallNodeFactory.DownCallNode}.
  */
-public final class NativeCharArray extends NativeUInt8Array {
+public class NativeCallNode extends Node {
+    @Child private DownCallNodeFactory.DownCallNode downCallNode;
 
-    public NativeCharArray(byte[] bytes) {
-        super(bytes, true);
+    public NativeCallNode(DownCallNodeFactory.DownCallNode downCallNode) {
+        this.downCallNode = downCallNode;
     }
 
-    @Override
-    public ForeignAccess getForeignAccess() {
-        return NativeCharArrayMRForeign.ACCESS;
+    protected Object call(Object... args) {
+        return downCallNode.call(args);
     }
 }
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 90489b5ffc..4bc7d2fc40 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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,22 +22,41 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.NodeInterface;
+import java.nio.charset.StandardCharsets;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.interop.NativeCharArray;
+import com.oracle.truffle.r.runtime.ffi.interop.pcre.CaptureNamesResult;
+import com.oracle.truffle.r.runtime.ffi.interop.pcre.CompileResult;
 
 /**
  * An interface to the <a href="http://www.pcre.org/original/doc/html/index.html">PCRE</a> library
  * for Perl regular expressions.
  */
-public interface PCRERFFI {
-    int NOTBOL = 0x00000080;
-    int CASELESS = 0x1;
+public final class PCRERFFI {
+    public static final int NOTBOL = 0x00000080;
+    public static final int CASELESS = 0x1;
+
+    private final DownCallNodeFactory downCallNodeFactory;
+
+    public PCRERFFI(DownCallNodeFactory downCallNodeFactory) {
+        this.downCallNodeFactory = downCallNodeFactory;
+    }
 
     /**
      * PCRE uses call by reference for error-related information, which we encapsulate and sanitize
      * in this class. The {@code result} value (which is typically an opaque pointer to an internal
      * C struct), is the actual result of the function as per the PCRE spec.
      */
-    class Result {
+    public static final class Result {
         public final long result;
         public final String errorMessage;
         public final int errOffset;
@@ -49,64 +68,142 @@ public interface PCRERFFI {
         }
     }
 
-    interface MaketablesNode extends NodeInterface {
-        long execute();
+    public static final class MaketablesNode extends NativeCallNode {
+        @Child private Node asPointerNode;
+
+        private MaketablesNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.maketables));
+        }
+
+        public long execute() {
+            Object result = call();
+            if (result instanceof Long) {
+                return (long) result;
+            }
+            assert result instanceof TruffleObject;
+            if (asPointerNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                asPointerNode = insert(Message.AS_POINTER.createNode());
+            }
+            try {
+                return ForeignAccess.sendAsPointer(asPointerNode, (TruffleObject) result);
+            } catch (UnsupportedMessageException e) {
+                throw RInternalError.shouldNotReachHere("PCRE function maketables should return long or TruffleObject that represents a pointer.");
+            }
+        }
 
-        static MaketablesNode create() {
+        public static MaketablesNode create() {
             return RFFIFactory.getPCRERFFI().createMaketablesNode();
         }
     }
 
-    interface CompileNode extends NodeInterface {
-        Result execute(String pattern, int options, long tables);
+    public static final class CompileNode extends NativeCallNode {
+        private CompileNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.compile));
+        }
+
+        public Result execute(String pattern, int options, long tables) {
+            CompileResult data = new CompileResult();
+            call(data, pattern, options, tables);
+            return data.getResult();
+        }
 
-        static CompileNode create() {
+        public static CompileNode create() {
             return RFFIFactory.getPCRERFFI().createCompileNode();
         }
     }
 
-    interface GetCaptureCountNode extends NodeInterface {
-        int execute(long code, long extra);
+    public static final class GetCaptureCountNode extends NativeCallNode {
+        private GetCaptureCountNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.getcapturecount));
+        }
+
+        public int execute(long code, long extra) {
+            return (int) call(code, extra);
+        }
 
-        static GetCaptureCountNode create() {
+        public static GetCaptureCountNode create() {
             return RFFIFactory.getPCRERFFI().createGetCaptureCountNode();
         }
     }
 
-    interface GetCaptureNamesNode extends NodeInterface {
-        String[] execute(long code, long extra, int captureCount);
+    public static final class GetCaptureNamesNode extends NativeCallNode {
+        private GetCaptureNamesNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.getcapturenames));
+        }
+
+        public String[] execute(long code, long extra, int captureCount) {
+            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();
+            }
+        }
 
-        static GetCaptureNamesNode create() {
+        public static GetCaptureNamesNode create() {
             return RFFIFactory.getPCRERFFI().createGetCaptureNamesNode();
         }
     }
 
-    interface StudyNode extends NodeInterface {
-        Result execute(long code, int options);
+    public static final class StudyNode extends NativeCallNode {
+        private StudyNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.study));
+        }
+
+        public Result execute(long code, int options) {
+            throw RInternalError.shouldNotReachHere("The factory method should throw unimplemented already");
+        }
 
-        static StudyNode create() {
+        public static StudyNode create() {
             return RFFIFactory.getPCRERFFI().createStudyNode();
         }
     }
 
-    interface ExecNode extends NodeInterface {
-        int execute(long code, long extra, String subject, int offset, int options, int[] ovector);
+    public static final class ExecNode extends NativeCallNode {
+        private ExecNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.exec));
+        }
+
+        public int execute(long code, long extra, String subject, int offset, int options, int[] ovector) {
+            byte[] subjectBytes = getBytes(subject);
+            NativeCharArray subjectChars = new NativeCharArray(subjectBytes);
+            return (int) call(code, extra, subjectChars, subjectBytes.length, offset, options, ovector, ovector.length);
+        }
+
+        @TruffleBoundary
+        private static byte[] getBytes(String subject) {
+            return subject.getBytes(StandardCharsets.UTF_8);
+        }
 
-        static ExecNode create() {
+        public static ExecNode create() {
             return RFFIFactory.getPCRERFFI().createExecNode();
         }
     }
 
-    MaketablesNode createMaketablesNode();
-
-    CompileNode createCompileNode();
+    public MaketablesNode createMaketablesNode() {
+        return new MaketablesNode(downCallNodeFactory);
+    }
 
-    GetCaptureCountNode createGetCaptureCountNode();
+    public CompileNode createCompileNode() {
+        return new CompileNode(downCallNodeFactory);
+    }
 
-    GetCaptureNamesNode createGetCaptureNamesNode();
+    public GetCaptureCountNode createGetCaptureCountNode() {
+        return new GetCaptureCountNode(downCallNodeFactory);
+    }
 
-    StudyNode createStudyNode();
+    public GetCaptureNamesNode createGetCaptureNamesNode() {
+        return new GetCaptureNamesNode(downCallNodeFactory);
+    }
 
-    ExecNode createExecNode();
+    public StudyNode createStudyNode() {
+        throw RInternalError.unimplemented("study function in PCRE");
+    }
 
+    public ExecNode createExecNode() {
+        return new ExecNode(downCallNodeFactory);
+    }
 }
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 5fcbb0417f..7bb4b22dbf 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StatsRFFI.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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,41 +22,69 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
-import com.oracle.truffle.api.nodes.NodeInterface;
-
 /**
  * Interface to native (C) methods provided by the {@code stats} package that are used to implement
- * {@code.Call(C_fft)}. The implementation is split into a Java part which calls the
+ * {@code .Call(C_fft)}. The implementation is split into a Java part which calls the
  * {@code fft_factor} and {@code fft_work}. functions from the GNU R C code.
  */
-public interface StatsRFFI {
-    interface FactorNode extends NodeInterface {
-        void execute(int n, int[] pmaxf, int[] pmaxp);
+public final class StatsRFFI {
+    private final DownCallNodeFactory downCallNodeFactory;
+
+    public StatsRFFI(DownCallNodeFactory downCallNodeFactory) {
+        this.downCallNodeFactory = downCallNodeFactory;
+    }
+
+    public static final class FactorNode extends NativeCallNode {
+        private FactorNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.fft_factor));
+        }
+
+        public void execute(int n, int[] pmaxf, int[] pmaxp) {
+            call(n, pmaxf, pmaxp);
+        }
 
-        static FactorNode create() {
+        public static FactorNode create() {
             return RFFIFactory.getStatsRFFI().createFactorNode();
         }
     }
 
-    interface WorkNode extends NodeInterface {
-        int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork);
+    public static final class WorkNode extends NativeCallNode {
+        private WorkNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.fft_work));
+        }
+
+        public int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
+            return (int) call(a, nseg, n, nspn, isn, work, iwork);
+        }
 
-        static WorkNode create() {
+        public static WorkNode create() {
             return RFFIFactory.getStatsRFFI().createWorkNode();
         }
     }
 
-    interface LminflNode extends NodeInterface {
-        void execute(double[] x, int ldx, int n, int k, int docoef, double[] qraux, double[] resid, double[] hat, double[] coef, double[] sigma, double tol);
+    public static final class LminflNode extends NativeCallNode {
+        private LminflNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.lminfl));
+        }
+
+        public void execute(double[] x, int ldx, int n, int k, int docoef, double[] qraux, double[] resid, double[] hat, double[] coef, double[] sigma, double tol) {
+            call(x, ldx, n, k, docoef, qraux, resid, hat, coef, sigma, tol);
+        }
 
-        static LminflNode create() {
+        public static LminflNode create() {
             return RFFIFactory.getStatsRFFI().createLminflNode();
         }
     }
 
-    FactorNode createFactorNode();
+    public FactorNode createFactorNode() {
+        return new FactorNode(downCallNodeFactory);
+    }
 
-    WorkNode createWorkNode();
+    public WorkNode createWorkNode() {
+        return new WorkNode(downCallNodeFactory);
+    }
 
-    LminflNode createLminflNode();
+    public LminflNode createLminflNode() {
+        return new LminflNode(downCallNodeFactory);
+    }
 }
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 b02f3fefda..122b0af2d4 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
@@ -24,43 +24,77 @@ package com.oracle.truffle.r.runtime.ffi;
 
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.NodeInterface;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.ffi.interop.NativeRawArray;
 
 /**
  * zip compression/uncompression.
  */
-public interface ZipRFFI {
+public final class ZipRFFI {
+
+    private final DownCallNodeFactory downCallNodeFactory;
+
+    public ZipRFFI(DownCallNodeFactory downCallNodeFactory) {
+        this.downCallNodeFactory = downCallNodeFactory;
+    }
+
+    public static final class CompressNode extends NativeCallNode {
+        private CompressNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.compress));
+        }
 
-    interface CompressNode extends NodeInterface {
         /**
          * compress {@code source} into {@code dest}.
          *
          * @return standard return code (0 ok)
          */
-        int execute(byte[] dest, byte[] source);
+        public int execute(byte[] dest, byte[] source) {
+            NativeRawArray nativeDest = new NativeRawArray(dest);
+            NativeRawArray nativeSource = new NativeRawArray(source);
+            try {
+                return (int) call(nativeDest, dest.length, nativeSource, source.length);
+            } finally {
+                nativeDest.getValue();
+            }
+        }
 
-        static CompressNode create() {
+        public static CompressNode create() {
             return RFFIFactory.getZipRFFI().createCompressNode();
         }
     }
 
-    interface UncompressNode extends NodeInterface {
+    public static final class UncompressNode extends NativeCallNode {
+        private UncompressNode(DownCallNodeFactory factory) {
+            super(factory.createDownCallNode(NativeFunction.uncompress));
+        }
+
         /**
          * uncompress {@code source} into {@code dest}.
          *
          * @return standard return code (0 ok)
          */
-        int execute(byte[] dest, byte[] source);
+        public int execute(byte[] dest, byte[] source) {
+            NativeRawArray nativeDest = new NativeRawArray(dest);
+            NativeRawArray nativeSource = new NativeRawArray(source);
+            try {
+                return (int) call(nativeDest, dest.length, nativeSource, source.length);
+            } finally {
+                nativeDest.getValue();
+            }
+        }
     }
 
-    CompressNode createCompressNode();
+    public CompressNode createCompressNode() {
+        return new CompressNode(downCallNodeFactory);
+    }
 
-    UncompressNode createUncompressNode();
+    public UncompressNode createUncompressNode() {
+        return new UncompressNode(downCallNodeFactory);
+    }
 
     // RootNodes for calling when not in Truffle context
 
-    final class CompressRootNode extends RFFIRootNode<CompressNode> {
+    public static final class CompressRootNode extends RFFIRootNode<CompressNode> {
         protected CompressRootNode(CompressNode wrapped) {
             super(wrapped);
         }
@@ -76,7 +110,7 @@ public interface ZipRFFI {
         }
     }
 
-    final class UncompressRootNode extends RFFIRootNode<UncompressNode> {
+    public static final class UncompressRootNode extends RFFIRootNode<UncompressNode> {
         protected UncompressRootNode(UncompressNode wrapped) {
             super(wrapped);
         }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ESoftVersionResult.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ESoftVersionResult.java
similarity index 96%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ESoftVersionResult.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ESoftVersionResult.java
index a3e8e86c2f..c4416b12ec 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ESoftVersionResult.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ESoftVersionResult.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import java.util.HashMap;
 import java.util.Map;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ESoftVersionResultMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ESoftVersionResultMR.java
similarity index 97%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ESoftVersionResultMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ESoftVersionResultMR.java
index fc958b7b30..6eab2ce42b 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ESoftVersionResultMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ESoftVersionResultMR.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResult.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/GlobResult.java
similarity index 92%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResult.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/GlobResult.java
index f16e91a2fd..13bbc84cdd 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResult.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/GlobResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import java.util.ArrayList;
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResultMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/GlobResultMR.java
similarity index 94%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResultMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/GlobResultMR.java
index 2c7d26e85e..1aaf118a52 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResultMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/GlobResultMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResult.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ReadlinkResult.java
similarity index 92%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResult.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ReadlinkResult.java
index 266efc5acd..3466cf3bb0 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResult.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ReadlinkResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResultMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ReadlinkResultMR.java
similarity index 98%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResultMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ReadlinkResultMR.java
index c17152410e..98e1f175a9 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResultMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/ReadlinkResultMR.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.ForeignAccess;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResult.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/StrtolResult.java
similarity index 92%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResult.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/StrtolResult.java
index 1884ad0bfa..4585fa57f1 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResult.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/StrtolResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResultMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/StrtolResultMR.java
similarity index 94%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResultMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/StrtolResultMR.java
index 0b30658738..be8acf978e 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResultMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/StrtolResultMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResult.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/UnameResult.java
similarity index 88%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResult.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/UnameResult.java
index 097610794d..ef8c8e5ce5 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResult.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/UnameResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,13 +20,13 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
-import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI.UtsName;
 
-public final class UnameResult implements BaseRFFI.UtsName, RTruffleObject {
+public final class UnameResult implements UtsName, RTruffleObject {
     private String sysname;
     private String release;
     private String version;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResultMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/UnameResultMR.java
similarity index 94%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResultMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/UnameResultMR.java
index e63607fbad..bf9a16a1ac 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResultMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/base/UnameResultMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.base;
+package com.oracle.truffle.r.runtime.ffi.base;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArray.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArray.java
new file mode 100644
index 0000000000..cf414ccbd0
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArray.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.ffi.interop;
+
+import java.nio.charset.StandardCharsets;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.TruffleObject;
+
+/**
+ * A {@link TruffleObject} that represents an array of {@code unsigned char} values, that is
+ * {@code NULL} terminated in the C domain.
+ */
+public final class NativeCharArray extends NativeUInt8Array {
+
+    public NativeCharArray(byte[] bytes) {
+        super(bytes, true);
+    }
+
+    @TruffleBoundary
+    public NativeCharArray(String value) {
+        super(value.getBytes(StandardCharsets.UTF_8), true);
+    }
+
+    private NativeCharArray(byte[] bytes, boolean nullTerminate) {
+        super(bytes, nullTerminate);
+    }
+
+    /**
+     * Creates {@link NativeCharArray} of given length that can be used for output parameters, it is
+     * not null terminated.
+     */
+    public static NativeCharArray crateOutputBuffer(int length) {
+        return new NativeCharArray(new byte[length], false);
+    }
+
+    /**
+     * Finds the null terminator and creates the Java String accordingly.
+     */
+    public String getStringFromOutputBuffer() {
+        assert !fakesNullTermination() : "create the buffer string via createOutputBuffer()";
+        byte[] mbuf = getValue();
+        int i = 0;
+        while (mbuf[i] != 0 && i < mbuf.length) {
+            i++;
+        }
+        return new String(mbuf, 0, i);
+    }
+
+    public String getString() {
+        byte[] val = getValue();
+        return new String(val, 0, fakesNullTermination() ? val.length : val.length - 1);
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return NativeCharArrayMRForeign.ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArrayMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArrayMR.java
similarity index 98%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArrayMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArrayMR.java
index ed1b0f2f72..f52dd8e2e1 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArrayMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArrayMR.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop;
+package com.oracle.truffle.r.runtime.ffi.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointer.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointer.java
similarity index 87%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointer.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointer.java
index 4202b55d79..a42e1133dc 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointer.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -20,12 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop;
+package com.oracle.truffle.r.runtime.ffi.interop;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_Utils;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 
@@ -79,16 +78,6 @@ public class NativePointer implements TruffleObject {
         return obj instanceof NativePointer;
     }
 
-    public static TruffleObject check(TruffleObject object) {
-        long nativePointer = TruffleLLVM_Utils.getNativeAddress(object);
-        for (int i = tableHwm - 1; i >= 0; i--) {
-            if (table[i].nativePointer == nativePointer) {
-                return table[i].object;
-            }
-        }
-        return null;
-    }
-
     final long asPointer() {
         long result = asPointerImpl();
         boolean newPointer = true;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointerMR.java
similarity index 93%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointerMR.java
index b8b73f6378..604aef2eb8 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointerMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop;
+package com.oracle.truffle.r.runtime.ffi.interop;
 
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.Resolve;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArray.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArray.java
similarity index 91%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArray.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArray.java
index 631188e69b..7194fccdbf 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArray.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArray.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop;
+package com.oracle.truffle.r.runtime.ffi.interop;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArrayMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArrayMR.java
similarity index 96%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArrayMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArrayMR.java
index a17becc8ed..21d367ac68 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArrayMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArrayMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop;
+package com.oracle.truffle.r.runtime.ffi.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeUInt8Array.java
similarity index 77%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeUInt8Array.java
index 4bf73b78c4..033d78e53b 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeUInt8Array.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, 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
@@ -20,11 +20,10 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop;
+package com.oracle.truffle.r.runtime.ffi.interop;
 
-import static com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter.UNSAFE;
+import static com.oracle.truffle.r.runtime.ffi.UnsafeAdapter.UNSAFE;
 
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 
@@ -38,26 +37,26 @@ import sun.misc.Unsafe;
  * N.B. Java never stores a {@code NULL} value in a String or the byte array from
  * {@link String#getBytes}.
  *
- * If {@link #fakeNull()} is {@code true}, then {@link #read} returns 0, else it is an error;
- * similar for {@link #write}.
+ * If {@link #fakesNullTermination()} is {@code true}, then {@link #read} returns 0, else it is an
+ * error; similar for {@link #write}.
  */
 public abstract class NativeUInt8Array implements RTruffleObject {
 
-    public final byte[] bytes;
+    public byte[] bytes;
 
     /**
      * 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;
-    private final int effectiveLength;
+    protected long nativeAddress;
+    private int effectiveLength;
 
     protected NativeUInt8Array(byte[] bytes, boolean nullTerminate) {
         this.bytes = bytes;
         this.effectiveLength = bytes.length + (nullTerminate ? 1 : 0);
     }
 
-    private boolean fakeNull() {
+    public boolean fakesNullTermination() {
         return bytes.length != effectiveLength;
     }
 
@@ -72,7 +71,7 @@ public abstract class NativeUInt8Array implements RTruffleObject {
             checkNativeIndex(index);
             UNSAFE.putByte(nativeAddress + index, value);
         } else {
-            if (index == bytes.length && fakeNull()) {
+            if (index == bytes.length && fakesNullTermination()) {
                 // ignore
             } else {
                 bytes[index] = value;
@@ -85,7 +84,7 @@ public abstract class NativeUInt8Array implements RTruffleObject {
             checkNativeIndex(index);
             return UNSAFE.getByte(nativeAddress + index);
         } else {
-            if (index == bytes.length && fakeNull()) {
+            if (index == bytes.length && fakesNullTermination()) {
                 return (byte) 0;
             }
             return bytes[index];
@@ -107,21 +106,26 @@ public abstract class NativeUInt8Array implements RTruffleObject {
     private void allocateNative() {
         nativeAddress = UNSAFE.allocateMemory(effectiveLength);
         UNSAFE.copyMemory(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, nativeAddress, bytes.length);
-        if (fakeNull()) {
+        if (fakesNullTermination()) {
             UNSAFE.putByte(nativeAddress + bytes.length, (byte) 0);
         }
     }
 
     public byte[] getValue() {
         if (nativeAddress != 0) {
-            copyBackFromNative();
+            copyBackFromNative(bytes);
         }
         return bytes;
     }
 
+    public void setValue(byte[] newBytes, boolean isNullTerminated) {
+        bytes = newBytes;
+        effectiveLength = isNullTerminated ? bytes.length + 1 : bytes.length;
+    }
+
     @TruffleBoundary
-    private void copyBackFromNative() {
+    private void copyBackFromNative(byte[] target) {
         // copy back
-        UNSAFE.copyMemory(null, nativeAddress, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, bytes.length);
+        UNSAFE.copyMemory(null, nativeAddress, target, Unsafe.ARRAY_BYTE_BASE_OFFSET, bytes.length);
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResult.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CaptureNamesResult.java
similarity index 92%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResult.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CaptureNamesResult.java
index e54b5787ad..3e8ab84265 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResult.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CaptureNamesResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.pcre;
+package com.oracle.truffle.r.runtime.ffi.interop.pcre;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResultMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CaptureNamesResultMR.java
similarity index 95%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResultMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CaptureNamesResultMR.java
index b836331256..32f06e7313 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResultMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CaptureNamesResultMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.pcre;
+package com.oracle.truffle.r.runtime.ffi.interop.pcre;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.ForeignAccess;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResult.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CompileResult.java
similarity index 85%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResult.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CompileResult.java
index 946d2a8b8d..fb3fb04f81 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResult.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CompileResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,17 +20,18 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.pcre;
+package com.oracle.truffle.r.runtime.ffi.interop.pcre;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
+import com.oracle.truffle.r.runtime.ffi.PCRERFFI.Result;
 
 public final class CompileResult implements RTruffleObject {
     private PCRERFFI.Result result;
 
     public void set(long pcreResult, String errorMessage, int errOffset) {
-        result = new PCRERFFI.Result(pcreResult, errorMessage, errOffset);
+        result = new Result(pcreResult, errorMessage, errOffset);
     }
 
     public PCRERFFI.Result getResult() {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResultMR.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CompileResultMR.java
similarity index 96%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResultMR.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CompileResultMR.java
index 76562a7e33..e6ecb9e2f8 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResultMR.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/pcre/CompileResultMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.interop.pcre;
+package com.oracle.truffle.r.runtime.ffi.interop.pcre;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
-- 
GitLab