From 3a7bfb2e9920d45504f52dd166e95127dc8cd361 Mon Sep 17 00:00:00 2001
From: Mick Jordan <mick.jordan@oracle.com>
Date: Wed, 1 Mar 2017 16:37:22 -0800
Subject: [PATCH] Refactor ffi impls into com.oracle.truffle.r.ffi.impl
 project; add ffi AP; TruffleLLVM all RFFI interfaces implemented; no JNI

---
 .../r/engine/interop/ActiveBindingMR.java     |   2 +-
 .../interop/RAbstractVectorAccessFactory.java |  27 +
 .../r/engine/interop/REnvironmentMR.java      |   9 +
 .../interop/RForeignAccessFactoryImpl.java    |  51 +-
 .../truffle/r/engine/interop/RListMR.java     |   8 +
 .../truffle/r/engine/interop/RNullMR.java     |  13 +-
 .../truffle/r/engine/interop/RPairListMR.java |   8 +
 .../ffi/llvm/TruffleLLVM_RFFIFactory.java     |  97 ----
 .../ffi/llvm/TruffleLLVM_UpCallsRFFIImpl.java | 322 ------------
 .../interop/ffi/llvm/VariableUpCallsRFFI.java | 108 ----
 .../interop/ffi/nfi/TruffleNFI_Base.java      | 399 --------------
 .../engine/interop/ffi/nfi/TruffleNFI_C.java  |  66 ---
 .../r/ffi/impl/common}/Generic_Tools.java     |   2 +-
 .../ffi/impl/common}/JavaUpCallsRFFIImpl.java | 120 +++--
 .../truffle/r/ffi/impl/common}/LibPaths.java  |   2 +-
 .../r/ffi/impl/common}/ParseResult.java       |   4 +-
 .../truffle/r/ffi/impl/common}/RFFIUtils.java |   5 +-
 .../impl/common}/TracingUpCallsRFFIImpl.java  | 100 ++--
 .../r/ffi/impl/common/package-info.java       |  26 +
 .../r/ffi/impl}/interop/CharSXPWrapperMR.java |   5 +-
 .../r/ffi/impl}/interop/DLLDotSymbolMR.java   |   5 +-
 .../r/ffi/impl}/interop/DLLInfoMR.java        |   5 +-
 .../FFI_RForeignAccessFactoryImpl.java        |  42 ++
 .../r/ffi/impl}/interop/NativeCharArray.java  |  10 +-
 .../ffi/impl}/interop/NativeCharArrayMR.java  |  29 +-
 .../r/ffi/impl/interop/NativeDoubleArray.java |  88 ++++
 .../impl}/interop/NativeDoubleArrayMR.java    |  33 +-
 .../ffi/impl/interop/NativeIntegerArray.java  |  88 ++++
 .../impl}/interop/NativeIntegerArrayMR.java   |  32 +-
 .../ffi/impl}/interop/NativeLogicalArray.java |  10 +-
 .../impl}/interop/NativeLogicalArrayMR.java   |   5 +-
 .../r/ffi/impl}/interop/NativeNACheck.java    |   2 +-
 .../r/ffi/impl/interop/NativePointer.java     | 117 +++++
 .../r/ffi/impl/interop/NativePointerMR.java   |  49 ++
 .../r/ffi/impl}/interop/NativeRawArray.java   |  11 +-
 .../r/ffi/impl}/interop/NativeRawArrayMR.java |  29 +-
 .../r/ffi/impl}/interop/NativeUInt8Array.java |   6 +-
 .../r/ffi/impl}/interop/UnsafeAdapter.java    |   2 +-
 .../r/ffi/impl/interop/base/GlobResult.java   |  46 ++
 .../r/ffi/impl/interop/base/GlobResultMR.java |  19 +-
 .../ffi/impl/interop/base/ReadlinkResult.java |  49 ++
 .../impl/interop/base/ReadlinkResultMR.java   |  19 +-
 .../r/ffi/impl/interop/base/StrtolResult.java |  49 ++
 .../ffi/impl/interop/base/StrtolResultMR.java |  20 +-
 .../r/ffi/impl/interop/base/UnameResult.java  |  73 +++
 .../ffi/impl/interop/base/UnameResultMR.java  |  50 ++
 .../r/ffi/impl/interop/package-info.java      |  33 ++
 .../impl/interop/pcre/CaptureNamesResult.java |  47 ++
 .../interop/pcre/CaptureNamesResultMR.java    |  18 +-
 .../ffi/impl/interop/pcre/CompileResult.java  |  44 ++
 .../impl/interop/pcre/CompileResultMR.java    |  18 +-
 .../r/ffi/impl}/jni/JNIUpCallsRFFIImpl.java   |   6 +-
 .../truffle/r/ffi/impl}/jni/JNI_Base.java     |   2 +-
 .../oracle/truffle/r/ffi/impl}/jni/JNI_C.java |   6 +-
 .../truffle/r/ffi/impl}/jni/JNI_Call.java     |  12 +-
 .../truffle/r/ffi/impl}/jni/JNI_DLL.java      |   2 +-
 .../truffle/r/ffi/impl}/jni/JNI_Glob.java     |   2 +-
 .../truffle/r/ffi/impl}/jni/JNI_Lapack.java   |   2 +-
 .../truffle/r/ffi/impl}/jni/JNI_Misc.java     |   8 +-
 .../truffle/r/ffi/impl}/jni/JNI_PCRE.java     |   2 +-
 .../truffle/r/ffi/impl}/jni/JNI_PkgInit.java  |   2 +-
 .../truffle/r/ffi/impl}/jni/JNI_RAppl.java    |   2 +-
 .../truffle/r/ffi/impl}/jni/JNI_REmbed.java   |   4 +-
 .../r/ffi/impl}/jni/JNI_RFFIFactory.java      |   6 +-
 .../truffle/r/ffi/impl}/jni/JNI_Stats.java    |   6 +-
 .../truffle/r/ffi/impl}/jni/JNI_UserRng.java  |   2 +-
 .../truffle/r/ffi/impl}/jni/JNI_UtsName.java  |   4 +-
 .../truffle/r/ffi/impl}/jni/JNI_Zip.java      |   2 +-
 .../truffle/r/ffi/impl/jni/package-info.java  |  26 +
 .../truffle/r/ffi/impl/llvm/LLVMFunction.java |  91 ++++
 .../truffle/r/ffi/impl/llvm}/LLVM_IR.java     |   2 +-
 .../truffle/r/ffi/impl/llvm}/MachOAccess.java |   3 +-
 .../r/ffi/impl/llvm/TruffleLLVM_Base.java     | 378 ++++++++++++++
 .../r/ffi/impl}/llvm/TruffleLLVM_C.java       |  37 +-
 .../r/ffi/impl}/llvm/TruffleLLVM_CAccess.java |   4 +-
 .../r/ffi/impl}/llvm/TruffleLLVM_Call.java    | 154 ++----
 .../r/ffi/impl}/llvm/TruffleLLVM_DLL.java     | 160 +++---
 .../r/ffi/impl/llvm/TruffleLLVM_Lapack.java   | 490 ++++++++++++++++++
 .../r/ffi/impl/llvm/TruffleLLVM_Misc.java     |  62 +++
 .../ffi/impl/llvm/TruffleLLVM_NativeDLL.java  | 178 +++++++
 .../r/ffi/impl/llvm/TruffleLLVM_PCRE.java     | 222 ++++++++
 .../r/ffi/impl}/llvm/TruffleLLVM_PkgInit.java |  11 +-
 .../r/ffi/impl/llvm/TruffleLLVM_RAppl.java    | 151 ++++++
 .../r/ffi/impl/llvm/TruffleLLVM_REmbed.java   |  55 ++
 .../llvm/TruffleLLVM_RFFIContextState.java    |  39 +-
 .../impl/llvm/TruffleLLVM_RFFIFactory.java    | 190 +++++++
 .../r/ffi/impl}/llvm/TruffleLLVM_Stats.java   |  23 +-
 .../r/ffi/impl/llvm/TruffleLLVM_Tools.java    |  82 +++
 .../llvm/TruffleLLVM_UpCallsRFFIImpl.java     | 202 ++++++++
 .../r/ffi/impl}/llvm/TruffleLLVM_UserRng.java |   2 +-
 .../r/ffi/impl/llvm/TruffleLLVM_Utils.java    |  83 +++
 .../r/ffi/impl/llvm/TruffleLLVM_Zip.java      |  94 ++++
 .../truffle/r/ffi/impl/llvm/package-info.java |  26 +
 .../r/ffi/impl}/managed/FilesystemUtils.java  |   2 +-
 .../r/ffi/impl}/managed/Managed_Base.java     |   6 +-
 .../ffi/impl}/managed/Managed_LapackRFFI.java |   4 +-
 .../r/ffi/impl}/managed/Managed_PCRERFFI.java |   4 +-
 .../ffi/impl}/managed/Managed_REmbedRFFI.java |   4 +-
 .../impl}/managed/Managed_RFFIFactory.java    |   2 +-
 .../r/ffi/impl/managed/package-info.java      |  27 +
 .../truffle/r/ffi/impl/nfi/NFIFunction.java   | 113 ++++
 .../r/ffi/impl/nfi/TruffleNFI_Base.java       | 275 ++++++++++
 .../truffle/r/ffi/impl/nfi/TruffleNFI_C.java  | 158 ++++++
 .../r/ffi/impl}/nfi/TruffleNFI_CAccess.java   |   4 +-
 .../r/ffi/impl}/nfi/TruffleNFI_Call.java      |  49 +-
 .../r/ffi/impl}/nfi/TruffleNFI_DLL.java       |   2 +-
 .../r/ffi/impl}/nfi/TruffleNFI_Lapack.java    |  93 +---
 .../r/ffi/impl}/nfi/TruffleNFI_Misc.java      |  13 +-
 .../ffi/impl}/nfi/TruffleNFI_NativeArray.java |   2 +-
 .../r/ffi/impl}/nfi/TruffleNFI_PCRE.java      |  99 +---
 .../r/ffi/impl}/nfi/TruffleNFI_PkgInit.java   |   2 +-
 .../r/ffi/impl}/nfi/TruffleNFI_RAppl.java     |  47 +-
 .../r/ffi/impl}/nfi/TruffleNFI_REmbed.java    |   2 +-
 .../ffi/impl}/nfi/TruffleNFI_RFFIFactory.java |   4 +-
 .../r/ffi/impl}/nfi/TruffleNFI_Stats.java     |  17 +-
 .../r/ffi/impl}/nfi/TruffleNFI_Tools.java     |  13 +-
 .../impl}/nfi/TruffleNFI_UpCallsRFFIImpl.java |  10 +-
 .../r/ffi/impl}/nfi/TruffleNFI_UserRng.java   |   2 +-
 .../r/ffi/impl}/nfi/TruffleNFI_Utils.java     |  52 +-
 .../r/ffi/impl}/nfi/TruffleNFI_Zip.java       |  38 +-
 .../truffle/r/ffi/impl/nfi/package-info.java  |  26 +
 .../truffle/r/ffi/impl/nodes}/AsCharNode.java |   2 +-
 .../r/ffi/impl/nodes}/AsIntegerNode.java      |   2 +-
 .../r/ffi/impl/nodes}/AsLogicalNode.java      |   2 +-
 .../truffle/r/ffi/impl/nodes}/AsRealNode.java |   2 +-
 .../r/ffi/impl/nodes}/CoerceVectorNode.java   |  13 +-
 .../r/ffi/impl/nodes}/FFIUpCallNode.java      |   2 +-
 .../r/ffi/impl/nodes}/FFIUpCallRootNode.java  |  57 +-
 .../r/ffi/impl/nodes}/ListAccessNodes.java    |  15 +-
 .../truffle/r/ffi/impl/nodes}/MiscNodes.java  |   2 +-
 .../r/ffi/impl/nodes/package-info.java        |  26 +
 .../r/ffi/impl/upcalls}/IDEUpCallsRFFI.java   |   4 +-
 .../r/ffi/impl/upcalls}/StdUpCallsRFFI.java   |  60 ++-
 .../r/ffi/impl/upcalls}/UpCallsRFFI.java      |   7 +-
 .../ffi/impl/upcalls/VariableUpCallsRFFI.java |  58 +++
 .../r/ffi/impl/upcalls/package-info.java      |  29 ++
 .../javax.annotation.processing.Processor     |   1 +
 .../truffle/r/ffi/processor/FFIProcessor.java | 386 ++++++++++++++
 .../truffle/r/ffi/processor}/RFFICstring.java |   2 +-
 .../r/ffi/processor/RFFIUpCallRoot.java       |  22 +-
 .../truffle/r/library/tools/C_ParseRd.java    |   6 +-
 com.oracle.truffle.r.native/fficall/Makefile  |  12 +-
 .../rffi_upcalls.h}                           |   6 +-
 .../fficall/src/common/rffi_upcallsindex.h    | 141 +++++
 .../rffi_variablesindex.h}                    |  18 +-
 .../fficall/src/jni/Connections.c             |   4 +-
 .../fficall/src/jni/Parse.c                   |   2 +-
 .../fficall/src/jni/Random.c                  |   6 +-
 .../fficall/src/jni/Rdynload_fastr.c          |   4 +-
 .../fficall/src/jni/Rembedded.c               |   2 +-
 .../fficall/src/jni/Rinternals.c              |  42 +-
 .../fficall/src/jni/appl_rffi.c               |   8 +-
 .../fficall/src/jni/base_rffi.c               |  22 +-
 .../fficall/src/jni/c_rffi.c                  |   4 +-
 .../fficall/src/jni/call_rffi.c               |  30 +-
 .../fficall/src/jni/lapack_rffi.c             |  26 +-
 .../fficall/src/jni/misc_rffi.c               |   4 +-
 .../fficall/src/jni/pcre_rffi.c               |  10 +-
 .../fficall/src/jni/stats_fft.c               |   6 +-
 .../fficall/src/jni/userrng_rffi.c            |  10 +-
 .../fficall/src/jni/zip_rffi.c                |  10 +-
 .../fficall/src/jniboot/jniboot.c             |   6 +-
 .../fficall/src/truffle_llvm/Makefile         |  20 +-
 .../fficall/src/truffle_llvm/Memory.c         | 117 +++++
 .../fficall/src/truffle_llvm/Rinternals.c     | 335 ++++++------
 .../fficall/src/truffle_llvm/base_rffi.c      | 122 +++++
 .../fficall/src/truffle_llvm/call_dlopen.c    |  52 ++
 .../fficall/src/truffle_llvm/pcre_rffi.c      |  81 +++
 .../fficall/src/truffle_llvm/rffiutils.c      |   8 +
 .../fficall/src/truffle_llvm/rffiutils.h      |   2 +
 .../fficall/src/truffle_llvm/variables.c      | 152 ++----
 .../fficall/src/truffle_llvm/zip_rffi.c       |  26 +-
 .../fficall/src/truffle_nfi/Makefile          |   5 +-
 .../fficall/src/truffle_nfi/Rinternals.c      | 146 +++---
 .../fficall/src/truffle_nfi/appl_rffi.c       |   6 +-
 .../fficall/src/truffle_nfi/base_rffi.c       |   8 +-
 .../fficall/src/truffle_nfi/lapack_rffi.c     |  26 +-
 .../fficall/src/truffle_nfi/pcre_rffi.c       |  15 +-
 .../src/truffle_nfi/rffi_callbacksindex.h     | 163 ------
 .../fficall/src/truffle_nfi/rffiutils.c       |  80 +++
 .../fficall/src/truffle_nfi/rffiutils.h       |  13 +-
 .../fficall/src/truffle_nfi/variables.c       | 159 +++---
 .../library/tools/Makefile                    |   6 +
 .../tools/src/truffle_llvm/gramRd_llvm.c      |  19 +-
 com.oracle.truffle.r.native/version.source    |   2 +-
 .../r/nodes/builtin/base/SeqFunctions.java    |   4 +-
 .../truffle/r/nodes/ffi/RFFIUpCallMethod.java | 181 -------
 .../context/RForeignAccessFactory.java        |  11 +-
 .../r/runtime/data/RTruffleObject.java        |  15 +-
 .../com/oracle/truffle/r/runtime/ffi/DLL.java |   1 -
 .../oracle/truffle/r/runtime/ffi/RFFI.java    |   7 +-
 .../truffle/r/runtime/ffi/RFFIFactory.java    |   8 +-
 .../truffle/r/runtime/ffi/RFFIRootNode.java   |   2 +-
 .../truffle/r/runtime/ffi/RFFIVariables.java  |  21 +-
 .../test/tools/RFFIUpCallMethodGenerate.java  | 133 -----
 .../truffle/r/test/tools/ShowLLVMIR.java      |   2 +-
 documentation/dev/truffle_llvm_ffi.md         |  50 +-
 mx.fastr/copyrights/overrides                 |   6 +-
 mx.fastr/mx_fastr.py                          |   3 +-
 mx.fastr/mx_fastr_compile.py                  |   5 +-
 mx.fastr/mx_fastr_junit.py                    |  11 +-
 mx.fastr/suite.py                             |  28 +-
 202 files changed, 6383 insertions(+), 3066 deletions(-)
 delete mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_RFFIFactory.java
 delete mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_UpCallsRFFIImpl.java
 delete mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/VariableUpCallsRFFI.java
 delete mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Base.java
 delete mode 100644 com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_C.java
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common}/Generic_Tools.java (98%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common}/JavaUpCallsRFFIImpl.java (93%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common}/LibPaths.java (98%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common}/ParseResult.java (93%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common}/RFFIUtils.java (97%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common}/TracingUpCallsRFFIImpl.java (89%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/package-info.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/CharSXPWrapperMR.java (89%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/DLLDotSymbolMR.java (89%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/DLLInfoMR.java (89%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/FFI_RForeignAccessFactoryImpl.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeCharArray.java (82%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeCharArrayMR.java (76%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArray.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeDoubleArrayMR.java (68%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArray.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeIntegerArrayMR.java (70%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeLogicalArray.java (86%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeLogicalArrayMR.java (92%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeNACheck.java (97%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointer.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeRawArray.java (80%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeRawArrayMR.java (75%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/NativeUInt8Array.java (96%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/interop/UnsafeAdapter.java (97%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResult.java
 rename com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/GlobUpCallImplMR.java => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResultMR.java (68%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResult.java
 rename com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/SetResultImplMR.java => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResultMR.java (68%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResult.java
 rename com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/UnameUpCallImplMR.java => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResultMR.java (63%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResult.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResultMR.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/package-info.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResult.java
 rename com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/CaptureNamesImplMR.java => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResultMR.java (75%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResult.java
 rename com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/MakeResultImplMR.java => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResultMR.java (76%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNIUpCallsRFFIImpl.java (93%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_Base.java (99%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_C.java (91%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_Call.java (95%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_DLL.java (98%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_Glob.java (96%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_Lapack.java (99%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_Misc.java (87%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_PCRE.java (98%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_PkgInit.java (98%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_RAppl.java (98%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_REmbed.java (94%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_RFFIFactory.java (97%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_Stats.java (93%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_UserRng.java (98%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_UtsName.java (94%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/jni/JNI_Zip.java (98%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/package-info.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVMFunction.java
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/truffle => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm}/LLVM_IR.java (98%)
 rename {com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/truffle => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm}/MachOAccess.java (99%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Base.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/llvm/TruffleLLVM_C.java (68%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/llvm/TruffleLLVM_CAccess.java (95%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/llvm/TruffleLLVM_Call.java (53%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/llvm/TruffleLLVM_DLL.java (71%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Lapack.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Misc.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_NativeDLL.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_PCRE.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/llvm/TruffleLLVM_PkgInit.java (95%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RAppl.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/llvm/TruffleLLVM_RFFIContextState.java (62%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIFactory.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/llvm/TruffleLLVM_Stats.java (91%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Tools.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UpCallsRFFIImpl.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/llvm/TruffleLLVM_UserRng.java (98%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Utils.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Zip.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/package-info.java
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/managed/FilesystemUtils.java (97%)
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/managed/Managed_Base.java (96%)
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/managed/Managed_LapackRFFI.java (94%)
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/managed/Managed_PCRERFFI.java (92%)
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/managed/Managed_REmbedRFFI.java (92%)
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/managed/Managed_RFFIFactory.java (99%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/package-info.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/NFIFunction.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Base.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_CAccess.java (96%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_Call.java (94%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_DLL.java (98%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_Lapack.java (64%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_Misc.java (83%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_NativeArray.java (99%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_PCRE.java (54%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_PkgInit.java (99%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_RAppl.java (72%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_REmbed.java (97%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_RFFIFactory.java (98%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_Stats.java (87%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_Tools.java (93%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_UpCallsRFFIImpl.java (92%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_UserRng.java (99%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_Utils.java (66%)
 rename {com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl}/nfi/TruffleNFI_Zip.java (69%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/package-info.java
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes}/AsCharNode.java (98%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes}/AsIntegerNode.java (98%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes}/AsLogicalNode.java (98%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes}/AsRealNode.java (98%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes}/CoerceVectorNode.java (92%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes}/FFIUpCallNode.java (97%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes}/FFIUpCallRootNode.java (57%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes}/ListAccessNodes.java (86%)
 rename {com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes}/MiscNodes.java (99%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/package-info.java
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls}/IDEUpCallsRFFI.java (95%)
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls}/StdUpCallsRFFI.java (77%)
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls}/UpCallsRFFI.java (83%)
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/VariableUpCallsRFFI.java
 create mode 100644 com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/package-info.java
 create mode 100644 com.oracle.truffle.r.ffi.processor/src/META-INF/services/javax.annotation.processing.Processor
 create mode 100644 com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java
 rename {com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi => com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor}/RFFICstring.java (97%)
 rename com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeDoubleArray.java => com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFIUpCallRoot.java (66%)
 rename com.oracle.truffle.r.native/fficall/src/{truffle_nfi/rffi_callbacks.h => common/rffi_upcalls.h} (99%)
 create mode 100644 com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h
 rename com.oracle.truffle.r.native/fficall/src/{truffle_llvm/variables.h => common/rffi_variablesindex.h} (88%)
 create mode 100644 com.oracle.truffle.r.native/fficall/src/truffle_llvm/Memory.c
 create mode 100644 com.oracle.truffle.r.native/fficall/src/truffle_llvm/base_rffi.c
 create mode 100644 com.oracle.truffle.r.native/fficall/src/truffle_llvm/call_dlopen.c
 create mode 100644 com.oracle.truffle.r.native/fficall/src/truffle_llvm/pcre_rffi.c
 rename com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RContextUpCallsRFFI.java => com.oracle.truffle.r.native/fficall/src/truffle_llvm/zip_rffi.c (68%)
 delete mode 100644 com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffi_callbacksindex.h
 rename com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeIntegerArray.java => com.oracle.truffle.r.native/library/tools/src/truffle_llvm/gramRd_llvm.c (72%)
 delete mode 100644 com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUpCallMethod.java
 rename {com.oracle.truffle.r.runtime.ffi => com.oracle.truffle.r.runtime}/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java (89%)
 delete mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java

diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ActiveBindingMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ActiveBindingMR.java
index b028bd676d..1054292594 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ActiveBindingMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ActiveBindingMR.java
@@ -32,7 +32,7 @@ import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.RCloseable;
 import com.oracle.truffle.r.runtime.env.frame.ActiveBinding;
 
-@MessageResolution(receiverType = ActiveBinding.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = ActiveBinding.class)
 public class ActiveBindingMR {
     @Resolve(message = "IS_BOXED")
     public abstract static class ActiveBindingIsBoxedNode extends Node {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java
index c5ee552ba3..599364a700 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java
@@ -34,6 +34,7 @@ import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.r.engine.TruffleRLanguage;
 import com.oracle.truffle.r.engine.interop.RAbstractVectorAccessFactoryFactory.VectorReadNodeGen;
+import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
@@ -193,4 +194,30 @@ public final class RAbstractVectorAccessFactory implements Factory26 {
     public CallTarget accessKeyInfo() {
         return null;
     }
+
+    @Override
+    public CallTarget accessIsPointer() {
+        return Truffle.getRuntime().createCallTarget(new InteropRootNode() {
+            @Override
+            public Object execute(VirtualFrame frame) {
+                return false;
+            }
+        });
+    }
+
+    @Override
+    public CallTarget accessAsPointer() {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessToNative() {
+        return Truffle.getRuntime().createCallTarget(new InteropRootNode() {
+            @Override
+            public Object execute(VirtualFrame frame) {
+                RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame);
+                return new NativePointer(arg);
+            }
+        });
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REnvironmentMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REnvironmentMR.java
index c3e7e288f8..516c79d50a 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REnvironmentMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/REnvironmentMR.java
@@ -31,6 +31,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.engine.TruffleRLanguage;
+import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNode;
@@ -48,6 +49,14 @@ public class REnvironmentMR {
         }
     }
 
+    @Resolve(message = "TO_NATIVE")
+    public abstract static class REnvironmentToNativeNode extends Node {
+        protected Object access(REnvironment receiver) {
+            return new NativePointer(receiver);
+        }
+
+    }
+
     @Resolve(message = "HAS_SIZE")
     public abstract static class REnvironmentHasSizeNode extends Node {
         protected Object access(@SuppressWarnings("unused") REnvironment receiver) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java
index fa203c367a..7315388422 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java
@@ -27,8 +27,7 @@ import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.r.engine.TruffleRLanguage;
-import com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_Base;
-import com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_PCRE;
+import com.oracle.truffle.r.ffi.impl.interop.FFI_RForeignAccessFactoryImpl;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -53,14 +52,18 @@ import com.oracle.truffle.r.runtime.data.RUnboundValue;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.frame.ActiveBinding;
-import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
-import com.oracle.truffle.r.runtime.ffi.DLL;
 
 /**
  * For most types we use the {@link MessageResolution} facility to automatically generate the
  * factory for creating the {@link ForeignAccess} instance. The exceptions are the (many) subclasses
  * of {@link RAbstractVector} as these have the same handling but the generator cannot handle
  * abstract classes.
+ *
+ * The types that must flow across the interop boundary fall into (at least) two categories. Those
+ * listed in this class are those that other peer languages might expect to receive and make sense
+ * of. There are also a large number that are involved in implementing the R Foreign Function
+ * Interface, which are essentially private to the implementation. These are enumerated in
+ * {@link FFI_RForeignAccessFactoryImpl}.
  */
 public final class RForeignAccessFactoryImpl implements RForeignAccessFactory {
 
@@ -77,46 +80,20 @@ public final class RForeignAccessFactoryImpl implements RForeignAccessFactory {
             return RPairListMRForeign.ACCESS;
         } else if (obj instanceof RFunction) {
             return RFunctionMRForeign.ACCESS;
-        } else if (obj instanceof DLL.DLLInfo) {
-            return DLLInfoMRForeign.ACCESS;
-        } else if (obj instanceof DLL.DotSymbol) {
-            return DLLDotSymbolMRForeign.ACCESS;
         } else if (obj instanceof RSymbol) {
             return RSymbolMRForeign.ACCESS;
         } else if (obj instanceof RExternalPtr) {
             return RExternalPtrMRForeign.ACCESS;
         } else if (obj instanceof RUnboundValue) {
             return RUnboundValueMRForeign.ACCESS;
-        } else if (obj instanceof NativeRawArray) {
-            return NativeRawArrayMRForeign.ACCESS;
-        } else if (obj instanceof NativeLogicalArray) {
-            return NativeLogicalArrayMRForeign.ACCESS;
-        } else if (obj instanceof NativeCharArray) {
-            return NativeCharArrayMRForeign.ACCESS;
-        } else if (obj instanceof NativeDoubleArray) {
-            return NativeDoubleArrayMRForeign.ACCESS;
-        } else if (obj instanceof NativeIntegerArray) {
-            return NativeIntegerArrayMRForeign.ACCESS;
         } else if (obj instanceof RInteger) {
             return RIntegerMRForeign.ACCESS;
         } else if (obj instanceof RDouble) {
             return RDoubleMRForeign.ACCESS;
-        } else if (obj instanceof CharSXPWrapper) {
-            return CharSXPWrapperMRForeign.ACCESS;
         } else if (obj instanceof RConnection) {
             return RConnectionMRForeign.ACCESS;
         } else if (obj instanceof RContext) {
             return RContextMRForeign.ACCESS;
-        } else if (obj instanceof TruffleNFI_Base.TruffleNFI_UnameNode.UnameUpCallImpl) {
-            return UnameUpCallImplMRForeign.ACCESS;
-        } else if (obj instanceof TruffleNFI_Base.TruffleNFI_ReadlinkNode.SetResultImpl) {
-            return SetResultImplMRForeign.ACCESS;
-        } else if (obj instanceof TruffleNFI_Base.TruffleNFI_GlobNode.GlobUpCallImpl) {
-            return GlobUpCallImplMRForeign.ACCESS;
-        } else if (obj instanceof TruffleNFI_PCRE.TruffleNFI_CompileNode.MakeResultImpl) {
-            return MakeResultImplMRForeign.ACCESS;
-        } else if (obj instanceof TruffleNFI_PCRE.TruffleNFI_GetCaptureNamesNode.CaptureNamesImpl) {
-            return CaptureNamesImplMRForeign.ACCESS;
         } else if (obj instanceof RS4Object) {
             return RS4ObjectMRForeign.ACCESS;
         } else if (obj instanceof RPromise) {
@@ -133,19 +110,25 @@ public final class RForeignAccessFactoryImpl implements RForeignAccessFactory {
             return RMissingMRForeign.ACCESS;
         } else if (obj instanceof REmpty) {
             return REmptyMRForeign.ACCESS;
-
+        } else if (obj instanceof RAbstractVector) {
+            return ForeignAccess.create(RAbstractVector.class, new RAbstractVectorAccessFactory());
         } else {
-            if (obj instanceof RAbstractVector) {
-                return ForeignAccess.create(RAbstractVector.class, new RAbstractVectorAccessFactory());
+            ForeignAccess access = FFI_RForeignAccessFactoryImpl.getForeignAccess(obj);
+            if (access != null) {
+                return access;
             } else {
                 throw RInternalError.unimplemented("missing foreign access factory for " + obj.getClass().getSimpleName());
             }
         }
-
     }
 
     @Override
     public Class<? extends TruffleLanguage<RContext>> getTruffleLanguage() {
         return TruffleRLanguage.class;
     }
+
+    @Override
+    public boolean setIsNull(boolean value) {
+        return RNullMR.setIsNull(value);
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java
index c202c48960..8f6e2f68db 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java
@@ -33,6 +33,7 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.engine.TruffleRLanguage;
+import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNode;
@@ -151,6 +152,13 @@ public class RListMR {
         }
     }
 
+    @Resolve(message = "TO_NATIVE")
+    public abstract static class RListToNativeNode extends Node {
+        protected Object access(RList receiver) {
+            return new NativePointer(receiver);
+        }
+    }
+
     @CanResolve
     public abstract static class RListCheck extends Node {
 
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 4fa755eacc..dcbe27c3bc 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,10 +27,10 @@ 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.engine.TruffleRLanguage;
+import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.runtime.data.RNull;
 
-@MessageResolution(receiverType = RNull.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = RNull.class)
 public class RNullMR {
     /**
      * Workaround to avoid NFI converting {@link RNull} to {@code null}.
@@ -58,6 +58,13 @@ public class RNullMR {
         }
     }
 
+    @Resolve(message = "TO_NATIVE")
+    public abstract static class RNullToNativeNode extends Node {
+        protected Object access(@SuppressWarnings("unused") RNull receiver) {
+            return NativePointer.NULL_NATIVEPOINTER;
+        }
+    }
+
     @CanResolve
     public abstract static class RNullCheck extends Node {
 
@@ -66,7 +73,7 @@ public class RNullMR {
         }
     }
 
-    public static boolean setIsNull(boolean value) {
+    static boolean setIsNull(boolean value) {
         boolean prev = isNull;
         isNull = value;
         return prev;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPairListMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPairListMR.java
index 8aab5a3e3b..82dce0790b 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPairListMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RPairListMR.java
@@ -29,6 +29,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.engine.TruffleRLanguage;
+import com.oracle.truffle.r.ffi.impl.interop.NativePointer;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
@@ -77,6 +78,13 @@ public class RPairListMR {
         }
     }
 
+    @Resolve(message = "TO_NATIVE")
+    public abstract static class RPairListToNativeNode extends Node {
+        protected Object access(RPairList receiver) {
+            return new NativePointer(receiver);
+        }
+    }
+
     @CanResolve
     public abstract static class RPairListCheck extends Node {
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_RFFIFactory.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_RFFIFactory.java
deleted file mode 100644
index 5a3324b951..0000000000
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_RFFIFactory.java
+++ /dev/null
@@ -1,97 +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.engine.interop.ffi.llvm;
-
-import com.oracle.truffle.r.runtime.context.RContext.ContextState;
-import com.oracle.truffle.r.runtime.ffi.CRFFI;
-import com.oracle.truffle.r.runtime.ffi.CallRFFI;
-import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
-import com.oracle.truffle.r.runtime.ffi.RFFI;
-import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
-import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
-import com.oracle.truffle.r.runtime.ffi.jni.JNI_RFFIFactory;
-
-/**
- * Incremental approach to using Truffle, defaults to the JNI factory.
- *
- */
-public class TruffleLLVM_RFFIFactory extends JNI_RFFIFactory implements RFFI {
-
-    @Override
-    public ContextState newContextState() {
-        return new TruffleLLVM_RFFIContextState(super.newContextState());
-    }
-
-    private CRFFI cRFFI;
-
-    @Override
-    public CRFFI getCRFFI() {
-        if (cRFFI == null) {
-            cRFFI = new TruffleLLVM_C();
-        }
-        return cRFFI;
-    }
-
-    private DLLRFFI dllRFFI;
-
-    @Override
-    public DLLRFFI getDLLRFFI() {
-        if (dllRFFI == null) {
-            dllRFFI = new TruffleLLVM_DLL();
-        }
-        return dllRFFI;
-    }
-
-    private UserRngRFFI truffleUserRngRFFI;
-
-    @Override
-    public UserRngRFFI getUserRngRFFI() {
-        if (truffleUserRngRFFI == null) {
-            truffleUserRngRFFI = new TruffleLLVM_UserRng();
-        }
-        return truffleUserRngRFFI;
-    }
-
-    private CallRFFI truffleCallRFFI;
-
-    @Override
-    public CallRFFI getCallRFFI() {
-        if (truffleCallRFFI == null) {
-            truffleCallRFFI = new TruffleLLVM_Call();
-        }
-        return truffleCallRFFI;
-    }
-
-    private StatsRFFI truffleStatsRFFI;
-
-    @Override
-    public StatsRFFI getStatsRFFI() {
-        if (TruffleLLVM_DLL.isBlacklisted("stats")) {
-            return super.getStatsRFFI();
-        }
-        if (truffleStatsRFFI == null) {
-            truffleStatsRFFI = new TruffleLLVM_Stats();
-        }
-        return truffleStatsRFFI;
-    }
-}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_UpCallsRFFIImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_UpCallsRFFIImpl.java
deleted file mode 100644
index bf5eeed417..0000000000
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_UpCallsRFFIImpl.java
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * 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.engine.interop.ffi.llvm;
-
-import java.nio.charset.StandardCharsets;
-
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.r.engine.interop.NativeCharArray;
-import com.oracle.truffle.r.engine.interop.NativeDoubleArray;
-import com.oracle.truffle.r.engine.interop.NativeIntegerArray;
-import com.oracle.truffle.r.engine.interop.NativeLogicalArray;
-import com.oracle.truffle.r.engine.interop.NativeRawArray;
-import com.oracle.truffle.r.nodes.ffi.RFFIUtils;
-import com.oracle.truffle.r.nodes.ffi.JavaUpCallsRFFIImpl;
-import com.oracle.truffle.r.runtime.REnvVars;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDouble;
-import com.oracle.truffle.r.runtime.data.RInteger;
-import com.oracle.truffle.r.runtime.data.RLogical;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RScalar;
-import com.oracle.truffle.r.runtime.data.RTypedValue;
-import com.oracle.truffle.r.runtime.data.RUnboundValue;
-import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
-import com.oracle.truffle.r.runtime.ffi.RFFIVariables;
-import com.oracle.truffle.r.runtime.ffi.jni.JNIUpCallsRFFIImpl;
-
-/**
- * (Incomplete) Variant of {@link JavaUpCallsRFFIImpl} for Truffle LLVM.
- *
- */
-public class TruffleLLVM_UpCallsRFFIImpl extends JNIUpCallsRFFIImpl implements VariableUpCallsRFFI {
-    private static TruffleLLVM_UpCallsRFFIImpl singleton;
-    private static TruffleObject singletonTruffleObject;
-
-    public static TruffleObject initialize() {
-        if (singleton == null) {
-            singleton = new TruffleLLVM_UpCallsRFFIImpl();
-            singletonTruffleObject = JavaInterop.asTruffleObject(singleton);
-        }
-        return singletonTruffleObject;
-    }
-
-    public Object charSXPToNativeCharArray(Object x) {
-        CharSXPWrapper chars = RFFIUtils.guaranteeInstanceOf(x, CharSXPWrapper.class);
-        return new NativeCharArray(chars.getContents().getBytes());
-    }
-
-    // Checkstyle: stop method name check
-
-    @Override
-    public Object Rf_mkCharLenCE(Object bytes, int len, int encoding) {
-        if (bytes instanceof NativeCharArray) {
-            return super.Rf_mkCharLenCE(((NativeCharArray) bytes).getBytes(), len, encoding);
-        } else {
-            throw RInternalError.unimplemented();
-        }
-    }
-
-    @Override
-    public Object Rf_install(Object name) {
-        if (name instanceof NativeCharArray) {
-            return RDataFactory.createSymbolInterned(new String(((NativeCharArray) name).getBytes(), StandardCharsets.UTF_8));
-        } else {
-            throw RInternalError.unimplemented();
-        }
-    }
-
-    @Override
-    public Object RAW(Object x) {
-        byte[] value = (byte[]) super.RAW(x);
-        return new NativeRawArray(value);
-    }
-
-    @Override
-    public Object LOGICAL(Object x) {
-        byte[] value = (byte[]) super.LOGICAL(x);
-        return new NativeLogicalArray(x, value);
-    }
-
-    @Override
-    public Object INTEGER(Object x) {
-        int[] value = (int[]) super.INTEGER(x);
-        return new NativeIntegerArray(x, value);
-    }
-
-    @Override
-    public Object REAL(Object x) {
-        // Special handling in Truffle variant
-        double[] value = (double[]) super.REAL(x);
-        return new NativeDoubleArray(x, value);
-    }
-
-    public Object R_Home() {
-        byte[] sbytes = REnvVars.rHome().getBytes();
-        return new NativeCharArray(sbytes);
-    }
-
-    @Override
-    public Object Rf_findVar(Object symbolArg, Object envArg) {
-        Object v = super.Rf_findVar(symbolArg, envArg);
-        if (v instanceof RTypedValue) {
-            return v;
-        } else {
-            return wrapPrimitive(v);
-        }
-    }
-
-    public Object bytesToNativeCharArray(byte[] bytes) {
-        return new NativeCharArray(bytes);
-    }
-
-    private static RScalar wrapPrimitive(Object x) {
-        if (x instanceof Double) {
-            return RDouble.valueOf((double) x);
-        } else if (x instanceof Integer) {
-            return RInteger.valueOf((int) x);
-        } else if (x instanceof Byte) {
-            return RLogical.valueOf((byte) x);
-        } else {
-            throw RInternalError.shouldNotReachHere();
-        }
-    }
-
-    @Override
-    public Object R_NilValue() {
-        return RNull.instance;
-    }
-
-    @Override
-    public Object R_UnboundValue() {
-        return RUnboundValue.instance;
-    }
-
-    @Override
-    public Object R_Srcref() {
-        return RFFIVariables.R_Srcref.getValue();
-    }
-
-    @Override
-    public Object R_MissingArg() {
-        return RFFIVariables.R_MissingArg.getValue();
-    }
-
-    @Override
-    public Object R_BaseSymbol() {
-        return RFFIVariables.R_BaseSymbol.getValue();
-    }
-
-    @Override
-    public Object R_BraceSymbol() {
-        return RFFIVariables.R_BraceSymbol.getValue();
-    }
-
-    @Override
-    public Object R_Bracket2Symbol() {
-        return RFFIVariables.R_Bracket2Symbol.getValue();
-    }
-
-    @Override
-    public Object R_BracketSymbol() {
-        return RFFIVariables.R_BracketSymbol.getValue();
-    }
-
-    @Override
-    public Object R_ClassSymbol() {
-        return RFFIVariables.R_ClassSymbol.getValue();
-    }
-
-    @Override
-    public Object R_DeviceSymbol() {
-        return RFFIVariables.R_DeviceSymbol.getValue();
-    }
-
-    @Override
-    public Object R_DimNamesSymbol() {
-        return RFFIVariables.R_DimNamesSymbol.getValue();
-    }
-
-    @Override
-    public Object R_DimSymbol() {
-        return RFFIVariables.R_DimSymbol.getValue();
-    }
-
-    @Override
-    public Object R_DollarSymbol() {
-        return RFFIVariables.R_DollarSymbol.getValue();
-    }
-
-    @Override
-    public Object R_DotsSymbol() {
-        return RFFIVariables.R_DotsSymbol.getValue();
-    }
-
-    @Override
-    public Object R_DropSymbol() {
-        return RFFIVariables.R_DropSymbol.getValue();
-    }
-
-    @Override
-    public Object R_LastvalueSymbol() {
-        return RFFIVariables.R_LastvalueSymbol.getValue();
-    }
-
-    @Override
-    public Object R_LevelsSymbol() {
-        return RFFIVariables.R_LevelsSymbol.getValue();
-    }
-
-    @Override
-    public Object R_ModeSymbol() {
-        return RFFIVariables.R_ModeSymbol.getValue();
-    }
-
-    @Override
-    public Object R_NaRmSymbol() {
-        return RFFIVariables.R_NaRmSymbol.getValue();
-    }
-
-    @Override
-    public Object R_NameSymbol() {
-        return RFFIVariables.R_NameSymbol.getValue();
-    }
-
-    @Override
-    public Object R_NamesSymbol() {
-        return RFFIVariables.R_NamesSymbol.getValue();
-    }
-
-    @Override
-    public Object R_NamespaceEnvSymbol() {
-        return RFFIVariables.R_NamespaceEnvSymbol.getValue();
-    }
-
-    @Override
-    public Object R_PackageSymbol() {
-        return RFFIVariables.R_PackageSymbol.getValue();
-    }
-
-    @Override
-    public Object R_QuoteSymbol() {
-        return RFFIVariables.R_QuoteSymbol.getValue();
-    }
-
-    @Override
-    public Object R_RowNamesSymbol() {
-        return RFFIVariables.R_RowNamesSymbol.getValue();
-    }
-
-    @Override
-    public Object R_SeedsSymbol() {
-        return RFFIVariables.R_SeedsSymbol.getValue();
-    }
-
-    @Override
-    public Object R_SourceSymbol() {
-        return RFFIVariables.R_SourceSymbol.getValue();
-    }
-
-    @Override
-    public Object R_TspSymbol() {
-        return RFFIVariables.R_TspSymbol.getValue();
-    }
-
-    @Override
-    public Object R_dot_defined() {
-        return RFFIVariables.R_dot_defined.getValue();
-    }
-
-    @Override
-    public Object R_dot_Method() {
-        return RFFIVariables.R_dot_Method.getValue();
-    }
-
-    @Override
-    public Object R_dot_target() {
-        return RFFIVariables.R_dot_target.getValue();
-    }
-
-    @Override
-    public Object R_NaString() {
-        return RFFIVariables.R_NaString.getValue();
-    }
-
-    @Override
-    public Object R_BlankString() {
-        return RFFIVariables.R_BlankString.getValue();
-    }
-
-    @Override
-    public Object R_BlankScalarString() {
-        return RFFIVariables.R_BlankScalarString.getValue();
-    }
-
-    @Override
-    public Object R_EmptyEnv() {
-        return RFFIVariables.R_EmptyEnv.getValue();
-    }
-
-}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/VariableUpCallsRFFI.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/VariableUpCallsRFFI.java
deleted file mode 100644
index b1749e8e99..0000000000
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/VariableUpCallsRFFI.java
+++ /dev/null
@@ -1,108 +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.engine.interop.ffi.llvm;
-
-import com.oracle.truffle.api.interop.TruffleObject;
-
-/**
- * This exists because {@link TruffleObject} instances cannot currently be stored in memory, so
- * upcall to get the values.
- *
- * TODO Some of these, e.g. {@link #R_NilValue}, are performance sensitive, but most are not. So we
- * could collapse those into a single upcall that returned all the values in one go and extract the
- * value with another upcall.
- */
-public interface VariableUpCallsRFFI {
-    // Checkstyle: stop method name check
-
-    Object R_EmptyEnv();
-
-    Object R_NilValue();
-
-    Object R_UnboundValue();
-
-    Object R_Srcref();
-
-    Object R_MissingArg();
-
-    Object R_BaseSymbol();
-
-    Object R_BraceSymbol();
-
-    Object R_Bracket2Symbol();
-
-    Object R_BracketSymbol();
-
-    Object R_ClassSymbol();
-
-    Object R_DeviceSymbol();
-
-    Object R_DimNamesSymbol();
-
-    Object R_DimSymbol();
-
-    Object R_DollarSymbol();
-
-    Object R_DotsSymbol();
-
-    Object R_DropSymbol();
-
-    Object R_LastvalueSymbol();
-
-    Object R_LevelsSymbol();
-
-    Object R_ModeSymbol();
-
-    Object R_NaRmSymbol();
-
-    Object R_NameSymbol();
-
-    Object R_NamesSymbol();
-
-    Object R_NamespaceEnvSymbol();
-
-    Object R_PackageSymbol();
-
-    Object R_QuoteSymbol();
-
-    Object R_RowNamesSymbol();
-
-    Object R_SeedsSymbol();
-
-    Object R_SourceSymbol();
-
-    Object R_TspSymbol();
-
-    Object R_dot_defined();
-
-    Object R_dot_Method();
-
-    Object R_dot_target();
-
-    Object R_NaString();
-
-    Object R_BlankString();
-
-    Object R_BlankScalarString();
-
-}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Base.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Base.java
deleted file mode 100644
index 3ad8a7f1da..0000000000
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Base.java
+++ /dev/null
@@ -1,399 +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.engine.interop.ffi.nfi;
-
-import java.io.IOException;
-import java.util.ArrayList;
-
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.interop.ForeignAccess;
-import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.data.RTruffleObject;
-import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
-
-public class TruffleNFI_Base implements BaseRFFI {
-
-    private enum Function {
-        getpid("(): sint32", true),
-        getcwd("([uint8], sint32): sint32", true),
-        chdir("(string): sint32", true),
-        mkdir("(string, sint32): sint32", true),
-        call_readlink("((string, sint32): void, string): void", false),
-        mkdtemp("([uint8]): sint32", true),
-        chmod("(string, sint32): sint32", true),
-        call_strtol("((sint64, sint32): void, string, sint32): void", false),
-        call_uname("((string, string, string, string, string): void): void", false),
-        call_glob("(string, (string): void): void", false);
-
-        private final int argCount;
-        private final String signature;
-        private final boolean useDefaultLibrary;
-        private Node message;
-        private TruffleObject function;
-
-        Function() {
-            this(null, true);
-        }
-
-        Function(String signature, boolean useDefaultLibrary) {
-            this.argCount = TruffleNFI_Utils.getArgCount(signature);
-            this.signature = signature;
-            this.useDefaultLibrary = useDefaultLibrary;
-        }
-
-        private void initialize() {
-            if (message == null) {
-                message = Message.createExecute(argCount).createNode();
-            }
-            if (function == null) {
-                function = TruffleNFI_Utils.lookupAndBind(name(), useDefaultLibrary, signature);
-            }
-        }
-    }
-
-    public static class TruffleNFI_GetpidNode extends GetpidNode {
-        @Override
-        public int execute() {
-            Function.getpid.initialize();
-            try {
-                int result = (int) ForeignAccess.sendExecute(Function.getpid.message, Function.getpid.function);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
-
-        }
-    }
-
-    public static class TruffleNFI_GetwdNode extends GetwdNode {
-        @TruffleBoundary
-        @Override
-        public String execute() {
-            byte[] buf = new byte[4096];
-            Function.getcwd.initialize();
-            try {
-                int result = (int) ForeignAccess.sendExecute(Function.getcwd.message, Function.getcwd.function, 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);
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
-        }
-    }
-
-    public static class TruffleNFI_SetwdNode extends SetwdNode {
-        @Override
-        public int execute(String dir) {
-            Function.chdir.initialize();
-            try {
-                int result = (int) ForeignAccess.sendExecute(Function.chdir.message, Function.chdir.function, dir);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
-        }
-    }
-
-    public static class TruffleNFI_MkdirNode extends MkdirNode {
-        @Override
-        public void execute(String dir, int mode) throws IOException {
-            Function.mkdir.initialize();
-            try {
-                int result = (int) ForeignAccess.sendExecute(Function.mkdir.message, Function.mkdir.function, dir, mode);
-                if (result != 0) {
-                    throw new IOException("mkdir " + dir + " failed");
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
-        }
-    }
-
-    public static class TruffleNFI_ReadlinkNode extends ReadlinkNode {
-        private static final int EINVAL = 22;
-
-        interface SetResult {
-            void setResult(String link, int errno);
-        }
-
-        public static class SetResultImpl implements SetResult, RTruffleObject {
-            private String link;
-            private int errno;
-
-            @Override
-            public void setResult(String link, int errno) {
-                this.link = link;
-                this.errno = errno;
-            }
-
-        }
-
-        @Override
-        public String execute(String path) throws IOException {
-            Function.call_readlink.initialize();
-            try {
-                SetResultImpl setResultImpl = new SetResultImpl();
-                ForeignAccess.sendExecute(Function.call_readlink.message, Function.call_readlink.function, setResultImpl, path);
-                if (setResultImpl.link == null) {
-                    if (setResultImpl.errno == EINVAL) {
-                        return path;
-                    } else {
-                        // some other error
-                        throw new IOException("readlink failed: " + setResultImpl.errno);
-                    }
-                }
-                return setResultImpl.link;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
-        }
-    }
-
-    public static class TruffleNFI_MkdtempNode extends MkdtempNode {
-        @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;
-            Function.mkdtemp.initialize();
-            try {
-                int result = (int) ForeignAccess.sendExecute(Function.mkdtemp.message, Function.mkdtemp.function, JavaInterop.asTruffleObject(ztbytes));
-                if (result == 0) {
-                    return null;
-                } else {
-                    return new String(ztbytes, 0, bytes.length);
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
-        }
-    }
-
-    public static class TruffleNFI_ChmodNode extends ChmodNode {
-        @Override
-        public int execute(String path, int mode) {
-            Function.chmod.initialize();
-            try {
-                int result = (int) ForeignAccess.sendExecute(Function.chmod.message, Function.chmod.function, path, mode);
-                return result;
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
-        }
-    }
-
-    public static class TruffleNFI_StrolNode extends StrolNode {
-        interface SetResult {
-            void setResult(long result, int errno);
-        }
-
-        private static class SetResultImpl implements SetResult {
-            private long result;
-            private int errno;
-
-            @Override
-            public void setResult(long result, int errno) {
-                this.result = result;
-                this.errno = errno;
-            }
-        }
-
-        @Override
-        public long execute(String s, int base) throws IllegalArgumentException {
-            Function.call_strtol.initialize();
-            try {
-                SetResultImpl setResultImpl = new SetResultImpl();
-                ForeignAccess.sendExecute(Function.call_strtol.message, Function.call_strtol.function, JavaInterop.asTruffleFunction(SetResult.class, setResultImpl), s, base);
-                if (setResultImpl.errno != 0) {
-                    throw new IllegalArgumentException("strtol failure");
-                } else {
-                    return setResultImpl.result;
-                }
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
-        }
-    }
-
-    public static class TruffleNFI_UnameNode extends UnameNode {
-        private static UnameUpCallImpl unameUpCallImpl;
-
-        private interface UnameUpCall {
-            void unameUpCall(String sysname, String release, String version, String machine, String nodename);
-        }
-
-        public static class UnameUpCallImpl implements UnameUpCall, UtsName, RTruffleObject {
-            private String sysname;
-            private String release;
-            private String version;
-            private String machine;
-            private String nodename;
-
-            @Override
-            public void unameUpCall(String sysnameA, String releaseA, String versionA, String machineA, String nodenameA) {
-                sysname = sysnameA;
-                release = releaseA;
-                version = versionA;
-                machine = machineA;
-                nodename = nodenameA;
-            }
-
-            @Override
-            public String sysname() {
-                return sysname;
-            }
-
-            @Override
-            public String release() {
-                return release;
-            }
-
-            @Override
-            public String version() {
-                return version;
-            }
-
-            @Override
-            public String machine() {
-                return machine;
-            }
-
-            @Override
-            public String nodename() {
-                return nodename;
-            }
-
-        }
-
-        @Override
-        public UtsName execute() {
-            Function.call_uname.initialize();
-            if (unameUpCallImpl == null) {
-                unameUpCallImpl = new UnameUpCallImpl();
-                try {
-                    ForeignAccess.sendExecute(Function.call_uname.message, Function.call_uname.function, unameUpCallImpl);
-                } catch (InteropException e) {
-                    throw RInternalError.shouldNotReachHere(e);
-                }
-            }
-            return unameUpCallImpl;
-        }
-    }
-
-    public static class TruffleNFI_GlobNode extends GlobNode {
-        private interface GlobUpCall {
-            void addPath(String path);
-        }
-
-        public static class GlobUpCallImpl implements GlobUpCall, RTruffleObject {
-            private ArrayList<String> paths = new ArrayList<>();
-
-            @Override
-            public void addPath(String path) {
-                paths.add(path);
-            }
-        }
-
-        @Override
-        public ArrayList<String> glob(String pattern) {
-            Function.call_glob.initialize();
-            GlobUpCallImpl globUpCallImpl = new GlobUpCallImpl();
-            try {
-                ForeignAccess.sendExecute(Function.call_glob.message, Function.call_glob.function, pattern, globUpCallImpl);
-            } catch (InteropException e) {
-                throw RInternalError.shouldNotReachHere(e);
-            }
-            return globUpCallImpl.paths;
-        }
-
-    }
-
-    @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 StrolNode createStrolNode() {
-        return new TruffleNFI_StrolNode();
-    }
-
-    @Override
-    public UnameNode createUnameNode() {
-        return new TruffleNFI_UnameNode();
-    }
-
-    @Override
-    public GlobNode createGlobNode() {
-        return new TruffleNFI_GlobNode();
-    }
-
-}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_C.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_C.java
deleted file mode 100644
index dbb6ece117..0000000000
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_C.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.engine.interop.ffi.nfi;
-
-import com.oracle.truffle.api.dsl.Cached;
-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.interop.TruffleObject;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_CFactory.TruffleNFI_InvokeCNodeGen;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.ffi.CRFFI;
-import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
-
-public class TruffleNFI_C implements CRFFI {
-    public abstract static class TruffleNFI_InvokeCNode extends InvokeCNode {
-
-        @Child Node bindNode = Message.createInvoke(1).createNode();
-
-        @Specialization(guards = "args.length == 0")
-        protected void invokeCall0(NativeCallInfo nativeCallInfo, @SuppressWarnings("unused") Object[] args,
-                        @Cached("createExecute(args.length)") Node executeNode) {
-            synchronized (TruffleNFI_Call.class) {
-                try {
-                    TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode,
-                                    nativeCallInfo.address.asTruffleObject(), "bind", "(): object");
-                    ForeignAccess.sendExecute(executeNode, callFunction);
-                } catch (InteropException ex) {
-                    throw RInternalError.shouldNotReachHere(ex);
-                }
-            }
-        }
-
-        public static Node createExecute(int n) {
-            return Message.createExecute(n).createNode();
-        }
-
-    }
-
-    @Override
-    public InvokeCNode createInvokeCNode() {
-        return TruffleNFI_InvokeCNodeGen.create();
-    }
-}
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Tools.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/Generic_Tools.java
similarity index 98%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Tools.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/Generic_Tools.java
index 06d6fa23d7..3bda331b58 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/generic/Generic_Tools.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/Generic_Tools.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.generic;
+package com.oracle.truffle.r.ffi.impl.common;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.r.runtime.RInternalError;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
similarity index 93%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
index a6e270047d..91599706e2 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/JavaUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
@@ -20,11 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.common;
 
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.guarantee;
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.guaranteeInstanceOf;
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.unimplemented;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.guarantee;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.guaranteeInstanceOf;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.unimplemented;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -41,8 +41,11 @@ import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.ffi.impl.upcalls.RFFIUpCallTable;
+import com.oracle.truffle.r.ffi.impl.common.ParseResult.ParseStatus;
+import com.oracle.truffle.r.ffi.impl.nodes.FFIUpCallRootNode;
+import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;
 import com.oracle.truffle.r.nodes.RASTUtils;
-import com.oracle.truffle.r.nodes.ffi.ParseResult.ParseStatus;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -101,7 +104,6 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
-import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 import com.oracle.truffle.r.runtime.nodes.DuplicationHelper;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -110,7 +112,7 @@ import com.oracle.truffle.r.runtime.rng.RRNG;
 
 /**
  * This class provides a simple Java-based implementation of {@link UpCallsRFFI}, where all the
- * argument values are standarde Java types, i.e. no special types used by Truffle NFI or Truffle
+ * argument values are standard Java types, i.e. no special types used by Truffle NFI or Truffle
  * LLVM.
  *
  * TODO Many of the implementations here are incomplete and/or duplicate code that exists in the
@@ -155,27 +157,27 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
 
     @Override
     public int Rf_asInteger(Object x) {
-        return (int) FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.Rf_asInteger).call(x);
+        return (int) FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.Rf_asInteger).call(x);
     }
 
     @Override
     public double Rf_asReal(Object x) {
-        return (double) FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.Rf_asReal).call(x);
+        return (double) FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.Rf_asReal).call(x);
     }
 
     @Override
     public int Rf_asLogical(Object x) {
-        return (int) FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.Rf_asLogical).call(x);
+        return (int) FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.Rf_asLogical).call(x);
     }
 
     @Override
     public Object Rf_asChar(Object x) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.Rf_asChar).call(x);
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.Rf_asChar).call(x);
     }
 
     @Override
     public Object Rf_coerceVector(Object x, int mode) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.Rf_coerceVector).call(x, mode);
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.Rf_coerceVector).call(x, mode);
     }
 
     @Override
@@ -190,7 +192,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void Rf_defineVar(Object symbolArg, Object value, Object envArg) {
+    public int Rf_defineVar(Object symbolArg, Object value, Object envArg) {
         REnvironment env = (REnvironment) envArg;
         RSymbol name = (RSymbol) symbolArg;
         try {
@@ -198,6 +200,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
         } catch (PutException ex) {
             throw RError.error(RError.SHOW_CALLER2, ex);
         }
+        return 0;
     }
 
     @Override
@@ -209,7 +212,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
 
     @Override
     public Object R_do_new_object(Object classDef) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.R_do_new_object).call(classDef);
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.R_do_new_object).call(classDef);
     }
 
     @Override
@@ -271,7 +274,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
 
     @Override
     @TruffleBoundary
-    public void Rf_setAttrib(Object obj, Object name, Object val) {
+    public int Rf_setAttrib(Object obj, Object name, Object val) {
         if (obj instanceof RAttributable) {
             RAttributable attrObj = (RAttributable) obj;
             String nameAsString;
@@ -289,7 +292,10 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
             } else {
                 attrObj.setAttr(nameAsString, val);
             }
+        } else {
+            throw RInternalError.shouldNotReachHere();
         }
+        return 0;
     }
 
     @TruffleBoundary
@@ -356,18 +362,20 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void Rf_error(Object msg) {
+    public int Rf_error(Object msg) {
         throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, msg);
     }
 
     @Override
-    public void Rf_warning(Object msg) {
+    public int Rf_warning(Object msg) {
         RError.warning(RError.SHOW_CALLER2, RError.Message.GENERIC, msg);
+        return 0;
     }
 
     @Override
-    public void Rf_warningcall(Object call, Object msg) {
+    public int Rf_warningcall(Object call, Object msg) {
         RErrorHandling.warningcallRFFI(call, (String) msg);
+        return 0;
     }
 
     @Override
@@ -461,20 +469,22 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
 
     @Override
     public int LENGTH(Object x) {
-        return (int) FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.LENGTH).call(x);
+        return (int) FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.LENGTH).call(x);
     }
 
     @Override
-    public void SET_STRING_ELT(Object x, int i, Object v) {
+    public int SET_STRING_ELT(Object x, int i, Object v) {
         RStringVector vector = guaranteeInstanceOf(x, RStringVector.class);
         CharSXPWrapper element = guaranteeInstanceOf(v, CharSXPWrapper.class);
         vector.setElement(i, element.getContents());
+        return 0;
     }
 
     @Override
-    public void SET_VECTOR_ELT(Object x, int i, Object v) {
+    public int SET_VECTOR_ELT(Object x, int i, Object v) {
         RList list = guaranteeInstanceOf(x, RList.class);
         list.setElement(i, v);
+        return 0;
     }
 
     @Override
@@ -629,27 +639,28 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
 
     @Override
     public Object CAR(Object e) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.CAR).call(e);
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.CAR).call(e);
     }
 
     @Override
     public Object CDR(Object e) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.CDR).call(e);
+        Object result = FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.CDR).call(e);
+        return result;
     }
 
     @Override
     public Object CADR(Object e) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.CADR).call(e);
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.CADR).call(e);
     }
 
     @Override
     public Object CADDR(Object e) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.CADDR).call(e);
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.CADDR).call(e);
     }
 
     @Override
     public Object CDDR(Object e) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.CDDR).call(e);
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.CDDR).call(e);
     }
 
     @Override
@@ -698,11 +709,12 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void SET_SYMVALUE(Object x, Object v) {
+    public int SET_SYMVALUE(Object x, Object v) {
         if (!(x instanceof RSymbol)) {
             throw RInternalError.shouldNotReachHere();
         }
         REnvironment.baseEnv().safePut(((RSymbol) x).getName(), v);
+        return 0;
     }
 
     @Override
@@ -770,7 +782,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void Rf_gsetVar(Object symbol, Object value, Object rho) {
+    public int Rf_gsetVar(Object symbol, Object value, Object rho) {
         guarantee(symbol instanceof RSymbol);
         REnvironment baseEnv = RContext.getInstance().stateREnvironment.getBaseEnv();
         guarantee(rho == baseEnv);
@@ -779,16 +791,18 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
         } catch (PutException e) {
             e.printStackTrace();
         }
+        return 0;
     }
 
     @Override
-    public void DUPLICATE_ATTRIB(Object to, Object from) {
+    public int DUPLICATE_ATTRIB(Object to, Object from) {
         if (from instanceof RAttributable) {
             guaranteeInstanceOf(to, RAttributable.class);
             DynamicObject attributes = ((RAttributable) from).getAttributes();
             ((RAttributable) to).initAttributes(attributes == null ? null : RAttributesLayout.copy(attributes));
         }
         // TODO: copy OBJECT? and S4 attributes
+        return 0;
     }
 
     @Override
@@ -800,12 +814,12 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void Rf_copyListMatrix(Object t, Object s, int byrow) {
+    public int Rf_copyListMatrix(Object t, Object s, int byrow) {
         throw unimplemented();
     }
 
     @Override
-    public void Rf_copyMatrix(Object t, Object s, int byRow) {
+    public int Rf_copyMatrix(Object t, Object s, int byRow) {
         int tRows = RRuntime.nrows(t);
         int tCols = RRuntime.ncols(t);
         final Object sav = RRuntime.asAbstractVector(s);
@@ -925,6 +939,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
         } else { // source is non-RAbstractContainer
             throw unimplemented();
         }
+        return 0;
     }
 
     /**
@@ -975,7 +990,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void SET_RDEBUG(Object x, int v) {
+    public int SET_RDEBUG(Object x, int v) {
         REnvironment env = guaranteeInstanceOf(x, REnvironment.class);
         if (env instanceof REnvironment.Function) {
             REnvironment.Function funcEnv = (REnvironment.Function) env;
@@ -986,6 +1001,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
                 RContext.getRRuntimeASTAccess().disableDebug(func);
             }
         }
+        return 0;
     }
 
     @Override
@@ -996,7 +1012,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void SET_RSTEP(Object x, int v) {
+    public int SET_RSTEP(Object x, int v) {
         @SuppressWarnings("unused")
         REnvironment env = guaranteeInstanceOf(x, REnvironment.class);
         throw RInternalError.unimplemented("SET_RSTEP");
@@ -1050,8 +1066,9 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void R_CleanUp(int sa, int status, int runlast) {
+    public int R_CleanUp(int sa, int status, int runlast) {
         RCleanUp.stdCleanUp(SA_TYPE.values()[sa], status, runlast != 0);
+        return 0;
     }
 
     @Override
@@ -1099,28 +1116,33 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void SET_S4_OBJECT(Object x) {
+    public int SET_S4_OBJECT(Object x) {
         guaranteeInstanceOf(x, RTypedValue.class).setS4();
+        return 0;
     }
 
     @Override
-    public void UNSET_S4_OBJECT(Object x) {
+    public int UNSET_S4_OBJECT(Object x) {
         guaranteeInstanceOf(x, RTypedValue.class).unsetS4();
+        return 0;
     }
 
     @Override
-    public void Rprintf(Object message) {
+    public int Rprintf(Object message) {
         RContext.getInstance().getConsoleHandler().print((String) message);
+        return 0;
     }
 
     @Override
-    public void GetRNGstate() {
+    public int GetRNGstate() {
         RRNG.getRNGState();
+        return 0;
     }
 
     @Override
-    public void PutRNGstate() {
+    public int PutRNGstate() {
         RRNG.putRNGState();
+        return 0;
     }
 
     @Override
@@ -1285,21 +1307,24 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void R_SetExternalPtrAddr(Object x, long addr) {
+    public int R_SetExternalPtrAddr(Object x, long addr) {
         RExternalPtr p = guaranteeInstanceOf(x, RExternalPtr.class);
         p.setAddr(new SymbolHandle(addr));
+        return 0;
     }
 
     @Override
-    public void R_SetExternalPtrTag(Object x, Object tag) {
+    public int R_SetExternalPtrTag(Object x, Object tag) {
         RExternalPtr p = guaranteeInstanceOf(x, RExternalPtr.class);
         p.setTag(tag);
+        return 0;
     }
 
     @Override
-    public void R_SetExternalPtrProtected(Object x, Object prot) {
+    public int R_SetExternalPtrProtected(Object x, Object prot) {
         RExternalPtr p = guaranteeInstanceOf(x, RExternalPtr.class);
         p.setProt(prot);
+        return 0;
     }
 
     @Override
@@ -1352,8 +1377,8 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public int R_ReadConnection(int fd, byte[] buf) {
-
+    public int R_ReadConnection(int fd, Object bufObj) {
+        byte[] buf = (byte[]) bufObj;
         try (BaseRConnection fromIndex = RConnection.fromIndex(fd)) {
             Arrays.fill(buf, (byte) 0);
             return fromIndex.readBin(ByteBuffer.wrap(buf));
@@ -1363,7 +1388,8 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public int R_WriteConnection(int fd, byte[] buf) {
+    public int R_WriteConnection(int fd, Object bufObj) {
+        byte[] buf = (byte[]) bufObj;
         try (BaseRConnection fromIndex = RConnection.fromIndex(fd)) {
             Arrays.fill(buf, (byte) 0);
             final ByteBuffer wrapped = ByteBuffer.wrap(buf);
@@ -1405,12 +1431,12 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
 
     @Override
     public Object R_do_slot(Object o, Object name) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.R_do_slot).call(o, name);
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.R_do_slot).call(o, name);
     }
 
     @Override
     public Object R_do_slot_assign(Object o, Object name, Object value) {
-        return FFIUpCallRootNode.getCallTarget(RFFIUpCallMethod.R_do_slot_assign).call(o, name, value);
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.R_do_slot_assign).call(o, name, value);
     }
 
     @Override
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/LibPaths.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/LibPaths.java
similarity index 98%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/LibPaths.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/LibPaths.java
index 86b8d0a62c..bc6dbebba2 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/LibPaths.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/LibPaths.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi;
+package com.oracle.truffle.r.ffi.impl.common;
 
 import java.nio.file.FileSystems;
 import java.nio.file.Path;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/ParseResult.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/ParseResult.java
similarity index 93%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/ParseResult.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/ParseResult.java
index 1a5dc11242..58cf577ae8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/ParseResult.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/ParseResult.java
@@ -20,9 +20,9 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.common;
 
-import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
+import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;
 
 /**
  * Used in implementation of {@link UpCallsRFFI#R_ParseVector(Object, int, Object)}.
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUtils.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/RFFIUtils.java
similarity index 97%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUtils.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/RFFIUtils.java
index 46cb7f66b6..eba39a0222 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUtils.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/RFFIUtils.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.common;
 
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
@@ -28,13 +28,14 @@ import java.io.IOException;
 import java.nio.file.Path;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.ffi.impl.nodes.FFIUpCallRootNode;
+import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
-import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
 
 /**
  * Mostly support for tracing R FFI up/down calls. Currently tracing of the arguments to calls is
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/TracingUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/TracingUpCallsRFFIImpl.java
similarity index 89%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/TracingUpCallsRFFIImpl.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/TracingUpCallsRFFIImpl.java
index 2bf8a0d955..54dc54165a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/TracingUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/TracingUpCallsRFFIImpl.java
@@ -20,15 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.common;
 
+import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
-import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
 
 final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     // Checkstyle: stop method name check
@@ -106,9 +106,9 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void Rf_defineVar(Object symbolArg, Object value, Object envArg) {
+    public int Rf_defineVar(Object symbolArg, Object value, Object envArg) {
         RFFIUtils.traceUpCall("Rf_defineVar", symbolArg, value, envArg);
-        delegate.Rf_defineVar(symbolArg, value, envArg);
+        return delegate.Rf_defineVar(symbolArg, value, envArg);
     }
 
     @Override
@@ -148,9 +148,9 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void Rf_setAttrib(Object obj, Object name, Object val) {
+    public int Rf_setAttrib(Object obj, Object name, Object val) {
         RFFIUtils.traceUpCall("Rf_setAttrib", obj, name, val);
-        delegate.Rf_setAttrib(obj, name, val);
+        return delegate.Rf_setAttrib(obj, name, val);
     }
 
     @Override
@@ -196,21 +196,21 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void Rf_error(Object msg) {
+    public int Rf_error(Object msg) {
         RFFIUtils.traceUpCall("Rf_error", msg);
-        delegate.Rf_error(msg);
+        return delegate.Rf_error(msg);
     }
 
     @Override
-    public void Rf_warning(Object msg) {
+    public int Rf_warning(Object msg) {
         RFFIUtils.traceUpCall("Rf_warning", msg);
-        delegate.Rf_warning(msg);
+        return delegate.Rf_warning(msg);
     }
 
     @Override
-    public void Rf_warningcall(Object call, Object msg) {
+    public int Rf_warningcall(Object call, Object msg) {
         RFFIUtils.traceUpCall("Rf_warningcall", call, msg);
-        delegate.Rf_warningcall(call, msg);
+        return delegate.Rf_warningcall(call, msg);
     }
 
     @Override
@@ -250,15 +250,15 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void SET_STRING_ELT(Object x, int i, Object v) {
+    public int SET_STRING_ELT(Object x, int i, Object v) {
         RFFIUtils.traceUpCall("SET_STRING_ELT", x, i, v);
-        delegate.SET_STRING_ELT(x, i, v);
+        return delegate.SET_STRING_ELT(x, i, v);
     }
 
     @Override
-    public void SET_VECTOR_ELT(Object x, int i, Object v) {
+    public int SET_VECTOR_ELT(Object x, int i, Object v) {
         RFFIUtils.traceUpCall("SET_VECTOR_ELT", i, v);
-        delegate.SET_VECTOR_ELT(x, i, v);
+        return delegate.SET_VECTOR_ELT(x, i, v);
     }
 
     @Override
@@ -406,9 +406,9 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void SET_SYMVALUE(Object x, Object v) {
+    public int SET_SYMVALUE(Object x, Object v) {
         RFFIUtils.traceUpCall("SET_SYMVALUE", x, v);
-        delegate.SET_SYMVALUE(x, v);
+        return delegate.SET_SYMVALUE(x, v);
     }
 
     @Override
@@ -442,15 +442,15 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void Rf_gsetVar(Object symbol, Object value, Object rho) {
+    public int Rf_gsetVar(Object symbol, Object value, Object rho) {
         RFFIUtils.traceUpCall("Rf_gsetVar", symbol, value, rho);
-        delegate.Rf_gsetVar(symbol, value, rho);
+        return delegate.Rf_gsetVar(symbol, value, rho);
     }
 
     @Override
-    public void DUPLICATE_ATTRIB(Object to, Object from) {
+    public int DUPLICATE_ATTRIB(Object to, Object from) {
         RFFIUtils.traceUpCall("DUPLICATE_ATTRIB", to, from);
-        delegate.DUPLICATE_ATTRIB(to, from);
+        return delegate.DUPLICATE_ATTRIB(to, from);
     }
 
     @Override
@@ -460,15 +460,15 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void Rf_copyListMatrix(Object s, Object t, int byrow) {
+    public int Rf_copyListMatrix(Object s, Object t, int byrow) {
         RFFIUtils.traceUpCall("Rf_copyListMatrix", t, byrow);
-        delegate.Rf_copyListMatrix(s, t, byrow);
+        return delegate.Rf_copyListMatrix(s, t, byrow);
     }
 
     @Override
-    public void Rf_copyMatrix(Object s, Object t, int byrow) {
+    public int Rf_copyMatrix(Object s, Object t, int byrow) {
         RFFIUtils.traceUpCall("Rf_copyMatrix", t, byrow);
-        delegate.Rf_copyMatrix(s, t, byrow);
+        return delegate.Rf_copyMatrix(s, t, byrow);
     }
 
     @Override
@@ -490,9 +490,9 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void SET_RDEBUG(Object x, int v) {
+    public int SET_RDEBUG(Object x, int v) {
         RFFIUtils.traceUpCall("SET_RDEBUG", x, v);
-        delegate.SET_RDEBUG(x, v);
+        return delegate.SET_RDEBUG(x, v);
     }
 
     @Override
@@ -502,9 +502,9 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void SET_RSTEP(Object x, int v) {
+    public int SET_RSTEP(Object x, int v) {
         RFFIUtils.traceUpCall("SET_RSTEP", x, v);
-        delegate.SET_RSTEP(x, v);
+        return delegate.SET_RSTEP(x, v);
     }
 
     @Override
@@ -538,9 +538,9 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void R_CleanUp(int sa, int status, int runlast) {
+    public int R_CleanUp(int sa, int status, int runlast) {
         RFFIUtils.traceUpCall("R_Cleanup", sa, status, runlast);
-        delegate.R_CleanUp(sa, status, runlast);
+        return delegate.R_CleanUp(sa, status, runlast);
     }
 
     @Override
@@ -586,33 +586,33 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void SET_S4_OBJECT(Object x) {
+    public int SET_S4_OBJECT(Object x) {
         RFFIUtils.traceUpCall("setS4Object");
-        delegate.SET_S4_OBJECT(x);
+        return delegate.SET_S4_OBJECT(x);
     }
 
     @Override
-    public void UNSET_S4_OBJECT(Object x) {
+    public int UNSET_S4_OBJECT(Object x) {
         RFFIUtils.traceUpCall("unsetS4Object");
-        delegate.UNSET_S4_OBJECT(x);
+        return delegate.UNSET_S4_OBJECT(x);
     }
 
     @Override
-    public void Rprintf(Object message) {
+    public int Rprintf(Object message) {
         RFFIUtils.traceUpCall("Rprintf", message);
-        delegate.Rprintf(message);
+        return delegate.Rprintf(message);
     }
 
     @Override
-    public void GetRNGstate() {
+    public int GetRNGstate() {
         RFFIUtils.traceUpCall("GetRNGstate");
-        delegate.GetRNGstate();
+        return delegate.GetRNGstate();
     }
 
     @Override
-    public void PutRNGstate() {
+    public int PutRNGstate() {
         RFFIUtils.traceUpCall("PutRNGstate");
-        delegate.PutRNGstate();
+        return delegate.PutRNGstate();
     }
 
     @Override
@@ -706,21 +706,21 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public void R_SetExternalPtrAddr(Object x, long addr) {
+    public int R_SetExternalPtrAddr(Object x, long addr) {
         RFFIUtils.traceUpCall("R_SetExternalPtrAddr", x);
-        delegate.R_SetExternalPtrAddr(x, addr);
+        return delegate.R_SetExternalPtrAddr(x, addr);
     }
 
     @Override
-    public void R_SetExternalPtrTag(Object x, Object tag) {
+    public int R_SetExternalPtrTag(Object x, Object tag) {
         RFFIUtils.traceUpCall("R_SetExternalPtrTag", x);
-        delegate.R_SetExternalPtrTag(x, tag);
+        return delegate.R_SetExternalPtrTag(x, tag);
     }
 
     @Override
-    public void R_SetExternalPtrProtected(Object x, Object prot) {
+    public int R_SetExternalPtrProtected(Object x, Object prot) {
         RFFIUtils.traceUpCall("R_ExternalPtrProt", x);
-        delegate.R_SetExternalPtrProtected(x, prot);
+        return delegate.R_SetExternalPtrProtected(x, prot);
     }
 
     @Override
@@ -766,13 +766,13 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     @Override
-    public int R_ReadConnection(int fd, byte[] buf) {
+    public int R_ReadConnection(int fd, Object buf) {
         RFFIUtils.traceUpCall("R_ReadConnection", fd, buf);
         return delegate.R_ReadConnection(fd, buf);
     }
 
     @Override
-    public int R_WriteConnection(int fd, byte[] buf) {
+    public int R_WriteConnection(int fd, Object buf) {
         RFFIUtils.traceUpCall("R_WriteConnection", fd, buf);
         return delegate.R_WriteConnection(fd, buf);
     }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/package-info.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/package-info.java
new file mode 100644
index 0000000000..94d20325b8
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+/**
+ * Classes that are common to all FFI implementations.
+ */
+package com.oracle.truffle.r.ffi.impl.common;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/CharSXPWrapperMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/CharSXPWrapperMR.java
similarity index 89%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/CharSXPWrapperMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/CharSXPWrapperMR.java
index 4e5a73a607..71f11773d0 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/CharSXPWrapperMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/CharSXPWrapperMR.java
@@ -20,16 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.engine.TruffleRLanguage;
 import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
 
-@MessageResolution(receiverType = CharSXPWrapper.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = CharSXPWrapper.class)
 public class CharSXPWrapperMR {
     @CanResolve
     public abstract static class CharSXPWrapperCheck extends Node {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLDotSymbolMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/DLLDotSymbolMR.java
similarity index 89%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLDotSymbolMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/DLLDotSymbolMR.java
index 2a0374f7ec..6c29f5f505 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLDotSymbolMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/DLLDotSymbolMR.java
@@ -20,16 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.engine.TruffleRLanguage;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 
-@MessageResolution(receiverType = DLL.DotSymbol.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = DLL.DotSymbol.class)
 public class DLLDotSymbolMR {
     @CanResolve
     public abstract static class DotSymbolCheck extends Node {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLInfoMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/DLLInfoMR.java
similarity index 89%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLInfoMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/DLLInfoMR.java
index 1161da07d4..60657d6cd2 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/DLLInfoMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/DLLInfoMR.java
@@ -20,16 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.engine.TruffleRLanguage;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 
-@MessageResolution(receiverType = DLL.DLLInfo.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = DLL.DLLInfo.class)
 public class DLLInfoMR {
     @CanResolve
     public abstract static class DLLInfolCheck extends Node {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/FFI_RForeignAccessFactoryImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/FFI_RForeignAccessFactoryImpl.java
new file mode 100644
index 0000000000..d7c09261ed
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/FFI_RForeignAccessFactoryImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.interop;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+
+public class FFI_RForeignAccessFactoryImpl {
+    public static ForeignAccess getForeignAccess(RTruffleObject obj) {
+        if (obj instanceof DLL.DLLInfo) {
+            return DLLInfoMRForeign.ACCESS;
+        } else if (obj instanceof DLL.DotSymbol) {
+            return DLLDotSymbolMRForeign.ACCESS;
+        } else if (obj instanceof CharSXPWrapper) {
+            return CharSXPWrapperMRForeign.ACCESS;
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeCharArray.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArray.java
similarity index 82%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeCharArray.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArray.java
index e963bddd8e..cbba5538b3 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeCharArray.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArray.java
@@ -20,17 +20,23 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
+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 class NativeCharArray extends NativeUInt8Array {
+public final class NativeCharArray extends NativeUInt8Array {
 
     public NativeCharArray(byte[] bytes) {
         super(bytes, true);
     }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return NativeCharArrayMRForeign.ACCESS;
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeCharArrayMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArrayMR.java
similarity index 76%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeCharArrayMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArrayMR.java
index 63a819b589..7326307656 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeCharArrayMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeCharArrayMR.java
@@ -20,16 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 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.engine.TruffleRLanguage;
 
-@MessageResolution(receiverType = NativeCharArray.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = NativeCharArray.class)
 public class NativeCharArrayMR {
     @Resolve(message = "READ")
     public abstract static class NCAReadNode extends Node {
@@ -60,10 +59,10 @@ public class NativeCharArrayMR {
         }
     }
 
-    @Resolve(message = "UNBOX")
-    public abstract static class NCAUnboxNode extends Node {
-        protected long access(NativeCharArray receiver) {
-            return receiver.convertToNative();
+    @Resolve(message = "TO_NATIVE")
+    public abstract static class NCAToNativeNode extends Node {
+        protected Object access(NativeCharArray receiver) {
+            return new CharNativePointer(receiver);
         }
     }
 
@@ -74,4 +73,20 @@ public class NativeCharArrayMR {
             return receiver instanceof NativeCharArray;
         }
     }
+
+    private static final class CharNativePointer extends NativePointer {
+        private final NativeCharArray nativeCharArray;
+
+        private CharNativePointer(NativeCharArray object) {
+            super(object);
+            this.nativeCharArray = object;
+        }
+
+        @Override
+        protected long asPointerImpl() {
+            long result = nativeCharArray.convertToNative();
+            return result;
+        }
+
+    }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArray.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArray.java
new file mode 100644
index 0000000000..55d6e3e0d6
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArray.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * 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.interop;
+
+import static com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter.UNSAFE;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+
+import sun.misc.Unsafe;
+
+public final class NativeDoubleArray extends NativeNACheck implements RTruffleObject {
+    public final double[] value;
+    /**
+     * If the array escapes the Truffle world via {@link #convertToNative()}, this value will be
+     * non-zero and is used exclusively thereafter.
+     */
+    @CompilationFinal protected long nativeAddress;
+
+    public NativeDoubleArray(Object obj, double[] value) {
+        super(obj);
+        this.value = value;
+    }
+
+    public NativeDoubleArray(double[] value) {
+        this(null, value);
+    }
+
+    double read(int index) {
+        if (nativeAddress != 0) {
+            return UNSAFE.getDouble(nativeAddress + index * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+        } else {
+            return value[index];
+        }
+    }
+
+    void write(int index, double nv) {
+        if (nativeAddress != 0) {
+            UNSAFE.putDouble(nativeAddress + index * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, nv);
+        } else {
+            value[index] = nv;
+        }
+    }
+
+    long convertToNative() {
+        if (nativeAddress == 0) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            nativeAddress = UNSAFE.allocateMemory(value.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+            UNSAFE.copyMemory(value, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, null, nativeAddress, value.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+        }
+        return nativeAddress;
+    }
+
+    public double[] getValue() {
+        if (nativeAddress != 0) {
+            // copy back
+            UNSAFE.copyMemory(null, nativeAddress, value, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, value.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
+        }
+        return value;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return NativeDoubleArrayMRForeign.ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeDoubleArrayMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArrayMR.java
similarity index 68%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeDoubleArrayMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArrayMR.java
index 573bdb6100..556fd3763c 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeDoubleArrayMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeDoubleArrayMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,23 +20,22 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 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.engine.TruffleRLanguage;
 import com.oracle.truffle.r.runtime.RRuntime;
 
-@MessageResolution(receiverType = NativeDoubleArray.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = NativeDoubleArray.class)
 public class NativeDoubleArrayMR {
 
     @Resolve(message = "READ")
     public abstract static class NDAReadNode extends Node {
         protected double access(NativeDoubleArray receiver, int index) {
-            return receiver.value[index];
+            return receiver.read(index);
         }
     }
 
@@ -46,11 +45,33 @@ public class NativeDoubleArrayMR {
             if (value == RRuntime.DOUBLE_NA) {
                 receiver.setIncomplete();
             }
-            receiver.value[index] = value;
+            receiver.write(index, value);
             return value;
         }
     }
 
+    @Resolve(message = "IS_POINTER")
+    public abstract static class NDAIsPointer extends Node {
+
+        public Object access(@SuppressWarnings("unused") NativeDoubleArray receiver) {
+            return true;
+        }
+    }
+
+    @Resolve(message = "AS_POINTER")
+    public abstract static class NDAAsPointerNode extends Node {
+        protected long access(NativeDoubleArray receiver) {
+            return receiver.convertToNative();
+        }
+    }
+
+    @Resolve(message = "TO_NATIVE")
+    public abstract static class NDAToNativeNode extends Node {
+        protected Object access(@SuppressWarnings("unused") NativeDoubleArray receiver) {
+            return this;
+        }
+    }
+
     @CanResolve
     public abstract static class NDACheck extends Node {
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArray.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArray.java
new file mode 100644
index 0000000000..fd86d545e7
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArray.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * 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.interop;
+
+import static com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter.UNSAFE;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+
+import sun.misc.Unsafe;
+
+public final class NativeIntegerArray extends NativeNACheck implements RTruffleObject {
+    public final int[] value;
+    /**
+     * If the array escapes the Truffle world via {@link #convertToNative()}, this value will be
+     * non-zero and is used exclusively thereafter.
+     */
+    @CompilationFinal protected long nativeAddress;
+
+    public NativeIntegerArray(Object obj, int[] value) {
+        super(obj);
+        this.value = value;
+    }
+
+    public NativeIntegerArray(int[] value) {
+        this(null, value);
+    }
+
+    int read(int index) {
+        if (nativeAddress != 0) {
+            return UNSAFE.getInt(nativeAddress + index * Unsafe.ARRAY_INT_INDEX_SCALE);
+        } else {
+            return value[index];
+        }
+    }
+
+    void write(int index, int nv) {
+        if (nativeAddress != 0) {
+            UNSAFE.putInt(nativeAddress + index * Unsafe.ARRAY_INT_INDEX_SCALE, nv);
+        } else {
+            value[index] = nv;
+        }
+    }
+
+    long convertToNative() {
+        if (nativeAddress == 0) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            nativeAddress = UNSAFE.allocateMemory(value.length * Unsafe.ARRAY_INT_INDEX_SCALE);
+            UNSAFE.copyMemory(value, Unsafe.ARRAY_INT_BASE_OFFSET, null, nativeAddress, value.length * Unsafe.ARRAY_INT_INDEX_SCALE);
+        }
+        return nativeAddress;
+    }
+
+    public int[] getValue() {
+        if (nativeAddress != 0) {
+            // copy back
+            UNSAFE.copyMemory(null, nativeAddress, value, Unsafe.ARRAY_INT_BASE_OFFSET, value.length * Unsafe.ARRAY_INT_INDEX_SCALE);
+        }
+        return value;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return NativeIntegerArrayMRForeign.ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeIntegerArrayMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArrayMR.java
similarity index 70%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeIntegerArrayMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArrayMR.java
index ed06bb6f14..5449219679 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeIntegerArrayMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeIntegerArrayMR.java
@@ -20,23 +20,22 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 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.engine.TruffleRLanguage;
 import com.oracle.truffle.r.runtime.RRuntime;
 
-@MessageResolution(receiverType = NativeIntegerArray.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = NativeIntegerArray.class)
 public class NativeIntegerArrayMR {
 
     @Resolve(message = "READ")
     public abstract static class NIAReadNode extends Node {
         protected int access(NativeIntegerArray receiver, int index) {
-            return receiver.value[index];
+            return receiver.read(index);
         }
     }
 
@@ -46,11 +45,18 @@ public class NativeIntegerArrayMR {
             if (value == RRuntime.INT_NA) {
                 receiver.setIncomplete();
             }
-            receiver.value[index] = value;
+            receiver.write(index, value);
             return value;
         }
     }
 
+    @Resolve(message = "TO_NATIVE")
+    public abstract static class NIAToNativeNode extends Node {
+        protected Object access(NativeIntegerArray receiver) {
+            return new IntegerNativePointer(receiver);
+        }
+    }
+
     @CanResolve
     public abstract static class NIACheck extends Node {
 
@@ -58,4 +64,20 @@ public class NativeIntegerArrayMR {
             return receiver instanceof NativeIntegerArray;
         }
     }
+
+    private static final class IntegerNativePointer extends NativePointer {
+        private final NativeIntegerArray nativeIntegerArray;
+
+        private IntegerNativePointer(NativeIntegerArray object) {
+            super(object);
+            this.nativeIntegerArray = object;
+        }
+
+        @Override
+        protected long asPointerImpl() {
+            long result = nativeIntegerArray.convertToNative();
+            return result;
+        }
+
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeLogicalArray.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArray.java
similarity index 86%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeLogicalArray.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArray.java
index d8229921a1..0ea52d5c5f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeLogicalArray.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArray.java
@@ -20,9 +20,10 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 
@@ -30,7 +31,7 @@ import com.oracle.truffle.r.runtime.data.RTruffleObject;
  * Handles the requirement that the R FFI sees "logical" arrays as {@code int[]} but the actual
  * array in FastR is represented as {@code byte[]}.
  */
-public class NativeLogicalArray extends NativeNACheck implements RTruffleObject {
+public final class NativeLogicalArray extends NativeNACheck implements RTruffleObject {
     @CompilationFinal(dimensions = 1) public final byte[] data;
 
     public NativeLogicalArray(Object obj, byte[] value) {
@@ -52,4 +53,9 @@ public class NativeLogicalArray extends NativeNACheck implements RTruffleObject
         }
         data[index] = newVal;
     }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return NativeLogicalArrayMRForeign.ACCESS;
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeLogicalArrayMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArrayMR.java
similarity index 92%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeLogicalArrayMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArrayMR.java
index 63612d60f4..ceb96f5a3f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeLogicalArrayMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeLogicalArrayMR.java
@@ -20,16 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 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.engine.TruffleRLanguage;
 
-@MessageResolution(receiverType = NativeLogicalArray.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = NativeLogicalArray.class)
 public class NativeLogicalArrayMR {
     @Resolve(message = "READ")
     public abstract static class NLAReadNode extends Node {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeNACheck.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeNACheck.java
similarity index 97%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeNACheck.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeNACheck.java
index 5d05ca62d8..3648cf500b 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeNACheck.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeNACheck.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.r.runtime.data.RVector;
 
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointer.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointer.java
new file mode 100644
index 0000000000..46d4ce53d4
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointer.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * 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.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;
+
+/**
+ * Created when a {@link RTruffleObject} subclass has no meaningful native representation,
+ * nevertheless a {@link Message#TO_NATIVE} message is sent to it.
+ */
+public class NativePointer implements TruffleObject {
+
+    /**
+     * This is used when an {@link RNull} is stored in memory (LLVM).
+     */
+    private static final class NullNativePointer extends NativePointer {
+        private NullNativePointer() {
+            super(RNull.instance);
+        }
+
+        @Override
+        protected long asPointerImpl() {
+            return 0;
+        }
+    }
+
+    public static final NullNativePointer NULL_NATIVEPOINTER = new NullNativePointer();
+
+    private static Table[] table = new Table[16];
+    private static int tableHwm;
+
+    private static class Table {
+        private final RTruffleObject object;
+        private final long nativePointer;
+
+        Table(RTruffleObject object, long nativePointer) {
+            this.object = object;
+            this.nativePointer = nativePointer;
+        }
+    }
+
+    final RTruffleObject object;
+
+    public NativePointer(RTruffleObject object) {
+        this.object = object;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return NativePointerMRForeign.ACCESS;
+    }
+
+    public static boolean isInstance(TruffleObject obj) {
+        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;
+        for (int i = 0; i < tableHwm; i++) {
+            if (table[i].nativePointer == result) {
+                newPointer = false;
+                break;
+            }
+        }
+        if (newPointer) {
+            // System.out.printf("as_pointer: %x from %s\n", result, object.getClass().getName());
+            if (tableHwm >= table.length) {
+                Table[] newTable = new Table[table.length * 2];
+                System.arraycopy(table, 0, newTable, 0, table.length);
+                table = newTable;
+            }
+            table[tableHwm++] = new Table(object, result);
+        }
+        return result;
+    }
+
+    protected long asPointerImpl() {
+        return System.identityHashCode(object);
+    }
+
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java
new file mode 100644
index 0000000000..73721be813
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativePointerMR.java
@@ -0,0 +1,49 @@
+/*
+ * 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.interop;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
+import com.oracle.truffle.api.nodes.Node;
+
+@MessageResolution(receiverType = NativePointer.class)
+public class NativePointerMR {
+    @Resolve(message = "IS_POINTER")
+    public abstract static class AcceptIsPointer extends Node {
+        @SuppressWarnings("unused")
+        public Object access(VirtualFrame frame, NativePointer object) {
+            return true;
+        }
+    }
+
+    @Resolve(message = "AS_POINTER")
+    public abstract static class AcceptAsPointer extends Node {
+        @SuppressWarnings("unused")
+        public long access(VirtualFrame frame, NativePointer object) {
+            long result = object.asPointer();
+            return result;
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeRawArray.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArray.java
similarity index 80%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeRawArray.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArray.java
index dfd1e629cd..631188e69b 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeRawArray.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArray.java
@@ -20,11 +20,18 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
-public class NativeRawArray extends NativeUInt8Array {
+import com.oracle.truffle.api.interop.ForeignAccess;
+
+public final class NativeRawArray extends NativeUInt8Array {
 
     public NativeRawArray(byte[] bytes) {
         super(bytes, false);
     }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return NativeRawArrayMRForeign.ACCESS;
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeRawArrayMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArrayMR.java
similarity index 75%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeRawArrayMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArrayMR.java
index b694a9f56f..13d0a91adc 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeRawArrayMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeRawArrayMR.java
@@ -20,16 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import com.oracle.truffle.api.interop.CanResolve;
 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.engine.TruffleRLanguage;
 
-@MessageResolution(receiverType = NativeRawArray.class, language = TruffleRLanguage.class)
+@MessageResolution(receiverType = NativeRawArray.class)
 public class NativeRawArrayMR {
     @Resolve(message = "READ")
     public abstract static class NRAReadNode extends Node {
@@ -53,10 +52,10 @@ public class NativeRawArrayMR {
         }
     }
 
-    @Resolve(message = "UNBOX")
-    public abstract static class NRAUnboxNode extends Node {
-        protected long access(NativeRawArray receiver) {
-            return receiver.convertToNative();
+    @Resolve(message = "TO_NATIVE")
+    public abstract static class NRAToNativeNode extends Node {
+        protected Object access(NativeRawArray receiver) {
+            return new RawNativePointer(receiver);
         }
     }
 
@@ -67,4 +66,20 @@ public class NativeRawArrayMR {
             return receiver instanceof NativeRawArray;
         }
     }
+
+    private static final class RawNativePointer extends NativePointer {
+        private final NativeRawArray nativeRawArray;
+
+        private RawNativePointer(NativeRawArray object) {
+            super(object);
+            this.nativeRawArray = object;
+        }
+
+        @Override
+        protected long asPointerImpl() {
+            long result = nativeRawArray.convertToNative();
+            return result;
+        }
+
+    }
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeUInt8Array.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java
similarity index 96%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeUInt8Array.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java
index d8c54ac6e3..4ecff42f80 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeUInt8Array.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/NativeUInt8Array.java
@@ -20,9 +20,9 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
-import static com.oracle.truffle.r.engine.interop.UnsafeAdapter.UNSAFE;
+import static com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter.UNSAFE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -107,7 +107,7 @@ public abstract class NativeUInt8Array implements RTruffleObject {
         return nativeAddress;
     }
 
-    public byte[] getBytes() {
+    public byte[] getValue() {
         if (nativeAddress != 0) {
             // copy back
             UNSAFE.copyMemory(null, nativeAddress, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, bytes.length);
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/UnsafeAdapter.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/UnsafeAdapter.java
similarity index 97%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/UnsafeAdapter.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/UnsafeAdapter.java
index 7ef0df76f7..413e72ff60 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/UnsafeAdapter.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/UnsafeAdapter.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop;
 
 import java.lang.reflect.Field;
 
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.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResult.java
new file mode 100644
index 0000000000..2731df1cdf
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResult.java
@@ -0,0 +1,46 @@
+/*
+ * 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.interop.base;
+
+import java.util.ArrayList;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+
+public final class GlobResult implements RTruffleObject {
+    private final ArrayList<String> paths = new ArrayList<>();
+
+    public void addPath(String path) {
+        paths.add(path);
+    }
+
+    public ArrayList<String> getPaths() {
+        return paths;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return GlobResultMRForeign.ACCESS;
+    }
+
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/GlobUpCallImplMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResultMR.java
similarity index 68%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/GlobUpCallImplMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResultMR.java
index 3a614c0a7b..cf8b79b9db 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/GlobUpCallImplMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/GlobResultMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop.base;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
@@ -28,24 +28,23 @@ 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.engine.TruffleRLanguage;
-import com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_Base;
 
-@MessageResolution(receiverType = TruffleNFI_Base.TruffleNFI_GlobNode.GlobUpCallImpl.class, language = TruffleRLanguage.class)
-public class GlobUpCallImplMR {
+@MessageResolution(receiverType = GlobResult.class)
+public class GlobResultMR {
     @CanResolve
-    public abstract static class GlobUpCallImplCheck extends Node {
+    public abstract static class BaseGlobResultCallbackCheck extends Node {
 
         protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof TruffleNFI_Base.TruffleNFI_GlobNode.GlobUpCallImpl;
+            return receiver instanceof GlobResult;
         }
     }
 
     @Resolve(message = "EXECUTE")
-    public abstract static class GlobUpCallImplExecute extends Node {
-        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, TruffleNFI_Base.TruffleNFI_GlobNode.GlobUpCallImpl receiver, Object[] arguments) {
+    public abstract static class BaseGlobResultCallbackExecute extends Node {
+        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, GlobResult receiver, Object[] arguments) {
             receiver.addPath((String) arguments[0]);
             return receiver;
         }
     }
+
 }
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.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResult.java
new file mode 100644
index 0000000000..266efc5acd
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResult.java
@@ -0,0 +1,49 @@
+/*
+ * 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.interop.base;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+
+public final class ReadlinkResult implements RTruffleObject {
+    private String link;
+    private int errno;
+
+    public void setResult(String link, int errno) {
+        this.link = link;
+        this.errno = errno;
+    }
+
+    public String getLink() {
+        return link;
+    }
+
+    public int getErrno() {
+        return errno;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return ReadlinkResultMRForeign.ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/SetResultImplMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResultMR.java
similarity index 68%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/SetResultImplMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResultMR.java
index 69cf410359..f36a55442f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/SetResultImplMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/ReadlinkResultMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop.base;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
@@ -28,24 +28,23 @@ 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.engine.TruffleRLanguage;
-import com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_Base;
 
-@MessageResolution(receiverType = TruffleNFI_Base.TruffleNFI_ReadlinkNode.SetResultImpl.class, language = TruffleRLanguage.class)
-public class SetResultImplMR {
+@MessageResolution(receiverType = ReadlinkResult.class)
+public class ReadlinkResultMR {
     @CanResolve
-    public abstract static class SetResultImplCheck extends Node {
+    public abstract static class BaseReadlinkResultCallbackCheck extends Node {
 
         protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof TruffleNFI_Base.TruffleNFI_ReadlinkNode.SetResultImpl;
+            return receiver instanceof ReadlinkResult;
         }
     }
 
     @Resolve(message = "EXECUTE")
-    public abstract static class SetResultImplExecute extends Node {
-        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, TruffleNFI_Base.TruffleNFI_ReadlinkNode.SetResultImpl receiver, Object[] arguments) {
+    public abstract static class BaseReadlinkResultCallbackExecute extends Node {
+        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, ReadlinkResult receiver, Object[] arguments) {
             receiver.setResult((String) arguments[0], (int) arguments[1]);
             return receiver;
         }
     }
+
 }
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.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResult.java
new file mode 100644
index 0000000000..1884ad0bfa
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResult.java
@@ -0,0 +1,49 @@
+/*
+ * 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.interop.base;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+
+public final class StrtolResult implements RTruffleObject {
+    private long result;
+    private int errno;
+
+    public void setResult(long result, int errno) {
+        this.result = result;
+        this.errno = errno;
+    }
+
+    public long getResult() {
+        return result;
+    }
+
+    public int getErrno() {
+        return errno;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return StrtolResultMRForeign.ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/UnameUpCallImplMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResultMR.java
similarity index 63%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/UnameUpCallImplMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResultMR.java
index 6382443b4f..7ca8fbd422 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/UnameUpCallImplMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/StrtolResultMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop.base;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
@@ -28,23 +28,21 @@ 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.engine.TruffleRLanguage;
-import com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_Base;
 
-@MessageResolution(receiverType = TruffleNFI_Base.TruffleNFI_UnameNode.UnameUpCallImpl.class, language = TruffleRLanguage.class)
-public class UnameUpCallImplMR {
+@MessageResolution(receiverType = StrtolResult.class)
+public class StrtolResultMR {
     @CanResolve
-    public abstract static class UnameUpCallImplCheck extends Node {
+    public abstract static class BaseStrtolResultCallbackCheck extends Node {
 
         protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof TruffleNFI_Base.TruffleNFI_UnameNode.UnameUpCallImpl;
+            return receiver instanceof StrtolResult;
         }
     }
 
     @Resolve(message = "EXECUTE")
-    public abstract static class UnameUpCallImplExecute extends Node {
-        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, TruffleNFI_Base.TruffleNFI_UnameNode.UnameUpCallImpl receiver, Object[] arguments) {
-            receiver.unameUpCall((String) arguments[0], (String) arguments[1], (String) arguments[2], (String) arguments[3], (String) arguments[4]);
+    public abstract static class BaseStrtolResultCallbackExecute extends Node {
+        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, StrtolResult receiver, Object[] arguments) {
+            receiver.setResult((long) arguments[0], (int) arguments[1]);
             return receiver;
         }
     }
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.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResult.java
new file mode 100644
index 0000000000..097610794d
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResult.java
@@ -0,0 +1,73 @@
+/*
+ * 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.interop.base;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
+
+public final class UnameResult implements BaseRFFI.UtsName, RTruffleObject {
+    private String sysname;
+    private String release;
+    private String version;
+    private String machine;
+    private String nodename;
+
+    public void setResult(String sysnameA, String releaseA, String versionA, String machineA, String nodenameA) {
+        sysname = sysnameA;
+        release = releaseA;
+        version = versionA;
+        machine = machineA;
+        nodename = nodenameA;
+    }
+
+    @Override
+    public String sysname() {
+        return sysname;
+    }
+
+    @Override
+    public String release() {
+        return release;
+    }
+
+    @Override
+    public String version() {
+        return version;
+    }
+
+    @Override
+    public String machine() {
+        return machine;
+    }
+
+    @Override
+    public String nodename() {
+        return nodename;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return UnameResultMRForeign.ACCESS;
+    }
+}
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.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResultMR.java
new file mode 100644
index 0000000000..4ef12020e4
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/base/UnameResultMR.java
@@ -0,0 +1,50 @@
+/*
+ * 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.interop.base;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.CanResolve;
+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;
+
+@MessageResolution(receiverType = UnameResult.class)
+public class UnameResultMR {
+    @CanResolve
+    public abstract static class BaseUnameResultCallbackCheck extends Node {
+
+        protected static boolean test(TruffleObject receiver) {
+            return receiver instanceof UnameResult;
+        }
+    }
+
+    @Resolve(message = "EXECUTE")
+    public abstract static class BaseUnameResultCallbackExecute extends Node {
+        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, UnameResult receiver, Object[] arguments) {
+            receiver.setResult((String) arguments[0], (String) arguments[1], (String) arguments[2], (String) arguments[3], (String) arguments[4]);
+            return receiver;
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/package-info.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/package-info.java
new file mode 100644
index 0000000000..fbc35fd5b8
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/package-info.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+/**
+ * A collection of types and {@link com.oracle.truffle.api.interop.MessageResolution} classes that
+ * support the implementations. Not all are used in every implementation, particularly JNI since
+ * that does not use Truffle interop.
+ *
+ * See {@link com.oracle.truffle.r.ffi.impl.interop.base} and
+ * {@link com.oracle.truffle.r.ffi.impl.interop.pcre} for similar classes specific to the
+ * {@link com.oracle.truffle.r.runtime.ffi.BaseRFFI} and
+ * {@link com.oracle.truffle.r.runtime.ffi.PCRERFFI} interfaces.
+ */
+package com.oracle.truffle.r.ffi.impl.interop;
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.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResult.java
new file mode 100644
index 0000000000..e54b5787ad
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResult.java
@@ -0,0 +1,47 @@
+/*
+ * 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.interop.pcre;
+
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+
+public final class CaptureNamesResult implements RTruffleObject {
+    private final String[] captureNames;
+
+    public CaptureNamesResult(int captureCount) {
+        this.captureNames = new String[captureCount];
+    }
+
+    public void addName(int i, String name) {
+        captureNames[i] = name;
+    }
+
+    public String[] getCaptureNames() {
+        return captureNames;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return CaptureNamesResultMRForeign.ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/CaptureNamesImplMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResultMR.java
similarity index 75%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/CaptureNamesImplMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResultMR.java
index 5063771196..6462f2ef7a 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/CaptureNamesImplMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CaptureNamesResultMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop.pcre;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
@@ -31,23 +31,21 @@ 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.engine.TruffleRLanguage;
-import com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_PCRE;
 import com.oracle.truffle.r.runtime.RInternalError;
 
-@MessageResolution(receiverType = TruffleNFI_PCRE.TruffleNFI_GetCaptureNamesNode.CaptureNamesImpl.class, language = TruffleRLanguage.class)
-public class CaptureNamesImplMR {
+@MessageResolution(receiverType = CaptureNamesResult.class)
+public class CaptureNamesResultMR {
     @CanResolve
-    public abstract static class CaptureNamesImplCheck extends Node {
+    public abstract static class CaptureNamesCallbackCheck extends Node {
 
         protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof TruffleNFI_PCRE.TruffleNFI_GetCaptureNamesNode.CaptureNamesImpl;
+            return receiver instanceof CaptureNamesResult;
         }
     }
 
     @Resolve(message = "EXECUTE")
-    public abstract static class CaptureNamesImplExecute extends Node {
-        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, TruffleNFI_PCRE.TruffleNFI_GetCaptureNamesNode.CaptureNamesImpl receiver, Object[] arguments) {
+    public abstract static class CaptureNamesCallbackExecute extends Node {
+        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, CaptureNamesResult receiver, Object[] arguments) {
             try {
                 Object arg1 = arguments[1];
                 if (arg1 instanceof TruffleObject) {
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.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResult.java
new file mode 100644
index 0000000000..946d2a8b8d
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResult.java
@@ -0,0 +1,44 @@
+/*
+ * 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.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;
+
+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);
+    }
+
+    public PCRERFFI.Result getResult() {
+        return result;
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return CompileResultMRForeign.ACCESS;
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/MakeResultImplMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResultMR.java
similarity index 76%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/MakeResultImplMR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResultMR.java
index 34c97f9c38..f519b18f39 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/MakeResultImplMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pcre/CompileResultMR.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.impl.interop.pcre;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.interop.CanResolve;
@@ -31,23 +31,21 @@ 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.engine.TruffleRLanguage;
-import com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_PCRE;
 import com.oracle.truffle.r.runtime.RInternalError;
 
-@MessageResolution(receiverType = TruffleNFI_PCRE.TruffleNFI_CompileNode.MakeResultImpl.class, language = TruffleRLanguage.class)
-public class MakeResultImplMR {
+@MessageResolution(receiverType = CompileResult.class)
+public class CompileResultMR {
     @CanResolve
-    public abstract static class MakeResultImplCheck extends Node {
+    public abstract static class ResultCallbackCheck extends Node {
 
         protected static boolean test(TruffleObject receiver) {
-            return receiver instanceof TruffleNFI_PCRE.TruffleNFI_CompileNode.MakeResultImpl;
+            return receiver instanceof CompileResult;
         }
     }
 
     @Resolve(message = "EXECUTE")
-    public abstract static class MakeResultImplExecute extends Node {
-        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, TruffleNFI_PCRE.TruffleNFI_CompileNode.MakeResultImpl receiver, Object[] arguments) {
+    public abstract static class ResultCallbackExecute extends Node {
+        protected Object access(@SuppressWarnings("unused") VirtualFrame frame, CompileResult receiver, Object[] arguments) {
             try {
                 Object arg1 = arguments[1];
                 if (arg1 instanceof TruffleObject) {
@@ -57,7 +55,7 @@ public class MakeResultImplMR {
                         arg1 = ForeignAccess.sendUnbox(Message.UNBOX.createNode(), (TruffleObject) arg1);
                     }
                 }
-                receiver.makeresult((long) arguments[0], (String) arg1, (int) arguments[2]);
+                receiver.set((long) arguments[0], (String) arg1, (int) arguments[2]);
                 return receiver;
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNIUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNIUpCallsRFFIImpl.java
similarity index 93%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNIUpCallsRFFIImpl.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNIUpCallsRFFIImpl.java
index 233e055e8e..ada7b6c5f6 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNIUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNIUpCallsRFFIImpl.java
@@ -20,11 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.guaranteeInstanceOf;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.guaranteeInstanceOf;
 
-import com.oracle.truffle.r.nodes.ffi.JavaUpCallsRFFIImpl;
+import com.oracle.truffle.r.ffi.impl.common.JavaUpCallsRFFIImpl;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RVector;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Base.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Base.java
similarity index 99%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Base.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Base.java
index dac6e7af76..a854b68fd1 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Base.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Base.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import java.io.IOException;
 import java.util.ArrayList;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java
similarity index 91%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_C.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java
index 93311fa0b1..2739f2ab28 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_C.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_C.java
@@ -20,10 +20,10 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceDownCall;
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceEnabled;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCall;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceEnabled;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.ffi.CRFFI;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Call.java
similarity index 95%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Call.java
index 115c562a7d..f8f77e9bc1 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Call.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Call.java
@@ -20,19 +20,19 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceDownCall;
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceDownCallReturn;
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceEnabled;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCall;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCallReturn;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceEnabled;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.r.nodes.ffi.RFFIUtils;
+import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
+import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 import com.oracle.truffle.r.runtime.ffi.RFFIVariables;
-import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
 
 /**
  * The only variety in the signatures for {@code .Call} is the number of arguments. GnuR supports a
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_DLL.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_DLL.java
similarity index 98%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_DLL.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_DLL.java
index 697fe085b4..06ddc4f689 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_DLL.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_DLL.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Glob.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Glob.java
similarity index 96%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Glob.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Glob.java
index e39000a549..a3e344944b 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Glob.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Glob.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import java.util.ArrayList;
 
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Lapack.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Lapack.java
similarity index 99%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Lapack.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Lapack.java
index 25c8ad506d..7317964c5a 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Lapack.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Lapack.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Misc.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Misc.java
similarity index 87%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Misc.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Misc.java
index 1e4ae3a4ac..19c651622a 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Misc.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Misc.java
@@ -20,11 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceDownCall;
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceDownCallReturn;
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceEnabled;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCall;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCallReturn;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceEnabled;
 
 import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_PCRE.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PCRE.java
similarity index 98%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_PCRE.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PCRE.java
index 069de23ca3..b43da7a604 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_PCRE.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PCRE.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.r.runtime.RError;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_PkgInit.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PkgInit.java
similarity index 98%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_PkgInit.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PkgInit.java
index 2bd8a296ee..f35a117c29 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_PkgInit.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_PkgInit.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_RAppl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RAppl.java
similarity index 98%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_RAppl.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RAppl.java
index bf64d0c96d..c72cd68f6a 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_RAppl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RAppl.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_REmbed.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_REmbed.java
similarity index 94%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_REmbed.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_REmbed.java
index bf44eeafa0..2837227abd 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_REmbed.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_REmbed.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.r.runtime.ffi.REmbedRFFI;
 
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_RFFIFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RFFIFactory.java
similarity index 97%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_RFFIFactory.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RFFIFactory.java
index 0ec293f8b1..b107f68525 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_RFFIFactory.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_RFFIFactory.java
@@ -20,10 +20,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.r.ffi.impl.common.Generic_Tools;
+import com.oracle.truffle.r.ffi.impl.common.LibPaths;
 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;
@@ -32,7 +34,6 @@ import com.oracle.truffle.r.runtime.ffi.CallRFFI;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
 import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
-import com.oracle.truffle.r.runtime.ffi.LibPaths;
 import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
@@ -43,7 +44,6 @@ import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
 import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
 import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
 import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
-import com.oracle.truffle.r.runtime.ffi.generic.Generic_Tools;
 
 /**
  * JNI-based factory. The majority of the FFI instances are instantiated on demand.
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Stats.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Stats.java
similarity index 93%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Stats.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Stats.java
index 49ab145984..5db4dc87d7 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Stats.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Stats.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -35,7 +35,7 @@ public class JNI_Stats implements StatsRFFI {
 
     public static class JNI_WorkNode extends WorkNode {
         private static final String FFT_WORK = "fft_work";
-        @Child DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child private DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
         private SymbolHandle fftWorkAddress;
 
         @Override
@@ -50,7 +50,7 @@ public class JNI_Stats implements StatsRFFI {
 
     public static class JNI_FactorNode extends FactorNode {
         private static final String FFT_FACTOR = "fft_factor";
-        @Child DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child private DLLRFFI.DLSymNode dlSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
         private SymbolHandle fftFactorAddress;
 
         @Override
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_UserRng.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UserRng.java
similarity index 98%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_UserRng.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UserRng.java
index 038ddc5ae7..356f7cebbb 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_UserRng.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UserRng.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_UtsName.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UtsName.java
similarity index 94%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_UtsName.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UtsName.java
index 51151f9636..fbdd602e97 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_UtsName.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_UtsName.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.r.runtime.ffi.BaseRFFI.UtsName;
 
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Zip.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Zip.java
similarity index 98%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Zip.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Zip.java
index 7c93d004be..6afb225e5c 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jni/JNI_Zip.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/JNI_Zip.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.jni;
+package com.oracle.truffle.r.ffi.impl.jni;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/package-info.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/package-info.java
new file mode 100644
index 0000000000..841d7f3c65
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/jni/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+/**
+ * The JNI implementation of the {@link com.oracle.truffle.r.runtime.ffi.RFFI} interfaces.
+ */
+package com.oracle.truffle.r.ffi.impl.jni;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVMFunction.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVMFunction.java
new file mode 100644
index 0000000000..fcf8853952
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVMFunction.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.ffi.impl.llvm;
+
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.nodes.Node;
+
+/**
+ * Enumerates all the C functions that are internal to the implementation and called via Truffle
+ * LLVM. All of the functions are called indirectly via a wrapper, typically to enable a callback
+ * with a complex result or, in the case of Fortran, to handle call by reference conveniently, or to
+ * just have LLVM handle the underlying native call. The wrapper functions names are all of the form
+ * {@code call_xxx_function}, where {@code xxx} is the subsystem.
+ *
+ */
+public enum LLVMFunction {
+    // base
+    getpid(0, "call_base_"),
+    getwd(2, "call_base_"),
+    setwd(1, "call_base_"),
+    mkdir(2, "call_base_"),
+    readlink(2, "call_base_"),
+    mkdtemp(1, "call_base_"),
+    chmod(2, "call_base_"),
+    strtol(2, "call_base_"),
+    uname(0, "call_base_"),
+    glob(2, "call_base_"),
+    // PCRE
+    maketables(0, "call_pcre_"),
+    compile(3, "call_pcre_"),
+    getcapturecount(0, "call_pcre_"),
+    getcapturenames(2, "call_pcre_"),
+    study(2, "call_pcre_"),
+    exec(8, "call_pcre_"),
+    // RAppl
+    dqrdc2(9, "call_appl_"),
+    dqrcf(8, "call_appl_"),
+    dqrls(13, "call_appl_"),
+    // zip
+    compress(4, "call_zip_"),
+    uncompress(4, "call_zip_"),
+    // lapack
+    ilaver(1, "call_lapack_"),
+    dgeev(13, "call_lapack_"),
+    dgeqp3(8, "call_lapack_"),
+    dormq(12, "call_lapack_"),
+    dtrtrs(9, "call_lapack_"),
+    dgetrf(5, "call_lapack_"),
+    dpotrf(4, "call_lapack_"),
+    dpotri(4, "call_lapack_"),
+    dpstrf(9, "call_lapack_"),
+    dgesv(7, "call_lapack_"),
+    dlange(6, "call_lapack_"),
+    dgecon(8, "call_lapack_"),
+    dsyevr(20, "call_lapack_");
+
+    private final int argCount;
+    final String callName;
+
+    LLVMFunction(int argCount, String prefix) {
+        this.argCount = argCount;
+        callName = prefix + name();
+    }
+
+    Node createMessage() {
+        CompilerAsserts.neverPartOfCompilation();
+        return Message.createExecute(argCount).createNode();
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/truffle/LLVM_IR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVM_IR.java
similarity index 98%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/truffle/LLVM_IR.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVM_IR.java
index 89f9b2f3aa..03743e3293 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/truffle/LLVM_IR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/LLVM_IR.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.truffle;
+package com.oracle.truffle.r.ffi.impl.llvm;
 
 import java.io.IOException;
 import java.util.Base64;
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/truffle/MachOAccess.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/MachOAccess.java
similarity index 99%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/truffle/MachOAccess.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/MachOAccess.java
index cbc7fe7691..f8568635e5 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/truffle/MachOAccess.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/MachOAccess.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.truffle;
+package com.oracle.truffle.r.ffi.impl.llvm;
 
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -151,6 +151,7 @@ final class MachOAccess implements AutoCloseable {
         LC_LOAD_DYLIB(0xc),
         LC_ID_DYLIB(0xd),
         LC_SUB_FRAMEWORK(0x12),
+        LC_LOAD_WEAK_DYLIB(0x18),
         LC_SEGMENT_64(0x19),
         LC_UUID(0x1b),
         LC_RPATH(0x1C),
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
new file mode 100644
index 0000000000..b7bba57a88
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Base.java
@@ -0,0 +1,378 @@
+/*
+ * 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 java.io.IOException;
+import java.util.ArrayList;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.InteropException;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.interop.NativeCharArray;
+import com.oracle.truffle.r.ffi.impl.interop.base.GlobResult;
+import com.oracle.truffle.r.ffi.impl.interop.base.ReadlinkResult;
+import com.oracle.truffle.r.ffi.impl.interop.base.StrtolResult;
+import com.oracle.truffle.r.ffi.impl.interop.base.UnameResult;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.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.DLL.SymbolHandle;
+import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+
+public class TruffleLLVM_Base implements BaseRFFI {
+    private static TruffleObject truffleBaseTruffleObject;
+
+    TruffleLLVM_Base() {
+        truffleBaseTruffleObject = JavaInterop.asTruffleObject(this);
+    }
+
+    static class ContextStateImpl implements RContext.ContextState {
+        @Override
+        public ContextState initialize(RContext context) {
+            RFFIFactory.getRFFI().getBaseRFFI();
+            context.addExportedSymbol("_fastr_rffi_base", truffleBaseTruffleObject);
+            return this;
+        }
+
+    }
+
+    public static class TruffleLLVM_GetpidNode extends GetpidNode {
+        @Child private Node message = LLVMFunction.getpid.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute() {
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.getpid.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject());
+                return result;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleLLVM_GetwdNode extends GetwdNode {
+        @Child private Node message = LLVMFunction.getwd.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public String execute() {
+            byte[] buf = new byte[4096];
+            NativeCharArray nativeBuf = new NativeCharArray(buf);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.getwd.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativeBuf, buf.length);
+                if (result == 0) {
+                    return null;
+                } else {
+                    byte[] mbuf = nativeBuf.getValue();
+                    int i = 0;
+                    while (mbuf[i] != 0 && i < mbuf.length) {
+                        i++;
+                    }
+                    return new String(mbuf, 0, i);
+                }
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleLLVM_SetwdNode extends SetwdNode {
+        @Child private Node message = LLVMFunction.setwd.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(String dir) {
+            NativeCharArray nativeBuf = new NativeCharArray(dir.getBytes());
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.setwd.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativeBuf);
+                return result;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleLLVM_MkdirNode extends MkdirNode {
+        @Child private Node message = LLVMFunction.mkdir.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public void execute(String dir, int mode) throws IOException {
+            NativeCharArray nativeBuf = new NativeCharArray(dir.getBytes());
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.mkdir.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativeBuf, mode);
+                if (result != 0) {
+                    throw new IOException("mkdir " + dir + " failed");
+                }
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public void setReadlinkResult(ReadlinkResult baseReadlinkResultCallback, NativeCharArray nativePath, int errno) {
+        String path = null;
+        if (nativePath != null) {
+            path = new String(nativePath.getValue());
+        }
+        baseReadlinkResultCallback.setResult(path, errno);
+    }
+
+    public static class TruffleLLVM_ReadlinkNode extends ReadlinkNode {
+        private static final int EINVAL = 22;
+        @Child private Node message = LLVMFunction.readlink.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public String execute(String path) throws IOException {
+            NativeCharArray nativePath = new NativeCharArray(path.getBytes());
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.readlink.callName, null);
+                }
+                ReadlinkResult callback = new ReadlinkResult();
+                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), callback, nativePath);
+                String link = callback.getLink();
+                if (link == null) {
+                    if (callback.getErrno() == EINVAL) {
+                        return path;
+                    } else {
+                        // some other error
+                        throw new IOException("readlink failed: " + callback.getErrno());
+                    }
+                }
+                return link;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleLLVM_MkdtempNode extends MkdtempNode {
+        @Child private Node message = LLVMFunction.mkdtemp.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @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);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.mkdtemp.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativeZtbytes);
+                if (result == 0) {
+                    return null;
+                } else {
+                    byte[] mztBytes = nativeZtbytes.getValue();
+                    String path = new String(mztBytes, 0, bytes.length);
+                    return path;
+                }
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleLLVM_ChmodNode extends ChmodNode {
+        @Child private Node message = LLVMFunction.chmod.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(String path, int mode) {
+            NativeCharArray nativePath = new NativeCharArray(path.getBytes());
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.chmod.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), nativePath, mode);
+                return result;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public void setStrtolResult(StrtolResult callback, long value, int errno) {
+        callback.setResult(value, errno);
+    }
+
+    public static class TruffleLLVM_StrolNode extends StrolNode {
+        @Child private Node message = LLVMFunction.strtol.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public long execute(String s, int base) throws IllegalArgumentException {
+            NativeCharArray nativeString = new NativeCharArray(s.getBytes());
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.strtol.callName, null);
+                }
+                StrtolResult callback = new StrtolResult();
+                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), callback, nativeString, base);
+                if (callback.getErrno() != 0) {
+                    throw new IllegalArgumentException("strtol failure");
+                } else {
+                    return callback.getResult();
+                }
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public void setUnameResult(UnameResult baseUnameResultCallback, NativeCharArray sysname, NativeCharArray release,
+                    NativeCharArray version, NativeCharArray machine, NativeCharArray nodename) {
+        baseUnameResultCallback.setResult(new String(sysname.getValue()), new String(release.getValue()), new String(version.getValue()),
+                        new String(machine.getValue()), new String(nodename.getValue()));
+    }
+
+    public static class TruffleLLVM_UnameNode extends UnameNode {
+        @Child private Node message = LLVMFunction.uname.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public UtsName execute() {
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.uname.callName, null);
+                }
+                UnameResult baseUnameResultCallback = new UnameResult();
+                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), baseUnameResultCallback);
+                return baseUnameResultCallback;
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            }
+        }
+    }
+
+    public void setGlobResult(GlobResult baseGlobResultCallback, NativeCharArray path) {
+        baseGlobResultCallback.addPath(new String(path.getValue()));
+    }
+
+    public static class TruffleLLVM_GlobNode extends GlobNode {
+        @Child private Node message = LLVMFunction.glob.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public ArrayList<String> glob(String pattern) {
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.glob.callName, null);
+                }
+                NativeCharArray nativePattern = new NativeCharArray(pattern.getBytes());
+                GlobResult baseGlobResultCallback = new GlobResult();
+                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), baseGlobResultCallback, nativePattern);
+                return baseGlobResultCallback.getPaths();
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            }
+        }
+    }
+
+    @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 StrolNode createStrolNode() {
+        return new TruffleLLVM_StrolNode();
+    }
+
+    @Override
+    public UnameNode createUnameNode() {
+        return new TruffleLLVM_UnameNode();
+    }
+
+    @Override
+    public GlobNode createGlobNode() {
+        return new TruffleLLVM_GlobNode();
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java
similarity index 68%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_C.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java
index 89fb551c08..8ec43a990d 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_C.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_C.java
@@ -20,35 +20,40 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.llvm;
+package com.oracle.truffle.r.ffi.impl.llvm;
 
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.engine.interop.NativeDoubleArray;
-import com.oracle.truffle.r.engine.interop.NativeIntegerArray;
-import com.oracle.truffle.r.engine.interop.NativeRawArray;
+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.jni.JNI_C.JNI_InvokeCNode;
 
 class TruffleLLVM_C implements CRFFI {
-    private static class TruffleLLVM_InvokeCNode extends JNI_InvokeCNode {
+    private static class TruffleLLVM_InvokeCNode extends InvokeCNode {
+
+        @Child private Node messageNode;
+        private int numArgs;
 
         @Override
         public synchronized void execute(NativeCallInfo nativeCallInfo, Object[] args) {
-            if (nativeCallInfo.address.value instanceof Long) {
-                super.execute(nativeCallInfo, args);
-            } else {
-                TruffleLLVM_DLL.ensureParsed(nativeCallInfo);
-                Object[] wargs = wrap(args);
-                try {
-                    Node messageNode = Message.createExecute(0).createNode();
-                    ForeignAccess.sendExecute(messageNode, nativeCallInfo.address.asTruffleObject(), wargs);
-                } catch (Throwable t) {
-                    throw RInternalError.shouldNotReachHere(t);
+            TruffleLLVM_DLL.ensureParsed(nativeCallInfo);
+            Object[] wargs = wrap(args);
+            try {
+                if (messageNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    // TODO: we assume that the number of args doesn't change, is that correct?
+                    messageNode = Message.createExecute(args.length).createNode();
+                    numArgs = args.length;
                 }
+                assert numArgs == args.length;
+                ForeignAccess.sendExecute(messageNode, nativeCallInfo.address.asTruffleObject(), wargs);
+            } catch (Throwable t) {
+                throw RInternalError.shouldNotReachHere(t);
             }
         }
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_CAccess.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_CAccess.java
similarity index 95%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_CAccess.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_CAccess.java
index 16f57a750e..50e1d83d90 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_CAccess.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_CAccess.java
@@ -20,11 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.llvm;
+package com.oracle.truffle.r.ffi.impl.llvm;
 
+import com.oracle.truffle.r.ffi.impl.jni.JNI_DLL;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
-import com.oracle.truffle.r.runtime.ffi.jni.JNI_DLL;
 import com.oracle.truffle.r.runtime.rng.user.UserRNG;
 
 /**
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Call.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java
similarity index 53%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Call.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java
index 9b76276804..483d43d2c3 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Call.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java
@@ -20,45 +20,49 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.llvm;
+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.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.engine.interop.ffi.llvm.TruffleLLVM_CallFactory.InvokeTruffleNodeGen;
-import com.oracle.truffle.r.engine.interop.ffi.llvm.TruffleLLVM_CallFactory.SplitTruffleCallRFFINodeGen;
+import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
+import com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_CallFactory.TruffleLLVM_InvokeCallNodeGen;
+import com.oracle.truffle.r.ffi.impl.upcalls.Callbacks;
+import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextState;
-import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.RFFIVariables;
-import com.oracle.truffle.r.runtime.ffi.jni.JNI_Call;
 
-class TruffleLLVM_Call implements CallRFFI {
+final class TruffleLLVM_Call implements CallRFFI {
     private static TruffleLLVM_Call truffleCall;
     private static TruffleObject truffleCallTruffleObject;
     private static TruffleObject truffleCallHelper;
+    private static TruffleObject truffleCallHelperImpl;
+    private static UpCallsRFFI upCallsRFFI;
 
-    @SuppressWarnings("unused")
     TruffleLLVM_Call() {
-        new JNI_Call();
         truffleCall = this;
         truffleCallTruffleObject = JavaInterop.asTruffleObject(truffleCall);
-        truffleCallHelper = TruffleLLVM_UpCallsRFFIImpl.initialize();
+        TruffleLLVM_UpCallsRFFIImpl upCallsRFFIImpl = new TruffleLLVM_UpCallsRFFIImpl();
+        truffleCallHelperImpl = JavaInterop.asTruffleObject(upCallsRFFIImpl);
+        upCallsRFFI = RFFIUtils.initialize(upCallsRFFIImpl);
+        truffleCallHelper = JavaInterop.asTruffleObject(upCallsRFFIImpl);
     }
 
     static class ContextStateImpl implements RContext.ContextState {
         private RContext context;
-        private boolean initVariablesDone;
+        private boolean initDone;
 
         @Override
         public ContextState initialize(RContext contextA) {
@@ -66,21 +70,21 @@ class TruffleLLVM_Call implements CallRFFI {
             RFFIFactory.getRFFI().getCallRFFI();
             context.addExportedSymbol("_fastr_rffi_call", truffleCallTruffleObject);
             context.addExportedSymbol("_fastr_rffi_callhelper", truffleCallHelper);
+            context.addExportedSymbol("_fastr_rffi_callhelper_impl", truffleCallHelperImpl);
+            if (!initDone) {
+                initVariables(context);
+                initCallbacks(context, upCallsRFFI);
+                initDone = true;
+            }
             return this;
         }
 
-        @Override
-        public void beforeDestroy(RContext contextA) {
-        }
-    }
-
-    static ContextStateImpl newContextState() {
-        return new ContextStateImpl();
     }
 
     private enum INIT_VAR_FUN {
         DOUBLE,
-        INT;
+        INT,
+        OBJ;
 
         private final String funName;
         private SymbolHandle symbolHandle;
@@ -98,9 +102,8 @@ class TruffleLLVM_Call implements CallRFFI {
         }
         Node executeNode = Message.createExecute(2).createNode();
         RFFIVariables[] variables = RFFIVariables.initialize();
+        boolean isNullSetting = RContext.getRForeignAccessFactory().setIsNull(false);
         for (int i = 0; i < variables.length; i++) {
-            // The TruffleObject instances are not passed down as
-            // they cannot be stored in memory at the moment
             RFFIVariables var = variables[i];
             Object value = var.getValue();
             if (value == null) {
@@ -111,124 +114,74 @@ class TruffleLLVM_Call implements CallRFFI {
                     ForeignAccess.sendExecute(executeNode, INIT_VAR_FUN.DOUBLE.symbolHandle.asTruffleObject(), i, value);
                 } else if (value instanceof Integer) {
                     ForeignAccess.sendExecute(executeNode, INIT_VAR_FUN.INT.symbolHandle.asTruffleObject(), i, value);
+                } else if (value instanceof TruffleObject) {
+                    ForeignAccess.sendExecute(executeNode, INIT_VAR_FUN.OBJ.symbolHandle.asTruffleObject(), i, value);
                 }
             } catch (Throwable t) {
                 throw RInternalError.shouldNotReachHere(t);
+            } finally {
+                RContext.getRForeignAccessFactory().setIsNull(isNullSetting);
             }
         }
     }
 
-    public static class InvokeJNICall extends Node {
-        @Child CallRFFI.InvokeCallNode jniCall = new JNI_Call.JNI_InvokeCallNode();
-
-        public Object execute(NativeCallInfo nativeCallInfo, Object[] args) {
-            return jniCall.execute(nativeCallInfo, args);
-        }
-    }
-
-    public static class InvokeJNIVoidCall extends Node {
-        @Child CallRFFI.InvokeVoidCallNode jniCall = new JNI_Call.JNI_InvokeVoidCallNode();
+    private static void initCallbacks(RContext context, UpCallsRFFI upCallsImpl) {
+        TruffleLLVM_DLL.ensureParsed("libR", "Rinternals_addCallback", true);
+        Node executeNode = Message.createExecute(1).createNode();
+        SymbolHandle symbolHandle = new SymbolHandle(context.getEnv().importSymbol("@" + "Rinternals_addCallback"));
 
-        public Object execute(NativeCallInfo nativeCallInfo, Object[] args) {
-            jniCall.execute(nativeCallInfo, args);
-            return RNull.instance;
+        try {
+            Callbacks.createCalls(upCallsImpl);
+            for (Callbacks callback : Callbacks.values()) {
+                ForeignAccess.sendExecute(executeNode, symbolHandle.asTruffleObject(), callback.ordinal(), callback.call);
+            }
+        } catch (Throwable t) {
+            throw RInternalError.shouldNotReachHere(t);
         }
     }
 
-    /**
-     * Experimentally the node created for the message send contains cached information regarding
-     * the target, which is {@link RContext} specific, leading to invalid data being accessed in
-     * SHARED_PARENT_RW contexts (specifically the cached exported symbols used for package
-     * initialization). So we guard the node with a check that the context has not changed.
-     *
-     */
     @ImportStatic({Message.class, RContext.class})
-    public abstract static class InvokeTruffle extends Node {
-        public abstract Object execute(NativeCallInfo nativeCallInfo, Object[] args, RContext context);
+    public abstract static class TruffleLLVM_InvokeCallNode extends InvokeCallNode {
+        @Child private Node messageNode = Message.createExecute(0).createNode();
 
-        @Specialization(guards = {"context == cachedContext"})
-        protected Object invokeCallCached(NativeCallInfo nativeCallInfo, Object[] args, @SuppressWarnings("unused") RContext context,
-                        @SuppressWarnings("unused") @Cached("getInstance()") RContext cachedContext,
-                        @Cached("createExecute(0).createNode()") Node messageNode,
+        @Specialization(guards = {"cachedNativeCallInfo.name.equals(nativeCallInfo.name)"})
+        protected Object invokeCallCached(NativeCallInfo nativeCallInfo, Object[] args,
+                        @SuppressWarnings("unused") @Cached("nativeCallInfo") NativeCallInfo cachedNativeCallInfo,
                         @SuppressWarnings("unused") @Cached("ensureReady(nativeCallInfo)") boolean ready) {
             return doInvoke(messageNode, nativeCallInfo, args);
         }
 
         @Specialization(replaces = "invokeCallCached")
-        protected Object invokeCallNormal(NativeCallInfo nativeCallInfo, Object[] args, @SuppressWarnings("unused") RContext context) {
+        protected Object invokeCallNormal(NativeCallInfo nativeCallInfo, Object[] args) {
             return doInvoke(Message.createExecute(0).createNode(), nativeCallInfo, args);
         }
 
         private static Object doInvoke(Node messageNode, NativeCallInfo nativeCallInfo, Object[] args) {
+            boolean isNullSetting = RContext.getRForeignAccessFactory().setIsNull(false);
             try {
-                return ForeignAccess.sendExecute(messageNode, nativeCallInfo.address.asTruffleObject(), args);
-            } catch (Throwable t) {
+                Object result = TruffleLLVM_Utils.checkNativeAddress(ForeignAccess.sendExecute(messageNode, nativeCallInfo.address.asTruffleObject(), args));
+                return result;
+            } catch (InteropException t) {
                 throw RInternalError.shouldNotReachHere(t);
+            } finally {
+                RContext.getRForeignAccessFactory().setIsNull(isNullSetting);
             }
         }
 
         public static boolean ensureReady(NativeCallInfo nativeCallInfo) {
             TruffleLLVM_DLL.ensureParsed(nativeCallInfo);
             ContextStateImpl contextState = TruffleLLVM_RFFIContextState.getContextState().callState;
-            if (!contextState.initVariablesDone) {
-                initVariables(contextState.context);
-                contextState.initVariablesDone = true;
-            }
             return true;
         }
 
-        public static InvokeTruffle create() {
-            return InvokeTruffleNodeGen.create();
-        }
-    }
-
-    /**
-     * This class exists to separate out the delegated JNI calls from the Truffle calls.
-     */
-    public abstract static class SplitTruffleCallRFFINode extends Node {
-        public abstract Object execute(NativeCallInfo nativeCallInfo, Object[] args, boolean voidCall);
-
-        @Specialization(guards = {"isJNICall(nativeCallInfo)", "!voidCall"})
-        protected Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args, @SuppressWarnings("unused") boolean voidCall,
-                        @Cached("new()") InvokeJNICall invokeJNI) {
-            return invokeJNI.execute(nativeCallInfo, args);
-
-        }
-
-        @Specialization(guards = {"isJNICall(nativeCallInfo)", "voidCall"})
-        protected Object invokeVoidCall(NativeCallInfo nativeCallInfo, Object[] args, @SuppressWarnings("unused") boolean voidCall,
-                        @Cached("new()") InvokeJNICall invokeJNI) {
-            return invokeJNI.execute(nativeCallInfo, args);
-
-        }
-
-        @Specialization(guards = "!isJNICall(nativeCallInfo)")
-        protected Object invokeCall(NativeCallInfo nativeCallInfo, Object[] args, @SuppressWarnings("unused") boolean voidCall,
-                        @Cached("create()") InvokeTruffle invokeTruffle) {
-            return invokeTruffle.execute(nativeCallInfo, args, RContext.getInstance());
-        }
-
-        public static boolean isJNICall(NativeCallInfo nativeCallInfo) {
-            return nativeCallInfo.address.value instanceof Long;
-        }
-    }
-
-    private static class TruffleLLVM_InvokeCallNode extends InvokeCallNode {
-        @Child SplitTruffleCallRFFINode splitTruffleCallRFFINode = SplitTruffleCallRFFINodeGen.create();
-
-        @Override
-        public synchronized Object execute(NativeCallInfo nativeCallInfo, Object[] args) {
-            return splitTruffleCallRFFINode.execute(nativeCallInfo, args, false);
-
-        }
     }
 
     private static class TruffleLLVM_InvokeVoidCallNode extends InvokeVoidCallNode {
-        @Child SplitTruffleCallRFFINode splitTruffleCallRFFINode = SplitTruffleCallRFFINodeGen.create();
+        @Child private TruffleLLVM_InvokeCallNode invokeCallNode = TruffleLLVM_InvokeCallNodeGen.create();
 
         @Override
         public synchronized void execute(NativeCallInfo nativeCallInfo, Object[] args) {
-            splitTruffleCallRFFINode.execute(nativeCallInfo, args, true);
+            invokeCallNode.execute(nativeCallInfo, args);
         }
 
     }
@@ -238,13 +191,14 @@ class TruffleLLVM_Call implements CallRFFI {
      *
      * @param function
      */
+    @SuppressWarnings("static-method")
     public void unimplemented(String function) {
         throw RInternalError.unimplemented("RFFI function: '" + function + "' not implemented");
     }
 
     @Override
     public InvokeCallNode createInvokeCallNode() {
-        return new TruffleLLVM_InvokeCallNode();
+        return TruffleLLVM_InvokeCallNodeGen.create();
     }
 
     @Override
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_DLL.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java
similarity index 71%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_DLL.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java
index 13eeb36eb0..49e1405c87 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_DLL.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.llvm;
+package com.oracle.truffle.r.ffi.impl.llvm;
 
 import java.nio.file.FileSystems;
 import java.util.ArrayList;
@@ -32,21 +32,18 @@ import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextState;
 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.DotSymbol;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
-import com.oracle.truffle.r.runtime.ffi.jni.JNI_DLL;
-import com.oracle.truffle.r.runtime.ffi.truffle.LLVM_IR;
 
 /**
  * The Truffle version of {@link DLLRFFI}. {@code dlopen} expects to find the LLVM IR embedded in
- * the shared library. If it exists it is used, unless the library is blacklisted. Otherwise we fall
- * back to the standard JNI implementation.
+ * the shared library.
  *
  * The LLVM bitcode is stored (opaquely) in the shared library file, and access through the
  * {@link LLVM_IR} class. The {@link LLVM_IR#getLLVMIR(String)} method will return an array of
@@ -78,11 +75,8 @@ import com.oracle.truffle.r.runtime.ffi.truffle.LLVM_IR;
  * present time this can only be done by re-parsing the library contents.</li>
  * </ol>
  *
- * Note also that {@code libR} is the only library that is opened in native and LLVM mode, as the
- * native code is used by non-LLVM packages (libraries) and the LLVM code is used by the LLVM
- * packages (libraries).
  */
-class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
+class TruffleLLVM_DLL implements DLLRFFI {
     /**
      * Supports lazy parsing of LLVM modules.
      */
@@ -130,8 +124,10 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
          */
         @Override
         public ContextState initialize(RContext context) {
-            for (LLVM_IR ir : truffleDLL.libRModules) {
-                addExportsToMap(this, "libR", ir, (name) -> name.endsWith("_llvm"));
+            if (!context.isInitial()) {
+                for (LLVM_IR ir : truffleDLL.libRModules) {
+                    addExportsToMap(this, "libR", ir, (name) -> name.endsWith("_llvm"));
+                }
             }
             if (context.getKind() == RContext.ContextKind.SHARE_PARENT_RW) {
                 // must propagate all LLVM library exports
@@ -167,21 +163,6 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
         return new ContextStateImpl();
     }
 
-    static boolean isBlacklisted(String libName) {
-        String libs = System.getenv("FASTR_TRUFFLE_LIBS");
-        if (libs == null) {
-            Utils.warn(String.format("TruffleDLL: %s, FASTR_TRUFFLE_LIBS is unset, defaulting to JNI", libName));
-            return true;
-        }
-        String[] libsElems = libs.split(",");
-        for (String libsElem : libsElems) {
-            if (libName.equals(libsElem)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     static class LLVM_Handle {
         private final String libName;
         private final LLVM_IR[] irs;
@@ -197,7 +178,8 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
         boolean match(String name);
     }
 
-    private static class TruffleLLVM_DLOpenNode extends JNI_DLOpenNode {
+    private static class TruffleLLVM_DLOpenNode extends DLOpenNode {
+        @Child private TruffleLLVM_NativeDLL.TruffleLLVM_NativeDLOpen nativeDLLOpenNode;
 
         /**
          * If a library is enabled for LLVM, the IR for all the modules is retrieved and analyzed.
@@ -209,60 +191,59 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
         public Object execute(String path, boolean local, boolean now) {
             try {
                 LLVM_IR[] irs = LLVM_IR.getLLVMIR(path);
+                if (irs == null) {
+                    return tryOpenNative(path, local, now);
+                }
                 String libName = getLibName(path);
-                // even if libR is not all LLVM executed, some parts have to be
-                // but they can't be parsed now
+                // libR can't be parsed at this stage in the startup
                 if (libName.equals("libR")) {
                     truffleDLL.libRModules = irs;
                 }
-                if (irs == null || isBlacklisted(libName)) {
-                    return super.execute(path, local, now);
-                } else {
-                    ContextStateImpl contextState = getContextState();
-                    for (int i = 0; i < irs.length; i++) {
-                        LLVM_IR ir = irs[i];
-                        addExportsToMap(contextState, libName, ir, (name) -> true);
-                    }
-                    return new LLVM_Handle(libName, irs);
+                ContextStateImpl contextState = getContextState();
+                for (int i = 0; i < irs.length; i++) {
+                    LLVM_IR ir = irs[i];
+                    addExportsToMap(contextState, libName, ir, libName.equals("libR") ? (name) -> name.endsWith("_llvm") : (name) -> true);
                 }
+                return new LLVM_Handle(libName, irs);
             } catch (Exception ex) {
-                return null;
+                throw new UnsatisfiedLinkError(ex.getMessage());
             }
         }
+
+        private long tryOpenNative(String path, boolean local, boolean now) throws UnsatisfiedLinkError {
+            if (nativeDLLOpenNode == null) {
+                nativeDLLOpenNode = insert(new TruffleLLVM_NativeDLL.TruffleLLVM_NativeDLOpen());
+            }
+            return nativeDLLOpenNode.execute(path, local, now);
+        }
     }
 
-    private static class TruffleLLVM_DLSymNode extends JNI_DLSymNode {
+    private static class TruffleLLVM_DLSymNode extends DLSymNode {
         @Override
         public SymbolHandle execute(Object handle, String symbol) throws UnsatisfiedLinkError {
-            if (handle instanceof LLVM_Handle) {
-                // If the symbol exists it will be in the map
-                ParseStatus parseStatus = getContextState().parseStatusMap.get(symbol);
-                if (parseStatus != null && parseStatus.libName.equals(((LLVM_Handle) handle).libName)) {
-                    // force a parse so we have a "value"
-                    if (!parseStatus.parsed) {
-                        ensureParsed(parseStatus.libName, symbol, true);
-                    }
-                    Object symValue = RContext.getInstance().getEnv().importSymbol("@" + symbol);
-                    assert symValue != null;
-                    return new SymbolHandle(symValue);
-                } else {
-                    // symbol not found (or not in requested library)
-                    throw new UnsatisfiedLinkError();
+            assert handle instanceof LLVM_Handle;
+            // If the symbol exists it will be in the map
+            ParseStatus parseStatus = getContextState().parseStatusMap.get(symbol);
+            if (parseStatus != null && parseStatus.libName.equals(((LLVM_Handle) handle).libName)) {
+                // force a parse so we have a "value"
+                if (!parseStatus.parsed) {
+                    ensureParsed(parseStatus.libName, symbol, true);
                 }
+                Object symValue = RContext.getInstance().getEnv().importSymbol("@" + symbol);
+                assert symValue != null;
+                return new SymbolHandle(symValue);
             } else {
-                return super.execute(handle, symbol);
+                // symbol not found (or not in requested library)
+                throw new UnsatisfiedLinkError();
             }
         }
     }
 
-    private static class TruffleLLVM_DLCloseNode extends JNI_DLCloseNode {
+    private static class TruffleLLVM_DLCloseNode extends DLCloseNode {
         @Override
         public int execute(Object handle) {
-            if (handle instanceof LLVM_Handle) {
-                return 0;
-            } else {
-                return super.execute(handle);
-            }
+            assert handle instanceof LLVM_Handle;
+            return 0;
         }
 
     }
@@ -286,8 +267,11 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
         ParseStatus parseStatus = new ParseStatus(libName, ir, false);
         for (String export : ir.exports) {
             if (moduleNameMatch.match(ir.name)) {
-                assert contextState.parseStatusMap.get(export) == null;
-                contextState.parseStatusMap.put(export, parseStatus);
+                ParseStatus exportParseStatus = contextState.parseStatusMap.get(export);
+                // libR and graphics both define the same symbols (which should be fixed)
+                if (exportParseStatus == null) {
+                    contextState.parseStatusMap.put(export, parseStatus);
+                }
             }
         }
     }
@@ -319,6 +303,33 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
         ensureParsed(nativeCallInfo.dllInfo.name, nativeCallInfo.name, true);
     }
 
+    private static final String[] MustEagerlyParsePackages = new String[]{"grDevices:colors", "graphics:registerBase"};
+
+    private static boolean mustEagerlyParse(String libName, String moduleName) {
+        for (String pkg : MustEagerlyParsePackages) {
+            String[] parts = pkg.split(":");
+            if (parts[0].equals(libName) && parts[1].equals(moduleName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called from {@link TruffleLLVM_PkgInit} to catch internal (aka non-exported) symbols that may
+     * nevertheless be invoked.
+     */
+    static void registerSymbols(DLLInfo dllInfo, DotSymbol[] dotSymbols) {
+        ContextStateImpl contextState = getContextState();
+        Map<String, ParseStatus> parseStatusMap = contextState.parseStatusMap;
+        for (DotSymbol ds : dotSymbols) {
+            ParseStatus parseStatus = parseStatusMap.get(ds.name);
+            if (parseStatus == null) {
+                parseStatusMap.put(ds.name, new ParseStatus(dllInfo.name, null, true));
+            }
+        }
+    }
+
     /**
      * Similar to {@link #ensureParsed(NativeCallInfo)} but with a function specified as a string
      * (for internal use) and an optional check whether the function must exist.
@@ -330,19 +341,22 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
         ParseStatus parseStatus = parseStatusMap.get(name);
         assert parseStatus != null || !fatalIfMissing;
         if (parseStatus != null && !parseStatus.parsed) {
-            parseLLVM(parseStatus.ir);
+            parseLLVM(libName, parseStatus.ir);
             parseStatus.parsed = true;
             boolean isPackageInit = isPackageInit(libName, name);
             for (String importee : parseStatus.ir.imports) {
                 /*
                  * If we are resolving a package init call, we do not want to resolve all the
-                 * imports if functions in the same library as this will cause everything in the
-                 * library to be parsed eagerly!
+                 * imports of functions in the same library as this will cause everything in the
+                 * library to be parsed eagerly! However, any package that actually invokes an
+                 * imported symbol in the R_init_package function must do eager parsing.
+                 * Unfortunately, we can't easily tell if that is the case, so we have a backlist of
+                 * known problem packages.
                  */
                 ParseStatus importeeParseStatus = parseStatusMap.get(importee);
                 boolean internal = isPackageInit && importeeParseStatus.libName.equals(libName);
-                if (importeeParseStatus != null && !internal) {
-                    ensureParsed(libName, importee, false);
+                if (importeeParseStatus != null && (!internal || mustEagerlyParse(libName, importee))) {
+                    ensureParsed(importeeParseStatus.libName, importee, false);
                 }
             }
         }
@@ -356,16 +370,16 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
         }
     }
 
-    private static void parseLLVM(LLVM_IR ir) {
+    private static void parseLLVM(String libName, LLVM_IR ir) {
         if (ir instanceof LLVM_IR.Binary) {
             LLVM_IR.Binary bir = (LLVM_IR.Binary) ir;
-            parseBinary(bir);
+            parseBinary(libName, bir);
         } else {
             throw RInternalError.unimplemented("LLVM text IR");
         }
     }
 
-    private static CallTarget parseBinary(LLVM_IR.Binary ir) {
+    private static CallTarget parseBinary(String libName, LLVM_IR.Binary ir) {
         long start = System.nanoTime();
         RContext context = RContext.getInstance();
         long nanos = 1000 * 1000 * 1000;
@@ -373,7 +387,7 @@ class TruffleLLVM_DLL extends JNI_DLL implements DLLRFFI {
         CallTarget result = context.getEnv().parse(source);
         if (System.getenv("LLVM_PARSE_TIME") != null) {
             long end = System.nanoTime();
-            System.out.printf("parsed %s in %f secs%n", ir.name, ((double) (end - start)) / (double) nanos);
+            System.out.printf("parsed %s:%s in %f secs%n", libName, ir.name, ((double) (end - start)) / (double) nanos);
         }
         return result;
     }
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
new file mode 100644
index 0000000000..f176f495f8
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Lapack.java
@@ -0,0 +1,490 @@
+/*
+ * 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.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.InteropException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.common.LibPaths;
+import com.oracle.truffle.r.ffi.impl.interop.NativeDoubleArray;
+import com.oracle.truffle.r.ffi.impl.interop.NativeIntegerArray;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
+import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
+import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
+
+/**
+ * When the embedded GNU R is built, LLVM is created for the components of the {@code libRblas} and
+ * {@code libRlapack} libraries. In principle we could call the subroutines direct but since,
+ * Fortran passes every argument by reference we would have to create many length 1 arrays to wrap
+ * the scalar arguments. So we call through a thing veneer in {@code lapack_rffi.c} that forwards
+ * the call taking the address of the scalar arguments. We also take the liberty of defining the
+ * {@code info} argument taken my most all if the functions in the veneer, and returning the value
+ * as the result of the call.
+ *
+ * N.B. The usual implicit loading of {@code libRlapack} and {@code libRblas} that we get with
+ * native {@code dlopen} via {@code libR} does not happen with LLVM, so we must force their loading
+ * when this API is requested.
+ *
+ */
+public class TruffleLLVM_Lapack implements LapackRFFI {
+
+    TruffleLLVM_Lapack() {
+        /*
+         * 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
+         * have to load libR natively to get the rpath support. This code is OS X specific and
+         * depends on specific versions.
+         */
+        RootCallTarget callTarget;
+        boolean useLLVM = System.getenv("FASTR_LLVM_LAPACK") != null;
+        if (useLLVM) {
+            callTarget = openLLVMLibraries();
+        } else {
+            callTarget = openNativeLibraries();
+            callTarget.call(LibPaths.getBuiltinLibPath("gcc_s.1"), false, true);
+            callTarget.call(LibPaths.getBuiltinLibPath("quadmath.0"), false, true);
+            callTarget.call(LibPaths.getBuiltinLibPath("gfortran.3"), false, true);
+        }
+        callTarget.call(LibPaths.getBuiltinLibPath("Rblas"), false, true);
+        callTarget.call(LibPaths.getBuiltinLibPath("Rlapack"), false, true);
+    }
+
+    private static RootCallTarget openLLVMLibraries() {
+        DLLRFFI.DLOpenRootNode rootNode = DLLRFFI.DLOpenRootNode.create();
+        return rootNode.getCallTarget();
+    }
+
+    private static RootCallTarget openNativeLibraries() {
+        TruffleLLVM_NativeDLL.NativeDLOpenRootNode rootNode = TruffleLLVM_NativeDLL.NativeDLOpenRootNode.create();
+        return rootNode.getCallTarget();
+    }
+
+    private static class TruffleLLVM_IlaverNode extends IlaverNode {
+        @Child private Node message = LLVMFunction.ilaver.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public void execute(int[] version) {
+            NativeIntegerArray versionN = new NativeIntegerArray(version);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.ilaver.callName, null);
+                }
+                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), versionN);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                versionN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DgeevNode extends DgeevNode {
+        @Child private Node message = LLVMFunction.dgeev.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(char jobVL, char jobVR, int n, double[] a, int lda, double[] wr, double[] wi, double[] vl, int ldvl, double[] vr, int ldvr, double[] work, int lwork) {
+            // vl, vr may be null
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            NativeDoubleArray wrN = new NativeDoubleArray(wr);
+            NativeDoubleArray wiN = new NativeDoubleArray(wi);
+            Object vlN = vl == null ? 0 : new NativeDoubleArray(vl);
+            Object vrN = vr == null ? 0 : new NativeDoubleArray(vr);
+            NativeDoubleArray workN = new NativeDoubleArray(work);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dgeev.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), jobVL, jobVR, n, aN, lda,
+                                wrN, wiN, vlN, ldvl, vrN, ldvr, workN, lwork);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                aN.getValue();
+                wrN.getValue();
+                wiN.getValue();
+                if (vl != null) {
+                    ((NativeDoubleArray) vlN).getValue();
+                }
+                if (vr != null) {
+                    ((NativeDoubleArray) vrN).getValue();
+                }
+                workN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_Dgeqp3Node extends Dgeqp3Node {
+        @Child private Node message = LLVMFunction.dgeqp3.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            NativeIntegerArray jpvtN = new NativeIntegerArray(jpvt);
+            NativeDoubleArray tauN = new NativeDoubleArray(tau);
+            NativeDoubleArray workN = new NativeDoubleArray(work);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dgeqp3.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), m, n, aN, lda, jpvtN,
+                                tauN, workN, lwork);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                aN.getValue();
+                jpvtN.getValue();
+                tauN.getValue();
+                workN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DormqrNode extends DormqrNode {
+        @Child private Node message = LLVMFunction.dormq.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(char side, char trans, int m, int n, int k, double[] a, int lda, double[] tau, double[] c, int ldc, double[] work, int lwork) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            NativeDoubleArray tauN = new NativeDoubleArray(tau);
+            NativeDoubleArray cN = new NativeDoubleArray(c);
+            NativeDoubleArray workN = new NativeDoubleArray(work);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dormq.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), side, trans, m, n, k, aN, lda,
+                                tauN, cN, ldc, workN, lwork);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                cN.getValue();
+                workN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DtrtrsNode extends DtrtrsNode {
+        @Child private Node message = LLVMFunction.dtrtrs.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            NativeDoubleArray bN = new NativeDoubleArray(b);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dtrtrs.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), uplo, trans, diag, n, nrhs, aN, lda,
+                                bN, ldb);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                bN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DgetrfNode extends DgetrfNode {
+        @Child private Node message = LLVMFunction.dgetrf.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(int m, int n, double[] a, int lda, int[] ipiv) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            NativeIntegerArray ipivN = new NativeIntegerArray(ipiv);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dgetrf.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), m, n, aN, lda, ipivN);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                aN.getValue();
+                ipivN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DpotrfNode extends DpotrfNode {
+        @Child private Node message = LLVMFunction.dpotrf.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(char uplo, int n, double[] a, int lda) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dpotrf.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), uplo, n, aN, lda);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                aN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DpotriNode extends DpotriNode {
+        @Child private Node message = LLVMFunction.dpotri.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(char uplo, int n, double[] a, int lda) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dpotri.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), uplo, n, aN, lda);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                aN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DpstrfNode extends DpstrfNode {
+        @Child private Node message = LLVMFunction.dpstrf.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            NativeIntegerArray pivN = new NativeIntegerArray(piv);
+            NativeIntegerArray rankN = new NativeIntegerArray(rank);
+            NativeDoubleArray workN = new NativeDoubleArray(work);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dpstrf.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), uplo, n, aN, lda,
+                                pivN, rankN, tol, workN);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                aN.getValue();
+                pivN.getValue();
+                rankN.getValue();
+                workN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DgesvNode extends DgesvNode {
+        @Child private Node message = LLVMFunction.dgesv.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            NativeIntegerArray ipivN = new NativeIntegerArray(ipiv);
+            NativeDoubleArray bN = new NativeDoubleArray(b);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dgesv.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), n, nrhs, aN, lda, ipivN, bN, ldb);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                aN.getValue();
+                ipivN.getValue();
+                bN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DlangeNode extends DlangeNode {
+        @Child private Node message = LLVMFunction.dlange.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public double execute(char norm, int m, int n, double[] a, int lda, double[] work) {
+            // work may be null
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            Object workN = work == null ? 0 : new NativeDoubleArray(work);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dlange.callName, null);
+                }
+                return (double) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), norm, m, n, aN, lda, workN);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DgeconNode extends DgeconNode {
+        @Child private Node message = LLVMFunction.dgecon.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            NativeDoubleArray rcondN = new NativeDoubleArray(rcond);
+            NativeDoubleArray workN = new NativeDoubleArray(work);
+            NativeIntegerArray iworkN = new NativeIntegerArray(iwork);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dgecon.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), norm, n, aN, lda, anorm, rcondN, workN, iworkN);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                rcondN.getValue();
+                workN.getValue();
+                iworkN.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DsyevrNode extends DsyevrNode {
+        @Child private Node message = LLVMFunction.dsyevr.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(char jobz, char range, char uplo, int n, double[] a, int lda, double vl, double vu, int il, int iu, double abstol, int[] m, double[] w, double[] z, int ldz, int[] isuppz,
+                        double[] work, int lwork, int[] iwork, int liwork) {
+            NativeDoubleArray aN = new NativeDoubleArray(a);
+            NativeIntegerArray mN = new NativeIntegerArray(m);
+            NativeDoubleArray wN = new NativeDoubleArray(w);
+            Object zN = z == null ? 0 : new NativeDoubleArray(z);
+            NativeIntegerArray isuppzN = new NativeIntegerArray(isuppz);
+            NativeDoubleArray workN = new NativeDoubleArray(work);
+            NativeIntegerArray iworkN = new NativeIntegerArray(iwork);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dsyevr.callName, null);
+                }
+                return (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), jobz, range, uplo, n, aN,
+                                lda, vl, vu, il, iu, abstol, mN, wN, zN, ldz,
+                                isuppzN, workN, lwork, iworkN, liwork);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                aN.getValue();
+                mN.getValue();
+                wN.getValue();
+                if (z != null) {
+                    ((NativeDoubleArray) zN).getValue();
+
+                }
+                isuppzN.getValue();
+                workN.getValue();
+                iworkN.getValue();
+            }
+        }
+    }
+
+    @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 DlangeNode createDlangeNode() {
+        return new TruffleLLVM_DlangeNode();
+    }
+
+    @Override
+    public DgeconNode createDgeconNode() {
+        return new TruffleLLVM_DgeconNode();
+    }
+
+    @Override
+    public DsyevrNode createDsyevrNode() {
+        return new TruffleLLVM_DsyevrNode();
+    }
+}
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
new file mode 100644
index 0000000000..d4b5259068
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Misc.java
@@ -0,0 +1,62 @@
+/*
+ * 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.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.InteropException;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.interop.NativeDoubleArray;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
+import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
+
+public class TruffleLLVM_Misc implements MiscRFFI {
+
+    private static class TruffleLLVM_ExactSumNode extends ExactSumNode {
+        @CompilationFinal private SymbolHandle symbolHandle;
+        @Child private Node executeNode = Message.createExecute(4).createNode();
+
+        @Override
+        public double execute(double[] values, boolean hasNa, boolean naRm) {
+            NativeDoubleArray nativeValues = new NativeDoubleArray(values);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol("exactSum", null);
+                }
+                return (double) ForeignAccess.sendExecute(executeNode, symbolHandle.asTruffleObject(), nativeValues, values.length, hasNa ? 1 : 0, naRm ? 1 : 0);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    @Override
+    public ExactSumNode createExactSumNode() {
+        return new TruffleLLVM_ExactSumNode();
+    }
+}
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
new file mode 100644
index 0000000000..88f09e1fd7
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_NativeDLL.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.ffi.impl.llvm;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.interop.CanResolve;
+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.MessageResolution;
+import com.oracle.truffle.api.interop.Resolve;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.interop.NativeCharArray;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.context.RContext.ContextState;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
+import com.oracle.truffle.r.runtime.ffi.RFFIRootNode;
+
+/**
+ * Direct access to native {@code dlopen} for libraries for which no LLVM code is available.
+ */
+class TruffleLLVM_NativeDLL {
+    enum Function {
+        dlopen(3),
+        dlclose(1);
+
+        private final Node executeNode;
+        private final String callName;
+
+        Function(int argCount) {
+            executeNode = Message.createExecute(argCount).createNode();
+            callName = "call_" + name();
+        }
+    }
+
+    private static TruffleLLVM_NativeDLL truffleNativeDLL;
+    private static TruffleObject truffleNativeDLLTruffleObject;
+
+    static class ContextStateImpl implements RContext.ContextState {
+        @Override
+        public ContextState initialize(RContext context) {
+            if (truffleNativeDLL == null) {
+                truffleNativeDLL = new TruffleLLVM_NativeDLL();
+                truffleNativeDLLTruffleObject = JavaInterop.asTruffleObject(truffleNativeDLL);
+                context.addExportedSymbol("_fastr_dllnative_helper", truffleNativeDLLTruffleObject);
+            }
+            return this;
+        }
+    }
+
+    public interface ErrorCallback {
+        void setResult(String errorMessage);
+
+    }
+
+    private static class ErrorCallbackImpl implements ErrorCallback, TruffleObject {
+        private String errorMessage;
+
+        @Override
+        public void setResult(String errorMessage) {
+            this.errorMessage = errorMessage;
+        }
+
+        @Override
+        public ForeignAccess getForeignAccess() {
+            return ErrorCallbackCallbackMRForeign.ACCESS;
+        }
+    }
+
+    @MessageResolution(receiverType = ErrorCallback.class)
+    public static class ErrorCallbackCallbackMR {
+        @CanResolve
+        public abstract static class ErrorCallbackCheckNode extends Node {
+
+            protected static boolean test(TruffleObject receiver) {
+                return receiver instanceof ErrorCallback;
+            }
+        }
+
+        @Resolve(message = "EXECUTE")
+        public abstract static class ErrorCallbackExecuteNode extends Node {
+            protected Object access(@SuppressWarnings("unused") VirtualFrame frame, ErrorCallback receiver, Object[] arguments) {
+                receiver.setResult((String) arguments[0]);
+                return receiver;
+            }
+        }
+    }
+
+    public void setDlopenResult(ErrorCallback errorCallback, NativeCharArray errorMessage) {
+        errorCallback.setResult(new String(errorMessage.getValue()));
+    }
+
+    static class TruffleLLVM_NativeDLOpen extends Node {
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        public long execute(String path, boolean local, boolean now) throws UnsatisfiedLinkError {
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(Function.dlopen.callName, null);
+                }
+                ErrorCallbackImpl errorCallbackImpl = new ErrorCallbackImpl();
+                long result = (long) ForeignAccess.sendExecute(Function.dlopen.executeNode, symbolHandle.asTruffleObject(), errorCallbackImpl, new NativeCharArray(path.getBytes()), local ? 1 : 0,
+                                now ? 1 : 0);
+                if (result == 0) {
+                    throw new UnsatisfiedLinkError(errorCallbackImpl.errorMessage);
+                }
+                return result;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    static class TruffleLLVM_NativeDLClose extends Node {
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        public int execute(long handle) {
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(Function.dlclose.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(Function.dlclose.executeNode, symbolHandle.asTruffleObject(), handle);
+                return result;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static final class NativeDLOpenRootNode extends RFFIRootNode<TruffleLLVM_NativeDLOpen> {
+        private static NativeDLOpenRootNode nativeDLOpenRootNode;
+
+        private NativeDLOpenRootNode() {
+            super(new TruffleLLVM_NativeDLOpen());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            return rffiNode.execute((String) args[0], (boolean) args[1], (boolean) args[2]);
+        }
+
+        public static NativeDLOpenRootNode create() {
+            if (nativeDLOpenRootNode == null) {
+                nativeDLOpenRootNode = new NativeDLOpenRootNode();
+            }
+            return nativeDLOpenRootNode;
+        }
+    }
+}
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
new file mode 100644
index 0000000000..cfa0ee753e
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_PCRE.java
@@ -0,0 +1,222 @@
+/*
+ * 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.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.InteropException;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.common.LibPaths;
+import com.oracle.truffle.r.ffi.impl.interop.NativeCharArray;
+import com.oracle.truffle.r.ffi.impl.interop.NativeIntegerArray;
+import com.oracle.truffle.r.ffi.impl.interop.pcre.CaptureNamesResult;
+import com.oracle.truffle.r.ffi.impl.interop.pcre.CompileResult;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.context.RContext.ContextState;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
+import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
+import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+
+public class TruffleLLVM_PCRE implements PCRERFFI {
+    private static TruffleObject trufflePCRETruffleObject;
+
+    TruffleLLVM_PCRE() {
+        trufflePCRETruffleObject = JavaInterop.asTruffleObject(this);
+        // Need to ensure that the native pcre library is loaded
+        String pcrePath = LibPaths.getBuiltinLibPath("pcre");
+        System.load(pcrePath);
+    }
+
+    static class ContextStateImpl implements RContext.ContextState {
+        @Override
+        public ContextState initialize(RContext context) {
+            RFFIFactory.getRFFI().getPCRERFFI();
+            context.addExportedSymbol("_fastr_rffi_pcre", trufflePCRETruffleObject);
+            return this;
+        }
+    }
+
+    public CompileResult makeResult(TruffleObject pcreResultObj, NativeCharArray nativeErrorMessage, int errOffset) {
+        long pcreResult = TruffleLLVM_Utils.getNativeAddress(pcreResultObj);
+        String errorMessage = null;
+        if (nativeErrorMessage != null) {
+            errorMessage = new String(nativeErrorMessage.getValue());
+        }
+        CompileResult result = new CompileResult();
+        result.set(pcreResult, errorMessage, errOffset);
+        return result;
+    }
+
+    public void addCaptureName(int i, Object nameObj, CaptureNamesResult captureNamesCallback) {
+        captureNamesCallback.addName(i, new String(((NativeCharArray) nameObj).getValue()));
+    }
+
+    private static class TruffleLLVM_MaketablesNode extends MaketablesNode {
+        @Child private Node message = LLVMFunction.maketables.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public long execute() {
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.maketables.callName, null);
+                }
+                TruffleObject callResult = (TruffleObject) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject());
+                long result = TruffleLLVM_Utils.getNativeAddress(callResult);
+                return result;
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            }
+        }
+    }
+
+    private static class TruffleLLVM_GetCaptureCountNode extends GetCaptureCountNode {
+        @Child private Node message = LLVMFunction.getcapturecount.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(long code, long extra) {
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.getcapturecount.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), code, extra);
+                return result;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleLLVM_GetCaptureNamesNode extends GetCaptureNamesNode {
+        @Child private Node message = LLVMFunction.getcapturenames.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public String[] execute(long code, long extra, int captureCount) {
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.getcapturenames.callName, null);
+                }
+                CaptureNamesResult captureNamesCallback = new CaptureNamesResult(captureCount);
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(),
+                                code, extra, captureNamesCallback);
+                if (result < 0) {
+                    CompilerDirectives.transferToInterpreter();
+                    throw RError.error(RError.NO_CALLER, RError.Message.WRONG_PCRE_INFO, result);
+                } else {
+                    return captureNamesCallback.getCaptureNames();
+                }
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            }
+        }
+    }
+
+    public static class TruffleLLVM_CompileNode extends CompileNode {
+        @Child private Node message = LLVMFunction.compile.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public Result execute(String pattern, int options, long tables) {
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.compile.callName, null);
+                }
+                NativeCharArray pattenChars = new NativeCharArray(pattern.getBytes());
+                Object callResult = ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(),
+                                pattenChars, options, tables);
+                CompileResult result = (CompileResult) callResult;
+                return result.getResult();
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            }
+        }
+    }
+
+    private static class TruffleLLVM_ExecNode extends ExecNode {
+        @Child private Node message = LLVMFunction.exec.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(long code, long extra, String subject, int offset, int options, int[] ovector) {
+            NativeIntegerArray nativeOvector = new NativeIntegerArray(ovector);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.exec.callName, null);
+                }
+                byte[] subjectBytes = subject.getBytes();
+                NativeCharArray subjectChars = new NativeCharArray(subjectBytes);
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), code, extra,
+                                subjectChars, subjectBytes.length,
+                                offset, options, nativeOvector, ovector.length);
+                return result;
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            } finally {
+                nativeOvector.getValue();
+            }
+        }
+    }
+
+    @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.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_PkgInit.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_PkgInit.java
similarity index 95%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_PkgInit.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_PkgInit.java
index 6c4b235ca5..782b07e951 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_PkgInit.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_PkgInit.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.llvm;
+package com.oracle.truffle.r.ffi.impl.llvm;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
@@ -53,10 +53,6 @@ class TruffleLLVM_PkgInit {
         }
     }
 
-    static ContextStateImpl newContextState() {
-        return new ContextStateImpl();
-    }
-
     private static TruffleLLVM_PkgInit initialize() {
         if (trufflePkgInit == null) {
             trufflePkgInit = new TruffleLLVM_PkgInit();
@@ -73,12 +69,12 @@ class TruffleLLVM_PkgInit {
             array[i] = (DotSymbol) sym;
         }
         dllInfo.setNativeSymbols(nstOrd, array);
+        TruffleLLVM_DLL.registerSymbols(dllInfo, array);
     }
 
     private static Object setSymbol(int nstOrd, long routines, int index, SymbolHandle symbolHandle) {
         Node executeNode = Message.createExecute(3).createNode();
         try {
-
             Object result = ForeignAccess.sendExecute(executeNode, symbolHandle.asTruffleObject(), nstOrd, routines, index);
             return result;
         } catch (Throwable t) {
@@ -102,7 +98,8 @@ class TruffleLLVM_PkgInit {
      * Upcall from native to create a {@link DotSymbol} value.
      */
     public DotSymbol createDotSymbol(String name, Object fundesc, int numArgs) {
-        return new DotSymbol(name, new SymbolHandle(fundesc), numArgs);
+        DotSymbol result = new DotSymbol(name, new SymbolHandle(fundesc), numArgs);
+        return result;
     }
 
     public int useDynamicSymbols(DLLInfo dllInfo, int value) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RAppl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RAppl.java
new file mode 100644
index 0000000000..c650386078
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RAppl.java
@@ -0,0 +1,151 @@
+/*
+ * 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.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.InteropException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.interop.NativeDoubleArray;
+import com.oracle.truffle.r.ffi.impl.interop.NativeIntegerArray;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
+import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
+
+/**
+ * See the comments in {@link TruffleLLVM_Lapack} regarding argument passing.
+ */
+public class TruffleLLVM_RAppl implements RApplRFFI {
+
+    private static class TruffleLLVM_Dqrdc2Node extends Dqrdc2Node {
+        @Child private Node message = LLVMFunction.dqrdc2.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public void execute(double[] x, int ldx, int n, int p, double tol, int[] rank, double[] qraux, int[] pivot, double[] work) {
+            NativeDoubleArray xN = new NativeDoubleArray(x);
+            NativeIntegerArray rankN = new NativeIntegerArray(rank);
+            NativeDoubleArray qrauxN = new NativeDoubleArray(qraux);
+            NativeIntegerArray pivotN = new NativeIntegerArray(pivot);
+            NativeDoubleArray workN = new NativeDoubleArray(work);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dqrdc2.callName, null);
+                }
+                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), xN, ldx, n, p, tol, rankN, qrauxN, pivotN, workN);
+                // sync up in case copied to native memory
+                xN.getValue();
+                rankN.getValue();
+                qrauxN.getValue();
+                pivotN.getValue();
+                workN.getValue();
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DqrcfNode extends DqrcfNode {
+        @Child private Node message = LLVMFunction.dqrcf.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] b, int[] info) {
+            NativeDoubleArray xN = new NativeDoubleArray(x);
+            NativeDoubleArray qrauxN = new NativeDoubleArray(qraux);
+            NativeDoubleArray yN = new NativeDoubleArray(y);
+            NativeDoubleArray bN = new NativeDoubleArray(b);
+            NativeIntegerArray infoN = new NativeIntegerArray(info);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dqrcf.callName, null);
+                }
+                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), xN, n, k, qrauxN, yN, ny, bN, infoN);
+                // sync up in case copied to native memory
+                xN.getValue();
+                qrauxN.getValue();
+                yN.getValue();
+                bN.getValue();
+                infoN.getValue();
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            }
+        }
+    }
+
+    private static class TruffleLLVM_DqrlsNode extends DqrlsNode {
+        @Child private Node message = LLVMFunction.dqrls.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public void execute(double[] x, int n, int p, double[] y, int ny, double tol, double[] b, double[] rsd, double[] qty, int[] k, int[] jpvt, double[] qraux, double[] work) {
+            NativeDoubleArray xN = new NativeDoubleArray(x);
+            NativeDoubleArray yN = new NativeDoubleArray(y);
+            NativeDoubleArray bN = new NativeDoubleArray(b);
+            NativeDoubleArray rsdN = new NativeDoubleArray(rsd);
+            NativeDoubleArray qtyN = new NativeDoubleArray(qty);
+            NativeIntegerArray kN = new NativeIntegerArray(k);
+            NativeIntegerArray jpvtN = new NativeIntegerArray(jpvt);
+            NativeDoubleArray qrauxN = new NativeDoubleArray(qraux);
+            NativeDoubleArray workN = new NativeDoubleArray(work);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.dqrls.callName, null);
+                }
+                ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(), xN, n, p, yN, ny, tol, bN, rsdN, qtyN, kN, jpvtN, qrauxN, workN);
+                // sync up in case copied to native memory
+                xN.getValue();
+                yN.getValue();
+                bN.getValue();
+                rsdN.getValue();
+                qtyN.getValue();
+                kN.getValue();
+                jpvtN.getValue();
+                qrauxN.getValue();
+                workN.getValue();
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            }
+        }
+    }
+
+    @Override
+    public Dqrdc2Node createDqrdc2Node() {
+        return new TruffleLLVM_Dqrdc2Node();
+    }
+
+    @Override
+    public DqrcfNode createDqrcfNode() {
+        return new TruffleLLVM_DqrcfNode();
+    }
+
+    @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_REmbed.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java
new file mode 100644
index 0000000000..454c152d56
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_REmbed.java
@@ -0,0 +1,55 @@
+/*
+ * 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.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.REmbedRFFI;
+
+public class TruffleLLVM_REmbed implements REmbedRFFI {
+
+    @Override
+    public void suicide(String x) {
+        throw RInternalError.unimplemented();
+    }
+
+    @Override
+    public void cleanUp(int type, int x, int y) {
+        throw RInternalError.unimplemented();
+    }
+
+    @Override
+    public String readConsole(String prompt) {
+        throw RInternalError.unimplemented();
+    }
+
+    @Override
+    public void writeConsole(String x) {
+        throw RInternalError.unimplemented();
+    }
+
+    @Override
+    public void writeErrConsole(String x) {
+        throw RInternalError.unimplemented();
+    }
+
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_RFFIContextState.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIContextState.java
similarity index 62%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_RFFIContextState.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIContextState.java
index 9caaec1719..f04af0d81f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_RFFIContextState.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIContextState.java
@@ -20,28 +20,36 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.llvm;
+package com.oracle.truffle.r.ffi.impl.llvm;
 
+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.DLL;
 
 /**
  * A facade for the context state for the Truffle LLVM factory. Delegates to the various
- * module-specific pieces of state. This may get merged into a single instance eventually.
+ * module-specific pieces of state.
  */
 class TruffleLLVM_RFFIContextState implements ContextState {
     TruffleLLVM_DLL.ContextStateImpl dllState;
     TruffleLLVM_PkgInit.ContextStateImpl pkgInitState;
     TruffleLLVM_Call.ContextStateImpl callState;
     TruffleLLVM_Stats.ContextStateImpl statsState;
-    private final ContextState jniContextState;
+    TruffleLLVM_Tools.ContextStateImpl toolsState;
+    TruffleLLVM_PCRE.ContextStateImpl pcreState;
+    TruffleLLVM_Base.ContextStateImpl baseState;
+    TruffleLLVM_NativeDLL.ContextStateImpl nativeDllState;
 
-    TruffleLLVM_RFFIContextState(ContextState jniContextState) {
-        this.jniContextState = jniContextState;
-        dllState = TruffleLLVM_DLL.newContextState();
-        pkgInitState = TruffleLLVM_PkgInit.newContextState();
-        callState = TruffleLLVM_Call.newContextState();
-        statsState = TruffleLLVM_Stats.newContextState();
+    TruffleLLVM_RFFIContextState() {
+        dllState = new TruffleLLVM_DLL.ContextStateImpl();
+        pkgInitState = new TruffleLLVM_PkgInit.ContextStateImpl();
+        callState = new TruffleLLVM_Call.ContextStateImpl();
+        statsState = new TruffleLLVM_Stats.ContextStateImpl();
+        toolsState = new TruffleLLVM_Tools.ContextStateImpl();
+        pcreState = new TruffleLLVM_PCRE.ContextStateImpl();
+        baseState = new TruffleLLVM_Base.ContextStateImpl();
+        nativeDllState = new TruffleLLVM_NativeDLL.ContextStateImpl();
     }
 
     static TruffleLLVM_RFFIContextState getContextState() {
@@ -54,11 +62,18 @@ class TruffleLLVM_RFFIContextState implements ContextState {
 
     @Override
     public ContextState initialize(RContext context) {
-        jniContextState.initialize(context);
+        if (context.isInitial()) {
+            String librffiPath = LibPaths.getBuiltinLibPath("R");
+            DLL.loadLibR(librffiPath);
+        }
         dllState.initialize(context);
         pkgInitState.initialize(context);
         callState.initialize(context);
         statsState.initialize(context);
+        toolsState.initialize(context);
+        pcreState.initialize(context);
+        baseState.initialize(context);
+        nativeDllState.initialize(context);
         return this;
     }
 
@@ -68,5 +83,9 @@ class TruffleLLVM_RFFIContextState implements ContextState {
         pkgInitState.beforeDestroy(context);
         callState.beforeDestroy(context);
         statsState.beforeDestroy(context);
+        toolsState.beforeDestroy(context);
+        pcreState.beforeDestroy(context);
+        baseState.beforeDestroy(context);
+        nativeDllState.beforeDestroy(context);
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIFactory.java
new file mode 100644
index 0000000000..d36306b51e
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_RFFIFactory.java
@@ -0,0 +1,190 @@
+/*
+ * 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.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.r.runtime.context.RContext.ContextState;
+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.DLLRFFI;
+import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
+import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
+import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
+import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
+import com.oracle.truffle.r.runtime.ffi.REmbedRFFI;
+import com.oracle.truffle.r.runtime.ffi.RFFI;
+import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.StatsRFFI;
+import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
+import com.oracle.truffle.r.runtime.ffi.UserRngRFFI;
+import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
+
+public class TruffleLLVM_RFFIFactory extends RFFIFactory implements RFFI {
+
+    @Override
+    public ContextState newContextState() {
+        return new TruffleLLVM_RFFIContextState();
+    }
+
+    @Override
+    protected RFFI createRFFI() {
+        return this;
+    }
+
+    @CompilationFinal private BaseRFFI baseRFFI;
+
+    @Override
+    public BaseRFFI getBaseRFFI() {
+        if (baseRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            baseRFFI = new TruffleLLVM_Base();
+        }
+        return baseRFFI;
+    }
+
+    @CompilationFinal private CRFFI cRFFI;
+
+    @Override
+    public CRFFI getCRFFI() {
+        if (cRFFI == null) {
+            cRFFI = new TruffleLLVM_C();
+        }
+        return cRFFI;
+    }
+
+    @CompilationFinal private DLLRFFI dllRFFI;
+
+    @Override
+    public DLLRFFI getDLLRFFI() {
+        if (dllRFFI == null) {
+            dllRFFI = new TruffleLLVM_DLL();
+        }
+        return dllRFFI;
+    }
+
+    @CompilationFinal private UserRngRFFI truffleUserRngRFFI;
+
+    @Override
+    public UserRngRFFI getUserRngRFFI() {
+        if (truffleUserRngRFFI == null) {
+            truffleUserRngRFFI = new TruffleLLVM_UserRng();
+        }
+        return truffleUserRngRFFI;
+    }
+
+    @CompilationFinal private CallRFFI truffleCallRFFI;
+
+    @Override
+    public CallRFFI getCallRFFI() {
+        if (truffleCallRFFI == null) {
+            truffleCallRFFI = new TruffleLLVM_Call();
+        }
+        return truffleCallRFFI;
+    }
+
+    @CompilationFinal private StatsRFFI truffleStatsRFFI;
+
+    @Override
+    public StatsRFFI getStatsRFFI() {
+        if (truffleStatsRFFI == null) {
+            truffleStatsRFFI = new TruffleLLVM_Stats();
+        }
+        return truffleStatsRFFI;
+    }
+
+    @CompilationFinal private RApplRFFI rApplRFFI;
+
+    @Override
+    public RApplRFFI getRApplRFFI() {
+        if (rApplRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            rApplRFFI = new TruffleLLVM_RAppl();
+        }
+        return rApplRFFI;
+    }
+
+    @CompilationFinal private LapackRFFI lapackRFFI;
+
+    @Override
+    public LapackRFFI getLapackRFFI() {
+        if (lapackRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            lapackRFFI = new TruffleLLVM_Lapack();
+        }
+        return lapackRFFI;
+    }
+
+    @CompilationFinal private ToolsRFFI toolsRFFI;
+
+    @Override
+    public ToolsRFFI getToolsRFFI() {
+        if (toolsRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            toolsRFFI = new TruffleLLVM_Tools();
+        }
+        return toolsRFFI;
+    }
+
+    @CompilationFinal private PCRERFFI pcreRFFI;
+
+    @Override
+    public PCRERFFI getPCRERFFI() {
+        if (pcreRFFI == null) {
+            pcreRFFI = new TruffleLLVM_PCRE();
+        }
+        return pcreRFFI;
+    }
+
+    @CompilationFinal private ZipRFFI zipRFFI;
+
+    @Override
+    public ZipRFFI getZipRFFI() {
+        if (zipRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            zipRFFI = new TruffleLLVM_Zip();
+        }
+        return zipRFFI;
+    }
+
+    @CompilationFinal private MiscRFFI miscRFFI;
+
+    @Override
+    public MiscRFFI getMiscRFFI() {
+        if (miscRFFI == null) {
+            miscRFFI = new TruffleLLVM_Misc();
+        }
+        return miscRFFI;
+    }
+
+    private REmbedRFFI rEmbedRFFI;
+
+    @Override
+    public REmbedRFFI getREmbedRFFI() {
+        if (rEmbedRFFI == null) {
+            rEmbedRFFI = new TruffleLLVM_REmbed();
+        }
+        return rEmbedRFFI;
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Stats.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Stats.java
similarity index 91%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Stats.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Stats.java
index b3e1d89e4f..6de6f46056 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_Stats.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Stats.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.llvm;
+package com.oracle.truffle.r.ffi.impl.llvm;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.ImportStatic;
@@ -28,10 +28,10 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.engine.interop.NativeDoubleArray;
-import com.oracle.truffle.r.engine.interop.NativeIntegerArray;
-import com.oracle.truffle.r.engine.interop.ffi.llvm.TruffleLLVM_StatsFactory.ExecuteFactorNodeGen;
-import com.oracle.truffle.r.engine.interop.ffi.llvm.TruffleLLVM_StatsFactory.ExecuteWorkNodeGen;
+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.context.RContext.ContextState;
@@ -75,17 +75,10 @@ public class TruffleLLVM_Stats implements StatsRFFI {
             return this;
         }
 
-        @Override
-        public void beforeDestroy(RContext context) {
-        }
-    }
-
-    static ContextStateImpl newContextState() {
-        return new ContextStateImpl();
     }
 
     public abstract static class LookupAdapter extends Node {
-        @Child DLLRFFI.DLSymNode dllSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child private DLLRFFI.DLSymNode dllSymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
 
         public SymbolHandle lookup(String name) {
             DLLInfo dllInfo = DLL.findLibrary("stats");
@@ -184,7 +177,7 @@ public class TruffleLLVM_Stats implements StatsRFFI {
     }
 
     public static class Truffle_FactorNode extends FactorNode {
-        @Child ExecuteFactor executeFactor = ExecuteFactor.create();
+        @Child private ExecuteFactor executeFactor = ExecuteFactor.create();
 
         @Override
         public void execute(int n, int[] pmaxf, int[] pmaxp) {
@@ -193,7 +186,7 @@ public class TruffleLLVM_Stats implements StatsRFFI {
     }
 
     public static class Truffle_WorkNode extends WorkNode {
-        @Child ExecuteWork executeWork = ExecuteWork.create();
+        @Child private ExecuteWork executeWork = ExecuteWork.create();
 
         @Override
         public int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Tools.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Tools.java
new file mode 100644
index 0000000000..3bb3da3e89
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Tools.java
@@ -0,0 +1,82 @@
+/*
+ * 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 java.io.IOException;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.r.ffi.impl.common.Generic_Tools;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.conn.RConnection;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.context.RContext.ContextState;
+import com.oracle.truffle.r.runtime.data.RLogicalVector;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
+import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
+
+public class TruffleLLVM_Tools implements ToolsRFFI {
+
+    private static TruffleObject truffleToolsTruffleObject;
+
+    TruffleLLVM_Tools() {
+        truffleToolsTruffleObject = JavaInterop.asTruffleObject(this);
+    }
+
+    static class ContextStateImpl implements RContext.ContextState {
+        @Override
+        public ContextState initialize(RContext context) {
+            RFFIFactory.getRFFI().getToolsRFFI();
+            context.addExportedSymbol("_fastr_rffi_tools", truffleToolsTruffleObject);
+            return this;
+        }
+    }
+
+    private static class TruffleLLVM_ToolsRFFINode extends Generic_Tools.Generic_ToolsRFFINode {
+        /**
+         * Invoke C implementation, N.B., code is not thread safe.
+         */
+        @Override
+        public synchronized Object execute(RConnection con, REnvironment srcfile, RLogicalVector verbose, RLogicalVector fragment, RStringVector basename, RLogicalVector warningCalls, Object macros,
+                        RLogicalVector warndups) {
+            Object result = super.execute(con, srcfile, verbose, fragment, basename, warningCalls, macros, warndups);
+            return result;
+        }
+    }
+
+    public static int getC(RConnection conn) {
+        try {
+            return conn.getc();
+        } catch (IOException ex) {
+            throw RInternalError.shouldNotReachHere(ex);
+        }
+    }
+
+    @Override
+    public ParseRdNode createParseRdNode() {
+        return new TruffleLLVM_ToolsRFFINode();
+    }
+
+}
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
new file mode 100644
index 0000000000..9c80821300
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UpCallsRFFIImpl.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * 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.ffi.impl.llvm.TruffleLLVM_Utils.checkNativeAddress;
+
+import java.nio.charset.StandardCharsets;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+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.interop.NativeDoubleArray;
+import com.oracle.truffle.r.ffi.impl.interop.NativeIntegerArray;
+import com.oracle.truffle.r.ffi.impl.interop.NativeLogicalArray;
+import com.oracle.truffle.r.ffi.impl.interop.NativeRawArray;
+import com.oracle.truffle.r.ffi.impl.upcalls.Callbacks;
+import com.oracle.truffle.r.runtime.REnvVars;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RDouble;
+import com.oracle.truffle.r.runtime.data.RInteger;
+import com.oracle.truffle.r.runtime.data.RLogical;
+import com.oracle.truffle.r.runtime.data.RScalar;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
+
+/**
+ * (Incomplete) Variant of {@link JavaUpCallsRFFIImpl} for Truffle LLVM.
+ *
+ */
+public class TruffleLLVM_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
+
+    public Object charSXPToNativeCharArray(Object x) {
+        CharSXPWrapper chars = RFFIUtils.guaranteeInstanceOf(x, CharSXPWrapper.class);
+        return new NativeCharArray(chars.getContents().getBytes());
+    }
+
+    // Checkstyle: stop method name check
+
+    @Override
+    public Object Rf_mkCharLenCE(Object obj, int len, int encoding) {
+        if (obj instanceof NativeCharArray) {
+            byte[] bytes = ((NativeCharArray) obj).getValue();
+            return super.Rf_mkCharLenCE(bytes, bytes.length, encoding);
+        } else {
+            throw RInternalError.unimplemented();
+        }
+    }
+
+    @Override
+    public int Rf_error(Object msg) {
+        if (msg instanceof NativeCharArray) {
+            String smsg = new String(((NativeCharArray) msg).getValue(), StandardCharsets.UTF_8);
+            super.Rf_error(smsg);
+        } else {
+            throw RInternalError.unimplemented();
+        }
+        return 0;
+    }
+
+    @Override
+    public Object Rf_install(Object name) {
+        if (name instanceof NativeCharArray) {
+            return RDataFactory.createSymbolInterned(new String(((NativeCharArray) name).getValue(), StandardCharsets.UTF_8));
+        } else {
+            throw RInternalError.unimplemented();
+        }
+    }
+
+    @Override
+    public Object RAW(Object x) {
+        byte[] value = (byte[]) super.RAW(x);
+        return new NativeRawArray(value);
+    }
+
+    @Override
+    public Object LOGICAL(Object x) {
+        byte[] value = (byte[]) super.LOGICAL(x);
+        return new NativeLogicalArray(x, value);
+    }
+
+    @Override
+    public Object INTEGER(Object x) {
+        int[] value = (int[]) super.INTEGER(checkNativeAddress(x));
+        return new NativeIntegerArray(x, value);
+    }
+
+    @Override
+    public Object REAL(Object x) {
+        // Special handling in Truffle variant
+        double[] value = (double[]) super.REAL(x);
+        return new NativeDoubleArray(x, value);
+    }
+
+    @Override
+    public Object R_Home() {
+        byte[] sbytes = REnvVars.rHome().getBytes();
+        return new NativeCharArray(sbytes);
+    }
+
+    @Override
+    public Object Rf_findVar(Object symbolArg, Object envArg) {
+        Object v = super.Rf_findVar(symbolArg, checkNativeAddress(envArg));
+        if (v instanceof RTypedValue) {
+            return v;
+        } else {
+            return wrapPrimitive(v);
+        }
+    }
+
+    @Override
+    public int Rf_defineVar(Object symbolArg, Object value, Object envArg) {
+        super.Rf_defineVar(symbolArg, value, checkNativeAddress(envArg));
+        return 0;
+    }
+
+    @Override
+    @TruffleBoundary
+    public int Rf_setAttrib(Object obj, Object name, Object val) {
+        super.Rf_setAttrib(checkNativeAddress(obj), name, checkNativeAddress(val));
+        return 0;
+    }
+
+    @Override
+    public Object Rf_getAttrib(Object obj, Object name) {
+        Object checkedObj = checkNativeAddress(obj);
+        return super.Rf_getAttrib(checkedObj, name);
+    }
+
+    @Override
+    public Object Rf_cons(Object car, Object cdr) {
+        return super.Rf_cons(checkNativeAddress(car), checkNativeAddress(cdr));
+    }
+
+    @Override
+    public Object CAR(Object e) {
+        return super.CAR(checkNativeAddress(e));
+    }
+
+    @Override
+    public Object CDR(Object e) {
+        return super.CDR(checkNativeAddress(e));
+    }
+
+    @Override
+    public Object CADR(Object e) {
+        Object ne = checkNativeAddress(e);
+        return super.CADR(ne);
+    }
+
+    @Override
+    public Object SETCAR(Object x, Object y) {
+        return super.SETCAR(checkNativeAddress(x), y);
+    }
+
+    public Object bytesToNativeCharArray(byte[] bytes) {
+        Object result = new NativeCharArray(bytes);
+        return result;
+    }
+
+    private static RScalar wrapPrimitive(Object x) {
+        if (x instanceof Double) {
+            return RDouble.valueOf((double) x);
+        } else if (x instanceof Integer) {
+            return RInteger.valueOf((int) x);
+        } else if (x instanceof Byte) {
+            return RLogical.valueOf((byte) x);
+        } else {
+            throw RInternalError.shouldNotReachHere();
+        }
+    }
+
+    @Override
+    public Object R_CHAR(Object x) {
+        throw RInternalError.unimplemented();
+    }
+
+    public Object getCallback(int index) {
+        return Callbacks.values()[index].call;
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_UserRng.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UserRng.java
similarity index 98%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_UserRng.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UserRng.java
index 74dbb58f09..05bb289e37 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/llvm/TruffleLLVM_UserRng.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_UserRng.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.llvm;
+package com.oracle.truffle.r.ffi.impl.llvm;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Utils.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Utils.java
new file mode 100644
index 0000000000..47ea412fb2
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Utils.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.ffi.impl.llvm;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+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.ffi.impl.interop.NativePointer;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RTruffleObject;
+import com.oracle.truffle.r.runtime.ffi.RFFIRootNode;
+
+public class TruffleLLVM_Utils {
+    public static long getNativeAddress(TruffleObject llvmTruffleAddress) {
+        if (asPointerRootNode == null) {
+            asPointerRootNode = new AsPointerRootNode();
+        }
+        long result = (long) asPointerRootNode.getCallTarget().call(llvmTruffleAddress);
+        return result;
+    }
+
+    static Object checkNativeAddress(Object object) {
+        if (object instanceof RTruffleObject) {
+            return object;
+        }
+        TruffleObject useObj = (TruffleObject) object;
+        TruffleObject foo = NativePointer.check(useObj);
+        if (foo != null) {
+            useObj = foo;
+        }
+        return useObj;
+    }
+
+    static final class AsPointerRootNode extends RFFIRootNode<AsPointerNode> {
+        private AsPointerRootNode() {
+            super(new AsPointerNode());
+        }
+
+        @Override
+        public Object execute(VirtualFrame frame) {
+            Object[] args = frame.getArguments();
+            return rffiNode.execute((TruffleObject) args[0]);
+        }
+    }
+
+    private static AsPointerRootNode asPointerRootNode;
+
+    private static class AsPointerNode extends Node {
+        @Child private Node asPointerMessageNode = Message.AS_POINTER.createNode();
+
+        public long execute(TruffleObject pointer) {
+            try {
+                long result = ForeignAccess.sendAsPointer(asPointerMessageNode, pointer);
+                return result;
+            } catch (InteropException ex) {
+                throw RInternalError.shouldNotReachHere(ex);
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Zip.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Zip.java
new file mode 100644
index 0000000000..c6a8fab0ed
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Zip.java
@@ -0,0 +1,94 @@
+/*
+ * 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.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.InteropException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.interop.NativeRawArray;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.DLL;
+import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
+import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
+
+public class TruffleLLVM_Zip implements ZipRFFI {
+    private static class TruffleLLVM_CompressNode extends ZipRFFI.CompressNode {
+        @Child private Node message = LLVMFunction.compress.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(byte[] dest, byte[] source) {
+            NativeRawArray nativeDest = new NativeRawArray(dest);
+            NativeRawArray nativeSource = new NativeRawArray(source);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.compress.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(),
+                                nativeDest, dest.length, nativeSource, source.length);
+                return result;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } finally {
+                nativeDest.getValue();
+            }
+        }
+    }
+
+    private static class TruffleLLVM_UncompressNode extends ZipRFFI.UncompressNode {
+        @Child private Node message = LLVMFunction.uncompress.createMessage();
+        @CompilationFinal private SymbolHandle symbolHandle;
+
+        @Override
+        public int execute(byte[] dest, byte[] source) {
+            NativeRawArray nativeDest = new NativeRawArray(dest);
+            NativeRawArray nativeSource = new NativeRawArray(source);
+            try {
+                if (symbolHandle == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    symbolHandle = DLL.findSymbol(LLVMFunction.uncompress.callName, null);
+                }
+                int result = (int) ForeignAccess.sendExecute(message, symbolHandle.asTruffleObject(),
+                                nativeDest, dest.length, nativeSource, source.length);
+                return result;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            } 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/llvm/package-info.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/package-info.java
new file mode 100644
index 0000000000..17f5666117
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+/**
+ * The Truffle LLVM implementation of the {@link com.oracle.truffle.r.runtime.ffi.RFFI} interfaces.
+ */
+package com.oracle.truffle.r.ffi.impl.llvm;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/FilesystemUtils.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/FilesystemUtils.java
similarity index 97%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/FilesystemUtils.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/FilesystemUtils.java
index ea6d2db107..5a4a822f7a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/FilesystemUtils.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/FilesystemUtils.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.managed;
+package com.oracle.truffle.r.ffi.impl.managed;
 
 import java.nio.file.attribute.PosixFilePermission;
 import java.util.EnumSet;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_Base.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_Base.java
similarity index 96%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_Base.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_Base.java
index 988632777a..b9af2ea496 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_Base.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_Base.java
@@ -20,10 +20,10 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.managed;
+package com.oracle.truffle.r.ffi.impl.managed;
 
-import static com.oracle.truffle.r.runtime.ffi.managed.FilesystemUtils.permissionsFromMode;
-import static com.oracle.truffle.r.runtime.ffi.managed.Managed_RFFIFactory.unsupported;
+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;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_LapackRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_LapackRFFI.java
similarity index 94%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_LapackRFFI.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_LapackRFFI.java
index 2b532244fe..d6254bbab3 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_LapackRFFI.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_LapackRFFI.java
@@ -20,9 +20,9 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.managed;
+package com.oracle.truffle.r.ffi.impl.managed;
 
-import static com.oracle.truffle.r.runtime.ffi.managed.Managed_RFFIFactory.unsupported;
+import static com.oracle.truffle.r.ffi.impl.managed.Managed_RFFIFactory.unsupported;
 
 import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_PCRERFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_PCRERFFI.java
similarity index 92%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_PCRERFFI.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_PCRERFFI.java
index 622b78cc0c..7fe533a23f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_PCRERFFI.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_PCRERFFI.java
@@ -20,9 +20,9 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.managed;
+package com.oracle.truffle.r.ffi.impl.managed;
 
-import static com.oracle.truffle.r.runtime.ffi.managed.Managed_RFFIFactory.unsupported;
+import static com.oracle.truffle.r.ffi.impl.managed.Managed_RFFIFactory.unsupported;
 
 import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_REmbedRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java
similarity index 92%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_REmbedRFFI.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java
index 9d0a0a079f..17295f062a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_REmbedRFFI.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_REmbedRFFI.java
@@ -20,9 +20,9 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.managed;
+package com.oracle.truffle.r.ffi.impl.managed;
 
-import static com.oracle.truffle.r.runtime.ffi.managed.Managed_RFFIFactory.unsupported;
+import static com.oracle.truffle.r.ffi.impl.managed.Managed_RFFIFactory.unsupported;
 
 import com.oracle.truffle.r.runtime.ffi.REmbedRFFI;
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_RFFIFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java
similarity index 99%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_RFFIFactory.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java
index 36a30ba75a..68f7bf8497 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/managed/Managed_RFFIFactory.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/Managed_RFFIFactory.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi.managed;
+package com.oracle.truffle.r.ffi.impl.managed;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.RError;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/package-info.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/package-info.java
new file mode 100644
index 0000000000..6907d5b19c
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/managed/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+/**
+ * The "Managed", aka no-native code, partial implementation of the
+ * {@link com.oracle.truffle.r.runtime.ffi.RFFI} interfaces.
+ */
+package com.oracle.truffle.r.ffi.impl.managed;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/NFIFunction.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/NFIFunction.java
new file mode 100644
index 0000000000..dec5273c90
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/NFIFunction.java
@@ -0,0 +1,113 @@
+/*
+ * 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.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node;
+
+/**
+ * Enumerates all the C functions that are internal to the implementation and called via Truffle
+ * NFI. Some of the functions are called directly, but most are called via a wrapper, typically to
+ * enable a callback with a complex result or, in the case of Fortran, to handle call by reference
+ * conveniently. The wrapper functions names are all of the form {@code call_xxx_function}, where
+ * {@code xxx} is the subsystem.
+ *
+ */
+enum NFIFunction {
+    // base
+    getpid("(): sint32"),
+    getcwd("([uint8], sint32): sint32"),
+    chdir("(string): sint32"),
+    mkdir("(string, sint32): sint32"),
+    readlink("((string, sint32): void, string): void", "call_base_"),
+    mkdtemp("([uint8]): sint32"),
+    chmod("(string, sint32): sint32"),
+    strtol("((sint64, sint32): void, string, sint32): void", "call_base_"),
+    uname("((string, string, string, string, string): void): void", "call_base_"),
+    glob("(string, (string): void): void", "call_base_"),
+    // PCRE, N.B. The "pcre_" prefixes are actually direct calls
+    maketables("(): sint64", "pcre_"),
+    compile("((uint64, string, sint32): void, string, sint32, uint64): void", "call_pcre_"),
+    getcapturecount("(uint64, uint64): sint32", "call_pcre_"),
+    getcapturenames("((sint32, string): void, uint64, uint64): sint32", "call_pcre_"),
+    study("(uint64, sint32): void", "call_pcre_"),
+    exec("(uint64, uint64, [uint8], sint32, sint32, sint32, [sint32], sint32): sint32", "pcre_"),
+    // RAppl
+    dqrdc2("([double], sint32, sint32, sint32, double, [sint32], [double], [sint32], [double]): void", "call_appl_"),
+    dqrcf("([double], sint32, sint32, [double], [double], sint32, [double], [sint32]): void", "call_appl_"),
+    dqrls("([double], sint32, sint32, [double], sint32, double, [double], [double], [double], [sint32], [sint32], [double], [double]): void", "call_appl_"),
+    // zip
+    compress("([uint8], [uint64], [uint8], uint64): sint32"),
+    uncompress("([uint8], [uint64], [uint8], uint64): sint32"),
+    // lapack
+    ilaver("([sint32]): void", "call_lapack_"),
+    dgeev("(uint8, uint8, sint32, [double], sint32, [double], [double], [double], sint32, [double], sint32, [double], sint32) : sint32", "call_lapack_"),
+    dgeqp3("(sint32, sint32, [double], sint32, [sint32], [double], [double], sint32) : sint32", "call_lapack_"),
+    dormq("(uint8, uint8, sint32, sint32, sint32, [double], sint32, [double], [double], sint32, [double], sint32) : sint32", "call_lapack_"),
+    dtrtrs("(uint8, uint8, uint8, sint32, sint32, [double], sint32, [double], sint32) : sint32", "call_lapack_"),
+    dgetrf("(sint32, sint32, [double], sint32, [sint32]) : sint32", "call_lapack_"),
+    dpotrf("(uint8, sint32, [double], sint32) : sint32", "call_lapack_"),
+    dpotri("(uint8, sint32, [double], sint32) : sint32", "call_lapack_"),
+    dpstrf("uint8, sint32, [double], sint32, [sint32], [sint32], double, [double]) : sint32", "call_lapack_"),
+    dgesv("(sint32, sint32, [double], sint32, [sint32], [double], sint32) : sint32", "call_lapack_"),
+    dlange("(uint8, sint32, sint32, [double], sint32, [double]) : double", "call_lapack_"),
+    dgecon("(uint8, sint32, [double], sint32, double, [double], [double], [sint32]) : sint32", "call_lapack_"),
+    //@formatter:off
+    dsyevr("(uint8, uint8, uint8, sint32, [double], sint32, double, double, sint32, sint32, double, [sint32], [double], [double], sint32, [sint32], [double], sint32, " +
+                    "[sint32], sint32) : sint32", "call_lapack_");
+    //@formatter:on
+
+    private final int argCount;
+    private final String signature;
+    private final String callName;
+    @CompilationFinal private TruffleObject function;
+
+    NFIFunction(String signature) {
+        this.argCount = TruffleNFI_Utils.getArgCount(signature);
+        this.signature = signature;
+        this.callName = name();
+    }
+
+    NFIFunction(String signature, String prefix) {
+        this.argCount = TruffleNFI_Utils.getArgCount(signature);
+        this.signature = signature;
+        this.callName = prefix + name();
+    }
+
+    Node createMessage() {
+        CompilerAsserts.neverPartOfCompilation();
+        return Message.createExecute(argCount).createNode();
+    }
+
+    TruffleObject getFunction() {
+        if (function == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            function = TruffleNFI_Utils.lookupAndBind(callName, signature);
+        }
+        return function;
+    }
+}
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
new file mode 100644
index 0000000000..f908c5e466
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Base.java
@@ -0,0 +1,275 @@
+/*
+ * 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.io.IOException;
+import java.util.ArrayList;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.interop.ForeignAccess;
+import com.oracle.truffle.api.interop.InteropException;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.interop.base.GlobResult;
+import com.oracle.truffle.r.ffi.impl.interop.base.ReadlinkResult;
+import com.oracle.truffle.r.ffi.impl.interop.base.StrtolResult;
+import com.oracle.truffle.r.ffi.impl.interop.base.UnameResult;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.BaseRFFI;
+
+public class TruffleNFI_Base implements BaseRFFI {
+
+    public static class TruffleNFI_GetpidNode extends GetpidNode {
+        @Child private Node message = NFIFunction.getpid.createMessage();
+
+        @Override
+        public int execute() {
+            try {
+                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.getpid.getFunction());
+                return result;
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleNFI_GetwdNode extends GetwdNode {
+        @Child private Node message = NFIFunction.getcwd.createMessage();
+
+        @TruffleBoundary
+        @Override
+        public String execute() {
+            try {
+                byte[] buf = new byte[4096];
+                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.getcwd.getFunction(), JavaInterop.asTruffleObject(buf), buf.length);
+                if (result == 0) {
+                    return null;
+                } else {
+                    int i = 0;
+                    while (buf[i] != 0 && i < buf.length) {
+                        i++;
+                    }
+                    return new String(buf, 0, i);
+                }
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleNFI_SetwdNode extends SetwdNode {
+        @Child private Node message = NFIFunction.chdir.createMessage();
+
+        @Override
+        public int execute(String dir) {
+            try {
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.chdir.getFunction(), dir);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleNFI_MkdirNode extends MkdirNode {
+        @Child private Node message = NFIFunction.mkdir.createMessage();
+
+        @Override
+        public void execute(String dir, int mode) throws IOException {
+            try {
+                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.mkdir.getFunction(), dir, mode);
+                if (result != 0) {
+                    throw new IOException("mkdir " + dir + " failed");
+                }
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleNFI_ReadlinkNode extends ReadlinkNode {
+        private static final int EINVAL = 22;
+
+        @Child private Node message = NFIFunction.readlink.createMessage();
+
+        @Override
+        public String execute(String path) throws IOException {
+            try {
+                ReadlinkResult data = new ReadlinkResult();
+                ForeignAccess.sendExecute(message, NFIFunction.readlink.getFunction(), data, path);
+                if (data.getLink() == null) {
+                    if (data.getErrno() == EINVAL) {
+                        return path;
+                    } else {
+                        // some other error
+                        throw new IOException("readlink failed: " + data.getErrno());
+                    }
+                }
+                return data.getLink();
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleNFI_MkdtempNode extends MkdtempNode {
+        @Child private Node message = NFIFunction.mkdtemp.createMessage();
+
+        @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;
+            try {
+                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.mkdtemp.getFunction(), JavaInterop.asTruffleObject(ztbytes));
+                if (result == 0) {
+                    return null;
+                } else {
+                    return new String(ztbytes, 0, bytes.length);
+                }
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleNFI_ChmodNode extends ChmodNode {
+        @Child private Node message = NFIFunction.chmod.createMessage();
+
+        @Override
+        public int execute(String path, int mode) {
+            try {
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.chmod.getFunction(), path, mode);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleNFI_StrolNode extends StrolNode {
+
+        @Child private Node message = NFIFunction.strtol.createMessage();
+
+        @Override
+        public long execute(String s, int base) throws IllegalArgumentException {
+            try {
+                StrtolResult data = new StrtolResult();
+                ForeignAccess.sendExecute(message, NFIFunction.strtol.getFunction(), data, s, base);
+                if (data.getErrno() != 0) {
+                    throw new IllegalArgumentException("strtol failure");
+                } else {
+                    return data.getResult();
+                }
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+        }
+    }
+
+    public static class TruffleNFI_UnameNode extends UnameNode {
+
+        @Child private Node message = NFIFunction.uname.createMessage();
+
+        @Override
+        public UtsName execute() {
+            UnameResult data = new UnameResult();
+            try {
+                ForeignAccess.sendExecute(message, NFIFunction.uname.getFunction(), data);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+            return data;
+        }
+    }
+
+    public static class TruffleNFI_GlobNode extends GlobNode {
+
+        @Child private Node message = NFIFunction.glob.createMessage();
+
+        @Override
+        public ArrayList<String> glob(String pattern) {
+            GlobResult data = new GlobResult();
+            try {
+                ForeignAccess.sendExecute(message, NFIFunction.glob.getFunction(), pattern, data);
+            } catch (InteropException e) {
+                throw RInternalError.shouldNotReachHere(e);
+            }
+            return data.getPaths();
+        }
+    }
+
+    @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 StrolNode createStrolNode() {
+        return new TruffleNFI_StrolNode();
+    }
+
+    @Override
+    public UnameNode createUnameNode() {
+        return new TruffleNFI_UnameNode();
+    }
+
+    @Override
+    public GlobNode createGlobNode() {
+        return new TruffleNFI_GlobNode();
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
new file mode 100644
index 0000000000..939549b4f6
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_C.java
@@ -0,0 +1,158 @@
+/*
+ * 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.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
+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.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_CFactory.TruffleNFI_InvokeCNodeGen;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.ffi.CRFFI;
+import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
+
+public class TruffleNFI_C implements CRFFI {
+    public abstract static class TruffleNFI_InvokeCNode extends InvokeCNode {
+
+        @Child private Node bindNode = Message.createInvoke(1).createNode();
+
+        @Specialization(guards = "args.length == 0")
+        protected void invokeCall0(NativeCallInfo nativeCallInfo, @SuppressWarnings("unused") Object[] args,
+                        @Cached("createExecute(args.length)") Node executeNode) {
+            synchronized (TruffleNFI_Call.class) {
+                try {
+                    TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode,
+                                    nativeCallInfo.address.asTruffleObject(), "bind", "(): void");
+                    ForeignAccess.sendExecute(executeNode, callFunction);
+                } catch (InteropException ex) {
+                    throw RInternalError.shouldNotReachHere(ex);
+                }
+            }
+        }
+
+        @Specialization(guards = "args.length == 1")
+        protected void invokeCall1(NativeCallInfo nativeCallInfo, Object[] args,
+                        @Cached("createExecute(args.length)") Node executeNode) {
+            synchronized (TruffleNFI_Call.class) {
+                try {
+                    Object[] nargs = new Object[args.length];
+                    TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode,
+                                    nativeCallInfo.address.asTruffleObject(), "bind", getSignature(args, nargs));
+                    ForeignAccess.sendExecute(executeNode, callFunction, nargs[0]);
+                } catch (InteropException ex) {
+                    throw RInternalError.shouldNotReachHere(ex);
+                }
+            }
+        }
+
+        @Specialization(guards = "args.length == 2")
+        protected void invokeCall2(NativeCallInfo nativeCallInfo, Object[] args,
+                        @Cached("createExecute(args.length)") Node executeNode) {
+            synchronized (TruffleNFI_Call.class) {
+                try {
+                    Object[] nargs = new Object[args.length];
+                    TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode,
+                                    nativeCallInfo.address.asTruffleObject(), "bind", getSignature(args, nargs));
+                    ForeignAccess.sendExecute(executeNode, callFunction, nargs[0], nargs[1]);
+                } catch (InteropException ex) {
+                    throw RInternalError.shouldNotReachHere(ex);
+                }
+            }
+        }
+
+        @Specialization(guards = "args.length == 3")
+        protected void invokeCall3(NativeCallInfo nativeCallInfo, Object[] args,
+                        @Cached("createExecute(args.length)") Node executeNode) {
+            synchronized (TruffleNFI_Call.class) {
+                try {
+                    Object[] nargs = new Object[args.length];
+                    TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode,
+                                    nativeCallInfo.address.asTruffleObject(), "bind", getSignature(args, nargs));
+                    ForeignAccess.sendExecute(executeNode, callFunction, nargs[0], nargs[1], nargs[2]);
+                } catch (InteropException ex) {
+                    throw RInternalError.shouldNotReachHere(ex);
+                }
+            }
+        }
+
+        @Specialization(guards = "args.length == 4")
+        protected void invokeCall4(NativeCallInfo nativeCallInfo, Object[] args,
+                        @Cached("createExecute(args.length)") Node executeNode) {
+            synchronized (TruffleNFI_Call.class) {
+                try {
+                    Object[] nargs = new Object[args.length];
+                    TruffleObject callFunction = (TruffleObject) ForeignAccess.sendInvoke(bindNode,
+                                    nativeCallInfo.address.asTruffleObject(), "bind", getSignature(args, nargs));
+                    ForeignAccess.sendExecute(executeNode, callFunction, nargs[0], nargs[1], nargs[2], nargs[3]);
+                } catch (InteropException ex) {
+                    throw RInternalError.shouldNotReachHere(ex);
+                }
+            }
+        }
+
+        @Fallback
+        protected void invokeCallN(@SuppressWarnings("unused") NativeCallInfo nativeCallInfo, @SuppressWarnings("unused") Object[] args) {
+            synchronized (TruffleNFI_Call.class) {
+                throw RInternalError.unimplemented(".C (too many args)");
+            }
+        }
+
+        public static Node createExecute(int n) {
+            return Message.createExecute(n).createNode();
+        }
+
+    }
+
+    private static String getSignature(Object[] args, Object[] nargs) {
+        StringBuilder sb = new StringBuilder();
+        sb.append('(');
+        for (int i = 0; i < args.length; i++) {
+            Object arg = args[i];
+            if (arg instanceof int[]) {
+                sb.append("[sint32]");
+            } else if (arg instanceof double[]) {
+                sb.append("[double]");
+            } else if (arg instanceof byte[]) {
+                sb.append("[uint8]");
+            } else {
+                throw RInternalError.unimplemented(".C type: " + arg.getClass().getSimpleName());
+            }
+            nargs[i] = JavaInterop.asTruffleObject(arg);
+            if (i < args.length - 1) {
+                sb.append(", ");
+            }
+        }
+        sb.append("): void");
+        return sb.toString();
+    }
+
+    @Override
+    public InvokeCNode createInvokeCNode() {
+        return TruffleNFI_InvokeCNodeGen.create();
+    }
+}
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_CAccess.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_CAccess.java
similarity index 96%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_CAccess.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_CAccess.java
index a01fd2b14c..455749f2a4 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_CAccess.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_CAccess.java
@@ -20,17 +20,17 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.ffi.impl.common.LibPaths;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
-import com.oracle.truffle.r.runtime.ffi.LibPaths;
 
 public class TruffleNFI_CAccess {
     private static TruffleNFI_DLL.NFIHandle handle;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Call.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java
similarity index 94%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Call.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java
index 85f48b8d1d..a960f40485 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Call.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Call.java
@@ -20,11 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceDownCall;
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceDownCallReturn;
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.traceEnabled;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCall;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceDownCallReturn;
+import static com.oracle.truffle.r.ffi.impl.common.RFFIUtils.traceEnabled;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -34,17 +34,18 @@ import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.engine.interop.RNullMR;
-import com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_CallFactory.TruffleNFI_InvokeCallNodeGen;
-import com.oracle.truffle.r.nodes.ffi.RFFIUpCallMethod;
-import com.oracle.truffle.r.nodes.ffi.RFFIUtils;
+import com.oracle.truffle.r.ffi.impl.upcalls.RFFIUpCallTable;
+import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
+import com.oracle.truffle.r.ffi.impl.upcalls.Callbacks;
+import com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_CallFactory.TruffleNFI_InvokeCallNodeGen;
+import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 import com.oracle.truffle.r.runtime.ffi.RFFIVariables;
-import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 
 public class TruffleNFI_Call implements CallRFFI {
@@ -90,7 +91,7 @@ public class TruffleNFI_Call implements CallRFFI {
         for (int i = 0; i < variables.length; i++) {
             RFFIVariables var = variables[i];
             Object value = var.getValue();
-            if (value == null) {
+            if (value == null || var.alwaysUpCall) {
                 continue;
             }
             try {
@@ -113,14 +114,13 @@ public class TruffleNFI_Call implements CallRFFI {
         Node bind = Message.createInvoke(1).createNode();
         Node executeNode = Message.createExecute(1).createNode();
         SymbolHandle symbolHandle = DLL.findSymbol("Rinternals_addCallback", null);
-        TruffleObject upCallsObject = JavaInterop.asTruffleObject(upCallsImpl);
-        Node readNode = Message.READ.createNode();
+
         try {
-            for (RFFIUpCallMethod upCallMethod : RFFIUpCallMethod.values()) {
-                Object upCallMethodObject = ForeignAccess.sendRead(readNode, upCallsObject, upCallMethod.name());
-                String addCallbackSignature = String.format("(sint32, %s): void", upCallMethod.nfiSignature);
+            Callbacks.createCalls(upCallsImpl);
+            for (Callbacks callback : Callbacks.values()) {
+                String addCallbackSignature = String.format("(sint32, %s): void", callback.nfiSignature);
                 TruffleObject addCallbackFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, symbolHandle.asTruffleObject(), "bind", addCallbackSignature);
-                ForeignAccess.sendExecute(executeNode, addCallbackFunction, upCallMethod.ordinal(), upCallMethodObject);
+                ForeignAccess.sendExecute(executeNode, addCallbackFunction, callback.ordinal(), callback.call);
             }
         } catch (Throwable t) {
             throw RInternalError.shouldNotReachHere(t);
@@ -146,7 +146,6 @@ public class TruffleNFI_Call implements CallRFFI {
             this.funName = "return_" + name();
             this.executeNode = Message.createExecute(numArgs).createNode();
         }
-
     }
 
     private static void initReturnArray() {
@@ -217,8 +216,8 @@ public class TruffleNFI_Call implements CallRFFI {
             traceDownCall("initialize");
         }
         try {
-            initVariables();
             initCallbacks(upCallsImpl);
+            initVariables();
             initReturnArray();
         } finally {
             if (traceEnabled()) {
@@ -228,7 +227,7 @@ public class TruffleNFI_Call implements CallRFFI {
     }
 
     public abstract static class TruffleNFI_InvokeCallNode extends InvokeCallNode {
-        @Child Node bindNode = Message.createInvoke(1).createNode();
+        @Child private Node bindNode = Message.createInvoke(1).createNode();
 
         @Specialization(guards = "args.length == 0")
         protected Object invokeCall0(NativeCallInfo nativeCallInfo, Object[] args,
@@ -411,15 +410,14 @@ public class TruffleNFI_Call implements CallRFFI {
         public static Node createExecute(int n) {
             return Message.createExecute(n).createNode();
         }
-
     }
 
     public static class TruffleNFI_InvokeVoidCallNode extends InvokeVoidCallNode {
         private static final String CallVoid1Sig = "(object): void";
         private static final String CallVoid0Sig = "(): void";
-        @Child Node bindNode = Message.createInvoke(1).createNode();
-        @Child Node execute0Node = Message.createExecute(0).createNode();
-        @Child Node execute1Node = Message.createExecute(1).createNode();
+        @Child private Node bindNode = Message.createInvoke(1).createNode();
+        @Child private Node execute0Node = Message.createExecute(0).createNode();
+        @Child private Node execute1Node = Message.createExecute(1).createNode();
 
         @Override
         public void execute(NativeCallInfo nativeCallInfo, Object[] args) {
@@ -451,7 +449,7 @@ public class TruffleNFI_Call implements CallRFFI {
         if (traceEnabled()) {
             traceDownCall(name, args);
         }
-        boolean isNullSetting = RNullMR.setIsNull(false);
+        boolean isNullSetting = RContext.getRForeignAccessFactory().setIsNull(false);
         TruffleNFI_NativeArray.callEnter(callDepth);
         callDepth++;
         return isNullSetting;
@@ -462,7 +460,7 @@ public class TruffleNFI_Call implements CallRFFI {
             traceDownCallReturn(name, result);
         }
         TruffleNFI_NativeArray.callEnter(callDepth);
-        RNullMR.setIsNull(isNullSetting);
+        RContext.getRForeignAccessFactory().setIsNull(isNullSetting);
         callDepth--;
     }
 
@@ -475,5 +473,4 @@ public class TruffleNFI_Call implements CallRFFI {
     public InvokeVoidCallNode createInvokeVoidCallNode() {
         return new TruffleNFI_InvokeVoidCallNode();
     }
-
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_DLL.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java
similarity index 98%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_DLL.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java
index fe390032a4..e2cc8820c8 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_DLL.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.TruffleLanguage.Env;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Lapack.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Lapack.java
similarity index 64%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Lapack.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Lapack.java
index bfb50624dc..5dc7f533d3 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Lapack.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Lapack.java
@@ -20,75 +20,37 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
 
 public class TruffleNFI_Lapack implements LapackRFFI {
-    enum Function {
-        ilaver("([sint32]): void"),
-        dgeev("(uint8, uint8, sint32, [double], sint32, [double], [double], [double], sint32, [double], sint32, [double], sint32) : sint32"),
-        dgeqp3("(sint32, sint32, [double], sint32, [sint32], [double], [double], sint32) : sint32"),
-        dormq("(uint8, uint8, sint32, sint32, sint32, [double], sint32, [double], [double], sint32, [double], sint32) : sint32"),
-        dtrtrs("(uint8, uint8, uint8, sint32, sint32, [double], sint32, [double], sint32) : sint32"),
-        dgetrf("(sint32, sint32, [double], sint32, [sint32]) : sint32"),
-        dpotrf("(uint8, sint32, [double], sint32) : sint32"),
-        dpotri("(uint8, sint32, [double], sint32) : sint32"),
-        dpstrf("uint8, sint32, [double], sint32, [sint32], [sint32], double, [double]) : sint32"),
-        dgesv("(sint32, sint32, [double], sint32, [sint32], [double], sint32) : sint32"),
-        dlange("(uint8, sint32, sint32, [double], sint32, [double]) : double"),
-        dgecon("(uint8, sint32, [double], sint32, double, [double], [double], [sint32]) : sint32"),
-        dsyevr("(uint8, uint8, uint8, sint32, [double], sint32, double, double, sint32, sint32, double, [sint32], [double], [double], sint32, [sint32], [double], sint32, [sint32], sint32) : sint32");
-
-        private final int argCount;
-        private final String signature;
-        @CompilationFinal private Node executeNode;
-        @CompilationFinal private TruffleObject function;
-
-        Function(String signature) {
-            this.argCount = TruffleNFI_Utils.getArgCount(signature);
-            this.signature = signature;
-        }
-
-        private void initialize() {
-            if (executeNode == null) {
-                executeNode = Message.createExecute(argCount).createNode();
-            }
-            if (function == null) {
-                function = TruffleNFI_Utils.lookupAndBind("call_" + name(), false, signature);
-            }
-        }
-    }
 
     private static class TruffleNFI_IlaverNode extends IlaverNode {
+        @Child private Node message = NFIFunction.ilaver.createMessage();
 
         @Override
         public void execute(int[] version) {
-            Function.ilaver.initialize();
             try {
-                ForeignAccess.sendExecute(Function.ilaver.executeNode, Function.ilaver.function, JavaInterop.asTruffleObject(version));
+                ForeignAccess.sendExecute(message, NFIFunction.ilaver.getFunction(), JavaInterop.asTruffleObject(version));
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
-
         }
     }
 
     private static class TruffleNFI_DgeevNode extends DgeevNode {
+        @Child private Node message = NFIFunction.dgeev.createMessage();
 
         @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) {
-            Function.dgeev.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dgeev.executeNode, Function.dgeev.function, jobVL, jobVR, n, JavaInterop.asTruffleObject(a), lda,
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgeev.getFunction(), jobVL, jobVR, n, JavaInterop.asTruffleObject(a), lda,
                                 JavaInterop.asTruffleObject(wr), JavaInterop.asTruffleObject(wi), JavaInterop.asTruffleObject(vl), ldvl,
                                 JavaInterop.asTruffleObject(vr), ldvr, JavaInterop.asTruffleObject(work), lwork);
             } catch (InteropException e) {
@@ -98,12 +60,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_Dgeqp3Node extends Dgeqp3Node {
+        @Child private Node message = NFIFunction.dgeqp3.createMessage();
 
         @Override
         public int execute(int m, int n, double[] a, int lda, int[] jpvt, double[] tau, double[] work, int lwork) {
-            Function.dgeqp3.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dgeqp3.executeNode, Function.dgeqp3.function, m, n, JavaInterop.asTruffleObject(a), lda, JavaInterop.asTruffleObject(jpvt),
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgeqp3.getFunction(), m, n, JavaInterop.asTruffleObject(a), lda, JavaInterop.asTruffleObject(jpvt),
                                 JavaInterop.asTruffleObject(tau), JavaInterop.asTruffleObject(work), lwork);
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
@@ -112,12 +74,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DormqrNode extends DormqrNode {
+        @Child private Node message = NFIFunction.dormq.createMessage();
 
         @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) {
-            Function.dormq.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dormq.executeNode, Function.dormq.function, side, trans, m, n, k, JavaInterop.asTruffleObject(a), lda,
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dormq.getFunction(), side, trans, m, n, k, JavaInterop.asTruffleObject(a), lda,
                                 JavaInterop.asTruffleObject(tau), JavaInterop.asTruffleObject(c), ldc, JavaInterop.asTruffleObject(work), lwork);
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
@@ -126,12 +88,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DtrtrsNode extends DtrtrsNode {
+        @Child private Node message = NFIFunction.dtrtrs.createMessage();
 
         @Override
         public int execute(char uplo, char trans, char diag, int n, int nrhs, double[] a, int lda, double[] b, int ldb) {
-            Function.dtrtrs.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dtrtrs.executeNode, Function.dtrtrs.function, uplo, trans, diag, n, nrhs, JavaInterop.asTruffleObject(a), lda,
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dtrtrs.getFunction(), uplo, trans, diag, n, nrhs, JavaInterop.asTruffleObject(a), lda,
                                 JavaInterop.asTruffleObject(b), ldb);
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
@@ -140,12 +102,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DgetrfNode extends DgetrfNode {
+        @Child private Node message = NFIFunction.dgetrf.createMessage();
 
         @Override
         public int execute(int m, int n, double[] a, int lda, int[] ipiv) {
-            Function.dgetrf.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dgetrf.executeNode, Function.dgetrf.function, m, n, JavaInterop.asTruffleObject(a), lda, JavaInterop.asTruffleObject(ipiv));
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgetrf.getFunction(), m, n, JavaInterop.asTruffleObject(a), lda, JavaInterop.asTruffleObject(ipiv));
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
@@ -153,12 +115,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DpotrfNode extends DpotrfNode {
+        @Child private Node message = NFIFunction.dpotrf.createMessage();
 
         @Override
         public int execute(char uplo, int n, double[] a, int lda) {
-            Function.dpotrf.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dpotrf.executeNode, Function.dpotrf.function, uplo, n, JavaInterop.asTruffleObject(a), lda);
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dpotrf.getFunction(), uplo, n, JavaInterop.asTruffleObject(a), lda);
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
@@ -166,12 +128,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DpotriNode extends DpotriNode {
+        @Child private Node message = NFIFunction.dpotri.createMessage();
 
         @Override
         public int execute(char uplo, int n, double[] a, int lda) {
-            Function.dpotri.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dpotri.executeNode, Function.dpotri.function, uplo, n, JavaInterop.asTruffleObject(a), lda);
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dpotri.getFunction(), uplo, n, JavaInterop.asTruffleObject(a), lda);
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
@@ -179,12 +141,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DpstrfNode extends DpstrfNode {
+        @Child private Node message = NFIFunction.dpstrf.createMessage();
 
         @Override
         public int execute(char uplo, int n, double[] a, int lda, int[] piv, int[] rank, double tol, double[] work) {
-            Function.dpstrf.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dpstrf.executeNode, Function.dpstrf.function, uplo, n, JavaInterop.asTruffleObject(a), lda,
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dpstrf.getFunction(), uplo, n, JavaInterop.asTruffleObject(a), lda,
                                 JavaInterop.asTruffleObject(piv), JavaInterop.asTruffleObject(rank), tol, JavaInterop.asTruffleObject(work));
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
@@ -193,12 +155,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DgesvNode extends DgesvNode {
+        @Child private Node message = NFIFunction.dgesv.createMessage();
 
         @Override
         public int execute(int n, int nrhs, double[] a, int lda, int[] ipiv, double[] b, int ldb) {
-            Function.dgesv.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dgesv.executeNode, Function.dgesv.function, n, nrhs, JavaInterop.asTruffleObject(a), lda, JavaInterop.asTruffleObject(ipiv),
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgesv.getFunction(), n, nrhs, JavaInterop.asTruffleObject(a), lda, JavaInterop.asTruffleObject(ipiv),
                                 JavaInterop.asTruffleObject(b), ldb);
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
@@ -207,12 +169,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DlangeNode extends DlangeNode {
+        @Child private Node message = NFIFunction.dlange.createMessage();
 
         @Override
         public double execute(char norm, int m, int n, double[] a, int lda, double[] work) {
-            Function.dlange.initialize();
             try {
-                return (double) ForeignAccess.sendExecute(Function.dlange.executeNode, Function.dlange.function, norm, m, n, JavaInterop.asTruffleObject(a), lda,
+                return (double) ForeignAccess.sendExecute(message, NFIFunction.dlange.getFunction(), norm, m, n, JavaInterop.asTruffleObject(a), lda,
                                 JavaInterop.asTruffleObject(work));
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
@@ -221,12 +183,12 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DgeconNode extends DgeconNode {
+        @Child private Node message = NFIFunction.dgecon.createMessage();
 
         @Override
         public int execute(char norm, int n, double[] a, int lda, double anorm, double[] rcond, double[] work, int[] iwork) {
-            Function.dgecon.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dgecon.executeNode, Function.dgecon.function, norm, n, JavaInterop.asTruffleObject(a), lda, anorm, JavaInterop.asTruffleObject(rcond),
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dgecon.getFunction(), norm, n, JavaInterop.asTruffleObject(a), lda, anorm, JavaInterop.asTruffleObject(rcond),
                                 JavaInterop.asTruffleObject(work), JavaInterop.asTruffleObject(iwork));
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
@@ -235,13 +197,13 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     }
 
     private static class TruffleNFI_DsyevrNode extends DsyevrNode {
+        @Child private Node message = NFIFunction.dsyevr.createMessage();
 
         @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) {
-            Function.dsyevr.initialize();
             try {
-                return (int) ForeignAccess.sendExecute(Function.dsyevr.executeNode, Function.dsyevr.function, jobz, range, uplo, n, JavaInterop.asTruffleObject(a),
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.dsyevr.getFunction(), jobz, range, uplo, n, JavaInterop.asTruffleObject(a),
                                 lda, vl, vu, il, iu, abstol, JavaInterop.asTruffleObject(m), JavaInterop.asTruffleObject(w), JavaInterop.asTruffleObject(z), ldz,
                                 JavaInterop.asTruffleObject(isuppz), JavaInterop.asTruffleObject(work), lwork, JavaInterop.asTruffleObject(iwork), liwork);
             } catch (InteropException e) {
@@ -314,5 +276,4 @@ public class TruffleNFI_Lapack implements LapackRFFI {
     public DsyevrNode createDsyevrNode() {
         return new TruffleNFI_DsyevrNode();
     }
-
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Misc.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Misc.java
similarity index 83%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Misc.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Misc.java
index 9baa8a5011..80d3abefe8 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Misc.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Misc.java
@@ -20,8 +20,10 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.Message;
@@ -34,13 +36,14 @@ import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 public class TruffleNFI_Misc implements MiscRFFI {
 
     private static class TruffleNFI_ExactSumNode extends ExactSumNode {
-        @Child Node messageNode = Message.createExecute(4).createNode();
-        private TruffleObject function;
+        @Child private Node messageNode = Message.createExecute(4).createNode();
+        @CompilationFinal private TruffleObject function;
 
         @Override
         public double execute(double[] values, boolean hasNa, boolean naRm) {
             if (function == null) {
-                function = TruffleNFI_Utils.lookupAndBind("exactSumFunc", false, "([double], sint32, sint32, sint32): double");
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                function = TruffleNFI_Utils.lookupAndBind("exactSumFunc", "([double], sint32, sint32, sint32): double");
             }
             try {
                 return (double) ForeignAccess.sendExecute(messageNode, function, JavaInterop.asTruffleObject(values), values.length, hasNa ? 1 : 0, naRm ? 1 : 0);
@@ -48,12 +51,10 @@ public class TruffleNFI_Misc implements MiscRFFI {
                 throw RInternalError.shouldNotReachHere(e);
             }
         }
-
     }
 
     @Override
     public ExactSumNode createExactSumNode() {
         return new TruffleNFI_ExactSumNode();
     }
-
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_NativeArray.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_NativeArray.java
similarity index 99%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_NativeArray.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_NativeArray.java
index 8a69a8d47b..e7ee0f9127 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_NativeArray.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_NativeArray.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import java.util.Arrays;
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PCRE.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PCRE.java
similarity index 54%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PCRE.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PCRE.java
index 50b713e962..12aad365d6 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PCRE.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PCRE.java
@@ -20,75 +20,44 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import java.nio.charset.StandardCharsets;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.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.data.RTruffleObject;
 import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 
 public class TruffleNFI_PCRE implements PCRERFFI {
-    private enum Function {
-        maketables("(): sint64", true),
-        compile("((uint64, string, sint32): void, string, sint32, uint64): void", false),
-        getcapturecount("(uint64, uint64): sint32", false),
-        getcapturenames("((sint32, string): void, uint64, uint64): sint32", false),
-        study("(uint64, sint32): void", false),
-        exec("(uint64, uint64, [uint8], sint32, sint32, sint32, [sint32], sint32): sint32", true);
-
-        private final int argCount;
-        private final String signature;
-        private final String callName;
-        private Node executeNode;
-        private TruffleObject function;
-
-        Function(String signature, boolean direct) {
-            this.argCount = TruffleNFI_Utils.getArgCount(signature);
-            this.signature = signature;
-            this.callName = (direct ? "pcre_" : "call_") + name();
-        }
-
-        private void initialize() {
-            if (executeNode == null) {
-                executeNode = Message.createExecute(argCount).createNode();
-            }
-            if (function == null) {
-                function = TruffleNFI_Utils.lookupAndBind(callName, false, signature);
-            }
-        }
-    }
 
     private static class TruffleNFI_MaketablesNode extends MaketablesNode {
+        @Child private Node message = NFIFunction.maketables.createMessage();
 
         @Override
         public long execute() {
-            Function.maketables.initialize();
             try {
-                long result = (long) ForeignAccess.sendExecute(Function.maketables.executeNode, Function.maketables.function);
+                long result = (long) ForeignAccess.sendExecute(message, NFIFunction.maketables.getFunction());
                 return result;
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
         }
-
     }
 
     private static class TruffleNFI_GetCaptureCountNode extends GetCaptureCountNode {
+        @Child private Node message = NFIFunction.getcapturecount.createMessage();
 
         @Override
         public int execute(long code, long extra) {
-            Function.getcapturecount.initialize();
             try {
-                int result = (int) ForeignAccess.sendExecute(Function.getcapturecount.executeNode, Function.getcapturecount.function, code, extra);
+                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.getcapturecount.getFunction(), code, extra);
                 return result;
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
@@ -97,89 +66,56 @@ public class TruffleNFI_PCRE implements PCRERFFI {
     }
 
     public static class TruffleNFI_GetCaptureNamesNode extends GetCaptureNamesNode {
-        interface CaptureNames {
-            void addName(int i, String name);
-        }
-
-        public static final class CaptureNamesImpl implements CaptureNames, RTruffleObject {
-            private final String[] captureNames;
 
-            private CaptureNamesImpl(int captureCount) {
-                captureNames = new String[captureCount];
-            }
-
-            @Override
-            public void addName(int i, String name) {
-                captureNames[i] = name;
-            }
-
-        }
+        @Child private Node message = NFIFunction.getcapturenames.createMessage();
 
         @Override
         public String[] execute(long code, long extra, int captureCount) {
-            Function.getcapturenames.initialize();
             try {
-                CaptureNamesImpl captureNamesImpl = new CaptureNamesImpl(captureCount);
-                int result = (int) ForeignAccess.sendExecute(Function.getcapturenames.executeNode, Function.getcapturenames.function,
-                                captureNamesImpl, code, extra);
+                CaptureNamesResult data = new CaptureNamesResult(captureCount);
+                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.getcapturenames.getFunction(), data, code, extra);
                 if (result < 0) {
                     CompilerDirectives.transferToInterpreter();
                     throw RError.error(RError.NO_CALLER, RError.Message.WRONG_PCRE_INFO, result);
                 } else {
-                    return captureNamesImpl.captureNames;
+                    return data.getCaptureNames();
                 }
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
         }
-
     }
 
     public static class TruffleNFI_CompileNode extends CompileNode {
-        interface MakeResult {
-            void makeresult(long pcreResult, String errorMessage, int errOffset);
-        }
 
-        public static class MakeResultImpl implements MakeResult, RTruffleObject {
-            private PCRERFFI.Result result;
-
-            @Override
-            public void makeresult(long pcreResult, String errorMessage, int errOffset) {
-                result = new PCRERFFI.Result(pcreResult, errorMessage, errOffset);
-            }
-        }
+        @Child private Node message = NFIFunction.compile.createMessage();
 
         @Override
         public Result execute(String pattern, int options, long tables) {
-            Function.compile.initialize();
             try {
-                MakeResultImpl makeResultImpl = new MakeResultImpl();
-                ForeignAccess.sendExecute(Function.compile.executeNode, Function.compile.function, makeResultImpl,
-                                pattern, options, tables);
-                return makeResultImpl.result;
+                CompileResult data = new CompileResult();
+                ForeignAccess.sendExecute(message, NFIFunction.compile.getFunction(), data, pattern, options, tables);
+                return data.getResult();
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
         }
-
     }
 
     private static class TruffleNFI_ExecNode extends ExecNode {
+        @Child private Node message = NFIFunction.exec.createMessage();
 
         @Override
         public int execute(long code, long extra, String subject, int offset, int options, int[] ovector) {
-            Function.exec.initialize();
             try {
-
                 byte[] subjectBytes = subject.getBytes(StandardCharsets.UTF_8);
-                return (int) ForeignAccess.sendExecute(Function.exec.executeNode, Function.exec.function, code, extra,
+                return (int) ForeignAccess.sendExecute(message, NFIFunction.exec.getFunction(), code, extra,
                                 JavaInterop.asTruffleObject(subjectBytes), subjectBytes.length,
                                 offset, options, JavaInterop.asTruffleObject(ovector), ovector.length);
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
         }
-
     }
 
     @Override
@@ -211,5 +147,4 @@ public class TruffleNFI_PCRE implements PCRERFFI {
     public ExecNode createExecNode() {
         return new TruffleNFI_ExecNode();
     }
-
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PkgInit.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PkgInit.java
similarity index 99%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PkgInit.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PkgInit.java
index 2621894a90..a7769b0bbe 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_PkgInit.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_PkgInit.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_RAppl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RAppl.java
similarity index 72%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_RAppl.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RAppl.java
index e0289cdacb..5bd5362aa8 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_RAppl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RAppl.java
@@ -20,51 +20,24 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
 
 public class TruffleNFI_RAppl implements RApplRFFI {
-    enum Function {
-        dqrdc2("([double], sint32, sint32, sint32, double, [sint32], [double], [sint32], [double]): void"),
-        dqrcf("([double], sint32, sint32, [double], [double], sint32, [double], [sint32]): void"),
-        dqrls("([double], sint32, sint32, [double], sint32, double, [double], [double], [double], [sint32], [sint32], [double], [double]): void");
-
-        private final int argCount;
-        private final String signature;
-        @CompilationFinal private Node executeNode;
-        @CompilationFinal private TruffleObject function;
-
-        Function(String signature) {
-            this.argCount = TruffleNFI_Utils.getArgCount(signature);
-            this.signature = signature;
-        }
-
-        private void initialize() {
-            if (executeNode == null) {
-                executeNode = Message.createExecute(argCount).createNode();
-            }
-            if (function == null) {
-                function = TruffleNFI_Utils.lookupAndBind("call_" + name(), false, signature);
-            }
-        }
-    }
 
     private static class TruffleNFI_Dqrdc2Node extends Dqrdc2Node {
+        @Child private Node message = NFIFunction.dqrdc2.createMessage();
 
         @Override
         public void execute(double[] x, int ldx, int n, int p, double tol, int[] rank, double[] qraux, int[] pivot, double[] work) {
-            Function.dqrdc2.initialize();
             try {
-                ForeignAccess.sendExecute(Function.dqrdc2.executeNode, Function.dqrdc2.function,
+                ForeignAccess.sendExecute(message, NFIFunction.dqrdc2.getFunction(),
                                 JavaInterop.asTruffleObject(x),
                                 ldx, n, p, tol,
                                 JavaInterop.asTruffleObject(rank),
@@ -74,18 +47,16 @@ public class TruffleNFI_RAppl implements RApplRFFI {
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
-
         }
-
     }
 
     private static class TruffleNFI_DqrcfNode extends DqrcfNode {
+        @Child private Node message = NFIFunction.dqrcf.createMessage();
 
         @Override
         public void execute(double[] x, int n, int k, double[] qraux, double[] y, int ny, double[] b, int[] info) {
-            Function.dqrcf.initialize();
             try {
-                ForeignAccess.sendExecute(Function.dqrcf.executeNode, Function.dqrcf.function,
+                ForeignAccess.sendExecute(message, NFIFunction.dqrcf.getFunction(),
                                 JavaInterop.asTruffleObject(x),
                                 n, k,
                                 JavaInterop.asTruffleObject(qraux),
@@ -98,16 +69,15 @@ public class TruffleNFI_RAppl implements RApplRFFI {
                 throw RInternalError.shouldNotReachHere(e);
             }
         }
-
     }
 
     private static class TruffleNFI_DqrlsNode extends DqrlsNode {
+        @Child private Node message = NFIFunction.dqrls.createMessage();
 
         @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) {
-            Function.dqrls.initialize();
             try {
-                ForeignAccess.sendExecute(Function.dqrls.executeNode, Function.dqrls.function,
+                ForeignAccess.sendExecute(message, NFIFunction.dqrls.getFunction(),
                                 JavaInterop.asTruffleObject(x),
                                 n, p,
                                 JavaInterop.asTruffleObject(y),
@@ -123,9 +93,7 @@ public class TruffleNFI_RAppl implements RApplRFFI {
             } catch (InteropException e) {
                 throw RInternalError.shouldNotReachHere(e);
             }
-
         }
-
     }
 
     @Override
@@ -142,5 +110,4 @@ public class TruffleNFI_RAppl implements RApplRFFI {
     public DqrlsNode createDqrlsNode() {
         return new TruffleNFI_DqrlsNode();
     }
-
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_REmbed.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java
similarity index 97%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_REmbed.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java
index 798001ff89..bdd3f63f8a 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_REmbed.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_REmbed.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ffi.REmbedRFFI;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_RFFIFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RFFIFactory.java
similarity index 98%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_RFFIFactory.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RFFIFactory.java
index a892fb8876..3cd9176a1b 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_RFFIFactory.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_RFFIFactory.java
@@ -20,10 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.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;
@@ -32,7 +33,6 @@ import com.oracle.truffle.r.runtime.ffi.CallRFFI;
 import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
 import com.oracle.truffle.r.runtime.ffi.LapackRFFI;
-import com.oracle.truffle.r.runtime.ffi.LibPaths;
 import com.oracle.truffle.r.runtime.ffi.MiscRFFI;
 import com.oracle.truffle.r.runtime.ffi.PCRERFFI;
 import com.oracle.truffle.r.runtime.ffi.RApplRFFI;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Stats.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Stats.java
similarity index 87%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Stats.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Stats.java
index f6c8d87e16..313faae9f3 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Stats.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Stats.java
@@ -20,8 +20,9 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
@@ -43,8 +44,8 @@ public class TruffleNFI_Stats implements StatsRFFI {
         private static final String FFT_FACTOR = "fft_factor";
         private static final String FFT_FACTOR_SIGNATURE = "(sint32, [sint32], [sint32]): void";
 
-        @Child Node factorMessage = Message.createExecute(3).createNode();
-        @Child DLLRFFI.DLSymNode dlsymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child private Node factorMessage = Message.createExecute(3).createNode();
+        @Child private DLLRFFI.DLSymNode dlsymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
 
         @CompilationFinal private TruffleObject fftFactorFunction;
 
@@ -52,6 +53,7 @@ public class TruffleNFI_Stats implements StatsRFFI {
         public void execute(int n, int[] pmaxf, int[] pmaxp) {
             try {
                 if (fftFactorFunction == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
                     Node bind = Message.createInvoke(1).createNode();
                     fftFactorFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, findSymbol(FFT_FACTOR, dlsymNode).asTruffleObject(), "bind", FFT_FACTOR_SIGNATURE);
                 }
@@ -66,14 +68,16 @@ public class TruffleNFI_Stats implements StatsRFFI {
         private static final String FFT_WORK = "fft_work";
         private static final String FFT_WORK_SIGNATURE = "([double], sint32, sint32, sint32, sint32, [double], [sint32]): sint32";
 
-        @Child DLLRFFI.DLSymNode dlsymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
-        @Child Node workMessage = Message.createExecute(7).createNode();
+        @Child private DLLRFFI.DLSymNode dlsymNode = RFFIFactory.getRFFI().getDLLRFFI().createDLSymNode();
+        @Child private Node workMessage = Message.createExecute(7).createNode();
+
         @CompilationFinal private TruffleObject fftWorkFunction;
 
         @Override
         public int execute(double[] a, int nseg, int n, int nspn, int isn, double[] work, int[] iwork) {
             try {
                 if (fftWorkFunction == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
                     Node bind = Message.createInvoke(1).createNode();
                     fftWorkFunction = (TruffleObject) ForeignAccess.sendInvoke(bind, findSymbol(FFT_WORK, dlsymNode).asTruffleObject(), "bind", FFT_WORK_SIGNATURE);
                 }
@@ -83,13 +87,13 @@ public class TruffleNFI_Stats implements StatsRFFI {
                 throw RInternalError.shouldNotReachHere();
             }
         }
-
     }
 
     private static SymbolHandle findSymbol(String symbol, DLLRFFI.DLSymNode dlsymNode) {
         SymbolHandle fftAddress;
         DLLInfo dllInfo = DLL.findLibrary("stats");
         assert dllInfo != null;
+        // maybe DLL.findSymbol(symbol, dllInfo); ?
         fftAddress = dlsymNode.execute(dllInfo.handle, symbol);
         assert fftAddress != DLL.SYMBOL_NOT_FOUND;
         return fftAddress;
@@ -104,5 +108,4 @@ public class TruffleNFI_Stats implements StatsRFFI {
     public WorkNode createWorkNode() {
         return new TruffleNFI_WorkNode();
     }
-
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Tools.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Tools.java
similarity index 93%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Tools.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Tools.java
index dd72b7e5e0..1d4d9c202a 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Tools.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Tools.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import java.io.IOException;
 
@@ -28,9 +28,9 @@ import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.nodes.ffi.RFFIUtils;
+import com.oracle.truffle.r.ffi.impl.common.Generic_Tools;
+import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
@@ -38,11 +38,10 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RTruffleObject;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.ffi.DLL;
-import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
 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.generic.Generic_Tools;
+import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
 
 public class TruffleNFI_Tools implements ToolsRFFI {
 
@@ -76,7 +75,7 @@ public class TruffleNFI_Tools implements ToolsRFFI {
                 initCallback();
                 initialized = true;
             }
-            return super.execute(con, srcfile, verbose, fragment, basename, warningCalls, JavaInterop.asTruffleObject(macros), warndups);
+            return super.execute(con, srcfile, verbose, fragment, basename, warningCalls, macros, warndups);
         }
 
         private void initCallback() {
@@ -92,7 +91,6 @@ public class TruffleNFI_Tools implements ToolsRFFI {
             } catch (InteropException t) {
                 throw RInternalError.shouldNotReachHere(t);
             }
-
         }
     }
 
@@ -100,5 +98,4 @@ public class TruffleNFI_Tools implements ToolsRFFI {
     public ParseRdNode createParseRdNode() {
         return new TruffleNFI_ToolsRFFINode();
     }
-
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_UpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java
similarity index 92%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_UpCallsRFFIImpl.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java
index e302c637ca..6d4260c5f0 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_UpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UpCallsRFFIImpl.java
@@ -20,16 +20,16 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
-import com.oracle.truffle.r.nodes.ffi.JavaUpCallsRFFIImpl;
+import com.oracle.truffle.r.ffi.impl.common.JavaUpCallsRFFIImpl;
 import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 
 public class TruffleNFI_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
     @Override
     public Object Rf_mkCharLenCE(Object bytes, int len, int encoding) {
-        // "bytes" is actually a TruffleObject denoting a native pointer
+        // "bytes" is actually a Long unboxed from a NativePointer
         // TODO: handle encoding properly
         return CharSXPWrapper.create(TruffleNFI_Utils.convertCstring(bytes, len));
     }
@@ -63,7 +63,7 @@ public class TruffleNFI_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
     public Object REAL(Object x) {
         long arrayAddress = TruffleNFI_NativeArray.findArray(x);
         if (arrayAddress == 0) {
-            Object array = super.LOGICAL(x);
+            Object array = super.REAL(x);
             arrayAddress = TruffleNFI_NativeArray.recordArray(x, array, SEXPTYPE.REALSXP);
         } else {
             TruffleNFI_Call.returnArrayExisting(SEXPTYPE.REALSXP, arrayAddress);
@@ -76,7 +76,7 @@ public class TruffleNFI_UpCallsRFFIImpl extends JavaUpCallsRFFIImpl {
     public Object RAW(Object x) {
         long arrayAddress = TruffleNFI_NativeArray.findArray(x);
         if (arrayAddress == 0) {
-            Object array = super.LOGICAL(x);
+            Object array = super.RAW(x);
             arrayAddress = TruffleNFI_NativeArray.recordArray(x, array, SEXPTYPE.RAWSXP);
         } else {
             TruffleNFI_Call.returnArrayExisting(SEXPTYPE.RAWSXP, arrayAddress);
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_UserRng.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UserRng.java
similarity index 99%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_UserRng.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UserRng.java
index e94994edaf..b673b7ec20 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_UserRng.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_UserRng.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Utils.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java
similarity index 66%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Utils.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java
index 55b6efe6d4..0333bad16d 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Utils.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import java.nio.charset.StandardCharsets;
 
@@ -30,12 +30,12 @@ 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.UnsupportedMessageException;
+import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.r.engine.interop.UnsafeAdapter;
+import com.oracle.truffle.r.ffi.impl.interop.UnsafeAdapter;
 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.data.RTruffleObject;
 
 public class TruffleNFI_Utils {
 
@@ -48,12 +48,8 @@ public class TruffleNFI_Utils {
     }
 
     static String convertCstring(Object cstring, int len) {
-        try {
-            long address = (long) ForeignAccess.sendUnbox(Message.UNBOX.createNode(), (TruffleObject) cstring);
-            return getString(address, len);
-        } catch (UnsupportedMessageException ex) {
-            throw RInternalError.shouldNotReachHere(ex);
-        }
+        long address = (long) cstring;
+        return getString(address, len);
     }
 
     private static TruffleObject defaultLibrary;
@@ -68,19 +64,18 @@ public class TruffleNFI_Utils {
     }
 
     /**
-     * Looks up the symbol {@code name} in either the "default" library (e.g. C library symbols) or
-     * in one of the libraries loaded through {@link DLL}, and binds the given NFI signature to the
-     * result, returning the resulting Truffle function object. Failure is fatal.
+     * Looks up the symbol {@code name} in the "default" library and binds the given NFI signature
+     * to the result, returning the resulting Truffle function object. Failure is fatal.
+     *
+     * N.B. The "default" library can resolve a symbol in any of the libraries that are implicitly
+     * referenced from the main executable so, in particular, {@code libR} and any of the libraries
+     * specified as dependencies in {@code libR}, e.g. {@code libpcre}, {@code libz}, etc. All of
+     * the functions searched for by this method fall into that category.
      */
-    static TruffleObject lookupAndBind(String name, boolean inDefaultLibrary, String signature) {
+    static TruffleObject lookupAndBind(String name, String signature) {
         initDefaultLibrary();
         try {
-            TruffleObject symbol;
-            if (inDefaultLibrary) {
-                symbol = ((TruffleObject) ForeignAccess.sendRead(Message.READ.createNode(), defaultLibrary, name));
-            } else {
-                symbol = DLL.findSymbol(name, null).asTruffleObject();
-            }
+            TruffleObject symbol = ((TruffleObject) ForeignAccess.sendRead(Message.READ.createNode(), defaultLibrary, name));
             return (TruffleObject) ForeignAccess.sendInvoke(Message.createInvoke(1).createNode(), symbol, "bind", signature);
         } catch (InteropException e) {
             throw RInternalError.shouldNotReachHere(e);
@@ -115,6 +110,23 @@ public class TruffleNFI_Utils {
         throw RInternalError.shouldNotReachHere();
     }
 
+    /**
+     * There are three possibilities as enumerated below. For an {@link RTruffleObject} there is
+     * nothing to do, and indeed, calling {@code unbox} would be disastrous, as that means, e.g.,
+     * for a RVector, extract the first element! We could get a plain {@link Integer}, but we could
+     * also get a {@code JavaObject} (aka a {@code TruffleObject} that wraps such a value. That does
+     * have to be unboxed. Ditto a {@code NativePointer} encoding, say, a C char array.
+     */
+    public static Object unwrap(Object x) {
+        if (x instanceof RTruffleObject) {
+            return x;
+        } else if (x instanceof TruffleObject) {
+            return JavaInterop.unbox((TruffleObject) x);
+        } else {
+            return x;
+        }
+    }
+
     public static void main(String[] args) {
         System.out.printf("argCount: %s%n", getArgCount(args[0]));
     }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Zip.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Zip.java
similarity index 69%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Zip.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Zip.java
index 957b88ea24..b309d2d59b 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/ffi/nfi/TruffleNFI_Zip.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Zip.java
@@ -20,12 +20,10 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop.ffi.nfi;
+package com.oracle.truffle.r.ffi.impl.nfi;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.InteropException;
-import com.oracle.truffle.api.interop.Message;
-import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -33,38 +31,14 @@ import com.oracle.truffle.r.runtime.ffi.ZipRFFI;
 
 public class TruffleNFI_Zip implements ZipRFFI {
 
-    private enum Function {
-        compress("([uint8], [uint64], [uint8], uint64): sint32"),
-        uncompress("([uint8], [uint64], [uint8], uint64): sint32");
-
-        private final int argCount;
-        private final String signature;
-        private Node executeNode;
-        private TruffleObject function;
-
-        Function(String signature) {
-            this.argCount = TruffleNFI_Utils.getArgCount(signature);
-            this.signature = signature;
-        }
-
-        private void initialize() {
-            if (executeNode == null) {
-                executeNode = Message.createExecute(argCount).createNode();
-            }
-            if (function == null) {
-                function = TruffleNFI_Utils.lookupAndBind(name(), true, signature);
-            }
-        }
-    }
-
     private static class TruffleNFI_CompressNode extends ZipRFFI.CompressNode {
+        @Child private Node message = NFIFunction.compress.createMessage();
 
         @Override
         public int execute(byte[] dest, byte[] source) {
-            Function.compress.initialize();
             long[] destlen = new long[]{dest.length};
             try {
-                int result = (int) ForeignAccess.sendExecute(Function.compress.executeNode, Function.compress.function,
+                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.compress.getFunction(),
                                 JavaInterop.asTruffleObject(dest), JavaInterop.asTruffleObject(destlen),
                                 JavaInterop.asTruffleObject(source), JavaInterop.asTruffleObject(source.length));
                 return result;
@@ -75,12 +49,13 @@ public class TruffleNFI_Zip implements ZipRFFI {
     }
 
     private static class TruffleNFI_UncompressNode extends ZipRFFI.UncompressNode {
+        @Child private Node message = NFIFunction.uncompress.createMessage();
+
         @Override
         public int execute(byte[] dest, byte[] source) {
-            Function.uncompress.initialize();
             long[] destlen = new long[]{dest.length};
             try {
-                int result = (int) ForeignAccess.sendExecute(Function.uncompress.executeNode, Function.uncompress.function,
+                int result = (int) ForeignAccess.sendExecute(message, NFIFunction.uncompress.getFunction(),
                                 JavaInterop.asTruffleObject(dest), JavaInterop.asTruffleObject(destlen),
                                 JavaInterop.asTruffleObject(source), JavaInterop.asTruffleObject(source.length));
                 return result;
@@ -99,5 +74,4 @@ public class TruffleNFI_Zip implements ZipRFFI {
     public UncompressNode createUncompressNode() {
         return new TruffleNFI_UncompressNode();
     }
-
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/package-info.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/package-info.java
new file mode 100644
index 0000000000..ed85483bb7
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+/**
+ * The Truffle NFI implementation of the {@link com.oracle.truffle.r.runtime.ffi.RFFI} interfaces.
+ */
+package com.oracle.truffle.r.ffi.impl.nfi;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsCharNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsCharNode.java
similarity index 98%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsCharNode.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsCharNode.java
index 60061c0032..e015164f75 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsCharNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsCharNode.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.nodes;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsIntegerNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsIntegerNode.java
similarity index 98%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsIntegerNode.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsIntegerNode.java
index c37f801fcf..584c9986c2 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsIntegerNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsIntegerNode.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.nodes;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsLogicalNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsLogicalNode.java
similarity index 98%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsLogicalNode.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsLogicalNode.java
index 423574b454..44db7cbbe3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsLogicalNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsLogicalNode.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.nodes;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsRealNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsRealNode.java
similarity index 98%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsRealNode.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsRealNode.java
index b0667ef048..959ab83142 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsRealNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsRealNode.java
@@ -10,7 +10,7 @@
  *
  * All rights reserved.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.nodes;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/CoerceVectorNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/CoerceVectorNode.java
similarity index 92%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/CoerceVectorNode.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/CoerceVectorNode.java
index 1d4729c310..57616c037f 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/CoerceVectorNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/CoerceVectorNode.java
@@ -20,9 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
-
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.unimplemented;
+package com.oracle.truffle.r.ffi.impl.nodes;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
@@ -40,6 +38,7 @@ import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastSymbolNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
@@ -79,7 +78,7 @@ public abstract class CoerceVectorNode extends FFIUpCallNode.Arg2 {
     @TruffleBoundary
     Object doFallback(Object value, Object mode) {
         String type = value != null ? value.getClass().getSimpleName() : "null";
-        throw unimplemented(String.format("Rf_coerceVector unimplemented for type %s or mode %s.", type, mode));
+        throw RInternalError.unimplemented(String.format("Rf_coerceVector unimplemented for type %s or mode %s.", type, mode));
     }
 
     static boolean isS4Object(Object obj) {
@@ -112,9 +111,9 @@ public abstract class CoerceVectorNode extends FFIUpCallNode.Arg2 {
             case NILSXP:
                 return new CastNullNode();
             case LISTSXP:
-                throw unimplemented("Rf_coerceVector called with unimplemented for PairLists.");
+                throw RInternalError.unimplemented("Rf_coerceVector unimplemented for PairLists.");
             case LANGSXP:
-                throw unimplemented("Rf_coerceVector called with unimplemented for RLanguage.");
+                throw RInternalError.unimplemented("Rf_coerceVector unimplemented for RLanguage.");
             case ENVSXP:
                 return new EnvironmentCast();
             case VECSXP:
@@ -134,7 +133,7 @@ public abstract class CoerceVectorNode extends FFIUpCallNode.Arg2 {
             case RAWSXP:
                 return CastRawNode.createForRFFI(true, preserveDims, preserveAttrs);
             default:
-                throw unimplemented(String.format("Rf_coerceVector called with unimplemented mode %d (type %s).", mode, type));
+                throw RInternalError.unimplemented(String.format("Rf_coerceVector called with unimplemented mode %d (type %s).", mode, type));
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/FFIUpCallNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallNode.java
similarity index 97%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/FFIUpCallNode.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallNode.java
index 7cf4549706..94f0a35d72 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/FFIUpCallNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallNode.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.nodes;
 
 import com.oracle.truffle.api.nodes.Node;
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/FFIUpCallRootNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java
similarity index 57%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/FFIUpCallRootNode.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java
index f1420a80ce..09da32ca1c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/FFIUpCallRootNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.nodes;
 
 import java.util.function.Supplier;
 
@@ -29,20 +29,21 @@ import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.r.nodes.ffi.ListAccessNodesFactory.CADDRNodeGen;
-import com.oracle.truffle.r.nodes.ffi.ListAccessNodesFactory.CADRNodeGen;
-import com.oracle.truffle.r.nodes.ffi.ListAccessNodesFactory.CARNodeGen;
-import com.oracle.truffle.r.nodes.ffi.ListAccessNodesFactory.CDDRNodeGen;
-import com.oracle.truffle.r.nodes.ffi.ListAccessNodesFactory.CDRNodeGen;
-import com.oracle.truffle.r.nodes.ffi.MiscNodesFactory.LENGTHNodeGen;
-import com.oracle.truffle.r.nodes.ffi.MiscNodesFactory.RDoNewObjectNodeGen;
-import com.oracle.truffle.r.nodes.ffi.MiscNodesFactory.RDoSlotNodeGen;
-import com.oracle.truffle.r.nodes.ffi.MiscNodesFactory.RDoSlotAssignNodeGen;
+import com.oracle.truffle.r.ffi.impl.upcalls.RFFIUpCallTable;
+import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodesFactory.CADDRNodeGen;
+import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodesFactory.CADRNodeGen;
+import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodesFactory.CARNodeGen;
+import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodesFactory.CDDRNodeGen;
+import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodesFactory.CDRNodeGen;
+import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.LENGTHNodeGen;
+import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.RDoNewObjectNodeGen;
+import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.RDoSlotAssignNodeGen;
+import com.oracle.truffle.r.ffi.impl.nodes.MiscNodesFactory.RDoSlotNodeGen;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
 
 public final class FFIUpCallRootNode extends RootNode {
-    private static final RootCallTarget[] rootCallTargets = new RootCallTarget[RFFIUpCallMethod.values().length];
+    private static final RootCallTarget[] rootCallTargets = new RootCallTarget[RFFIUpCallTable.values().length];
 
     @Child private FFIUpCallNode theFFIUpCallNode;
     private final int numArgs;
@@ -71,33 +72,33 @@ public final class FFIUpCallRootNode extends RootNode {
         }
     }
 
-    static void add(RFFIUpCallMethod upCallMethod, Supplier<FFIUpCallNode> constructor) {
+    static void add(RFFIUpCallTable upCallMethod, Supplier<FFIUpCallNode> constructor) {
 
         FFIUpCallRootNode rootNode = new FFIUpCallRootNode(constructor.get());
         rootCallTargets[upCallMethod.ordinal()] = Truffle.getRuntime().createCallTarget(rootNode);
     }
 
-    public static RootCallTarget getCallTarget(RFFIUpCallMethod upCallMethod) {
+    public static RootCallTarget getCallTarget(RFFIUpCallTable upCallMethod) {
         RootCallTarget target = rootCallTargets[upCallMethod.ordinal()];
         assert target != null;
         return target;
     }
 
-    static void register() {
-        FFIUpCallRootNode.add(RFFIUpCallMethod.Rf_asReal, AsRealNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.Rf_asLogical, AsLogicalNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.Rf_asInteger, AsIntegerNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.Rf_asChar, AsCharNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.Rf_coerceVector, CoerceVectorNode::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.CAR, CARNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.CDR, CDRNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.CADR, CADRNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.CADDR, CADDRNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.CDDR, CDDRNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.LENGTH, LENGTHNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.R_do_new_object, RDoNewObjectNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.R_do_slot, RDoSlotNodeGen::create);
-        FFIUpCallRootNode.add(RFFIUpCallMethod.R_do_slot_assign, RDoSlotAssignNodeGen::create);
+    public static void register() {
+        FFIUpCallRootNode.add(RFFIUpCallTable.Rf_asReal, AsRealNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.Rf_asLogical, AsLogicalNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.Rf_asInteger, AsIntegerNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.Rf_asChar, AsCharNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.Rf_coerceVector, CoerceVectorNode::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.CAR, CARNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.CDR, CDRNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.CADR, CADRNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.CADDR, CADDRNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.CDDR, CDDRNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.LENGTH, LENGTHNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.R_do_new_object, RDoNewObjectNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.R_do_slot, RDoSlotNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.R_do_slot_assign, RDoSlotAssignNodeGen::create);
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/ListAccessNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java
similarity index 86%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/ListAccessNodes.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java
index 7dee1d0b06..e201076448 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/ListAccessNodes.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java
@@ -20,13 +20,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
-
-import static com.oracle.truffle.r.nodes.ffi.RFFIUtils.unimplemented;
+package com.oracle.truffle.r.ffi.impl.nodes;
 
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.TypeSystemReference;
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RTypes;
@@ -51,7 +50,7 @@ public final class ListAccessNodes {
 
         @Fallback
         protected Object car(@SuppressWarnings("unused") Object obj) {
-            throw unimplemented("CAR only works on pair lists and language objects");
+            throw RInternalError.unimplemented("CAR only works on pair lists and language objects");
         }
 
     }
@@ -71,7 +70,7 @@ public final class ListAccessNodes {
 
         @Fallback
         protected Object cdr(@SuppressWarnings("unused") Object obj) {
-            throw unimplemented("CDR only works on pair lists and language objects");
+            throw RInternalError.unimplemented("CDR only works on pair lists and language objects");
 
         }
     }
@@ -90,7 +89,7 @@ public final class ListAccessNodes {
 
         @Fallback
         protected Object cadr(@SuppressWarnings("unused") Object obj) {
-            throw unimplemented("CADR only works on pair lists and language objects");
+            throw RInternalError.unimplemented("CADR only works on pair lists and language objects");
         }
 
     }
@@ -109,7 +108,7 @@ public final class ListAccessNodes {
 
         @Fallback
         protected Object caddr(@SuppressWarnings("unused") Object obj) {
-            throw unimplemented("CADDR only works on pair lists and language objects");
+            throw RInternalError.unimplemented("CADDR only works on pair lists and language objects");
         }
 
     }
@@ -129,7 +128,7 @@ public final class ListAccessNodes {
 
         @Fallback
         protected Object cddr(@SuppressWarnings("unused") Object obj) {
-            throw unimplemented("CDDR only works on pair lists and language objects");
+            throw RInternalError.unimplemented("CDDR only works on pair lists and language objects");
 
         }
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/MiscNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java
similarity index 99%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/MiscNodes.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java
index 1391299116..dfe993c8a6 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/MiscNodes.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.ffi;
+package com.oracle.truffle.r.ffi.impl.nodes;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Fallback;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/package-info.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/package-info.java
new file mode 100644
index 0000000000..4b666daac9
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+/**
+ * Support for implementing certain upcalls as Truffle nodes.
+ */
+package com.oracle.truffle.r.ffi.impl.nodes;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/IDEUpCallsRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/IDEUpCallsRFFI.java
similarity index 95%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/IDEUpCallsRFFI.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/IDEUpCallsRFFI.java
index 4ba0c703c7..5876265a70 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/IDEUpCallsRFFI.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/IDEUpCallsRFFI.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi;
+package com.oracle.truffle.r.ffi.impl.upcalls;
 
 import com.oracle.truffle.api.frame.Frame;
 
@@ -30,6 +30,8 @@ import com.oracle.truffle.api.frame.Frame;
  */
 public interface IDEUpCallsRFFI {
     // Checkstyle: stop method name check
+    Object R_GlobalContext();
+
     Object R_getGlobalFunctionContext();
 
     Object R_getParentFunctionContext(Object c);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java
similarity index 77%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java
index 295eaa8c02..d85aab618a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/StdUpCallsRFFI.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java
@@ -20,8 +20,9 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi;
+package com.oracle.truffle.r.ffi.impl.upcalls;
 
+import com.oracle.truffle.r.ffi.processor.RFFICstring;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -42,7 +43,12 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
  *
  * N.B. It is important not to be too specific about types owing the support for Truffle interop
  * implementations. For example, many arguments are "strings" but we do not specify them as
- * {@code String} here.
+ * {@code String} here. In particular do not use array types as these are passed as custom Truffle
+ * objects in some implementations.
+ *
+ * N.B. Although some functions do not return results, the Truffle interop EXECUTE message machinery
+ * does not like {@code void} functions, so we just use {@code int}. Evidently the actual value does
+ * not matter.
  */
 public interface StdUpCallsRFFI {
     // Checkstyle: stop method name check
@@ -69,7 +75,7 @@ public interface StdUpCallsRFFI {
 
     Object Rf_cons(Object car, Object cdr);
 
-    void Rf_defineVar(Object symbolArg, Object value, Object envArg);
+    int /* void */ Rf_defineVar(Object symbolArg, Object value, Object envArg);
 
     Object R_do_MAKE_CLASS(@RFFICstring Object clazz);
 
@@ -86,7 +92,7 @@ public interface StdUpCallsRFFI {
 
     Object Rf_getAttrib(Object obj, Object name);
 
-    void Rf_setAttrib(Object obj, Object name, Object val);
+    int /* void */ Rf_setAttrib(Object obj, Object name, Object val);
 
     int Rf_inherits(@RFFICstring Object x, Object clazz);
 
@@ -102,11 +108,11 @@ public interface StdUpCallsRFFI {
 
     Object Rf_PairToVectorList(Object x);
 
-    void Rf_error(@RFFICstring Object msg);
+    int /* void */ Rf_error(@RFFICstring Object msg);
 
-    void Rf_warning(@RFFICstring Object msg);
+    int /* void */ Rf_warning(@RFFICstring Object msg);
 
-    void Rf_warningcall(Object call, @RFFICstring Object msg);
+    int /* void */ Rf_warningcall(Object call, @RFFICstring Object msg);
 
     Object Rf_allocVector(int mode, int n);
 
@@ -120,9 +126,9 @@ public interface StdUpCallsRFFI {
 
     int LENGTH(Object x);
 
-    void SET_STRING_ELT(Object x, int i, Object v);
+    int /* void */ SET_STRING_ELT(Object x, int i, Object v);
 
-    void SET_VECTOR_ELT(Object x, int i, Object v);
+    int /* void */ SET_VECTOR_ELT(Object x, int i, Object v);
 
     Object RAW(Object x);
 
@@ -172,7 +178,7 @@ public interface StdUpCallsRFFI {
 
     Object SYMVALUE(Object x);
 
-    void SET_SYMVALUE(Object x, Object v);
+    int /* void */ SET_SYMVALUE(Object x, Object v);
 
     int R_BindingIsLocked(Object sym, Object env);
 
@@ -184,15 +190,15 @@ public interface StdUpCallsRFFI {
 
     Object Rf_GetOption1(Object tag);
 
-    void Rf_gsetVar(Object symbol, Object value, Object rho);
+    int /* void */ Rf_gsetVar(Object symbol, Object value, Object rho);
 
-    void DUPLICATE_ATTRIB(Object to, Object from);
+    int /* void */ DUPLICATE_ATTRIB(Object to, Object from);
 
     int R_compute_identical(Object x, Object y, int flags);
 
-    void Rf_copyListMatrix(Object s, Object t, int byrow);
+    int /* void */ Rf_copyListMatrix(Object s, Object t, int byrow);
 
-    void Rf_copyMatrix(Object s, Object t, int byrow);
+    int /* void */ Rf_copyMatrix(Object s, Object t, int byrow);
 
     Object R_tryEval(Object expr, Object env, boolean silent);
 
@@ -200,11 +206,11 @@ public interface StdUpCallsRFFI {
 
     int RDEBUG(Object x);
 
-    void SET_RDEBUG(Object x, int v);
+    int /* void */ SET_RDEBUG(Object x, int v);
 
     int RSTEP(Object x);
 
-    void SET_RSTEP(Object x, int v);
+    int /* void */ SET_RSTEP(Object x, int v);
 
     Object ENCLOS(Object x);
 
@@ -218,15 +224,15 @@ public interface StdUpCallsRFFI {
 
     int IS_S4_OBJECT(Object x);
 
-    void SET_S4_OBJECT(Object x);
+    int /* void */ SET_S4_OBJECT(Object x);
 
-    void UNSET_S4_OBJECT(Object x);
+    int /* void */ UNSET_S4_OBJECT(Object x);
 
-    void Rprintf(@RFFICstring Object message);
+    int /* void */ Rprintf(@RFFICstring Object message);
 
-    void GetRNGstate();
+    int /* void */ GetRNGstate();
 
-    void PutRNGstate();
+    int /* void */ PutRNGstate();
 
     double unif_rand();
 
@@ -240,13 +246,13 @@ public interface StdUpCallsRFFI {
 
     Object R_ExternalPtrProtected(Object x);
 
-    void R_SetExternalPtrAddr(Object x, long addr);
+    int /* void */ R_SetExternalPtrAddr(Object x, long addr);
 
-    void R_SetExternalPtrTag(Object x, Object tag);
+    int /* void */ R_SetExternalPtrTag(Object x, Object tag);
 
-    void R_SetExternalPtrProtected(Object x, Object prot);
+    int /* void */ R_SetExternalPtrProtected(Object x, Object prot);
 
-    void R_CleanUp(int sa, int status, int runlast);
+    int /* void */ R_CleanUp(int sa, int status, int runlast);
 
     REnvironment R_NewHashedEnv(REnvironment parent, Object initialSize);
 
@@ -262,9 +268,9 @@ public interface StdUpCallsRFFI {
 
     Object R_new_custom_connection(@RFFICstring Object description, @RFFICstring Object mode, @RFFICstring Object className, Object readAddr);
 
-    int R_ReadConnection(int fd, byte[] buf);
+    int R_ReadConnection(int fd, Object bufObj);
 
-    int R_WriteConnection(int fd, byte[] buf);
+    int R_WriteConnection(int fd, Object bufObj);
 
     Object R_GetConnection(int fd);
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/UpCallsRFFI.java
similarity index 83%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/UpCallsRFFI.java
index 563e8ca60f..9d47187191 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UpCallsRFFI.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/UpCallsRFFI.java
@@ -20,11 +20,14 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi;
+package com.oracle.truffle.r.ffi.impl.upcalls;
+
+import com.oracle.truffle.r.ffi.processor.RFFIUpCallRoot;
 
 /**
  * Aggregation of all the FFI upcall interfaces.
  */
-public interface UpCallsRFFI extends StdUpCallsRFFI, RContextUpCallsRFFI, IDEUpCallsRFFI {
+@RFFIUpCallRoot
+public interface UpCallsRFFI extends StdUpCallsRFFI, IDEUpCallsRFFI, VariableUpCallsRFFI {
 
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/VariableUpCallsRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/VariableUpCallsRFFI.java
new file mode 100644
index 0000000000..01e71ad71d
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/VariableUpCallsRFFI.java
@@ -0,0 +1,58 @@
+/*
+ * 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.upcalls;
+
+import com.oracle.truffle.api.interop.TruffleObject;
+
+/**
+ * This exists because {@link TruffleObject} instances may not always be storable in memory, in all
+ * implementations, so upcall to get the values.
+ *
+ *
+ */
+public interface VariableUpCallsRFFI {
+    // Checkstyle: stop method name check
+
+    // The first five are context dependent and so are always implemented as callbacks.
+
+    Object R_GlobalEnv();
+
+    Object R_BaseEnv();
+
+    Object R_BaseNamespace();
+
+    Object R_NamespaceRegistry();
+
+    int R_Interactive();
+
+    // These may need to be upcalled in some implementations.
+
+    default Object R_Home() {
+        return null;
+    }
+
+    default Object R_TempDir() {
+        return null;
+    }
+
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/package-info.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/package-info.java
new file mode 100644
index 0000000000..c9050396d0
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/package-info.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+/**
+ * These interfaces define the current set of upcalls. They are factored for convenience into
+ * categories. The main interface is {@link com.oracle.truffle.r.ffi.impl.upcalls.StdUpCallsRFFI}.
+ * The annotation processor {@code com.oracle.truffle.r.ffi.FFIProcessor} drives off these
+ * interfaces and generates a number of additional classes.
+ */
+package com.oracle.truffle.r.ffi.impl.upcalls;
diff --git a/com.oracle.truffle.r.ffi.processor/src/META-INF/services/javax.annotation.processing.Processor b/com.oracle.truffle.r.ffi.processor/src/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000000..a60490420d
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.processor/src/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+com.oracle.truffle.r.ffi.processor.FFIProcessor
diff --git a/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java
new file mode 100644
index 0000000000..e413931ca4
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java
@@ -0,0 +1,386 @@
+/*
+ * 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.processor;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+import javax.tools.Diagnostic.Kind;
+
+import javax.tools.FileObject;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+
+public final class FFIProcessor extends AbstractProcessor {
+
+    @Override
+    public Set<String> getSupportedAnnotationTypes() {
+        Set<String> annotations = new HashSet<>();
+        annotations.add("com.oracle.truffle.r.ffi.processor.RFFIUpCallRoot");
+        return annotations;
+    }
+
+    @Override
+    public SourceVersion getSupportedSourceVersion() {
+        return SourceVersion.latest();
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if (roundEnv.processingOver()) {
+            return false;
+        }
+        process0(roundEnv);
+        return true;
+    }
+
+    private void process0(RoundEnvironment roundEnv) {
+        for (Element e : roundEnv.getElementsAnnotatedWith(RFFIUpCallRoot.class)) {
+            try {
+                processElement(e);
+            } catch (Throwable ex) {
+                ex.printStackTrace();
+                String message = "Uncaught error in " + this.getClass();
+                processingEnv.getMessager().printMessage(Kind.ERROR, message + ": " + printException(ex), e);
+            }
+        }
+    }
+
+    private void processElement(Element e) throws IOException {
+        if (e.getKind() != ElementKind.INTERFACE) {
+            processingEnv.getMessager().printMessage(Kind.ERROR, "RFFIUpCallRoot mjusty annotate an interface");
+        }
+        Types types = processingEnv.getTypeUtils();
+        TypeElement typeElement = (TypeElement) e;
+        List<? extends TypeMirror> extended = typeElement.getInterfaces();
+        int count = 0;
+        for (TypeMirror tm : extended) {
+            TypeElement x = (TypeElement) types.asElement(tm);
+            List<? extends Element> methods = x.getEnclosedElements();
+            count += methods.size();
+        }
+        ExecutableElement[] methods = new ExecutableElement[count];
+        count = 0;
+        for (TypeMirror tm : extended) {
+            TypeElement x = (TypeElement) types.asElement(tm);
+            List<? extends Element> encMethods = x.getEnclosedElements();
+            for (Element encMethod : encMethods) {
+                methods[count++] = (ExecutableElement) encMethod;
+            }
+        }
+        Arrays.sort(methods, new Comparator<ExecutableElement>() {
+            @Override
+            public int compare(ExecutableElement e1, ExecutableElement e2) {
+                return e1.getSimpleName().toString().compareTo(e2.getSimpleName().toString());
+            }
+        });
+        generateTable(methods);
+        generateMessageClasses(methods);
+        generateCallbacks(methods);
+        generateCallbacksIndexHeader(methods);
+    }
+
+    private void generateTable(ExecutableElement[] methods) throws IOException {
+        JavaFileObject fileObj = processingEnv.getFiler().createSourceFile("com.oracle.truffle.r.ffi.impl.upcalls.RFFIUpCallTable");
+        Writer w = fileObj.openWriter();
+        w.append("// GENERATED; DO NOT EDIT\n");
+        w.append("package com.oracle.truffle.r.ffi.impl.upcalls;\n");
+        w.append("public enum RFFIUpCallTable {\n");
+        for (int i = 0; i < methods.length; i++) {
+            ExecutableElement method = methods[i];
+            w.append("    ").append(method.getSimpleName().toString()).append(i == methods.length - 1 ? ";" : ",").append('\n');
+        }
+
+        w.append("}\n");
+        w.close();
+    }
+
+    private void generateCallbacksIndexHeader(ExecutableElement[] methods) throws IOException {
+        FileObject fileObj = processingEnv.getFiler().createResource(StandardLocation.SOURCE_OUTPUT, "com.oracle.truffle.r.ffi.impl.upcalls", "rffi_upcallsindex.h");
+        note("If you edited any UpCallsRFFI interfaces do: cp " + fileObj.toUri().getPath() + " com.oracle.truffle.r.native/fficall/src/common\n");
+        Writer w = fileObj.openWriter();
+        w.append("// GENERATED; DO NOT EDIT\n");
+        w.append("#ifndef RFFI_UPCALLSINDEX_H\n");
+        w.append("#define RFFI_UPCALLSINDEX_H\n");
+        w.append('\n');
+        for (int i = 0; i < methods.length; i++) {
+            ExecutableElement method = methods[i];
+            w.append("#define ").append(method.getSimpleName().toString()).append("_x ").append(Integer.toString(i)).append('\n');
+        }
+        w.append('\n');
+        w.append("#define ").append("UPCALLS_TABLE_SIZE ").append(Integer.toString(methods.length)).append('\n');
+        w.append('\n');
+        w.append("#endif // RFFI_UPCALLSINDEX_H\n");
+        w.close();
+    }
+
+    private void generateMessageClasses(ExecutableElement[] methods) throws IOException {
+        for (int i = 0; i < methods.length; i++) {
+            ExecutableElement m = methods[i];
+            generateCallClass(m);
+            generateMessageClass(m);
+        }
+    }
+
+    private void generateCallClass(ExecutableElement m) throws IOException {
+        String name = m.getSimpleName().toString();
+        String callName = name + "Call";
+        JavaFileObject fileObj = processingEnv.getFiler().createSourceFile("com.oracle.truffle.r.ffi.impl.upcalls." + callName);
+        Writer w = fileObj.openWriter();
+        w.append("// GENERATED; DO NOT EDIT\n");
+        w.append("package ").append("com.oracle.truffle.r.ffi.impl.upcalls").append(";\n\n");
+        w.append("import com.oracle.truffle.api.interop.ForeignAccess;\n");
+        w.append("import com.oracle.truffle.api.interop.TruffleObject;\n");
+        w.append("import com.oracle.truffle.r.runtime.data.RTruffleObject;\n");
+        w.append("// Checkstyle: stop method name check\n\n");
+        w.append("public final class ").append(callName).append(" implements RTruffleObject {\n");
+        w.append('\n');
+        w.append("    public final UpCallsRFFI upCallsImpl;\n");
+        w.append('\n');
+        w.append("    protected ").append(callName).append("(UpCallsRFFI upCallsImpl) {\n");
+        w.append("        this.upCallsImpl = upCallsImpl;\n");
+        w.append("    }\n");
+        w.append('\n');
+        w.append("    public static boolean isInstance(TruffleObject value) {\n");
+        w.append("        return value instanceof ").append(callName).append(";\n");
+        w.append("    }\n");
+        w.append('\n');
+
+        w.append("    @Override\n");
+        w.append("    public ForeignAccess getForeignAccess() {\n");
+        w.append("        return ").append(callName).append("MRForeign.ACCESS;\n");
+        w.append("    }\n");
+        w.append('\n');
+        w.append("}\n");
+        w.close();
+    }
+
+    private void generateMessageClass(ExecutableElement m) throws IOException {
+        String name = m.getSimpleName().toString();
+        String callName = name + "Call";
+        String returnType = getTypeName(m.getReturnType());
+        List<? extends VariableElement> params = m.getParameters();
+
+        StringBuilder arguments = new StringBuilder();
+        boolean usesUnwrap = false;
+
+        int lparams = params.size();
+        for (int i = 0; i < lparams; i++) {
+            String is = Integer.toString(i);
+            String paramTypeName = getTypeName(params.get(i).asType());
+            boolean isScalar = true;
+            boolean needCast = !paramTypeName.equals("java.lang.Object");
+            if (needCast) {
+                arguments.append('(').append(paramTypeName).append(") ");
+            }
+            if (isScalar) {
+                usesUnwrap = true;
+                arguments.append("unwrap(");
+            }
+            arguments.append("arguments[").append(is).append("]");
+            if (isScalar) {
+                arguments.append(')');
+            }
+            if (i != lparams - 1) {
+                arguments.append(", ");
+            }
+        }
+
+        JavaFileObject fileObj = processingEnv.getFiler().createSourceFile("com.oracle.truffle.r.ffi.impl.upcalls." + callName + "MR");
+        Writer w = fileObj.openWriter();
+        w.append("// GENERATED; DO NOT EDIT\n");
+        w.append("package ").append("com.oracle.truffle.r.ffi.impl.upcalls").append(";\n\n");
+        if (usesUnwrap) {
+            w.append("import static com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_Utils.unwrap;\n");
+        }
+        w.append("import com.oracle.truffle.api.interop.MessageResolution;\n");
+        w.append("import com.oracle.truffle.api.interop.Resolve;\n");
+        w.append("import com.oracle.truffle.api.nodes.Node;\n");
+        w.append("import com.oracle.truffle.r.ffi.impl.interop.NativePointer;\n");
+        w.append("// Checkstyle: stop method name check\n\n");
+
+        w.append("@MessageResolution(receiverType = ").append(name).append("Call.class)\n");
+        w.append("public class ").append(callName).append("MR {\n");
+        w.append("    @Resolve(message = \"EXECUTE\")\n");
+        w.append("    public abstract static class ").append(callName).append("Execute extends Node {\n");
+        w.append("        protected ").append(returnType).append(" access(").append(callName).append(" receiver, ");
+        if (params.size() == 0) {
+            w.append("@SuppressWarnings(\"unused\") ");
+        }
+        w.append("Object[] arguments) {\n");
+        w.append("            ").append("return").append(" receiver.upCallsImpl.").append(name).append("(");
+
+        w.append(arguments);
+
+        w.append(");\n");
+        w.append("        }\n");
+        w.append("    }\n");
+        w.append("\n");
+
+        w.append("    @Resolve(message = \"IS_EXECUTABLE\")\n");
+        w.append("    public abstract static class ").append(callName).append("IsExecutable extends Node {\n");
+        w.append("        protected Object access(@SuppressWarnings(\"unused\") ").append(callName).append(" receiver) {\n");
+        w.append("            return true;\n");
+        w.append("        }\n");
+        w.append("    }\n");
+        w.append("\n");
+
+        w.append("}\n");
+        w.close();
+
+    }
+
+    private void generateCallbacks(ExecutableElement[] methods) throws IOException {
+        JavaFileObject fileObj = processingEnv.getFiler().createSourceFile("com.oracle.truffle.r.ffi.impl.upcalls.Callbacks");
+        Writer w = fileObj.openWriter();
+        w.append("// GENERATED; DO NOT EDIT\n");
+        w.append("package ").append("com.oracle.truffle.r.ffi.impl.upcalls").append(";\n\n");
+        w.append("import com.oracle.truffle.api.interop.TruffleObject;\n");
+        w.append("import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;\n");
+        w.append("import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;\n\n");
+        w.append("public enum Callbacks {\n");
+        for (int i = 0; i < methods.length; i++) {
+            ExecutableElement m = methods[i];
+            String sig = getNFISignature(m);
+            w.append("    ").append(m.getSimpleName().toString()).append('(').append('"').append(sig).append('"').append(')');
+            w.append(i == methods.length - 1 ? ';' : ',');
+            w.append("\n");
+        }
+        w.append('\n');
+        w.append("    public final String nfiSignature;\n");
+        w.append("    @CompilationFinal public TruffleObject call;\n\n");
+        w.append("    Callbacks(String signature) {\n");
+        w.append("        this.nfiSignature = signature;\n");
+        w.append("    }\n\n");
+
+        w.append("    public static void createCalls(UpCallsRFFI upCallsRFFIImpl) {\n");
+        w.append("        for (Callbacks callback : values()) {\n");
+        w.append("            switch (callback) {\n");
+        for (int i = 0; i < methods.length; i++) {
+            ExecutableElement m = methods[i];
+            String callName = m.getSimpleName().toString() + "Call";
+            w.append("                case ").append(m.getSimpleName().toString()).append(":\n");
+            w.append("                    callback.call = new ").append(callName).append("(upCallsRFFIImpl);\n");
+            w.append("                    break;\n\n");
+        }
+        w.append("            }\n");
+        w.append("        }\n");
+        w.append("    }\n");
+        w.append("}\n");
+        w.close();
+    }
+
+    private String getNFISignature(ExecutableElement m) {
+        List<? extends VariableElement> params = m.getParameters();
+        int lparams = params.size();
+        StringBuilder sb = new StringBuilder();
+        sb.append('(');
+        for (int i = 0; i < lparams; i++) {
+            VariableElement param = params.get(i);
+            RFFICstring[] annotations = param.getAnnotationsByType(RFFICstring.class);
+            String nfiParam = nfiParamName(getTypeName(param.asType()), annotations.length == 0 ? null : annotations[0]);
+            sb.append(nfiParam);
+            if (i != lparams - 1) {
+                sb.append(", ");
+            }
+        }
+        sb.append(')');
+        sb.append(" : ");
+        sb.append(nfiParamName(getTypeName(m.getReturnType()), null));
+        return sb.toString();
+    }
+
+    private static String nfiParamName(String paramType, RFFICstring rffiCstring) {
+        switch (paramType) {
+            case "java.lang.Object":
+                if (rffiCstring == null) {
+                    return "object";
+                } else {
+                    return rffiCstring.convert() ? "string" : "pointer";
+                }
+            case "char":
+                return "uint8";
+            case "int":
+                return "sint32";
+            case "double":
+                return "double";
+            case "void":
+                return "void";
+            case "int[]":
+                return "[sint32]";
+            case "double[]":
+                return "[double]";
+            case "byte[]":
+                return "[uint8]";
+            default:
+                return "object";
+        }
+
+    }
+
+    private String getTypeName(TypeMirror type) {
+        Types types = processingEnv.getTypeUtils();
+        TypeKind kind = type.getKind();
+        String returnType;
+        if (kind.isPrimitive()) {
+            returnType = kind.name().toLowerCase();
+        } else {
+            Element rt = types.asElement(type);
+            returnType = rt.toString();
+        }
+        return returnType;
+    }
+
+    private void note(String msg) {
+        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg);
+    }
+
+    private static String printException(Throwable e) {
+        StringWriter string = new StringWriter();
+        PrintWriter writer = new PrintWriter(string);
+        e.printStackTrace(writer);
+        writer.flush();
+        string.flush();
+        return e.getMessage() + "\r\n" + string.toString();
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFICstring.java b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFICstring.java
similarity index 97%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFICstring.java
rename to com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFICstring.java
index 5d50ed6f2c..77a73ec9f9 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFICstring.java
+++ b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFICstring.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi;
+package com.oracle.truffle.r.ffi.processor;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeDoubleArray.java b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFIUpCallRoot.java
similarity index 66%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeDoubleArray.java
rename to com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFIUpCallRoot.java
index 1268562ed4..b0aae620e4 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeDoubleArray.java
+++ b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFIUpCallRoot.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -20,19 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+package com.oracle.truffle.r.ffi.processor;
 
-import com.oracle.truffle.r.runtime.data.RTruffleObject;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
 
-public class NativeDoubleArray extends NativeNACheck implements RTruffleObject {
-    public final double[] value;
-
-    public NativeDoubleArray(Object obj, double[] value) {
-        super(obj);
-        this.value = value;
-    }
+/**
+ * Tags the root interface for RFFI upcalls, for the annotation processor.
+ */
+@Target({ElementType.TYPE})
+public @interface RFFIUpCallRoot {
 
-    public NativeDoubleArray(double[] value) {
-        this(null, value);
-    }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
index 7b78cebbe2..c3f83f6235 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/C_ParseRd.java
@@ -30,9 +30,9 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.ffi.impl.nodes.AsIntegerNode;
+import com.oracle.truffle.r.ffi.impl.nodes.AsLogicalNode;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.nodes.ffi.AsIntegerNode;
-import com.oracle.truffle.r.nodes.ffi.AsLogicalNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.conn.RConnection;
@@ -44,7 +44,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.ffi.ToolsRFFI;
 
 public abstract class C_ParseRd extends RExternalBuiltinNode.Arg9 {
-    @Child ToolsRFFI.ParseRdNode parseRdNode = RFFIFactory.getRFFI().getToolsRFFI().createParseRdNode();
+    @Child private ToolsRFFI.ParseRdNode parseRdNode = RFFIFactory.getRFFI().getToolsRFFI().createParseRdNode();
 
     static {
         Casts casts = new Casts(C_ParseRd.class);
diff --git a/com.oracle.truffle.r.native/fficall/Makefile b/com.oracle.truffle.r.native/fficall/Makefile
index 68f128ec2b..5cf89be52e 100644
--- a/com.oracle.truffle.r.native/fficall/Makefile
+++ b/com.oracle.truffle.r.native/fficall/Makefile
@@ -81,13 +81,14 @@ fficall.done: common.done
 	touch fficall.done
 else
 ifeq ($(FASTR_RFFI),llvm)
-fficall.done: common.done
+fficall.done: common.done 
 	$(MAKE) -C src/truffle_llvm all
 	touch fficall.done
 else
 ifeq ($(FASTR_RFFI),jni)
 fficall.done: common.done $(JNIBOOT_LIB)
 	$(MAKE) -C src/jni all
+	touch fficall.done
 
 jniboot.done:
 	$(MAKE) -C src/jniboot all
@@ -100,11 +101,10 @@ ifeq ($(OS_NAME),Darwin)
 endif
 else
 	$(error unknown value for FASTR_RFFI)
-endif
-	touch fficall.done
-endif
-endif
-endif
+endif #jni
+endif #llvm
+endif #nfi
+endif #managed
 
 common.done:
 	$(MAKE) -C src/common all	
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffi_callbacks.h b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h
similarity index 99%
rename from com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffi_callbacks.h
rename to com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h
index 94d50549cc..a064a07ce8 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffi_callbacks.h
+++ b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h
@@ -23,9 +23,9 @@
 #ifndef CALLBACKS_H
 #define CALLBACKS_H
 
-#include "rffi_callbacksindex.h"
+#include "rffi_upcallsindex.h"
 
-extern void* callbacks[];
+extern void* *callbacks;
 
 // This is the complete set , including those not yet implemented
 
@@ -278,5 +278,7 @@ typedef void (*call_R_CleanUp)(int sa, int status, int runlast);
 typedef void (*call_Rf_gsetVar)(SEXP symbol, SEXP value, SEXP rho);
 typedef double (*call_unif_rand)();
 
+typedef SEXP (*call_getvar)();
+
 #endif
 
diff --git a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h
new file mode 100644
index 0000000000..4106eaa952
--- /dev/null
+++ b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h
@@ -0,0 +1,141 @@
+// GENERATED; DO NOT EDIT
+#ifndef RFFI_UPCALLSINDEX_H
+#define RFFI_UPCALLSINDEX_H
+
+#define CADDR_x 0
+#define CADR_x 1
+#define CAR_x 2
+#define CDDR_x 3
+#define CDR_x 4
+#define DUPLICATE_ATTRIB_x 5
+#define ENCLOS_x 6
+#define GetRNGstate_x 7
+#define INTEGER_x 8
+#define IS_S4_OBJECT_x 9
+#define LENGTH_x 10
+#define LOGICAL_x 11
+#define NAMED_x 12
+#define OBJECT_x 13
+#define PRCODE_x 14
+#define PRENV_x 15
+#define PRINTNAME_x 16
+#define PRSEEN_x 17
+#define PRVALUE_x 18
+#define PutRNGstate_x 19
+#define RAW_x 20
+#define RDEBUG_x 21
+#define REAL_x 22
+#define RSTEP_x 23
+#define R_BaseEnv_x 24
+#define R_BaseNamespace_x 25
+#define R_BindingIsLocked_x 26
+#define R_CHAR_x 27
+#define R_CleanUp_x 28
+#define R_ExternalPtrAddr_x 29
+#define R_ExternalPtrProtected_x 30
+#define R_ExternalPtrTag_x 31
+#define R_FindNamespace_x 32
+#define R_GetConnection_x 33
+#define R_GlobalContext_x 34
+#define R_GlobalEnv_x 35
+#define R_Home_x 36
+#define R_HomeDir_x 37
+#define R_Interactive_x 38
+#define R_MakeExternalPtr_x 39
+#define R_MethodsNamespace_x 40
+#define R_NamespaceRegistry_x 41
+#define R_NewHashedEnv_x 42
+#define R_ParseVector_x 43
+#define R_PromiseExpr_x 44
+#define R_ReadConnection_x 45
+#define R_SetExternalPtrAddr_x 46
+#define R_SetExternalPtrProtected_x 47
+#define R_SetExternalPtrTag_x 48
+#define R_TempDir_x 49
+#define R_ToplevelExec_x 50
+#define R_WriteConnection_x 51
+#define R_compute_identical_x 52
+#define R_do_MAKE_CLASS_x 53
+#define R_do_new_object_x 54
+#define R_do_slot_x 55
+#define R_do_slot_assign_x 56
+#define R_getContextCall_x 57
+#define R_getContextEnv_x 58
+#define R_getContextFun_x 59
+#define R_getContextSrcRef_x 60
+#define R_getGlobalFunctionContext_x 61
+#define R_getParentFunctionContext_x 62
+#define R_insideBrowser_x 63
+#define R_isEqual_x 64
+#define R_isGlobal_x 65
+#define R_lsInternal3_x 66
+#define R_new_custom_connection_x 67
+#define R_tryEval_x 68
+#define Rf_GetOption1_x 69
+#define Rf_PairToVectorList_x 70
+#define Rf_ScalarDouble_x 71
+#define Rf_ScalarInteger_x 72
+#define Rf_ScalarLogical_x 73
+#define Rf_ScalarString_x 74
+#define Rf_allocArray_x 75
+#define Rf_allocMatrix_x 76
+#define Rf_allocVector_x 77
+#define Rf_any_duplicated_x 78
+#define Rf_asChar_x 79
+#define Rf_asInteger_x 80
+#define Rf_asLogical_x 81
+#define Rf_asReal_x 82
+#define Rf_classgets_x 83
+#define Rf_coerceVector_x 84
+#define Rf_cons_x 85
+#define Rf_copyListMatrix_x 86
+#define Rf_copyMatrix_x 87
+#define Rf_defineVar_x 88
+#define Rf_duplicate_x 89
+#define Rf_error_x 90
+#define Rf_eval_x 91
+#define Rf_findFun_x 92
+#define Rf_findVar_x 93
+#define Rf_findVarInFrame_x 94
+#define Rf_findVarInFrame3_x 95
+#define Rf_getAttrib_x 96
+#define Rf_gsetVar_x 97
+#define Rf_inherits_x 98
+#define Rf_install_x 99
+#define Rf_installChar_x 100
+#define Rf_isNull_x 101
+#define Rf_isString_x 102
+#define Rf_lengthgets_x 103
+#define Rf_mkCharLenCE_x 104
+#define Rf_ncols_x 105
+#define Rf_nrows_x 106
+#define Rf_setAttrib_x 107
+#define Rf_warning_x 108
+#define Rf_warningcall_x 109
+#define Rprintf_x 110
+#define SETCADR_x 111
+#define SETCAR_x 112
+#define SETCDR_x 113
+#define SET_RDEBUG_x 114
+#define SET_RSTEP_x 115
+#define SET_S4_OBJECT_x 116
+#define SET_STRING_ELT_x 117
+#define SET_SYMVALUE_x 118
+#define SET_TAG_x 119
+#define SET_TYPEOF_FASTR_x 120
+#define SET_VECTOR_ELT_x 121
+#define STRING_ELT_x 122
+#define SYMVALUE_x 123
+#define TAG_x 124
+#define TYPEOF_x 125
+#define UNSET_S4_OBJECT_x 126
+#define VECTOR_ELT_x 127
+#define getConnectionClassString_x 128
+#define getOpenModeString_x 129
+#define getSummaryDescription_x 130
+#define isSeekable_x 131
+#define unif_rand_x 132
+
+#define UPCALLS_TABLE_SIZE 133
+
+#endif // RFFI_UPCALLSINDEX_H
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/variables.h b/com.oracle.truffle.r.native/fficall/src/common/rffi_variablesindex.h
similarity index 88%
rename from com.oracle.truffle.r.native/fficall/src/truffle_llvm/variables.h
rename to com.oracle.truffle.r.native/fficall/src/common/rffi_variablesindex.h
index 3a245b1fa9..4373f8998f 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/variables.h
+++ b/com.oracle.truffle.r.native/fficall/src/common/rffi_variablesindex.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -20,6 +20,9 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+#ifndef RFFI_VARIABLESINDEX_H
+#define RFFI_VARIABLESINDEX_H
+
 #define R_Home_x 0
 #define R_TempDir_x 1
 #define R_NilValue_x 2
@@ -67,9 +70,10 @@
 #define R_NaInt_x 44
 #define R_BlankString_x 45
 #define R_BlankScalarString_x 46
-#define R_TrueValue_x 47
-#define R_FalseValue_x 48
-#define R_LogicalNAValue_x 49
-#define R_BaseSymbol_x 50
-#define R_NamespaceEnvSymbol_x 51
-#define R_RestartToken_x 52
+#define R_BaseSymbol_x 47
+#define R_NamespaceEnvSymbol_x 48
+#define R_RestartToken_x 49
+
+#define VARIABLES_TABLE_SIZE 50
+
+#endif // RFFI_VARIABLESINDEX_H
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Connections.c b/com.oracle.truffle.r.native/fficall/src/jni/Connections.c
index 5a7fbf2b02..f642af5f5b 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Connections.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Connections.c
@@ -59,10 +59,10 @@ static void setFd(Rconnection con, jint fd) {
 
 void init_connections(JNIEnv *env) {
 	/* int readConn(int, byte[]) */
-	readConnMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ReadConnection", "(I[B)I", 0);
+	readConnMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ReadConnection", "(ILjava/lang/Object;)I", 0);
 
 	/* int writeConn(int, byte[]) */
-	writeConnMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_WriteConnection", "(I[B)I", 0);
+	writeConnMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_WriteConnection", "(ILjava/lang/Object;)I", 0);
 
 	/* RConnection getConnection(int) */
 	getConnMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_GetConnection", "(I)Ljava/lang/Object;", 0);
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Parse.c b/com.oracle.truffle.r.native/fficall/src/jni/Parse.c
index 723290cfea..c6509c52bb 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Parse.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Parse.c
@@ -31,7 +31,7 @@ static jfieldID parseExprFieldID;
 
 void init_parse(JNIEnv *env) {
 	parseMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ParseVector", "(Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object;", 0);
-	parseResultClass = checkFindClass(env, "com/oracle/truffle/r/nodes/ffi/ParseResult");
+	parseResultClass = checkFindClass(env, "com/oracle/truffle/r/ffi/impl/common/ParseResult");
 	parseStatusFieldID = checkGetFieldID(env, parseResultClass, "parseStatus", "I", 0);
 	parseExprFieldID = checkGetFieldID(env, parseResultClass, "expr", "Ljava/lang/Object;", 0);
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Random.c b/com.oracle.truffle.r.native/fficall/src/jni/Random.c
index 4c958521f1..5927610412 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Random.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Random.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,8 +27,8 @@ static jmethodID PutRNGstate_MethodID;
 static jmethodID UnifRand_MethodID;
 
 void init_random(JNIEnv *env) {
-	GetRNGstate_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "GetRNGstate", "()V", 0);
-	PutRNGstate_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "PutRNGstate", "()V", 0);
+	GetRNGstate_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "GetRNGstate", "()I", 0);
+	PutRNGstate_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "PutRNGstate", "()I", 0);
 	UnifRand_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "unif_rand", "()D", 0);
 }
 
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Rdynload_fastr.c b/com.oracle.truffle.r.native/fficall/src/jni/Rdynload_fastr.c
index 88a5b6e9b3..df4334d68d 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rdynload_fastr.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rdynload_fastr.c
@@ -31,7 +31,7 @@ static jmethodID findSymbolID;
 
 void init_dynload(JNIEnv *env) {
     DLLClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/DLL");
-    JNI_PkgInitClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/jni/JNI_PkgInit");
+    JNI_PkgInitClass = checkFindClass(env, "com/oracle/truffle/r/ffi/impl/jni/JNI_PkgInit");
     DotSymbolClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/DLL$DotSymbol");
     RegisteredNativeSymbolClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/DLL$RegisteredNativeSymbol");
 
@@ -88,7 +88,7 @@ void R_RegisterCCallable(const char *package, const char *name, DL_FUNC fptr) {
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1PkgInit_setSymbol(JNIEnv *env, jclass c, jint nstOrd, jlong routinesAddr, jint index) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1PkgInit_setSymbol(JNIEnv *env, jclass c, jint nstOrd, jlong routinesAddr, jint index) {
 	const char *name;
 	long fun;
 	int numArgs;
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c b/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c
index c38eab99ff..3442b2b89d 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c
@@ -307,7 +307,7 @@ void uR_Busy(int x) {
 
 void uR_CleanUp(SA_TYPE x, int y, int z) {
 	JNIEnv *jniEnv = getEnv();
-	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_CleanUp", "(III)V", 1);
+	jmethodID methodID = checkGetMethodID(jniEnv, UpCallsRFFIClass, "R_CleanUp", "(III)I", 1);
 	(*jniEnv)->CallStaticVoidMethod(jniEnv, UpCallsRFFIClass, methodID, x, y, z);
 }
 
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
index 1f63778f8f..37a58c9535 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
@@ -156,19 +156,19 @@ void init_internals(JNIEnv *env) {
 	Rf_consMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_cons", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	Rf_evalMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_eval", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	Rf_findFunMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_findFun", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
-	Rf_defineVarMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_defineVar", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	Rf_defineVarMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_defineVar", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I", 0);
 	Rf_findVarMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_findVar", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	Rf_findVarInFrameMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_findVarInFrame", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	Rf_findVarInFrame3MethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_findVarInFrame3", "(Ljava/lang/Object;Ljava/lang/Object;I)Ljava/lang/Object;", 0);
 	Rf_getAttribMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_getAttrib", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
-	Rf_setAttribMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_setAttrib", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	Rf_setAttribMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_setAttrib", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I", 0);
 	Rf_isStringMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_isString", "(Ljava/lang/Object;)I", 0);
 	Rf_isNullMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_isNull", "(Ljava/lang/Object;)I", 0);
 	Rf_installMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_install", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	Rf_installCharMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_installChar", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
-	Rf_warningMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_warning", "(Ljava/lang/Object;)V", 0);
-	Rf_warningcallMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_warningcall", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
-	Rf_errorMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_error", "(Ljava/lang/Object;)V", 0);
+	Rf_warningMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_warning", "(Ljava/lang/Object;)I", 0);
+	Rf_warningcallMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_warningcall", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
+	Rf_errorMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_error", "(Ljava/lang/Object;)I", 0);
 	Rf_allocVectorMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_allocVector", "(II)Ljava/lang/Object;", 0);
 	Rf_allocMatrixMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_allocMatrix", "(III)Ljava/lang/Object;", 0);
 	Rf_allocArrayMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_allocArray", "(ILjava/lang/Object;)Ljava/lang/Object;", 0);
@@ -176,13 +176,13 @@ void init_internals(JNIEnv *env) {
 	Rf_any_duplicatedMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_any_duplicated", "(Ljava/lang/Object;I)I", 0);
 	R_NewHashedEnvMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_NewHashedEnv", "(Lcom/oracle/truffle/r/runtime/env/REnvironment;Ljava/lang/Object;)Lcom/oracle/truffle/r/runtime/env/REnvironment;", 0);
 	Rf_classgetsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_classgets", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
-	RprintfMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rprintf", "(Ljava/lang/Object;)V", 0);
+	RprintfMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rprintf", "(Ljava/lang/Object;)I", 0);
 	R_do_MAKE_CLASS_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_do_MAKE_CLASS", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	R_do_new_object_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_do_new_object", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	R_FindNamespaceMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_FindNamespace", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	R_BindingIsLockedID = checkGetMethodID(env, UpCallsRFFIClass, "R_BindingIsLocked", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
 	Rf_GetOption1MethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_GetOption1", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
-	Rf_gsetVarMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_gsetVar", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	Rf_gsetVarMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_gsetVar", "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)I", 0);
 	Rf_inheritsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_inherits", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
 	Rf_lengthgetsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_lengthgets", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
 //	Rf_rPsortMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_rPsort", "(Lcom/oracle/truffle/r/runtime/data/RDoubleVector;II)", 0);
@@ -199,9 +199,9 @@ void init_internals(JNIEnv *env) {
 	SETCDR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SETCDR", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	SETCADR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SETCADR", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	SYMVALUE_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SYMVALUE", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
-	SET_SYMVALUE_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_SYMVALUE", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
-	SET_STRING_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_STRING_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)V", 0);
-	SET_VECTOR_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_VECTOR_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)V", 0);
+	SET_SYMVALUE_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_SYMVALUE", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
+	SET_STRING_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_STRING_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)I", 0);
+	SET_VECTOR_ELT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_VECTOR_ELT", "(Ljava/lang/Object;ILjava/lang/Object;)I", 0);
 	RAW_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "RAW", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	REAL_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "REAL", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	LOGICAL_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "LOGICAL", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
@@ -223,15 +223,15 @@ void init_internals(JNIEnv *env) {
 	SET_TYPEOF_FASTR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_TYPEOF_FASTR", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
 	TYPEOF_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "TYPEOF", "(Ljava/lang/Object;)I", 0);
 	OBJECT_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "OBJECT", "(Ljava/lang/Object;)I", 0);
-	DUPLICATE_ATTRIB_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "DUPLICATE_ATTRIB", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	DUPLICATE_ATTRIB_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "DUPLICATE_ATTRIB", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
 	IS_S4_OBJECTMethodID = checkGetMethodID(env, UpCallsRFFIClass, "IS_S4_OBJECT", "(Ljava/lang/Object;)I", 0);
-	SET_S4_OBJECTMethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_S4_OBJECT", "(Ljava/lang/Object;)V", 0);
-	UNSET_S4_OBJECTMethodID = checkGetMethodID(env, UpCallsRFFIClass, "UNSET_S4_OBJECT", "(Ljava/lang/Object;)V", 0);
+	SET_S4_OBJECTMethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_S4_OBJECT", "(Ljava/lang/Object;)I", 0);
+	UNSET_S4_OBJECTMethodID = checkGetMethodID(env, UpCallsRFFIClass, "UNSET_S4_OBJECT", "(Ljava/lang/Object;)I", 0);
 	R_tryEvalMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_tryEval", "(Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object;", 0);
 	RDEBUGMethodID = checkGetMethodID(env, UpCallsRFFIClass, "RDEBUG", "(Ljava/lang/Object;)I", 0);
-	SET_RDEBUGMethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_RDEBUG", "(Ljava/lang/Object;I)V", 0);
+	SET_RDEBUGMethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_RDEBUG", "(Ljava/lang/Object;I)I", 0);
 	RSTEPMethodID = checkGetMethodID(env, UpCallsRFFIClass, "RSTEP", "(Ljava/lang/Object;)I", 0);
-	SET_RSTEPMethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_RSTEP", "(Ljava/lang/Object;I)V", 0);
+	SET_RSTEPMethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_RSTEP", "(Ljava/lang/Object;I)I", 0);
 	ENCLOSMethodID = checkGetMethodID(env, UpCallsRFFIClass, "ENCLOS", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	PRVALUEMethodID = checkGetMethodID(env, UpCallsRFFIClass, "PRVALUE", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	R_lsInternal3MethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_lsInternal3", "(Ljava/lang/Object;II)Ljava/lang/Object;", 0);
@@ -246,18 +246,18 @@ void init_internals(JNIEnv *env) {
 	R_ExternalPtrAddrMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ExternalPtrAddr", "(Ljava/lang/Object;)J", 0);
 	R_ExternalPtrTagMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ExternalPtrTag", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	R_ExternalPtrProtectedMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_ExternalPtrProtected", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
-	R_SetExternalPtrAddrMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_SetExternalPtrAddr", "(Ljava/lang/Object;J)V", 0);
-	R_SetExternalPtrTagMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_SetExternalPtrTag", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
-	R_SetExternalPtrProtMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_SetExternalPtrProtected", "(Ljava/lang/Object;Ljava/lang/Object;)V", 0);
+	R_SetExternalPtrAddrMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_SetExternalPtrAddr", "(Ljava/lang/Object;J)I", 0);
+	R_SetExternalPtrTagMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_SetExternalPtrTag", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
+	R_SetExternalPtrProtMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_SetExternalPtrProtected", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
 
     R_compute_identicalMethodID = checkGetMethodID(env, UpCallsRFFIClass, "R_compute_identical", "(Ljava/lang/Object;Ljava/lang/Object;I)I", 0);
-    Rf_copyListMatrixMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_copyListMatrix", "(Ljava/lang/Object;Ljava/lang/Object;I)V", 0);
-    Rf_copyMatrixMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_copyMatrix", "(Ljava/lang/Object;Ljava/lang/Object;I)V", 0);
+    Rf_copyListMatrixMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_copyListMatrix", "(Ljava/lang/Object;Ljava/lang/Object;I)I", 0);
+    Rf_copyMatrixMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_copyMatrix", "(Ljava/lang/Object;Ljava/lang/Object;I)I", 0);
     Rf_nrowsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_nrows", "(Ljava/lang/Object;)I", 0);
     Rf_ncolsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_ncols", "(Ljava/lang/Object;)I", 0);
 
     // static JNI-specific methods
-	JNIUpCallsRFFIImplClass = checkFindClass(env, "com/oracle/truffle/r/runtime/ffi/jni/JNIUpCallsRFFIImpl");
+	JNIUpCallsRFFIImplClass = checkFindClass(env, "com/oracle/truffle/r/ffi/impl/jni/JNIUpCallsRFFIImpl");
 	restoreHandlerStacksMethodID = checkGetMethodID(env, JNIUpCallsRFFIImplClass, "R_ToplevelExecRestoreErrorHandlerStacks", "(Ljava/lang/Object;)V", 1);
     setCompleteMethodID = checkGetMethodID(env, JNIUpCallsRFFIImplClass, "setComplete", "(Ljava/lang/Object;Z)V", 1);
 	logNotCharSXPWrapperMethodID = checkGetMethodID(env, JNIUpCallsRFFIImplClass, "logNotCharSXPWrapper", "(Ljava/lang/Object;)V", 1);
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/appl_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/appl_rffi.c
index bd570ec502..feb67c1825 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/appl_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/appl_rffi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,7 @@ extern void dqrcf_(double *x, int *n, int *k, double *qraux, double *y, int *ny,
 extern void dqrls_(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);
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1RAppl_native_1dqrdc2(JNIEnv *env, jclass c,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1RAppl_native_1dqrdc2(JNIEnv *env, jclass c,
 		jdoubleArray jx, jint ldx, jint n, jint p, jdouble tol, jintArray jrank, jdoubleArray jqraux,
 		jintArray jpivot, jdoubleArray jwork) {
 	double *x = (*env)->GetPrimitiveArrayCritical(env, jx, NULL);
@@ -45,7 +45,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1RAppl_native_1dqrdc2(JNIEnv *env,
 }
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1RAppl_native_1dqrcf(JNIEnv *env, jclass c,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1RAppl_native_1dqrcf(JNIEnv *env, jclass c,
 		jdoubleArray jx, jint n, jint k, jdoubleArray jqraux, jdoubleArray jy, jint ny, jdoubleArray jb, jintArray jinfo) {
 	double *x = (*env)->GetPrimitiveArrayCritical(env, jx, NULL);
 	double *qraux = (*env)->GetPrimitiveArrayCritical(env, jqraux, NULL);
@@ -61,7 +61,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1RAppl_native_1dqrcf(JNIEnv *env,
 }
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1RAppl_native_1dqrls(JNIEnv *env, jclass c,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1RAppl_native_1dqrls(JNIEnv *env, jclass c,
 		jdoubleArray jx, int n, int p, jdoubleArray jy, int ny, double tol, jdoubleArray jb,
 		jdoubleArray jrsd, jdoubleArray jqty, jintArray jk, jintArray jjpvt, jdoubleArray jqraux, jdoubleArray jwork) {
 	double *x = (*env)->GetPrimitiveArrayCritical(env, jx, NULL);
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/base_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/base_rffi.c
index 6e19483674..1dfb0c1fbf 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/base_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/base_rffi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,13 +34,13 @@
 #include <errno.h>
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1getpid(JNIEnv *env, jclass c) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Base_native_1getpid(JNIEnv *env, jclass c) {
 	pid_t pid = getpid();
 	return (jint) pid;
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1getwd(JNIEnv *env, jclass c, jbyteArray jdest, int len) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Base_native_1getwd(JNIEnv *env, jclass c, jbyteArray jdest, int len) {
     char *dest = (*env)->GetPrimitiveArrayCritical(env, jdest, NULL);
     char *r = getcwd(dest, len);
     if (r == NULL) return 0;
@@ -49,7 +49,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1getwd(JNIEnv *env, j
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1setwd(JNIEnv *env, jclass c, jstring jdir) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Base_native_1setwd(JNIEnv *env, jclass c, jstring jdir) {
     const char *dir = (*env)->GetStringUTFChars(env, jdir, NULL);
     int rc = chdir(dir);
     (*env)->ReleaseStringUTFChars(env, jdir, dir);
@@ -57,7 +57,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1setwd(JNIEnv *env, j
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1mkdtemp(JNIEnv *env, jclass c, jbyteArray jtemplate) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Base_native_1mkdtemp(JNIEnv *env, jclass c, jbyteArray jtemplate) {
     char *template = (char*) (*env)->GetByteArrayElements(env, jtemplate, NULL);
     char *r = mkdtemp(template);
     int rc = 1;
@@ -70,7 +70,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1mkdtemp(JNIEnv *env,
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1mkdir(JNIEnv *env, jclass c, jstring jdir, jint mode) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Base_native_1mkdir(JNIEnv *env, jclass c, jstring jdir, jint mode) {
     const char *dir = (*env)->GetStringUTFChars(env, jdir, NULL);
     int rc = mkdir(dir, mode);
     (*env)->ReleaseStringUTFChars(env, jdir, dir);
@@ -78,7 +78,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1mkdir(JNIEnv *env, j
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1chmod(JNIEnv *env, jclass c, jstring jdir, jint mode) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Base_native_1chmod(JNIEnv *env, jclass c, jstring jdir, jint mode) {
     const char *dir = (*env)->GetStringUTFChars(env, jdir, NULL);
     int rc = chmod(dir, mode);
     (*env)->ReleaseStringUTFChars(env, jdir, dir);
@@ -86,7 +86,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1chmod(JNIEnv *env, j
 }
 
 JNIEXPORT jlong JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1strtol(JNIEnv *env, jclass c, jstring js, jint base, jintArray jerrno) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Base_native_1strtol(JNIEnv *env, jclass c, jstring js, jint base, jintArray jerrno) {
     const char *s = (*env)->GetStringUTFChars(env, js, NULL);
     jlong rc = strtol(s, NULL, base);
     if (errno) {
@@ -99,7 +99,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1strtol(JNIEnv *env,
 }
 
 JNIEXPORT jstring JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1readlink(JNIEnv *env, jclass c, jstring jpath, jintArray jerrno) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Base_native_1readlink(JNIEnv *env, jclass c, jstring jpath, jintArray jerrno) {
     const char *path = (*env)->GetStringUTFChars(env, jpath, NULL);
     char buf[4096];
     int len = readlink(path, buf, 4096);
@@ -120,7 +120,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Base_native_1readlink(JNIEnv *env
 static jmethodID addPathID = 0;
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Glob_doglob(JNIEnv *env, jobject obj, jstring pattern) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Glob_doglob(JNIEnv *env, jobject obj, jstring pattern) {
 	glob_t globstruct;
 
 	if (addPathID == 0) {
@@ -140,7 +140,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Glob_doglob(JNIEnv *env, jobject
 }
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1UtsName_getutsname(JNIEnv *env, jobject obj) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1UtsName_getutsname(JNIEnv *env, jobject obj) {
 	struct utsname name;
 
 	uname(&name);
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/c_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/c_rffi.c
index 9f1015280a..d41a946620 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/c_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/c_rffi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -332,7 +332,7 @@ typedef void (*c65func)(void *arg1, void *arg2, void *arg3, void *arg4, void *ar
 
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1C_c(JNIEnv *env, jclass c, jlong address, jobjectArray args) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1C_c(JNIEnv *env, jclass c, jlong address, jobjectArray args) {
 	int len = (*env)->GetArrayLength(env, args);
 	void *cargs[len];
 	jobject jarrays[len];
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/call_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/call_rffi.c
index 47ffc6ef39..cb031ad01c 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/call_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/call_rffi.c
@@ -27,7 +27,7 @@
 
 // The entry point from JNI_Call that initializes the system
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_initialize(JNIEnv *env, jclass c,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_initialize(JNIEnv *env, jclass c,
 		jobject upCallInstance, jobjectArray initialValues) {
 	init_utils(env, upCallInstance); // must be first
 	init_variables(env, initialValues);
@@ -42,7 +42,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_initialize(JNIEnv *env, jcla
 }
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_nativeSetTempDir(JNIEnv *env, jclass c, jstring tempDir) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_nativeSetTempDir(JNIEnv *env, jclass c, jstring tempDir) {
 	setTempDir(env, tempDir);
 }
 
@@ -345,7 +345,7 @@ typedef SEXP (*call64func)(SEXP arg1, SEXP arg2, SEXP arg3, SEXP arg4, SEXP arg5
         );
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call0(JNIEnv *env, jclass c, jlong address) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call0(JNIEnv *env, jclass c, jlong address) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
@@ -358,7 +358,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call0(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call1(JNIEnv *env, jclass c, jlong address, jobject arg1) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call1(JNIEnv *env, jclass c, jlong address, jobject arg1) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
@@ -371,7 +371,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call1(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call2(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call2(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
@@ -384,7 +384,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call2(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call3(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call3(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
@@ -398,7 +398,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call3(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call4(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call4(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
@@ -412,7 +412,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call4(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call5(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call5(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
@@ -426,7 +426,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call5(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call6(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call6(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
@@ -440,7 +440,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call6(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call7(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call7(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
@@ -454,7 +454,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call7(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call8(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call8(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7, jobject arg8) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
@@ -468,7 +468,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call8(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call9(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call9(JNIEnv *env, jclass c, jlong address, jobject arg1, jobject arg2,
 		jobject arg3, jobject arg4, jobject arg5, jobject arg6, jobject arg7, jobject arg8, jobject arg9) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
@@ -482,7 +482,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call9(JNIEnv *env, jclass c,
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call(JNIEnv *env, jclass c, jlong address, jobjectArray args) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_call(JNIEnv *env, jclass c, jlong address, jobjectArray args) {
 	jmp_buf error_jmpbuf;
 	jobject result = NULL;
 	callEnter(env, &error_jmpbuf);
@@ -1227,7 +1227,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_call(JNIEnv *env, jclass c,
 typedef void (*callVoid1func)(SEXP arg1);
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_callVoid1(JNIEnv *env, jclass c, jlong address, jobject arg1) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_callVoid1(JNIEnv *env, jclass c, jlong address, jobject arg1) {
 	jmp_buf error_jmpbuf;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
@@ -1240,7 +1240,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_callVoid1(JNIEnv *env, jclas
 typedef void (*callVoid0func)();
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Call_callVoid0(JNIEnv *env, jclass c, jlong address) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Call_callVoid0(JNIEnv *env, jclass c, jlong address) {
 	jmp_buf error_jmpbuf;
 	callEnter(env, &error_jmpbuf);
 	if (!setjmp(error_jmpbuf)) {
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/lapack_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/lapack_rffi.c
index daa89b365b..83606709d3 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/lapack_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/lapack_rffi.c
@@ -26,7 +26,7 @@
 extern void ilaver_(int *major, int *minor, int *patch);
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1ilaver(JNIEnv *env, jclass klass, jintArray jversion) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1ilaver(JNIEnv *env, jclass klass, jintArray jversion) {
 	int major;
 	int minor;
 	int patch;
@@ -42,7 +42,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1ilaver(JNIEnv *env
 extern int dgeev_(char *jobVL, char *jobVR, int *n, double *a, int *lda, double *wr, double *wi, double *vl, int *ldvl, double *vr, int *ldvr, double *work, int *lwork, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dgeev(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dgeev(JNIEnv *env, jclass klass,
         char jobVL, char jobVR, int n, jdoubleArray ja, int lda, jdoubleArray jwr, jdoubleArray jwi, jdoubleArray jvl, int ldvl, jdoubleArray jvr, int ldvr, jdoubleArray jwork, int lwork) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     double *wr = (*env)->GetPrimitiveArrayCritical(env, jwr, NULL);
@@ -64,7 +64,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dgeev(JNIEnv *env,
 extern int dgeqp3_(int *m, int *n, double *a, int *lda, int *jpvt, double *tau, double *work, int *lwork, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dgeqp3(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dgeqp3(JNIEnv *env, jclass klass,
         int m, int n, jdoubleArray ja, int lda, jintArray jjpvt, jdoubleArray jtau, jdoubleArray jwork, int lwork) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     int *jpvt = (*env)->GetPrimitiveArrayCritical(env, jjpvt, NULL);
@@ -82,7 +82,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dgeqp3(JNIEnv *env
 extern int dormqr_(char *side, char *trans, int *m, int *n, int *k, double *a, int *lda, double *tau, double *c, int *ldc, double *work, int *lwork, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dormqr(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dormqr(JNIEnv *env, jclass klass,
         char side, char trans, int m, int n, int k, jdoubleArray ja, int lda, jdoubleArray jtau, jdoubleArray jc, int ldc, jdoubleArray jwork, int lwork) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     double *tau = (*env)->GetPrimitiveArrayCritical(env, jtau, NULL);
@@ -100,7 +100,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dormqr(JNIEnv *env
 extern int dtrtrs_(char *uplo, char *trans, char *diag, int *n, int *nrhs, double *a, int *lda, double *b, int *ldb, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dtrtrs(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dtrtrs(JNIEnv *env, jclass klass,
         char uplo, char trans, char diag, int n, int nrhs, jdoubleArray ja, int lda, jdoubleArray jb, int ldb) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     double *b = (*env)->GetPrimitiveArrayCritical(env, jb, NULL);
@@ -114,7 +114,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dtrtrs(JNIEnv *env
 extern int dgetrf_(int *m, int *n, double *a, int *lda, int *ipiv, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dgetrf(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dgetrf(JNIEnv *env, jclass klass,
         int m, int n, jdoubleArray ja, int lda, jintArray jipiv) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     int *ipiv = (*env)->GetPrimitiveArrayCritical(env, jipiv, NULL);
@@ -128,7 +128,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dgetrf(JNIEnv *env
 extern int dpotrf_(char *uplo, int *n, double *a, int *lda, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dpotrf(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dpotrf(JNIEnv *env, jclass klass,
         char uplo, int n, jdoubleArray ja, int lda) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     int info;
@@ -140,7 +140,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dpotrf(JNIEnv *env
 extern int dpotri_(char *uplo, int *n, double *a, int *lda, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dpotri(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dpotri(JNIEnv *env, jclass klass,
         char uplo, int n, jdoubleArray ja, int lda) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     int info;
@@ -152,7 +152,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dpotri(JNIEnv *env
 extern int dpstrf_(char *uplo, int *n, double *a, int *lda, int *piv, int *rank, double *tol, double *work, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dpstrf(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dpstrf(JNIEnv *env, jclass klass,
         char uplo, int n, jdoubleArray ja, int lda, jintArray jpiv, jintArray jrank, double tol, jdoubleArray jwork) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     int *piv = (*env)->GetPrimitiveArrayCritical(env, jpiv, NULL);
@@ -170,7 +170,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dpstrf(JNIEnv *env
 extern int dgesv_(int *n, int *nrhs, double *a, int *lda, int *ipiv, double *b, int *ldb, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dgesv(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dgesv(JNIEnv *env, jclass klass,
         int n, int nrhs, jdoubleArray ja, int lda, jintArray jipiv, jdoubleArray jb, int ldb) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     int *ipiv = (*env)->GetPrimitiveArrayCritical(env, jipiv, NULL);
@@ -186,7 +186,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dgesv(JNIEnv *env,
 extern double dlange_(char *norm, int *m, int *n, double *a, int *lda, double *work);
 
 JNIEXPORT jdouble JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dlange(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dlange(JNIEnv *env, jclass klass,
         char norm, int m, int n, jdoubleArray ja, int lda, jdoubleArray jwork) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     double *work = jwork == NULL ? NULL : (*env)->GetPrimitiveArrayCritical(env, jwork, NULL);
@@ -199,7 +199,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dlange(JNIEnv *env
 extern int dgecon_(char *norm, int *n, double *a, int *lda, double *anorm, double *rcond, double *work, int *iwork, int *info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dgecon(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dgecon(JNIEnv *env, jclass klass,
         char norm, int n, jdoubleArray ja, int lda, double anorm, jdoubleArray jrcond, jdoubleArray jwork, jintArray jiwork) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
     double *rcond = (*env)->GetPrimitiveArrayCritical(env, jrcond, NULL);
@@ -218,7 +218,7 @@ extern int dsyevr_(char *jobz, char *range, char *uplo, int *n, double* a, int *
                 double* z, int *ldz, int* isuppz, double* work, int *lwork, int* iwork, int *liwork, int* info);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Lapack_native_1dsyevr(JNIEnv *env, jclass klass,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Lapack_native_1dsyevr(JNIEnv *env, jclass klass,
 		char jobz, char range, char uplo, int n, jdoubleArray ja, int lda, double vl, double vu, int il, int iu, double abstol, jintArray jm, jdoubleArray jw,
 		                    jdoubleArray jz, int ldz, jintArray jisuppz, jdoubleArray jwork, int lwork, jintArray jiwork, int liwork) {
     double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/misc_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/misc_rffi.c
index b4ee639d1e..baef388937 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/misc_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/misc_rffi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,7 @@
 #include <rffiutils.h>
 
 JNIEXPORT jdouble JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Misc_exactSumFunc(JNIEnv *env, jclass c, jdoubleArray values, jboolean hasNa, jboolean naRm) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Misc_exactSumFunc(JNIEnv *env, jclass c, jdoubleArray values, jboolean hasNa, jboolean naRm) {
 	jint length = (*env)->GetArrayLength(env, values);
 	jdouble* contents = (jdouble*) (*env)->GetPrimitiveArrayCritical(env, values, NULL);
 
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/pcre_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/pcre_rffi.c
index c8964b9ee0..8f804e4c4e 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/pcre_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/pcre_rffi.c
@@ -32,12 +32,12 @@ void init_pcre(JNIEnv *env) {
 }
 
 JNIEXPORT jlong JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1PCRE_nativeMaketables(JNIEnv *env, jclass c) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1PCRE_nativeMaketables(JNIEnv *env, jclass c) {
 	return (jlong) pcre_maketables();
 }
 
 JNIEXPORT jobject JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1PCRE_nativeCompile(JNIEnv *env, jclass c, jstring pattern, jint options, jlong tables) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1PCRE_nativeCompile(JNIEnv *env, jclass c, jstring pattern, jint options, jlong tables) {
 	const char *patternChars = (*env)->GetStringUTFChars(env, pattern, NULL);
 	char *errorMessage;
 	int errOffset;
@@ -51,14 +51,14 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1PCRE_nativeCompile(JNIEnv *env, j
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1PCRE_nativeGetCaptureCount(JNIEnv *env, jclass c, jlong code, jlong extra) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1PCRE_nativeGetCaptureCount(JNIEnv *env, jclass c, jlong code, jlong extra) {
     int captureCount;
 	int rc = pcre_fullinfo((void *)code, (void *)extra, PCRE_INFO_CAPTURECOUNT, &captureCount);
     return rc < 0 ? rc : captureCount;
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1PCRE_nativeGetCaptureNames(JNIEnv *env, jclass c, jlong code, jlong extra, jobjectArray ret) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1PCRE_nativeGetCaptureNames(JNIEnv *env, jclass c, jlong code, jlong extra, jobjectArray ret) {
     int nameCount;
     int nameEntrySize;
     char* nameTable;
@@ -85,7 +85,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1PCRE_nativeGetCaptureNames(JNIEnv
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1PCRE_nativeExec(JNIEnv *env, jclass c, jlong code, jlong extra, jstring subject,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1PCRE_nativeExec(JNIEnv *env, jclass c, jlong code, jlong extra, jstring subject,
 	jint startOffset, jint options, jintArray ovector, jint ovectorLen) {
 	const char *subjectChars = (*env)->GetStringUTFChars(env, subject, NULL);
 	int subjectLength = (*env)->GetStringUTFLength(env, subject);
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/stats_fft.c b/com.oracle.truffle.r.native/fficall/src/jni/stats_fft.c
index 4ed4a0dcf2..fbc7767895 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/stats_fft.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/stats_fft.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,7 @@ typedef Rboolean (*fft_work)(double *a, int nseg, int n, int nspn, int isn,
 		  double *work, int *iwork);
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Stats_native_1fft_1factor(JNIEnv *env, jclass c, jlong address,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Stats_native_1fft_1factor(JNIEnv *env, jclass c, jlong address,
 		jint n, jintArray jpmaxf, jintArray jpmaxp) {
 	fft_factor f = (fft_factor) address;
 	int *pmaxf = (*env)->GetPrimitiveArrayCritical(env, jpmaxf, NULL);
@@ -39,7 +39,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Stats_native_1fft_1factor(JNIEnv
 }
 
 JNIEXPORT int JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Stats_native_1fft_1work(JNIEnv *env, jclass c, jlong address,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Stats_native_1fft_1work(JNIEnv *env, jclass c, jlong address,
 		jdoubleArray ja, int nseg, int n, int nsps, int isn, jdoubleArray jwork, jintArray jiwork) {
 	fft_work f = (fft_work) address;
 	double *a = (*env)->GetPrimitiveArrayCritical(env, ja, NULL);
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/userrng_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/userrng_rffi.c
index fd510c9e56..dda2cf2db8 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/userrng_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/userrng_rffi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,27 +29,27 @@ typedef int* (*call_nSeed)(void);
 typedef int* (*call_seeds)(void);
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1UserRng_nativeInit(JNIEnv *env, jclass c, jlong address, jint seed) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1UserRng_nativeInit(JNIEnv *env, jclass c, jlong address, jint seed) {
 	call_init f = (call_init) address;
 	f(seed);
 }
 
 JNIEXPORT double JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1UserRng_nativeRand(JNIEnv *env, jclass c, jlong address) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1UserRng_nativeRand(JNIEnv *env, jclass c, jlong address) {
 	call_rand f = (call_rand) address;
 	double* dp = f();
 	return *dp;
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1UserRng_nativeNSeed(JNIEnv *env, jclass c, jlong address) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1UserRng_nativeNSeed(JNIEnv *env, jclass c, jlong address) {
 	call_nSeed f = (call_nSeed) address;
 	int *pn = f();
 	return *pn;
 }
 
 JNIEXPORT void JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1UserRng_nativeSeeds(JNIEnv *env, jclass c, jlong address, jintArray seedsArray) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1UserRng_nativeSeeds(JNIEnv *env, jclass c, jlong address, jintArray seedsArray) {
 	call_seeds f = (call_seeds) address;
 	int *pseeds = f();
 	int seedslen = (*env)->GetArrayLength(env, seedsArray);
diff --git a/com.oracle.truffle.r.native/fficall/src/jni/zip_rffi.c b/com.oracle.truffle.r.native/fficall/src/jni/zip_rffi.c
index e6bbec4c6c..ffde19de4d 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/zip_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/zip_rffi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,11 +23,11 @@
 
 #include <rffiutils.h>
 
-extern int compress(char *dest, long *destlen, char *source, long *sourcelen);
-extern int uncompress(char *dest, long *destlen, char *source, long *sourcelen);
+extern int compress(char *dest, long *destlen, char *source, long sourcelen);
+extern int uncompress(char *dest, long *destlen, char *source, long sourcelen);
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Zip_native_1compress(JNIEnv *env, jclass c,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Zip_native_1compress(JNIEnv *env, jclass c,
 		jbyteArray jdest, jlong destlen, jbyteArray jsource, jlong sourcelen) {
     char *dest = (*env)->GetPrimitiveArrayCritical(env, jdest, NULL);
     char *source = (*env)->GetPrimitiveArrayCritical(env, jsource, NULL);
@@ -38,7 +38,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Zip_native_1compress(JNIEnv *env,
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1Zip_native_1uncompress(JNIEnv *env, jclass c,
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1Zip_native_1uncompress(JNIEnv *env, jclass c,
 		jbyteArray jdest, jlong destlen, jbyteArray jsource, jlong sourcelen) {
     char *dest = (*env)->GetPrimitiveArrayCritical(env, jdest, NULL);
     char *source = (*env)->GetPrimitiveArrayCritical(env, jsource, NULL);
diff --git a/com.oracle.truffle.r.native/fficall/src/jniboot/jniboot.c b/com.oracle.truffle.r.native/fficall/src/jniboot/jniboot.c
index fb7783eb30..b313815395 100644
--- a/com.oracle.truffle.r.native/fficall/src/jniboot/jniboot.c
+++ b/com.oracle.truffle.r.native/fficall/src/jniboot/jniboot.c
@@ -36,7 +36,7 @@ static void initUnsatisfiedLinkError(JNIEnv *env) {
 }
 
 JNIEXPORT jlong JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1DLL_native_1dlopen(JNIEnv *env, jclass c, jstring jpath, jboolean local, jboolean now) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1DLL_native_1dlopen(JNIEnv *env, jclass c, jstring jpath, jboolean local, jboolean now) {
     const char *path = (*env)->GetStringUTFChars(env, jpath, NULL);
     int flags = (local ? RTLD_LOCAL : RTLD_GLOBAL) | (now ? RTLD_NOW : RTLD_LAZY);
     void *handle = dlopen(path, flags);
@@ -51,7 +51,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1DLL_native_1dlopen(JNIEnv *env, j
 }
 
 JNIEXPORT jlong JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1DLL_native_1dlsym(JNIEnv *env, jclass c, jlong handle, jstring jsymbol) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1DLL_native_1dlsym(JNIEnv *env, jclass c, jlong handle, jstring jsymbol) {
     const char *symbol = (*env)->GetStringUTFChars(env, jsymbol, NULL);
     void *address = dlsym((void *)handle, symbol);
     if (address == NULL) {
@@ -67,7 +67,7 @@ Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1DLL_native_1dlsym(JNIEnv *env, jc
 }
 
 JNIEXPORT jint JNICALL
-Java_com_oracle_truffle_r_runtime_ffi_jni_JNI_1DLL_native_1dlclose(JNIEnv *env, jclass c, jlong handle) {
+Java_com_oracle_truffle_r_ffi_impl_jni_JNI_1DLL_native_1dlclose(JNIEnv *env, jclass c, jlong handle) {
     int rc = dlclose((void *)handle);
     return rc;
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Makefile b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Makefile
index 99821495d2..6d769d9629 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Makefile
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Makefile
@@ -52,22 +52,25 @@ GNUR_MAIN_SRC = $(GNUR_HOME)/src/main
 
 GNUR_C_OBJECTS := $(addprefix $(OBJ)/, $(GNUR_APPL_C_FILES:.c=.o) $(GNUR_MAIN_C_FILES:.c=.o))
 LLVM_GNUR_C_OBJECTS := $(GNUR_C_OBJECTS:.o=_llvm.o)
-#$(info LLVM_GNUR_C_OBJECTS: $(LLVM_GNUR_C_OBJECTS))
+$(info LLVM_GNUR_C_OBJECTS: $(LLVM_GNUR_C_OBJECTS))
 
 GNUR_F_OBJECTS := $(addprefix $(OBJ)/, $(notdir $(GNUR_APPL_F_FILES:.f=.o)))
 LLVM_GNUR_F_OBJECTS := $(GNUR_F_OBJECTS:.o=_llvm.o)
-#$(info LLVM_GNUR_F_OBJECTS: $(LLVM_GNUR_F_OBJECTS))
+$(info LLVM_GNUR_F_OBJECTS: $(LLVM_GNUR_F_OBJECTS))
 
 C_HDRS := $(wildcard *.h)
 
 LOCAL_C_SOURCES := $(wildcard *.c) 
 COMMON_C_SOURCES := $(wildcard ../common/*.c)
-C_SOURCES := $(LOCAL_C_SOURCES) $(COMMON_C_SOURCES)
+SHARED_NFI_C_SOURCES := ../truffle_nfi/misc_rffi.c ../truffle_nfi/lapack_rffi.c ../truffle_nfi/appl_rffi.c
+C_SOURCES := $(LOCAL_C_SOURCES) $(COMMON_C_SOURCES) $(SHARED_NFI_C_SOURCES)
+$(info C_SOURCES=$(C_SOURCES))
 LOCAL_C_OBJECTS := $(addprefix $(OBJ)/, $(LOCAL_C_SOURCES:.c=.o))
 COMMON_C_OBJECTS := $(addprefix $(OBJ)/, $(notdir $(COMMON_C_SOURCES:.c=.o)))
-C_OBJECTS := $(LOCAL_C_OBJECTS) $(COMMON_C_OBJECTS)
+SHARED_NFI_C_OBJECTS := $(addprefix $(OBJ)/, $(notdir $(SHARED_NFI_C_SOURCES:.c=.o)))
+C_OBJECTS := $(LOCAL_C_OBJECTS) $(COMMON_C_OBJECTS) $(SHARED_NFI_C_OBJECTS)
 LLVM_C_OBJECTS := $(C_OBJECTS:.o=_llvm.o)
-#$(info LLVM_C_OBJECTS=$(LLVM_C_OBJECTS))
+$(info LLVM_C_OBJECTS=$(LLVM_C_OBJECTS))
 
 SULONG_DIR = $(abspath $(FASTR_R_HOME)/../sulong)
 
@@ -84,7 +87,7 @@ FFLAGS := -DFASTR_LLVM
 # uncomment to see exactly where headers are being read from
 #CFLAGS := $(CFLAGS) -H
 
-all: Makefile $(LLVM_C_OBJECTS) $(LLVM_GNUR_COBJECTS) $(LLVM_GNUR_F_OBJECTS)
+all: Makefile $(LLVM_C_OBJECTS) $(LLVM_GNUR_C_OBJECTS) $(LLVM_GNUR_F_OBJECTS)
 
 $(C_OBJECTS): | $(OBJ)
 
@@ -102,11 +105,14 @@ $(OBJ)/%_llvm.o: $(GNUR_MAIN_SRC)/%.c
 	$(CC) $(CFLAGS) $(INCLUDES) $(GNUR_HEADER_DEFS) $(SUPPRESS_WARNINGS) -c $< -o $@
 
 $(OBJ)/%_llvm.o: %.c $(FASTR_NATIVE_DIR)/include/Rinternals.h rffiutils.h
-	$(CC) $(CFLAGS) $(INCLUDES) $(GNUR_HEADER_DEFS) -I../variable_defs $(SUPPRESS_WARNINGS) -c $< -o $@
+	$(CC) $(CFLAGS) $(INCLUDES) $(GNUR_HEADER_DEFS) $(SUPPRESS_WARNINGS) -c $< -o $@
 
 $(OBJ)/%_llvm.o: ../common/%.c $(FASTR_NATIVE_DIR)/include/Rinternals.h
 	$(CC) $(CFLAGS) $(INCLUDES) $(GNUR_HEADER_DEFS) $(SUPPRESS_WARNINGS) -c $< -o $@
 
+$(OBJ)/%_llvm.o: ../truffle_nfi/%.c $(FASTR_NATIVE_DIR)/include/Rinternals.h
+	$(CC) $(CFLAGS) $(INCLUDES) $(GNUR_HEADER_DEFS) $(SUPPRESS_WARNINGS) -c $< -o $@
+
 $(OBJ)/%_llvm.o: $(GNUR_APPL_SRC)/%.f
 	$(F77) $(FFLAGS) $(FPICFLAGS) -c $< -o $@
 
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Memory.c b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Memory.c
new file mode 100644
index 0000000000..352ca45ce1
--- /dev/null
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Memory.c
@@ -0,0 +1,117 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 1995-2015, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+#include <rffiutils.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define T_MEM_TABLE_INITIAL_SIZE 0
+// The table of transient objects that have been allocated dur the current FFI call
+static void **tMemTable;
+// hwm of tMemTable
+static int tMemTableIndex;
+static int tMemTableLength;
+
+void *R_chk_calloc(size_t nelem, size_t elsize);
+
+// Memory that is auto-reclaimed across FFI calls
+char *R_alloc(size_t n, int size) {
+    void *p = R_chk_calloc(n, size);
+    if (tMemTableIndex >= tMemTableLength) {
+	int newLength = 2 * tMemTableLength;
+	void *newtMemTable = malloc(sizeof(void*) * newLength);
+	if (newtMemTable == NULL) {
+	    fatalError("malloc failure");
+	}
+	memcpy(newtMemTable, tMemTable, tMemTableLength * sizeof(void*));
+	free(tMemTable);
+	tMemTable = newtMemTable;
+	tMemTableLength = newLength;
+    }
+    tMemTable[tMemTableIndex] = p;
+    return (char*) p;
+}
+
+char* S_alloc(long n, int size) {
+	char *p = R_alloc(n, size);
+	memset(p, 0, n);
+	return p;
+}
+
+char* S_realloc(char *p, long a, long b, int size) {
+	return unimplemented("S_realloc");
+}
+
+void allocExit() {
+    int i;
+    for (i = 0; i < tMemTableIndex; i++) {
+	free(tMemTable[i]);
+    }
+}
+
+void *R_chk_calloc(size_t nelem, size_t elsize) {
+    void *p;
+#ifndef HAVE_WORKING_CALLOC
+    if (nelem == 0)
+	return (NULL);
+#endif
+    p = calloc(nelem, elsize);
+    if (!p) /* problem here is that we don't have a format for size_t. */
+	error(_("'Calloc' could not allocate memory (%.0f of %u bytes)"),
+		(double) nelem, elsize);
+    return (p);
+}
+
+void *R_chk_realloc(void *ptr, size_t size) {
+    void *p;
+    /* Protect against broken realloc */
+    if(ptr) p = realloc(ptr, size); else p = malloc(size);
+    if(!p)
+	error(_("'Realloc' could not re-allocate memory (%.0f bytes)"),
+	      (double) size);
+    return(p);
+}
+
+void R_chk_free(void *ptr) {
+    if(ptr) {
+	    free(ptr);
+    }
+}
+
+int VMAX_MAGIC = 1234;
+
+void* vmaxget(void) {
+//    unimplemented("vmaxget");
+    // ignored
+    return &VMAX_MAGIC;
+}
+
+void vmaxset(const void * x) {
+//    unimplemented("vmaxget");
+    if (x != &VMAX_MAGIC) {
+	unimplemented("vmaxset with different value");
+    }
+}
+
+void R_gc(void) {
+    unimplemented("R_gc");
+}
+
+int R_gc_running() {
+    unimplemented("R_gc_running");
+    return 0;
+}
+
+SEXP Rf_allocS4Object() {
+	unimplemented("Rf_allocS4Object unimplemented");
+	return NULL;
+}
+
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Rinternals.c b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Rinternals.c
index db953ec457..473de0261a 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Rinternals.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Rinternals.c
@@ -23,12 +23,47 @@
 
 #include <rffiutils.h>
 #include <truffle.h>
+#include "../common/rffi_upcalls.h"
 
 // Most everything in RInternals.h
 
-static char *ensure_truffle_chararray(const char *x);
+void **callbacks = NULL;
+
+void Rinternals_addCallback(int index, void *callback) {
+	if (callbacks == NULL) {
+		callbacks = truffle_managed_malloc(UPCALLS_TABLE_SIZE * sizeof(void*));
+	}
+	callbacks[index] = callback;
+}
+
 static char *ensure_truffle_chararray_n(const char *x, int n);
 
+// R_GlobalEnv et al are not a variables in FASTR as they are RContext specific
+SEXP FASTR_R_GlobalEnv() {
+	IMPORT_CALLHELPER_IMPL();
+	return truffle_invoke(obj, "R_GlobalEnv");
+}
+
+SEXP FASTR_R_BaseEnv() {
+	IMPORT_CALLHELPER_IMPL();
+	return truffle_invoke(obj, "R_BaseEnv");
+}
+
+SEXP FASTR_R_BaseNamespace() {
+	IMPORT_CALLHELPER_IMPL();
+	return truffle_invoke(obj, "R_BaseNamespace");
+}
+
+SEXP FASTR_R_NamespaceRegistry() {
+	IMPORT_CALLHELPER_IMPL();
+	return truffle_invoke(obj, "R_NamespaceRegistry");
+}
+
+Rboolean FASTR_R_Interactive() {
+	IMPORT_CALLHELPER_IMPL();
+	return (Rboolean) truffle_invoke_i(obj, "R_Interactive");
+}
+
 SEXP Rf_ScalarInteger(int value) {
 	IMPORT_CALLHELPER();
 	return truffle_invoke(obj, "Rf_ScalarInteger", value);
@@ -54,11 +89,12 @@ SEXP Rf_allocVector3(SEXPTYPE t, R_xlen_t len, R_allocator_t* allocator) {
 	    return unimplemented("RF_allocVector with custom allocator");
     }
 	IMPORT_CALLHELPER();
-	return truffle_invoke(obj, "Rf_allocateVector", t, len);
+	return truffle_invoke(obj, "Rf_allocVector", t, len);
 }
 
 SEXP Rf_allocArray(SEXPTYPE t, SEXP dims) {
-	return unimplemented("Rf_allocArray");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "Rf_allocArray", t, dims);
 }
 
 SEXP Rf_alloc3DArray(SEXPTYPE t, int x, int y, int z) {
@@ -66,7 +102,8 @@ SEXP Rf_alloc3DArray(SEXPTYPE t, int x, int y, int z) {
 }
 
 SEXP Rf_allocMatrix(SEXPTYPE mode, int nrow, int ncol) {
-	return unimplemented("Rf_allocMatrix");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "Rf_allocMatrix", mode, nrow, ncol);
 }
 
 SEXP Rf_allocList(int x) {
@@ -78,11 +115,13 @@ SEXP Rf_allocSExp(SEXPTYPE t) {
 }
 
 SEXP Rf_cons(SEXP car, SEXP cdr) {
-	return unimplemented("Rf_cons");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "Rf_cons", car, cdr);
 }
 
 void Rf_defineVar(SEXP symbol, SEXP value, SEXP rho) {
-	unimplemented("Rf_defineVar");
+	IMPORT_CALLHELPER();
+    truffle_invoke(obj, "Rf_defineVar", symbol, value, rho);
 }
 
 void Rf_setVar(SEXP x, SEXP y, SEXP z) {
@@ -115,11 +154,13 @@ SEXP Rf_findVarInFrame(SEXP symbol, SEXP rho) {
 }
 
 SEXP Rf_getAttrib(SEXP vec, SEXP name) {
-	return unimplemented("Rf_getAttrib");
+	IMPORT_CALLHELPER();
+    return truffle_invoke(obj, "Rf_getAttrib", vec, name);
 }
 
 SEXP Rf_setAttrib(SEXP vec, SEXP name, SEXP val) {
-	return unimplemented("Rf_setAttrib");
+	IMPORT_CALLHELPER();
+    return truffle_invoke(obj, "Rf_setAttrib", vec, name, val);
 }
 
 SEXP Rf_duplicate(SEXP x) {
@@ -133,11 +174,13 @@ SEXP Rf_shallow_duplicate(SEXP x) {
 }
 
 R_xlen_t Rf_any_duplicated(SEXP x, Rboolean from_last) {
-	return (R_xlen_t) unimplemented("Rf_any_duplicated");
+	IMPORT_CALLHELPER();
+    return (R_xlen_t) truffle_invoke(obj, "Rf_any_duplicated", x, from_last);
 }
 
 SEXP Rf_duplicated(SEXP x, Rboolean y) {
-	return unimplemented("Rf_duplicated");
+	IMPORT_CALLHELPER();
+    return truffle_invoke(obj, "Rf_duplicated", x, y);
 }
 
 SEXP Rf_applyClosure(SEXP x, SEXP y, SEXP z, SEXP a, SEXP b) {
@@ -153,32 +196,8 @@ void Rf_copyVector(SEXP x, SEXP y) {
 }
 
 Rboolean Rf_inherits(SEXP x, const char * klass) {
-	unimplemented("Rf_inherits");
-	return FALSE;
-}
-
-Rboolean Rf_isReal(SEXP x) {
-    return TYPEOF(x) == REALSXP;
-}
-
-Rboolean Rf_isSymbol(SEXP x) {
-    return TYPEOF(x) == SYMSXP;
-}
-
-Rboolean Rf_isComplex(SEXP x) {
-    return TYPEOF(x) == CPLXSXP;
-}
-
-Rboolean Rf_isEnvironment(SEXP x) {
-    return TYPEOF(x) == ENVSXP;
-}
-
-Rboolean Rf_isExpression(SEXP x) {
-    return TYPEOF(x) == EXPRSXP;
-}
-
-Rboolean Rf_isLogical(SEXP x) {
-    return TYPEOF(x) == LGLSXP;
+	IMPORT_CALLHELPER();
+    return (Rboolean) truffle_invoke(obj, "Rf_inherits", x, klass);
 }
 
 Rboolean Rf_isObject(SEXP s) {
@@ -196,7 +215,8 @@ SEXP Rf_install(const char *name) {
 }
 
 SEXP Rf_installChar(SEXP charsxp) {
-	return unimplemented("Rf_installChar");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "Rf_installChar", charsxp);
 }
 
 Rboolean Rf_isNull(SEXP s) {
@@ -219,20 +239,11 @@ cetype_t Rf_getCharCE(SEXP x) {
     return CE_NATIVE;
 }
 
-static char *ensure_truffle_chararray(const char *x) {
-	if (truffle_is_truffle_object(x)) {
-		return x;
-	} else {
-		IMPORT_CALLHELPER();
-		return truffle_invoke(obj, "bytesToNativeCharArray", truffle_read_bytes(x));
-	}
-}
-
 char *ensure_truffle_chararray_n(const char *x, int n) {
 	if (truffle_is_truffle_object(x)) {
 		return x;
 	} else {
-		IMPORT_CALLHELPER();
+		IMPORT_CALLHELPER_IMPL();
 		return truffle_invoke(obj, "bytesToNativeCharArray", truffle_read_n_bytes(x, n));
 	}
 }
@@ -240,7 +251,7 @@ char *ensure_truffle_chararray_n(const char *x, int n) {
 SEXP Rf_mkCharLenCE_truffle(const char *x, cetype_t enc) {
 	// Assumes x is a NativeCharArray
 	IMPORT_CALLHELPER();
-	return truffle_invoke(obj, "Rf_mkCharLenCE", x, enc);
+	return truffle_invoke(obj, "Rf_mkCharLenCE", x, 0, enc);
 }
 
 SEXP Rf_mkChar(const char *x) {
@@ -269,13 +280,13 @@ SEXP Rf_mkString(const char *s) {
 }
 
 int Rf_ncols(SEXP x) {
-	unimplemented("Rf_ncols");
-	return 0;
+	IMPORT_CALLHELPER();
+	return (int) truffle_invoke(obj, "Rf_ncols", x);
 }
 
 int Rf_nrows(SEXP x) {
-	unimplemented("Rf_nrows");
-	return 0;
+	IMPORT_CALLHELPER();
+	return (int) truffle_invoke(obj, "Rf_nrows", x);
 }
 
 
@@ -316,15 +327,13 @@ void Rf_error(const char *format, ...) {
 	// RError.error does quite a lot of stuff including potentially searching for R condition handlers
 	// and, if it finds any, does not return, but throws a different exception than RError.
 	// We definitely need to exit the FFI call and we certainly cannot return to our caller.
-	// So we call CallRFFIHelper.Rf_error to throw the RError exception. When the pending
-	// exception (whatever it is) is observed by JNI, the call to Rf_error will return where we do a
-	// non-local transfer of control back to the entry point (which will cleanup).
 	char buf[8192];
 	va_list(ap);
 	va_start(ap,format);
 	Rvsnprintf(buf, BUFSIZE - 1, format, ap);
 	va_end(ap);
-	unimplemented("Rf_error");
+	IMPORT_CALLHELPER();
+	truffle_invoke(obj, "Rf_error", ensure_truffle_chararray(buf));
 }
 
 void Rf_errorcall(SEXP x, const char *format, ...) {
@@ -397,11 +406,13 @@ void R_ProcessEvents(void) {
 // Tools package support, not in public API
 
 SEXP R_NewHashedEnv(SEXP parent, SEXP size) {
-	return unimplemented("R_NewHashedEnv");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "R_NewHashedEnv", parent, size);
 }
 
 SEXP Rf_classgets(SEXP x, SEXP y) {
-	return unimplemented("Rf_classgets");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "Rf_classgets", x, y);
 }
 
 const char *Rf_translateChar(SEXP x) {
@@ -440,64 +451,13 @@ SEXP Rf_namesgets(SEXP x, SEXP y) {
 }
 
 SEXP GetOption1(SEXP tag){
-	return unimplemented("GetOption1");
-}
-
-SEXP GetOption(SEXP tag, SEXP rho) {
-    return GetOption1(tag);
-}
-
-int GetOptionCutoff(void)
-{
-    int w;
-    w = asInteger(GetOption1(install("deparse.cutoff")));
-    if (w == NA_INTEGER || w <= 0) {
-	warning(_("invalid 'deparse.cutoff', used 60"));
-	w = 60;
-    }
-    return w;
-}
-
-#define R_MIN_WIDTH_OPT		10
-#define R_MAX_WIDTH_OPT		10000
-#define R_MIN_DIGITS_OPT	0
-#define R_MAX_DIGITS_OPT	22
-
-int GetOptionWidth(void)
-{
-    int w;
-    w = asInteger(GetOption1(install("width")));
-    if (w < R_MIN_WIDTH_OPT || w > R_MAX_WIDTH_OPT) {
-	warning(_("invalid printing width, used 80"));
-	return 80;
-    }
-    return w;
-}
-
-int GetOptionDigits(void)
-{
-    int d;
-    d = asInteger(GetOption1(install("digits")));
-    if (d < R_MIN_DIGITS_OPT || d > R_MAX_DIGITS_OPT) {
-	warning(_("invalid printing digits, used 7"));
-	return 7;
-    }
-    return d;
-}
-
-Rboolean Rf_GetOptionDeviceAsk(void)
-{
-    int ask;
-    ask = asLogical(GetOption1(install("device.ask.default")));
-    if(ask == NA_LOGICAL) {
-	warning(_("invalid value for \"device.ask.default\", using FALSE"));
-	return FALSE;
-    }
-    return ask != 0;
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "GetOption1", tag);
 }
 
 void Rf_gsetVar(SEXP symbol, SEXP value, SEXP rho) {
-	unimplemented("Rf_gsetVar");
+	IMPORT_CALLHELPER();
+    truffle_invoke(obj, "Rf_gsetVar", symbol, value, rho);
 }
 
 SEXP TAG(SEXP e) {
@@ -506,7 +466,8 @@ SEXP TAG(SEXP e) {
 }
 
 SEXP PRINTNAME(SEXP e) {
-	return unimplemented("PRINTNAME");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "PRINTNAME", e);
 }
 
 SEXP CAR(SEXP e) {
@@ -535,7 +496,8 @@ SEXP CADR(SEXP e) {
 }
 
 SEXP CDDR(SEXP e) {
-	return unimplemented("CDDR");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "CDDR", e);
 }
 
 SEXP CDDDR(SEXP e) {
@@ -544,8 +506,8 @@ SEXP CDDDR(SEXP e) {
 }
 
 SEXP CADDR(SEXP e) {
-    unimplemented("CADDR");
-    return NULL;
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "CADDR", e);
 }
 
 SEXP CADDDR(SEXP e) {
@@ -568,20 +530,23 @@ void SET_MISSING(SEXP x, int v) {
 }
 
 void SET_TAG(SEXP x, SEXP y) {
-	unimplemented("SET_TAG");
+	IMPORT_CALLHELPER();
+ truffle_invoke(obj, "SET_TAG", x, y);
 }
 
 SEXP SETCAR(SEXP x, SEXP y) {
-	return unimplemented("SETCAR");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "SETCAR", x, y);
 }
 
 SEXP SETCDR(SEXP x, SEXP y) {
-	return unimplemented("SETCDR");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "SETCDR", x, y);
 }
 
 SEXP SETCADR(SEXP x, SEXP y) {
-    unimplemented("SETCADR");
-    return NULL;
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "SETCADR", x, y);
 }
 
 SEXP SETCADDR(SEXP x, SEXP y) {
@@ -612,13 +577,13 @@ SEXP CLOENV(SEXP x) {
 }
 
 int RDEBUG(SEXP x) {
-    unimplemented("RDEBUG");
-    return 0;
+	IMPORT_CALLHELPER();
+	return (int) truffle_invoke(obj, "RDEBUG", x);
 }
 
 int RSTEP(SEXP x) {
-	unimplemented("RSTEP");
-    return 0;
+	IMPORT_CALLHELPER();
+	return (int) truffle_invoke(obj, "RSTEP", x);
 }
 
 int RTRACE(SEXP x) {
@@ -627,11 +592,13 @@ int RTRACE(SEXP x) {
 }
 
 void SET_RDEBUG(SEXP x, int v) {
-    unimplemented("SET_RDEBUG");
+	IMPORT_CALLHELPER();
+	truffle_invoke(obj, "SET_RDEBUG", x, v);
 }
 
 void SET_RSTEP(SEXP x, int v) {
-    unimplemented("SET_RSTEP");
+	IMPORT_CALLHELPER();
+	truffle_invoke(obj, "SET_RSTEP", x, v);
 }
 
 void SET_RTRACE(SEXP x, int v) {
@@ -651,7 +618,8 @@ void SET_CLOENV(SEXP x, SEXP v) {
 }
 
 SEXP SYMVALUE(SEXP x) {
-	return unimplemented("SYMVALUE");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "SYMVALUE", x);
 }
 
 SEXP INTERNAL(SEXP x) {
@@ -668,7 +636,8 @@ void SET_DDVAL(SEXP x, int v) {
 }
 
 void SET_SYMVALUE(SEXP x, SEXP v) {
-    unimplemented("SET_SYMVALUE");
+	IMPORT_CALLHELPER();
+    truffle_invoke(obj, "SET_SYMVALUE", x, v);
 }
 
 void SET_INTERNAL(SEXP x, SEXP v) {
@@ -681,7 +650,8 @@ SEXP FRAME(SEXP x) {
 }
 
 SEXP ENCLOS(SEXP x) {
-	return unimplemented("ENCLOS");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "ENCLOS", x);
 }
 
 SEXP HASHTAB(SEXP x) {
@@ -711,20 +681,23 @@ void SET_HASHTAB(SEXP x, SEXP v) {
 
 
 SEXP PRCODE(SEXP x) {
-	return unimplemented("PRCODE");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "PRCODE", x);
 }
 
 SEXP PRENV(SEXP x) {
-	unimplemented("PRENV");
-    return 0;
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "PRENV", x);
 }
 
 SEXP PRVALUE(SEXP x) {
-	return unimplemented("PRVALUE");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "PRVALUE", x);
 }
 
 int PRSEEN(SEXP x) {
-	return (int) unimplemented("PRSEEN");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "PRSEEN", x);
 }
 
 void SET_PRSEEN(SEXP x, int v) {
@@ -859,7 +832,8 @@ SEXP Rf_asChar(SEXP x){
 }
 
 SEXP Rf_PairToVectorList(SEXP x){
-	return unimplemented("Rf_PairToVectorList");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "Rf_PairToVectorList", x);
 }
 
 SEXP Rf_VectorToPairList(SEXP x){
@@ -871,7 +845,8 @@ SEXP Rf_asCharacterFactor(SEXP x){
 }
 
 int Rf_asLogical(SEXP x){
-	return (int) unimplemented("Rf_asLogical");
+	IMPORT_CALLHELPER();
+    return truffle_invoke_i(obj, "Rf_asLogical", x);
 }
 
 int Rf_asInteger(SEXP x) {
@@ -879,6 +854,11 @@ int Rf_asInteger(SEXP x) {
     return truffle_invoke_i(obj, "Rf_asInteger", x);
 }
 
+double Rf_asReal(SEXP x) {
+	IMPORT_CALLHELPER();
+	return (double) truffle_invoke_d(obj, "Rf_asReal", x);
+}
+
 Rcomplex Rf_asComplex(SEXP x){
 	unimplemented("Rf_asLogical");
 	Rcomplex c; return c;
@@ -899,7 +879,8 @@ int OBJECT(SEXP x){
 }
 
 int MARK(SEXP x){
-    return (int) unimplemented("MARK");
+	IMPORT_CALLHELPER();
+	return (int) truffle_invoke(obj, "MARK", x);
 }
 
 int NAMED(SEXP x){
@@ -920,7 +901,8 @@ void SET_TYPEOF(SEXP x, int v){
 }
 
 SEXP SET_TYPEOF_FASTR(SEXP x, int v){
-	return unimplemented("SET_TYPEOF_FASTR");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "SET_TYPEOF_FASTR", x, v);
 }
 
 void SET_NAMED(SEXP x, int v){
@@ -932,29 +914,15 @@ void SET_ATTRIB(SEXP x, SEXP v){
 }
 
 void DUPLICATE_ATTRIB(SEXP to, SEXP from){
-	unimplemented("DUPLICATE_ATTRIB");
-}
-
-char *dgettext(const char *domainname, const char *msgid) {
-	printf("dgettext: '%s'\n", msgid);
-	return (char*) msgid;
-}
-
-char *dngettext(const char *domainname, const char *msgid, const char * msgid_plural, unsigned long int n) {
-    printf("dngettext: singular - '%s' ; plural - '%s'\n", msgid, msgid_plural);
-    return (char*) (n == 1 ? msgid : msgid_plural);
+	IMPORT_CALLHELPER();
+ truffle_invoke(obj, "DUPLICATE_ATTRIB", to, from);
 }
 
 const char *R_CHAR(SEXP charsxp) {
-	IMPORT_CALLHELPER();
+	IMPORT_CALLHELPER_IMPL();
 	return (char *)truffle_invoke(obj, "charSXPToNativeCharArray", charsxp);
 }
 
-void *(R_DATAPTR)(SEXP x) {
-    unimplemented("R_DATAPTR");
-	return NULL;
-}
-
 void R_qsort_I  (double *v, int *II, int i, int j) {
 	unimplemented("R_qsort_I");
 }
@@ -972,10 +940,13 @@ int IS_S4_OBJECT(SEXP x) {
 }
 
 void SET_S4_OBJECT(SEXP x) {
-	unimplemented("SET_S4_OBJECT");
+	IMPORT_CALLHELPER();
+ truffle_invoke(obj, "SET_S4_OBJECT", x);
 }
+
 void UNSET_S4_OBJECT(SEXP x) {
-	unimplemented("UNSET_S4_OBJECT");
+	IMPORT_CALLHELPER();
+     truffle_invoke(obj, "UNSET_S4_OBJECT", x);
 }
 
 Rboolean R_ToplevelExec(void (*fun)(void *), void *data) {
@@ -1006,7 +977,8 @@ double R_strtod(const char *c, char **end) {
 }
 
 SEXP R_PromiseExpr(SEXP x) {
-	return unimplemented("R_PromiseExpr");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "R_PromiseExpr", x);
 }
 
 SEXP R_ClosureExpr(SEXP x) {
@@ -1028,27 +1000,33 @@ void *R_ExternalPtrAddr(SEXP s) {
 }
 
 SEXP R_ExternalPtrTag(SEXP s) {
-	return unimplemented("R_ExternalPtrTag");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "R_ExternalPtrTag", s);
 }
 
 SEXP R_ExternalPtrProt(SEXP s) {
-	return unimplemented("R_ExternalPtrProt");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "R_ExternalPtrProt", s);
 }
 
 void R_SetExternalPtrAddr(SEXP s, void *p) {
-	unimplemented("R_SetExternalPtrAddr");
+	IMPORT_CALLHELPER();
+	truffle_invoke(obj, "R_SetExternalPtrAddr", s, p);
 }
 
 void R_SetExternalPtrTag(SEXP s, SEXP tag) {
-	unimplemented("R_SetExternalPtrTag");
+	IMPORT_CALLHELPER();
+	truffle_invoke(obj, "R_SetExternalPtrTag", s, tag);
 }
 
 void R_SetExternalPtrProt(SEXP s, SEXP p) {
-	unimplemented("R_SetExternalPtrProt");
+	IMPORT_CALLHELPER();
+	truffle_invoke(obj, "R_SetExternalPtrProt", s, p);
 }
 
 void R_ClearExternalPtr(SEXP s) {
-	R_SetExternalPtrAddr(s, NULL);
+	IMPORT_CALLHELPER();
+	truffle_invoke(obj, "R_ClearExternalPtr", s);
 }
 
 void R_RegisterFinalizer(SEXP s, SEXP fun) {
@@ -1071,12 +1049,14 @@ void R_RunPendingFinalizers(void) {
 	// TODO implement, but not fail for now
 }
 
-SEXP R_do_slot(SEXP obj, SEXP name) {
-	return unimplemented("R_do_slot");
+SEXP R_do_slot(SEXP objx, SEXP name) {
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "R_do_slot", objx, name);
 }
 
-SEXP R_do_slot_assign(SEXP obj, SEXP name, SEXP value) {
-	return unimplemented("R_do_slot_assign");
+SEXP R_do_slot_assign(SEXP objx, SEXP name, SEXP value) {
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "R_do_slot_assign", objx, name, value);
 }
 
 int R_has_slot(SEXP obj, SEXP name) {
@@ -1084,7 +1064,8 @@ int R_has_slot(SEXP obj, SEXP name) {
 }
 
 SEXP R_do_MAKE_CLASS(const char *what) {
-	return unimplemented("R_do_MAKE_CLASS");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "R_do_MAKE_CLASS", what);
 }
 
 SEXP R_getClassDef (const char *what) {
@@ -1092,7 +1073,8 @@ SEXP R_getClassDef (const char *what) {
 }
 
 SEXP R_do_new_object(SEXP class_def) {
-	return unimplemented("R_do_new_object");
+	IMPORT_CALLHELPER();
+	return truffle_invoke(obj, "R_do_new_object", class_def);
 }
 
 int R_check_class_and_super(SEXP x, const char **valid, SEXP rho) {
@@ -1112,13 +1094,16 @@ void R_ReleaseObject(SEXP x) {
 }
 
 Rboolean R_compute_identical(SEXP x, SEXP y, int flags) {
-	return (Rboolean) unimplemented("R_compute_identical");
+	IMPORT_CALLHELPER();
+	return (Rboolean) truffle_invoke(obj, "R_compute_identical", x, y, flags);
 }
 
 void Rf_copyListMatrix(SEXP s, SEXP t, Rboolean byrow) {
-	unimplemented("Rf_copyListMatrix");
+	IMPORT_CALLHELPER();
+	truffle_invoke(obj, "Rf_copyListMatrix", s, t, byrow);
 }
 
 void Rf_copyMatrix(SEXP s, SEXP t, Rboolean byrow) {
-	unimplemented("Rf_copyMatrix");
+	IMPORT_CALLHELPER();
+	truffle_invoke(obj, "Rf_copyMatrix", s, t, byrow);
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/base_rffi.c b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/base_rffi.c
new file mode 100644
index 0000000000..0100364162
--- /dev/null
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/base_rffi.c
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+#include <rffiutils.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <glob.h>
+#include <sys/utsname.h>
+#include <errno.h>
+
+#define IMPORT_BASE_HELPER() void *base = truffle_import_cached("_fastr_rffi_base")
+
+int call_base_getpid() {
+	return getpid();
+}
+
+int call_base_getwd(char *buf, int len) {
+	char *r = getcwd(buf, len);
+	if (r == NULL) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+int call_base_setwd(char *dir) {
+	return chdir(dir);
+}
+
+int call_base_mkdir(char *dir, int mode) {
+	return mkdir(dir, mode);
+}
+
+int call_base_mkdtemp(char *template) {
+	char *r = mkdtemp(template);
+	if (r == NULL) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+void call_base_readlink(void *callback, char* path) {
+	char *link = NULL;
+    char buf[4096];
+	int cerrno = 0;
+    int len = readlink(path, buf, 4096);
+    IMPORT_BASE_HELPER();
+    if (len == -1) {
+    	cerrno = errno;
+    } else {
+    	buf[len] = 0;
+    	link = ensure_truffle_chararray(buf);
+    }
+    truffle_invoke(base,"setReadlinkResult", callback, link, cerrno);
+}
+
+void call_base_strtol(void *callback, char *s, int nbase) {
+	long rc = strtol(s, NULL, nbase);
+	IMPORT_BASE_HELPER();
+	truffle_invoke(base, "setStrtolResult", callback, rc, errno);
+}
+
+void call_base_uname(void *callback) {
+	struct utsname name;
+
+	uname(&name);
+	IMPORT_BASE_HELPER();
+	truffle_invoke(base, "setUnameResult",
+			callback,
+			ensure_truffle_chararray(name.sysname),
+			ensure_truffle_chararray(name.release),
+			ensure_truffle_chararray(name.version),
+			ensure_truffle_chararray(name.machine),
+			ensure_truffle_chararray(name.nodename));
+}
+
+int call_base_chmod(char *path, int mode) {
+	int rc = chmod(path, mode);
+	return rc;
+}
+
+int errfunc(const char* path, int error) {
+	return 0;
+}
+
+void call_base_glob(void *callback, char *pattern) {
+	glob_t globstruct;
+
+	int rc = glob(pattern, 0, errfunc, &globstruct);
+	if (rc == 0) {
+		IMPORT_BASE_HELPER();
+
+		int i;
+		for (i = 0; i < globstruct.gl_pathc; i++) {
+			char *path = globstruct.gl_pathv[i];
+			truffle_invoke(base, "setGlobResult", callback, ensure_truffle_chararray(path));
+		}
+	}
+
+}
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/call_dlopen.c b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/call_dlopen.c
new file mode 100644
index 0000000000..04fec88d23
--- /dev/null
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/call_dlopen.c
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+/*
+ * This is a wrapper to the native dlopen/dlclose calls. The wrapper allows it to be called through the LLVM
+ * RFFI machinery, and that takes care of the actual native call.
+ *
+ * In an LLVM build, this is only used in very special circumstances, namely when a native library
+ * for which no LLVM is available must be loaded, e.g., by the R dyn.load call. It must be separate
+ * because the DLLRFFI implementation in an LLVM build, expects, by default, that dlopen will open
+ * a library containing LLVM.
+ */
+
+#include <rffiutils.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <errno.h>
+
+long call_dlopen(void *callback, char *path, int local, int now) {
+	int flags = (local ? RTLD_LOCAL : RTLD_GLOBAL) | (now ? RTLD_NOW : RTLD_LAZY);
+	void *handle = dlopen(path, flags);
+	if (handle == NULL) {
+		int cerrno = errno;
+		char *error = dlerror();
+	    truffle_invoke(truffle_import_cached("_fastr_dllnative_helper"), "setDlopenResult", callback, error);
+	}
+	return (long) handle;
+}
+
+int call_dlclose(void *handle) {
+	return dlclose(handle);
+}
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/pcre_rffi.c b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/pcre_rffi.c
new file mode 100644
index 0000000000..5307bdc7a4
--- /dev/null
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/pcre_rffi.c
@@ -0,0 +1,81 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 1995, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (c) 1997-2015,  The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+#include <rffiutils.h>
+
+#define PCRE_INFO_CAPTURECOUNT       2
+#define PCRE_INFO_NAMEENTRYSIZE      7
+#define PCRE_INFO_NAMECOUNT          8
+#define PCRE_INFO_NAMETABLE          9
+
+extern char *pcre_maketables();
+extern void *pcre_compile(char *pattern, int options, char **errorMessage, int *errOffset, char *tables);
+extern int  pcre_exec(void *code, void *extra, char* subject, int subjectLength, int startOffset, int options, int *ovector, int ovecSize);
+int pcre_fullinfo(void *code, void *extra, int what, void *where);
+extern void pcre_free(void *code);
+
+#define IMPORT_PCRE_HELPER() void *pcre = truffle_import_cached("_fastr_rffi_pcre")
+
+char *call_pcre_maketables() {
+	return pcre_maketables();
+}
+
+void *call_pcre_compile(char *pattern, int options, long tables) {
+	char *errorMessage;
+	int errOffset;
+	void *pcre_result = pcre_compile(pattern, options, &errorMessage, &errOffset, (char*) tables);
+	void *msg = NULL;
+	if (pcre_result == NULL) {
+		msg = ensure_truffle_chararray(errorMessage);
+	}
+	IMPORT_PCRE_HELPER();
+	return truffle_invoke(pcre, "makeResult", pcre_result, msg, errOffset);
+}
+
+int call_pcre_getcapturecount(long code, long extra) {
+    int captureCount;
+	int rc = pcre_fullinfo((void*) code, (void*) extra, PCRE_INFO_CAPTURECOUNT, &captureCount);
+    return rc < 0 ? rc : captureCount;
+}
+
+int call_pcre_getcapturenames(long code, long extra, void *captureNamesCallback) {
+    int nameCount;
+    int nameEntrySize;
+    char* nameTable;
+    int res;
+	res = pcre_fullinfo((void*) code, (void*) extra, PCRE_INFO_NAMECOUNT, &nameCount);
+    if (res < 0) {
+        return res;
+    }
+    res = pcre_fullinfo((void*) code, (void*) extra, PCRE_INFO_NAMEENTRYSIZE, &nameEntrySize);
+    if (res < 0) {
+        return res;
+    }
+	res = pcre_fullinfo((void*) code, (void*) extra, PCRE_INFO_NAMETABLE, &nameTable);
+    if (res < 0) {
+        return res;
+    }
+	IMPORT_PCRE_HELPER();
+    // from GNU R's grep.c
+	for(int i = 0; i < nameCount; i++) {
+	    char* entry = nameTable + nameEntrySize * i;
+	    int captureNum = (entry[0] << 8) + entry[1] - 1;
+	    truffle_invoke(pcre, "addCaptureName", captureNum, ensure_truffle_chararray(entry + 2), captureNamesCallback);
+    }
+    return res;
+}
+
+int call_pcre_exec(long code, long extra, char *subject, int subjectLength, int startOffset, int options, int *ovectorElems, int ovectorLen) {
+	int rc = pcre_exec((void *) code, (void *) extra, (char *) subject, subjectLength, startOffset, options,
+			ovectorElems, ovectorLen);
+	return rc;
+}
+
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/rffiutils.c b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/rffiutils.c
index bc39a9ebed..ebfce9ea30 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/rffiutils.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/rffiutils.c
@@ -30,3 +30,11 @@ SEXP unimplemented(char *name) {
 	return result;
 }
 
+char *ensure_truffle_chararray(const char *x) {
+	if (truffle_is_truffle_object(x)) {
+		return (char *)x;
+	} else {
+		IMPORT_CALLHELPER_IMPL();
+		return truffle_invoke(obj, "bytesToNativeCharArray", truffle_read_n_bytes(x, strlen(x)));
+	}
+}
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/rffiutils.h b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/rffiutils.h
index 2a380ccc84..fdc0facaf0 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/rffiutils.h
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/rffiutils.h
@@ -30,7 +30,9 @@
 #include <truffle.h>
 
 #define IMPORT_CALLHELPER() void *obj = truffle_import_cached("_fastr_rffi_callhelper")
+#define IMPORT_CALLHELPER_IMPL() void *obj = truffle_import_cached("_fastr_rffi_callhelper_impl")
 
+char *ensure_truffle_chararray(const char *x);
 SEXP unimplemented(char *name);
 
 #endif /* RFFIUTILS_H */
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/variables.c b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/variables.c
index f41cc54934..7972251ef0 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/variables.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/variables.c
@@ -21,7 +21,7 @@
  * questions.
  */
 #include <rffiutils.h>
-#include <variables.h>
+#include "../common/rffi_variablesindex.h"
 
 // Arith.h
 double R_NaN;		/* IEEE NaN */
@@ -30,211 +30,160 @@ double R_NegInf;	/* IEEE -Inf */
 double R_NaReal;	/* NA_REAL: IEEE */
 int R_NaInt;	/* NA_INTEGER:= INT_MIN currently */
 
-// R_GlobalEnv et al are not a variables in FASTR as they are RContext specific
-SEXP FASTR_R_GlobalEnv() {
-	IMPORT_CALLHELPER();
-	return truffle_invoke(obj, "R_GlobalEnv");
-}
-
-SEXP FASTR_R_BaseEnv() {
-	IMPORT_CALLHELPER();
-	return truffle_invoke(obj, "R_BaseEnv");
-}
-
-SEXP FASTR_R_BaseNamespace() {
-	IMPORT_CALLHELPER();
-	return truffle_invoke(obj, "R_BaseNamespace");
-}
-
-SEXP FASTR_R_NamespaceRegistry() {
-	IMPORT_CALLHELPER();
-	return truffle_invoke(obj, "R_NamespaceRegistry");
-}
-
-Rboolean FASTR_R_Interactive() {
-	IMPORT_CALLHELPER();
-	return (Rboolean) truffle_invoke_i(obj, "R_Interactive");
-}
+void **variables = NULL;
 
 char *FASTR_R_Home() {
-	IMPORT_CALLHELPER();
+	IMPORT_CALLHELPER_IMPL();
 	return (char *) truffle_invoke(obj, "R_Home");
 }
 
-// Callbacks because cannot store TruffleObjects in memory (currently)
-
 SEXP FASTR_R_NilValue() {
-	IMPORT_CALLHELPER();
-	return truffle_invoke(obj, "R_NilValue");
-
+	return (SEXP) variables[R_NilValue_x];
 }
 
 SEXP FASTR_R_UnboundValue() {
-	IMPORT_CALLHELPER();
-	return truffle_invoke(obj, "R_UnboundValue");
+	return variables[R_UnboundValue_x];
 }
 
 SEXP FASTR_R_EmptyEnv() {
-	IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_EmptyEnv");
+    return variables[R_EmptyEnv_x];
 }
 
 SEXP FASTR_R_MissingArg() {
-    IMPORT_CALLHELPER();
-    return Truffle_Invoke(Obj, "R_MissingArg");
+    return variables[R_MissingArg_x];
 }
 
 SEXP FASTR_R_BaseSymbol() {
-    IMPORT_CALLHELPER();
-    Return Truffle_Invoke(Obj, "R_BaseSymbol");
+    return variables[R_BaseSymbol_x];
 }
 
 SEXP FASTR_R_BraceSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_BraceSymbol");
+    return variables[R_BraceSymbol_x];
 }
 
 SEXP FASTR_R_Bracket2Symbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_Bracket2Symbol");
+    return variables[R_Bracket2Symbol_x];
 }
 
 SEXP FASTR_R_BracketSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_BracketSymbol");
+    return variables[R_BracketSymbol_x];
 }
 
 SEXP FASTR_R_ClassSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_ClassSymbol");
+    return variables[R_ClassSymbol_x];
 }
 
 SEXP FASTR_R_DeviceSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_DeviceSymbol");
+    return variables[R_DeviceSymbol_x];
+}
+
+SEXP FASTR_R_DevicesSymbol() {
+    return variables[R_DevicesSymbol_x];
 }
 
 SEXP FASTR_R_DimNamesSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_DimNamesSymbol");
+    return variables[R_DimNamesSymbol_x];
 }
 
 SEXP FASTR_R_DimSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_DimSymbol");
+    return variables[R_DimSymbol_x];
 }
 
 SEXP FASTR_R_DollarSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_DollarSymbol");
+    return variables[R_DollarSymbol_x];
 }
 
 SEXP FASTR_R_DotsSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_DotsSymbol");
+    return variables[R_DotsSymbol_x];
 }
 
 SEXP FASTR_R_DropSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_DropSymbol");
+    return variables[R_DropSymbol_x];
 }
 
 SEXP FASTR_R_LastvalueSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_LastvalueSymbol");
+    return variables[R_LastvalueSymbol_x];
 }
 
 SEXP FASTR_R_LevelsSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_LevelsSymbol");
+    return variables[R_LevelsSymbol_x];
 }
 
 SEXP FASTR_R_ModeSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_ModeSymbol");
+    return variables[R_ModeSymbol_x];
 }
 
 SEXP FASTR_R_NaRmSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_NaRmSymbol");
+    return variables[R_NaRmSymbol_x];
 }
 
 SEXP FASTR_R_NameSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_NameSymbol");
+    return variables[R_NameSymbol_x];
 }
 
 SEXP FASTR_R_NamesSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_NamesSymbol");
+    return variables[R_NamesSymbol_x];
 }
 
 SEXP FASTR_R_NamespaceEnvSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_NamespaceEnvSymbol");
+    return variables[R_NamespaceEnvSymbol_x];
 }
 
 SEXP FASTR_R_PackageSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_PackageSymbol");
+    return variables[R_PackageSymbol_x];
 }
 
 SEXP FASTR_R_QuoteSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_QuoteSymbol");
+    return variables[R_QuoteSymbol_x];
 }
 
 SEXP FASTR_R_RowNamesSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_RowNamesSymbol");
+    return variables[R_RowNamesSymbol_x];
 }
 
 SEXP FASTR_R_SeedsSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_SeedsSymbol");
+    return variables[R_SeedsSymbol_x];
 }
 
 SEXP FASTR_R_SourceSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_SourceSymbol");
+    return variables[R_SourceSymbol_x];
 }
 
 SEXP FASTR_R_TspSymbol() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_TspSymbol");
+    return variables[R_TspSymbol_x];
 }
 
 SEXP FASTR_R_dot_defined() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_dot_defined");
+    return variables[R_dot_defined_x];
 }
 
 SEXP FASTR_R_dot_Method() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_dot_Method");
+    return variables[R_dot_Method_x];
 }
 
 SEXP FASTR_R_dot_target() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_dot_target");
+    return variables[R_dot_target_x];
 }
 
 SEXP FASTR_R_NaString() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_NaString");
+    return variables[R_NaString_x];
 }
 
 SEXP FASTR_R_BlankString() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_BlankString");
+    return variables[R_BlankString_x];
 }
 
 SEXP FASTR_R_BlankScalarString() {
-    IMPORT_CALLHELPER();
-    return truffle_invoke(obj, "R_BlankScalarString");
+    return variables[R_BlankScalarString_x];
 }
 
+SEXP FASTR_R_SrcrefSymbol() {
+    return variables[R_SrcrefSymbol_x];
+}
 
+SEXP FASTR_R_SrcfileSymbol() {
+    return variables[R_SrcfileSymbol_x];
+}
 void Call_initvar_double(int index, double value) {
 	switch (index) {
     case R_NaN_x: R_NaN = value; break;
@@ -250,4 +199,11 @@ void Call_initvar_int(int index, int value) {
 	}
 }
 
+void Call_initvar_obj(int index, void *value) {
+	if (variables == NULL) {
+		variables = truffle_managed_malloc(VARIABLES_TABLE_SIZE * sizeof(void*));
+	}
+	variables[index] = value;
+}
+
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RContextUpCallsRFFI.java b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/zip_rffi.c
similarity index 68%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RContextUpCallsRFFI.java
rename to com.oracle.truffle.r.native/fficall/src/truffle_llvm/zip_rffi.c
index ab6621014f..026aff63a0 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RContextUpCallsRFFI.java
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/zip_rffi.c
@@ -20,25 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.ffi;
+#include <rffiutils.h>
 
-/**
- * The following functions are global variables in the standard R FFI. However, owing to the support
- * for virtual R sessions (see .fastr.context) in FastR they are remapped as functions.
- */
-
-public interface RContextUpCallsRFFI {
-    // Checkstyle: stop method name check
-    Object R_GlobalContext();
-
-    Object R_GlobalEnv();
-
-    Object R_BaseEnv();
+extern int compress(char *dest, long *destlen, char *source, long sourcelen);
+extern int uncompress(char *dest, long *destlen, char *source, long sourcelen);
 
-    Object R_BaseNamespace();
-
-    Object R_NamespaceRegistry();
-
-    int R_Interactive();
+int call_zip_compress(char *dest, long destlen, char *source, long sourcelen) {
+	return compress(dest, &destlen, source, sourcelen);
+}
 
+int call_zip_uncompress(char *dest, long destlen, char *source, long sourcelen) {
+	return uncompress(dest, &destlen, source, sourcelen);
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile
index 1d14ab2e86..99b2dffdc1 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Makefile
@@ -33,6 +33,7 @@ OBJ = ../../lib
 C_HDRS := $(wildcard *.h)
 
 C_SOURCES = $(wildcard *.c)
+#$(info C_SOURCES=$(C_SOURCES))
 C_OBJECTS := $(patsubst %.c,$(OBJ)/%.o,$(C_SOURCES))
 #$(info C_OBJECTS=$(C_OBJECTS))
 
@@ -52,8 +53,8 @@ $(C_OBJECTS): | $(OBJ)
 $(OBJ):
 	mkdir -p $(OBJ)
 
-$(OBJ)/%.o: %.c $(TOPDIR)/include/Rinternals.h rffi_callbacksindex.h $(C_HDRS)
-	$(CC) $(CFLAGS) $(INCLUDES) -I../variable_defs -c $< -o $@
+$(OBJ)/%.o: %.c $(TOPDIR)/include/Rinternals.h ../common/rffi_upcallsindex.h $(C_HDRS)
+	$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
 
 # for debugging, to see what's really being compiled
 $(OBJ)/%.E: %.c $(TOPDIR)/include/Rinternals.h
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rinternals.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rinternals.c
index b3005b1448..8477aed8f8 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rinternals.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rinternals.c
@@ -22,12 +22,15 @@
  */
 #include <Rinterface.h>
 #include <rffiutils.h>
-#include <rffi_callbacks.h>
 #include <Rinternals_common.h>
+#include "../common/rffi_upcalls.h"
 
-void *callbacks[CALLBACK_TABLE_SIZE];
+void **callbacks = NULL;
 
 void Rinternals_addCallback(int index, void *closure) {
+	if (callbacks == NULL) {
+		callbacks = malloc(UPCALLS_TABLE_SIZE * sizeof(void*));
+	}
 	newClosureRef(closure);
 	callbacks[index] = closure;
 }
@@ -79,37 +82,12 @@ void return_FREE(void *address) {
 //	free(address);
 }
 
-// R_GlobalEnv et al are not a variables in FASTR as they are RContext specific
-SEXP FASTR_R_GlobalEnv() {
-	return ((call_R_GlobalEnv) callbacks[R_GlobalEnv_x])();
-}
-
-SEXP FASTR_R_BaseEnv() {
-	return ((call_R_BaseEnv) callbacks[R_BaseEnv_x])();
-}
-
-SEXP FASTR_R_BaseNamespace() {
-	return ((call_R_BaseNamespace) callbacks[R_BaseNamespace_x])();
-}
-
-SEXP FASTR_R_NamespaceRegistry() {
-	return ((call_R_NamespaceRegistry) callbacks[R_NamespaceRegistry_x])();
-}
-
-CTXT FASTR_GlobalContext() {
-	return ((call_R_GlobalContext) callbacks[R_GlobalContext_x])();
-}
-
-Rboolean FASTR_R_Interactive() {
-	return (int) ((call_R_Interactive) callbacks[R_Interactive_x])();
-}
-
 SEXP CAR(SEXP e) {
-	return ((call_CAR) callbacks[CAR_x])(e);
+	return checkRef(((call_CAR) callbacks[CAR_x])(e));
 }
 
 SEXP CDR(SEXP e) {
-	return ((call_CDR) callbacks[CDR_x])(e);
+	return checkRef(((call_CDR) callbacks[CDR_x])(e));
 }
 
 int *INTEGER(SEXP x) {
@@ -142,7 +120,7 @@ const char * R_CHAR(SEXP x) {
 }
 
 SEXP Rf_ScalarString(SEXP value) {
-	return ((call_Rf_ScalarString) callbacks[Rf_ScalarString_x])(value);
+	return checkRef(((call_Rf_ScalarString) callbacks[Rf_ScalarString_x])(value));
 }
 
 SEXP Rf_mkChar(const char *x) {
@@ -158,7 +136,7 @@ SEXP Rf_mkCharLen(const char *x, int y) {
 }
 
 SEXP Rf_mkCharLenCE(const char *x, int len, cetype_t enc) {
-	return ((call_Rf_mkCharLenCE) callbacks[Rf_mkCharLenCE_x])(x,len, enc);
+	return checkRef(((call_Rf_mkCharLenCE) callbacks[Rf_mkCharLenCE_x])(x,len, enc));
 }
 
 SEXP Rf_mkString(const char *s) {
@@ -170,15 +148,15 @@ void Rf_gsetVar(SEXP symbol, SEXP value, SEXP rho) {
 }
 
 SEXP Rf_coerceVector(SEXP x, SEXPTYPE mode) {
-	return ((call_Rf_coerceVector) callbacks[Rf_coerceVector_x])(x, mode);
+	return checkRef(((call_Rf_coerceVector) callbacks[Rf_coerceVector_x])(x, mode));
 }
 
 SEXP Rf_cons(SEXP car, SEXP cdr) {
-	return ((call_Rf_cons) callbacks[Rf_cons_x])(car, cdr);
+	return checkRef(((call_Rf_cons) callbacks[Rf_cons_x])(car, cdr));
 }
 
 SEXP Rf_GetOption1(SEXP tag) {
-	return ((call_Rf_GetOption1) callbacks[Rf_GetOption1_x])(tag);
+	return checkRef(((call_Rf_GetOption1) callbacks[Rf_GetOption1_x])(tag));
 }
 
 #define BUFSIZE 8192
@@ -270,15 +248,15 @@ void REvprintf(const char *format, va_list args) {
 
 
 SEXP Rf_ScalarInteger(int value) {
-	return ((call_Rf_ScalarInteger) callbacks[Rf_ScalarInteger_x])(value);
+	return checkRef(((call_Rf_ScalarInteger) callbacks[Rf_ScalarInteger_x])(value));
 }
 
 SEXP Rf_ScalarReal(double value) {
-	return ((call_Rf_ScalarReal) callbacks[Rf_ScalarDouble_x])(value);
+	return checkRef(((call_Rf_ScalarReal) callbacks[Rf_ScalarDouble_x])(value));
 }
 
 SEXP Rf_ScalarLogical(int value) {
-	return ((call_Rf_ScalarLogical) callbacks[Rf_ScalarLogical_x])(value);
+	return checkRef(((call_Rf_ScalarLogical) callbacks[Rf_ScalarLogical_x])(value));
 }
 
 SEXP Rf_allocVector3(SEXPTYPE t, R_xlen_t len, R_allocator_t* allocator) {
@@ -286,11 +264,11 @@ SEXP Rf_allocVector3(SEXPTYPE t, R_xlen_t len, R_allocator_t* allocator) {
   	    unimplemented("RF_allocVector with custom allocator");
 	    return NULL;
     }
-    return ((call_Rf_allocVector) callbacks[Rf_allocVector_x])(t, len);
+    return checkRef(((call_Rf_allocVector) callbacks[Rf_allocVector_x])(t, len));
 }
 
 SEXP Rf_allocArray(SEXPTYPE t, SEXP dims) {
-	return ((call_Rf_allocArray) callbacks[Rf_allocArray_x])(t, dims);
+	return checkRef(((call_Rf_allocArray) callbacks[Rf_allocArray_x])(t, dims));
 }
 
 SEXP Rf_alloc3DArray(SEXPTYPE t, int x, int y, int z) {
@@ -298,7 +276,7 @@ SEXP Rf_alloc3DArray(SEXPTYPE t, int x, int y, int z) {
 }
 
 SEXP Rf_allocMatrix(SEXPTYPE mode, int nrow, int ncol) {
-	return ((call_Rf_allocMatrix) callbacks[Rf_allocMatrix_x])(mode, nrow, ncol);
+	return checkRef(((call_Rf_allocMatrix) callbacks[Rf_allocMatrix_x])(mode, nrow, ncol));
 }
 
 SEXP Rf_allocList(int x) {
@@ -327,23 +305,23 @@ SEXP Rf_dimnamesgets(SEXP x, SEXP y) {
 }
 
 SEXP Rf_eval(SEXP expr, SEXP env) {
-	return ((call_Rf_eval) callbacks[Rf_eval_x])(expr, env);
+	return checkRef(((call_Rf_eval) callbacks[Rf_eval_x])(expr, env));
 }
 
 SEXP Rf_findFun(SEXP symbol, SEXP rho) {
-	return ((call_Rf_findFun) callbacks[Rf_findFun_x])(symbol, rho);
+	return checkRef(((call_Rf_findFun) callbacks[Rf_findFun_x])(symbol, rho));
 }
 
 SEXP Rf_findVar(SEXP sym, SEXP rho) {
-	return ((call_Rf_findVar) callbacks[Rf_findVar_x])(sym, rho);
+	return checkRef(((call_Rf_findVar) callbacks[Rf_findVar_x])(sym, rho));
 }
 
 SEXP Rf_findVarInFrame(SEXP rho, SEXP sym) {
-	return ((call_Rf_findVarInFrame) callbacks[Rf_findVarInFrame_x])(rho, sym);
+	return checkRef(((call_Rf_findVarInFrame) callbacks[Rf_findVarInFrame_x])(rho, sym));
 }
 
 SEXP Rf_findVarInFrame3(SEXP rho, SEXP sym, Rboolean b) {
-	return ((call_Rf_findVarInFrame3) callbacks[Rf_findVarInFrame3_x])(rho, sym, b);
+	return checkRef(((call_Rf_findVarInFrame3) callbacks[Rf_findVarInFrame3_x])(rho, sym, b));
 }
 
 SEXP Rf_getAttrib(SEXP vec, SEXP name) {
@@ -353,15 +331,15 @@ SEXP Rf_getAttrib(SEXP vec, SEXP name) {
 }
 
 SEXP Rf_setAttrib(SEXP vec, SEXP name, SEXP val) {
-	return ((call_Rf_setAttrib) callbacks[Rf_setAttrib_x])(vec, name, val);
+	return checkRef(((call_Rf_setAttrib) callbacks[Rf_setAttrib_x])(vec, name, val));
 }
 
 SEXP Rf_duplicate(SEXP x) {
-	return ((call_Rf_duplicate) callbacks[Rf_duplicate_x])(x, 1);
+	return checkRef(((call_Rf_duplicate) callbacks[Rf_duplicate_x])(x, 1));
 }
 
 SEXP Rf_shallow_duplicate(SEXP x) {
-	return ((call_Rf_duplicate) callbacks[Rf_duplicate_x])(x, 0);
+	return checkRef(((call_Rf_duplicate) callbacks[Rf_duplicate_x])(x, 0));
 }
 
 R_xlen_t Rf_any_duplicated(SEXP x, Rboolean from_last) {
@@ -403,11 +381,11 @@ void Rf_PrintValue(SEXP x) {
 }
 
 SEXP Rf_install(const char *name) {
-	return ((call_Rf_install) callbacks[Rf_install_x])(name);
+	return checkRef(((call_Rf_install) callbacks[Rf_install_x])(name));
 }
 
 SEXP Rf_installChar(SEXP charsxp) {
-	return ((call_Rf_installChar) callbacks[Rf_installChar_x])(charsxp);
+	return checkRef(((call_Rf_installChar) callbacks[Rf_installChar_x])(charsxp));
 }
 
 Rboolean Rf_isNull(SEXP s) {
@@ -474,11 +452,11 @@ void R_ProcessEvents(void) {
 
 // Tools package support, not in public API
 SEXP R_NewHashedEnv(SEXP parent, SEXP size) {
-	return ((call_R_NewHashedEnv) callbacks[R_NewHashedEnv_x])(parent, size);
+	return checkRef(((call_R_NewHashedEnv) callbacks[R_NewHashedEnv_x])(parent, size));
 }
 
 SEXP Rf_classgets(SEXP vec, SEXP klass) {
-	return ((call_Rf_classgets) callbacks[Rf_classgets_x])(vec, klass);
+	return checkRef(((call_Rf_classgets) callbacks[Rf_classgets_x])(vec, klass));
 }
 
 const char *Rf_translateChar(SEXP x) {
@@ -500,7 +478,7 @@ const char *Rf_translateCharUTF8(SEXP x) {
 }
 
 SEXP Rf_lengthgets(SEXP x, R_len_t y) {
-	return ((call_Rf_lengthgets) callbacks[Rf_lengthgets_x])(x, y);
+	return checkRef(((call_Rf_lengthgets) callbacks[Rf_lengthgets_x])(x, y));
 }
 
 SEXP Rf_xlengthgets(SEXP x, R_xlen_t y) {
@@ -512,7 +490,7 @@ SEXP R_lsInternal(SEXP env, Rboolean all) {
 }
 
 SEXP R_lsInternal3(SEXP env, Rboolean all, Rboolean sorted) {
-	return ((call_R_lsInternal3) callbacks[R_lsInternal3_x])(env, all, sorted);
+	return checkRef(((call_R_lsInternal3) callbacks[R_lsInternal3_x])(env, all, sorted));
 }
 
 SEXP Rf_namesgets(SEXP x, SEXP y) {
@@ -520,11 +498,11 @@ SEXP Rf_namesgets(SEXP x, SEXP y) {
 }
 
 SEXP TAG(SEXP e) {
-	return ((call_TAG) callbacks[TAG_x])(e);
+	return checkRef(((call_TAG) callbacks[TAG_x])(e));
 }
 
 SEXP PRINTNAME(SEXP e) {
-	return ((call_PRINTNAME) callbacks[PRINTNAME_x])(e);
+	return checkRef(((call_PRINTNAME) callbacks[PRINTNAME_x])(e));
 }
 
 
@@ -539,11 +517,11 @@ SEXP CDAR(SEXP e) {
 }
 
 SEXP CADR(SEXP e) {
-	return ((call_CADR) callbacks[CADR_x])(e);
+	return checkRef(((call_CADR) callbacks[CADR_x])(e));
 }
 
 SEXP CDDR(SEXP e) {
-	return ((call_CDDR) callbacks[CDDR_x])(e);
+	return checkRef(((call_CDDR) callbacks[CDDR_x])(e));
 }
 
 SEXP CDDDR(SEXP e) {
@@ -552,7 +530,7 @@ SEXP CDDDR(SEXP e) {
 }
 
 SEXP CADDR(SEXP e) {
-	return ((call_CADDR) callbacks[CADDR_x])(e);
+	return checkRef(((call_CADDR) callbacks[CADDR_x])(e));
 }
 
 SEXP CADDDR(SEXP e) {
@@ -579,15 +557,15 @@ void SET_TAG(SEXP x, SEXP y) {
 }
 
 SEXP SETCAR(SEXP x, SEXP y) {
-	return ((call_SETCAR) callbacks[SETCAR_x])(x, y);
+	return checkRef(((call_SETCAR) callbacks[SETCAR_x])(x, y));
 }
 
 SEXP SETCDR(SEXP x, SEXP y) {
-	return ((call_SETCDR) callbacks[SETCDR_x])(x, y);
+	return checkRef(((call_SETCDR) callbacks[SETCDR_x])(x, y));
 }
 
 SEXP SETCADR(SEXP x, SEXP y) {
-	return ((call_SETCADR) callbacks[SETCADR_x])(x, y);
+	return checkRef(((call_SETCADR) callbacks[SETCADR_x])(x, y));
 }
 
 SEXP SETCADDR(SEXP x, SEXP y) {
@@ -655,7 +633,7 @@ void SET_CLOENV(SEXP x, SEXP v) {
 }
 
 SEXP SYMVALUE(SEXP x) {
-	return ((call_SYMVALUE) callbacks[SYMVALUE_x])(x);
+	return checkRef(((call_SYMVALUE) callbacks[SYMVALUE_x])(x));
 }
 
 SEXP INTERNAL(SEXP x) {
@@ -684,7 +662,7 @@ SEXP FRAME(SEXP x) {
 }
 
 SEXP ENCLOS(SEXP x) {
-	return ((call_ENCLOS) callbacks[ENCLOS_x])(x);
+	return checkRef(((call_ENCLOS) callbacks[ENCLOS_x])(x));
 }
 
 SEXP HASHTAB(SEXP x) {
@@ -713,15 +691,15 @@ void SET_HASHTAB(SEXP x, SEXP v) {
 }
 
 SEXP PRCODE(SEXP x) {
-	return ((call_PRCODE) callbacks[PRCODE_x])(x);
+	return checkRef(((call_PRCODE) callbacks[PRCODE_x])(x));
 }
 
 SEXP PRENV(SEXP x) {
-	return ((call_PRENV) callbacks[PRENV_x])(x);
+	return checkRef(((call_PRENV) callbacks[PRENV_x])(x));
 }
 
 SEXP PRVALUE(SEXP x) {
-	return ((call_PRVALUE) callbacks[PRVALUE_x])(x);
+	return checkRef(((call_PRVALUE) callbacks[PRVALUE_x])(x));
 }
 
 int PRSEEN(SEXP x) {
@@ -795,12 +773,12 @@ Rcomplex *COMPLEX(SEXP x){
 }
 
 SEXP STRING_ELT(SEXP x, R_xlen_t i) {
-	return ((call_STRING_ELT) callbacks[STRING_ELT_x])(x, i);
+	return checkRef(((call_STRING_ELT) callbacks[STRING_ELT_x])(x, i));
 }
 
 
 SEXP VECTOR_ELT(SEXP x, R_xlen_t i){
-	return ((call_VECTOR_ELT) callbacks[VECTOR_ELT_x])(x, i);
+	return checkRef(((call_VECTOR_ELT) callbacks[VECTOR_ELT_x])(x, i));
 }
 
 void SET_STRING_ELT(SEXP x, R_xlen_t i, SEXP v){
@@ -809,7 +787,7 @@ void SET_STRING_ELT(SEXP x, R_xlen_t i, SEXP v){
 
 
 SEXP SET_VECTOR_ELT(SEXP x, R_xlen_t i, SEXP v){
-	return ((call_SET_VECTOR_ELT) callbacks[SET_VECTOR_ELT_x])(x, i, v);
+	return checkRef(((call_SET_VECTOR_ELT) callbacks[SET_VECTOR_ELT_x])(x, i, v));
 }
 
 SEXP *STRING_PTR(SEXP x){
@@ -823,11 +801,11 @@ SEXP * NORET VECTOR_PTR(SEXP x){
 }
 
 SEXP Rf_asChar(SEXP x){
-	return ((call_Rf_asChar) callbacks[Rf_asChar_x])(x);
+	return checkRef(((call_Rf_asChar) callbacks[Rf_asChar_x])(x));
 }
 
 SEXP Rf_PairToVectorList(SEXP x){
-	return ((call_Rf_PairToVectorList) callbacks[Rf_PairToVectorList_x])(x);
+	return checkRef(((call_Rf_PairToVectorList) callbacks[Rf_PairToVectorList_x])(x));
 }
 
 SEXP Rf_VectorToPairList(SEXP x){
@@ -852,7 +830,7 @@ double Rf_asReal(SEXP x) {
 }
 
 Rcomplex Rf_asComplex(SEXP x){
-	unimplemented("Rf_asLogical");
+	unimplemented("Rf_asComplex");
 	Rcomplex c; return c;
 }
 
@@ -892,7 +870,7 @@ void SET_TYPEOF(SEXP x, int v){
 }
 
 SEXP SET_TYPEOF_FASTR(SEXP x, int v){
-	return ((call_SET_TYPEOF_FASTR) callbacks[SET_TYPEOF_FASTR_x])(x, v);
+	return checkRef(((call_SET_TYPEOF_FASTR) callbacks[SET_TYPEOF_FASTR_x])(x, v));
 }
 
 void SET_NAMED(SEXP x, int v){
@@ -962,7 +940,7 @@ Rboolean R_IsNamespaceEnv(SEXP rho) {
 }
 
 SEXP R_FindNamespace(SEXP info) {
-	return ((call_R_FindNamespace) callbacks[R_FindNamespace_x])(info);
+	return checkRef(((call_R_FindNamespace) callbacks[R_FindNamespace_x])(info));
 }
 
 SEXP R_NamespaceEnvSpec(SEXP rho) {
@@ -1033,7 +1011,7 @@ double R_strtod(const char *c, char **end) {
 }
 
 SEXP R_PromiseExpr(SEXP x) {
-	return ((call_R_PromiseExpr) callbacks[R_PromiseExpr_x])(x);
+	return checkRef(((call_R_PromiseExpr) callbacks[R_PromiseExpr_x])(x));
 }
 
 SEXP R_ClosureExpr(SEXP x) {
@@ -1045,7 +1023,7 @@ SEXP R_forceAndCall(SEXP e, int n, SEXP rho) {
 }
 
 SEXP R_MakeExternalPtr(void *p, SEXP tag, SEXP prot) {
-	return ((call_R_MakeExternalPtr) callbacks[R_MakeExternalPtr_x])(p, tag, prot);
+	return checkRef(((call_R_MakeExternalPtr) callbacks[R_MakeExternalPtr_x])(p, tag, prot));
 }
 
 void *R_ExternalPtrAddr(SEXP s) {
@@ -1053,11 +1031,11 @@ void *R_ExternalPtrAddr(SEXP s) {
 }
 
 SEXP R_ExternalPtrTag(SEXP s) {
-	return ((call_R_ExternalPtrTag) callbacks[R_ExternalPtrTag_x])(s);
+	return checkRef(((call_R_ExternalPtrTag) callbacks[R_ExternalPtrTag_x])(s));
 }
 
 SEXP R_ExternalPtrProtected(SEXP s) {
-	return ((call_R_ExternalPtrProtected) callbacks[R_ExternalPtrProtected_x])(s);
+	return checkRef(((call_R_ExternalPtrProtected) callbacks[R_ExternalPtrProtected_x])(s));
 }
 
 void R_SetExternalPtrAddr(SEXP s, void *p) {
@@ -1117,11 +1095,11 @@ void R_RunWeakRefFinalizer(SEXP w) {
 }
 
 SEXP R_do_slot(SEXP obj, SEXP name) {
-	return ((call_R_do_slot) callbacks[R_do_slot_x])(obj, name);
+	return checkRef(((call_R_do_slot) callbacks[R_do_slot_x])(obj, name));
 }
 
 SEXP R_do_slot_assign(SEXP obj, SEXP name, SEXP value) {
-	return ((call_R_do_slot_assign) callbacks[R_do_slot_assign_x])(obj, name, value);
+	return checkRef(((call_R_do_slot_assign) callbacks[R_do_slot_assign_x])(obj, name, value));
 }
 
 int R_has_slot(SEXP obj, SEXP name) {
@@ -1129,7 +1107,7 @@ int R_has_slot(SEXP obj, SEXP name) {
 }
 
 SEXP R_do_MAKE_CLASS(const char *what) {
-	return ((call_R_do_MAKE_CLASS) callbacks[R_do_MAKE_CLASS_x])(what);
+	return checkRef(((call_R_do_MAKE_CLASS) callbacks[R_do_MAKE_CLASS_x])(what));
 }
 
 SEXP R_getClassDef (const char *what) {
@@ -1137,11 +1115,11 @@ SEXP R_getClassDef (const char *what) {
 }
 
 SEXP R_do_new_object(SEXP class_def) {
-	return ((call_R_do_new_object) callbacks[R_do_new_object_x])(class_def);
+	return checkRef(((call_R_do_new_object) callbacks[R_do_new_object_x])(class_def));
 }
 
 static SEXP nfiGetMethodsNamespace() {
-    return ((call_R_MethodsNamespace) callbacks[R_MethodsNamespace_x])();
+    return checkRef(((call_R_MethodsNamespace) callbacks[R_MethodsNamespace_x])());
 }
 
 int R_check_class_etc (SEXP x, const char **valid) {
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/appl_rffi.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/appl_rffi.c
index 5ebdbc607d..bd83ecff1e 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/appl_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/appl_rffi.c
@@ -27,14 +27,14 @@ extern void dqrdc2_(double *x, int *ldx, int *n, int *p, double *tol, int *rank,
 extern void dqrcf_(double *x, int *n, int *k, double *qraux, double *y, int *ny, double *b, int* info);
 extern void dqrls_(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);
 
-void call_dqrdc2(double *x, int ldx, int n, int p, double tol, int *rank, double *qraux, int* pivot, double *work) {
+void call_appl_dqrdc2(double *x, int ldx, int n, int p, double tol, int *rank, double *qraux, int* pivot, double *work) {
 	dqrdc2_(x, &ldx, &n, &p, &tol, rank, qraux, pivot, work);
 }
 
-void call_dqrcf(double *x, int n, int k, double *qraux, double *y, int ny, double *b, int* info) {
+void call_appl_dqrcf(double *x, int n, int k, double *qraux, double *y, int ny, double *b, int* info) {
 	dqrcf_(x, &n, &k, qraux, y, &ny, b, info);
 }
 
-void call_dqrls(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) {
+void call_appl_dqrls(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) {
 	dqrls_(x, &n, &p, y, &ny, &tol, b, rsd, qty, k, jpvt, qraux, work);
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/base_rffi.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/base_rffi.c
index 93188c9e9e..28031bee77 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/base_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/base_rffi.c
@@ -30,14 +30,14 @@
 #include <errno.h>
 
 
-void call_uname(void (*call_uname_setfields)(char *sysname, char *release, char *version, char *machine, char *nodename)) {
+void call_base_uname(void (*call_uname_setfields)(char *sysname, char *release, char *version, char *machine, char *nodename)) {
 	struct utsname name;
 
 	uname(&name);
 	call_uname_setfields(name.sysname, name.release, name.version, name.machine, name.nodename);
 }
 
-void call_glob(char *pattern, void *closure) {
+void call_base_glob(char *pattern, void *closure) {
 	void (*call_addpath)(char *path) = closure;
 
 	glob_t globstruct;
@@ -51,7 +51,7 @@ void call_glob(char *pattern, void *closure) {
 	}
 }
 
-void call_readlink(void (*call_setresult)(char *link, int cerrno), char *path) {
+void call_base_readlink(void (*call_setresult)(char *link, int cerrno), char *path) {
 	char *link = NULL;
 	int cerrno = 0;
     char buf[4096];
@@ -65,7 +65,7 @@ void call_readlink(void (*call_setresult)(char *link, int cerrno), char *path) {
 	call_setresult(link, cerrno);
 }
 
-void call_strtol(void (*call_setresult)(long result, int cerrno), char *s, int base) {
+void call_base_strtol(void (*call_setresult)(long result, int cerrno), char *s, int base) {
     long rc = strtol(s, NULL, base);
 	call_setresult(rc, errno);
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/lapack_rffi.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/lapack_rffi.c
index 17d0589252..e543efacf8 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/lapack_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/lapack_rffi.c
@@ -25,7 +25,7 @@
 
 extern void ilaver_(int *major, int *minor, int *patch);
 
-void call_ilaver(int* version) {
+void call_lapack_ilaver(int* version) {
 	int major;
 	int minor;
 	int patch;
@@ -37,7 +37,7 @@ void call_ilaver(int* version) {
 
 extern int dgeev_(char *jobVL, char *jobVR, int *n, double *a, int *lda, double *wr, double *wi, double *vl, int *ldvl, double *vr, int *ldvr, double *work, int *lwork, int *info);
 
-int call_dgeev(char jobVL, char jobVR, int n, double *a, int lda, double *wr, double *wi, double *vl, int ldvl, double *vr, int ldvr, double *work, int lwork) {
+int call_lapack_dgeev(char jobVL, char jobVR, int n, double *a, int lda, double *wr, double *wi, double *vl, int ldvl, double *vr, int ldvr, double *work, int lwork) {
     int info;
     dgeev_(&jobVL, &jobVR, &n, a, &lda, wr, wi, vl, &ldvl, vr, &ldvr, work, &lwork, &info);
     return info;
@@ -46,7 +46,7 @@ int call_dgeev(char jobVL, char jobVR, int n, double *a, int lda, double *wr, do
 extern int dgeqp3_(int *m, int *n, double *a, int *lda, int *jpvt, double *tau, double *work, int *lwork, int *info);
 
 
-int call_dgeqp3(int m, int n, double *a, int lda, int *jpvt, double *tau, double *work, int lwork) {
+int call_lapack_dgeqp3(int m, int n, double *a, int lda, int *jpvt, double *tau, double *work, int lwork) {
     int info;
     dgeqp3_(&m, &n, a, &lda, jpvt, tau, work, &lwork, &info);
     return info;
@@ -54,7 +54,7 @@ int call_dgeqp3(int m, int n, double *a, int lda, int *jpvt, double *tau, double
 
 extern int dormqr_(char *side, char *trans, int *m, int *n, int *k, double *a, int *lda, double *tau, double *c, int *ldc, double *work, int *lwork, int *info);
 
-int call_dormqr(char side, char trans, int m, int n, int k, double *a, int lda, double *tau, double *c, int ldc, double *work, int lwork) {
+int call_lapack_dormqr(char side, char trans, int m, int n, int k, double *a, int lda, double *tau, double *c, int ldc, double *work, int lwork) {
     int info;
     dormqr_(&side, &trans, &m, &n, &k, a, &lda, tau, c, &ldc, work, &lwork, &info);
     return info;
@@ -63,7 +63,7 @@ int call_dormqr(char side, char trans, int m, int n, int k, double *a, int lda,
 extern int dtrtrs_(char *uplo, char *trans, char *diag, int *n, int *nrhs, double *a, int *lda, double *b, int *ldb, int *info);
 
 
-int call_dtrtrs(char uplo, char trans, char diag, int n, int nrhs, double *a, int lda, double *b, int ldb) {
+int call_lapack_dtrtrs(char uplo, char trans, char diag, int n, int nrhs, double *a, int lda, double *b, int ldb) {
     int info;
     dtrtrs_(&uplo, &trans, &diag, &n, &nrhs, a, &lda, b, &ldb, &info);
     return info;
@@ -71,7 +71,7 @@ int call_dtrtrs(char uplo, char trans, char diag, int n, int nrhs, double *a, in
 
 extern int dgetrf_(int *m, int *n, double *a, int *lda, int *ipiv, int *info);
 
-int call_dgetrf(int m, int n, double *a, int lda, int *ipiv) {
+int call_lapack_dgetrf(int m, int n, double *a, int lda, int *ipiv) {
     int info;
     dgetrf_(&m, &n, a, &lda, ipiv, &info);
     return info;
@@ -79,7 +79,7 @@ int call_dgetrf(int m, int n, double *a, int lda, int *ipiv) {
 
 extern int dpotrf_(char *uplo, int *n, double *a, int *lda, int *info);
 
-int call_dpotrf(char uplo, int n, double *a, int lda) {
+int call_lapack_dpotrf(char uplo, int n, double *a, int lda) {
     int info;
     dpotrf_(&uplo, &n, a, &lda, &info);
     return info;
@@ -87,7 +87,7 @@ int call_dpotrf(char uplo, int n, double *a, int lda) {
 
 extern int dpotri_(char *uplo, int *n, double *a, int *lda, int *info);
 
-int call_dpotri(char uplo, int n, double *a, int lda) {
+int call_lapack_dpotri(char uplo, int n, double *a, int lda) {
     int info;
     dpotri_(&uplo, &n, a, &lda, &info);
     return info;
@@ -95,7 +95,7 @@ int call_dpotri(char uplo, int n, double *a, int lda) {
 
 extern int dpstrf_(char *uplo, int *n, double *a, int *lda, int *piv, int *rank, double *tol, double *work, int *info);
 
-int call_dpstrf(char uplo, int n, double *a, int lda, int *piv, int *rank, double tol, double *work) {
+int call_lapack_dpstrf(char uplo, int n, double *a, int lda, int *piv, int *rank, double tol, double *work) {
     int info;
     dpstrf_(&uplo, &n, a, &lda, piv, rank, &tol, work, &info);
     return info;
@@ -103,7 +103,7 @@ int call_dpstrf(char uplo, int n, double *a, int lda, int *piv, int *rank, doubl
 
 extern int dgesv_(int *n, int *nrhs, double *a, int *lda, int *ipiv, double *b, int *ldb, int *info);
 
-int call_dgesv(int n, int nrhs, double *a, int lda, int *ipiv, double *b, int ldb) {
+int call_lapack_dgesv(int n, int nrhs, double *a, int lda, int *ipiv, double *b, int ldb) {
     int info;
     dgesv_(&n, &nrhs, a, &lda, ipiv, b, &ldb, &info);
     return info;
@@ -111,14 +111,14 @@ int call_dgesv(int n, int nrhs, double *a, int lda, int *ipiv, double *b, int ld
 
 extern double dlange_(char *norm, int *m, int *n, double *a, int *lda, double *work);
 
-double call_dlange(char norm, int m, int n, double *a, int lda, double *work) {
+double call_lapack_dlange(char norm, int m, int n, double *a, int lda, double *work) {
     double info = dlange_(&norm, &m, &n, a, &lda, work);
     return info;
 }
 
 extern int dgecon_(char *norm, int *n, double *a, int *lda, double *anorm, double *rcond, double *work, int *iwork, int *info);
 
-int call_dgecon(char norm, int n, double *a, int lda, double anorm, double *rcond, double *work, int *iwork) {
+int call_lapack_dgecon(char norm, int n, double *a, int lda, double anorm, double *rcond, double *work, int *iwork) {
     int info;
     dgecon_(&norm, &n, a, &lda, &anorm, rcond, work, iwork, &info);
     return info;
@@ -127,7 +127,7 @@ int call_dgecon(char norm, int n, double *a, int lda, double anorm, double *rcon
 extern int dsyevr_(char *jobz, char *range, char *uplo, int *n, double* a, int *lda, double *vl, double *vu, int *il, int *iu, double *abstol, int* m, double* w,
                 double* z, int *ldz, int* isuppz, double* work, int *lwork, int* iwork, int *liwork, int* info);
 
-int call_dsyevr(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,
+int call_lapack_dsyevr(char jobz, char range, char uplo, int n, double *a, int lda, double vl, double vu, int il, int iu, double abstol, int *m, double *w,
 		                    double *z, int ldz, int *isuppz, double *work, int lwork, int *iwork, int liwork) {
     int info;
     dsyevr_(&jobz, &range, &uplo, &n, a, &lda, &vl, &vu, &il, &iu, &abstol, m, w,
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/pcre_rffi.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/pcre_rffi.c
index 28be2cb058..e577761080 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/pcre_rffi.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/pcre_rffi.c
@@ -22,22 +22,22 @@ void *pcre_compile(char *pattern, int options, char **errorMessage, int *errOffs
 int pcre_fullinfo(void *code, void *extra, int what, void *where);
 //void pcre_free(void *code);
 
-void call_compile(void *closure, char *pattern, int options, long tables) {
-	void (*call_makeresult)(long result, char *errMsg, int errOffset) = closure;
+void call_pcre_compile(void *closure, char *pattern, int options, long tables) {
+	void (*makeresult)(long result, char *errMsg, int errOffset) = closure;
 	char *errorMessage;
 	int errOffset;
 	void *pcre_result = pcre_compile(pattern, options, &errorMessage, &errOffset, (char*) tables);
-	call_makeresult((long) pcre_result, errorMessage, errOffset);
+	makeresult((long) pcre_result, errorMessage, errOffset);
 }
 
-int call_getcapturecount(long code, long extra) {
+int call_pcre_getcapturecount(long code, long extra) {
     int captureCount;
 	int rc = pcre_fullinfo((void*) code, (void*) extra, PCRE_INFO_CAPTURECOUNT, &captureCount);
     return rc < 0 ? rc : captureCount;
 }
 
-int call_getcapturenames(void *closure, long code, long extra) {
-	void (*call_setcapturename)(int i, char *name) = closure;
+int call_pcre_getcapturenames(void *closure, long code, long extra) {
+	void (*setcapturename)(int i, char *name) = closure;
     int nameCount;
     int nameEntrySize;
     char* nameTable;
@@ -58,8 +58,7 @@ int call_getcapturenames(void *closure, long code, long extra) {
 	for(int i = 0; i < nameCount; i++) {
 	    char* entry = nameTable + nameEntrySize * i;
 	    int captureNum = (entry[0] << 8) + entry[1] - 1;
-	    call_setcapturename(captureNum, entry + 2);
+	    setcapturename(captureNum, entry + 2);
     }
     return res;
-
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffi_callbacksindex.h b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffi_callbacksindex.h
deleted file mode 100644
index 0a457d98f0..0000000000
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffi_callbacksindex.h
+++ /dev/null
@@ -1,163 +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.
- */
-
-// TODO This file should be generated automatically from RFFIUpCallMethod.main
-
-#ifndef CALLBACKSINDEX_H
-#define CALLBACKSINDEX_H
-
-#define CADDR_x 0
-#define CADR_x 1
-#define CAR_x 2
-#define CDDR_x 3
-#define CDR_x 4
-#define DUPLICATE_ATTRIB_x 5
-#define ENCLOS_x 6
-#define GetRNGstate_x 7
-#define INTEGER_x 8
-#define IS_S4_OBJECT_x 9
-#define SET_S4_OBJECT_x 10
-#define UNSET_S4_OBJECT_x 11
-#define LENGTH_x 12
-#define LOGICAL_x 13
-#define NAMED_x 14
-#define OBJECT_x 15
-#define PRCODE_x 16
-#define PRENV_x 17
-#define PRINTNAME_x 18
-#define PRSEEN_x 19
-#define PRVALUE_x 20
-#define PutRNGstate_x 21
-#define RAW_x 22
-#define RDEBUG_x 23
-#define REAL_x 24
-#define RSTEP_x 25
-#define R_BaseEnv_x 26
-#define R_BaseNamespace_x 27
-#define R_BindingIsLocked_x 28
-#define R_CHAR_x 29
-#define R_CleanUp_x 30
-#define R_ExternalPtrAddr_x 31
-#define R_ExternalPtrProtected_x 32
-#define R_ExternalPtrTag_x 33
-#define R_FindNamespace_x 34
-#define R_GetConnection_x 35
-#define R_GlobalContext_x 36
-#define R_GlobalEnv_x 37
-#define R_HomeDir_x 38
-#define R_Interactive_x 39
-#define R_MakeExternalPtr_x 40
-#define R_MethodsNamespace_x 41
-#define R_NamespaceRegistry_x 42
-#define R_NewHashedEnv_x 43
-#define R_ParseVector_x 44
-#define R_PromiseExpr_x 45
-#define R_ReadConnection_x 46
-#define R_SetExternalPtrAddr_x 47
-#define R_SetExternalPtrProtected_x 48
-#define R_SetExternalPtrTag_x 49
-#define R_ToplevelExec_x 50
-#define R_WriteConnection_x 51
-#define R_compute_identical_x 52
-#define R_do_MAKE_CLASS_x 53
-#define R_do_new_object_x 54
-#define R_do_slot_x 55
-#define R_do_slot_assign_x 56
-#define R_getContextCall_x 57
-#define R_getContextEnv_x 58
-#define R_getContextFun_x 59
-#define R_getContextSrcRef_x 60
-#define R_getGlobalFunctionContext_x 61
-#define R_getParentFunctionContext_x 62
-#define R_insideBrowser_x 63
-#define R_isEqual_x 64
-#define R_isGlobal_x 65
-#define R_lsInternal3_x 66
-#define R_new_custom_connection_x 67
-#define R_tryEval_x 68
-#define Rf_GetOption1_x 69
-#define Rf_PairToVectorList_x 70
-#define Rf_ScalarDouble_x 71
-#define Rf_ScalarInteger_x 72
-#define Rf_ScalarLogical_x 73
-#define Rf_ScalarString_x 74
-#define Rf_allocArray_x 75
-#define Rf_allocMatrix_x 76
-#define Rf_allocVector_x 77
-#define Rf_any_duplicated_x 78
-#define Rf_asChar_x 79
-#define Rf_asInteger_x 80
-#define Rf_asLogical_x 81
-#define Rf_asReal_x 82
-#define Rf_classgets_x 83
-#define Rf_coerceVector_x 84
-#define Rf_cons_x 85
-#define Rf_copyListMatrix_x 86
-#define Rf_copyMatrix_x 87
-#define Rf_defineVar_x 88
-#define Rf_duplicate_x 89
-#define Rf_error_x 90
-#define Rf_eval_x 91
-#define Rf_findVar_x 92
-#define Rf_findVarInFrame_x 93
-#define Rf_findVarInFrame3_x 94
-#define Rf_findFun_x 95
-#define Rf_getAttrib_x 96
-#define Rf_gsetVar_x 97
-#define Rf_inherits_x 98
-#define Rf_install_x 99
-#define Rf_installChar_x 100
-#define Rf_isNull_x 101
-#define Rf_isString_x 102
-#define Rf_lengthgets_x 103
-#define Rf_mkCharLenCE_x 104
-#define Rf_ncols_x 105
-#define Rf_nrows_x 106
-#define Rf_setAttrib_x 107
-#define Rf_warning_x 108
-#define Rf_warningcall_x 109
-#define Rprintf_x 110
-#define SETCADR_x 111
-#define SETCAR_x 112
-#define SETCDR_x 113
-#define SET_RDEBUG_x 114
-#define SET_RSTEP_x 115
-#define SET_STRING_ELT_x 116
-#define SET_SYMVALUE_x 117
-#define SET_TAG_x 118
-#define SET_TYPEOF_FASTR_x 119
-#define SET_VECTOR_ELT_x 120
-#define STRING_ELT_x 121
-#define SYMVALUE_x 122
-#define TAG_x 123
-#define TYPEOF_x 124
-#define VECTOR_ELT_x 125
-#define getConnectionClassString_x 126
-#define getOpenModeString_x 127
-#define getSummaryDescription_x 128
-#define isSeekable_x 129
-#define unif_rand_x 130
-
-#define CALLBACK_TABLE_SIZE 131
-
-#endif // CALLBACKSINDEX_H
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffiutils.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffiutils.c
index 26a0c8bd18..fd2a3c2d49 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffiutils.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffiutils.c
@@ -21,9 +21,89 @@
  * questions.
  */
 #include <rffiutils.h>
+#include <trufflenfi.h>
 
 void* unimplemented(char *f) {
 	printf("unimplemented %s\n", f);
 	exit(1);
 }
 
+typedef struct globalRefTable_struct {
+    int permanent;
+    SEXP gref;         // The jobject (SEXP) global ref
+} GlobalRefElem;
+
+#define CACHED_GLOBALREFS_INITIAL_SIZE 64
+static GlobalRefElem *cachedGlobalRefs = NULL;
+static int cachedGlobalRefsHwm;
+static int cachedGlobalRefsLength;
+
+void init_utils() {
+	if (cachedGlobalRefs == NULL) {
+		cachedGlobalRefs = calloc(CACHED_GLOBALREFS_INITIAL_SIZE, sizeof(GlobalRefElem));
+		cachedGlobalRefsLength = CACHED_GLOBALREFS_INITIAL_SIZE;
+		cachedGlobalRefsHwm = 0;
+	}
+}
+static SEXP findCachedGlobalRef(SEXP obj) {
+    for (int i = 0; i < cachedGlobalRefsHwm; i++) {
+        GlobalRefElem elem = cachedGlobalRefs[i];
+        if (elem.gref == NULL) {
+            continue;
+        }
+        if (isSameObject(elem.gref, obj)) {
+            return elem.gref;
+        }
+    }
+    return NULL;
+}
+
+SEXP addGlobalRef(SEXP obj, int permanent) {
+    SEXP gref;
+    if (cachedGlobalRefsHwm >= cachedGlobalRefsLength) {
+        int newLength = cachedGlobalRefsLength * 2;
+        SEXP newCachedGlobalRefs = calloc(newLength, sizeof(GlobalRefElem));
+        if (newCachedGlobalRefs == NULL) {
+            fatalError("FFI global refs table expansion failure");
+        }
+        memcpy(newCachedGlobalRefs, cachedGlobalRefs, cachedGlobalRefsLength * sizeof(GlobalRefElem));
+        free(cachedGlobalRefs);
+        cachedGlobalRefs = newCachedGlobalRefs;
+        cachedGlobalRefsLength = newLength;
+    }
+    gref = newObjectRef(obj);
+    cachedGlobalRefs[cachedGlobalRefsHwm].gref = gref;
+    cachedGlobalRefs[cachedGlobalRefsHwm].permanent = permanent;
+    cachedGlobalRefsHwm++;
+    return gref;
+}
+
+SEXP checkRef(SEXP obj) {
+    SEXP gref = findCachedGlobalRef(obj);
+    if (gref == NULL) {
+        return obj;
+    } else {
+        return gref;
+    }
+}
+
+SEXP createGlobalRef(SEXP obj, int permanent) {
+    SEXP gref = findCachedGlobalRef(obj);
+    if (gref == NULL) {
+        gref = addGlobalRef(obj, permanent);
+    }
+    return gref;
+}
+
+void releaseGlobalRef(SEXP obj) {
+    for (int i = 0; i < cachedGlobalRefsHwm; i++) {
+        GlobalRefElem elem = cachedGlobalRefs[i];
+        if (elem.gref == NULL || elem.permanent) {
+            continue;
+        }
+        if (isSameObject(elem.gref, obj)) {
+        	releaseObjectRef(elem.gref);
+            cachedGlobalRefs[i].gref = NULL;
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffiutils.h b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffiutils.h
index 9a6c701305..3d2a16ad3b 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffiutils.h
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffiutils.h
@@ -27,14 +27,25 @@
 #include <string.h>
 #include <limits.h>
 #include <Rinternals.h>
-#include <rffi_callbacks.h>
 #include <trufflenfi.h>
 
+#include "../common/rffi_upcalls.h"
+
 extern void init_memory();
+extern void init_utils();
 
 // use for an unimplemented API function
 void *unimplemented(char *msg) __attribute__((noreturn));
 // use for any fatal error
 void fatalError(char *msg) __attribute__((noreturn));
 
+// checks x against the list of global refs, returning the global version if x matches (IsSameObject)
+SEXP checkRef(SEXP x);
+// creates a global JNI global ref from x. If permanent is non-zero, calls to
+// releaseGlobalRef are ignored and the global ref persists for the entire execution
+// (used for the R global variables such as R_NilValue).
+SEXP createGlobalRef(SEXP x, int permanent);
+// release a previously created global ref
+void releaseGlobalRef(SEXP x);
+
 #endif
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/variables.c b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/variables.c
index a16e5c6a92..e03bc412c4 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_nfi/variables.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_nfi/variables.c
@@ -24,66 +24,15 @@
 #include <Rinterface.h>
 #include <trufflenfi.h>
 #include <rffiutils.h>
+#include "../common/rffi_variablesindex.h"
 
-// Indices into RFFIVariables enum
-// The commented out entries are not used as they are remapped
-// as functions and the name clashes with the callback index for that
-#define R_Home_x 0
-#define R_TempDir_x 1
-#define R_NilValue_x 2
-#define R_UnboundValue_x 3
-#define R_MissingArg_x 4
-//#define R_GlobalEnv_x 5
-#define R_EmptyEnv_x 6
-//#define R_BaseEnv_x 7
-//#define R_BaseNamespace_x 8
-//#define R_NamespaceRegistry_x 9
-#define R_Srcref_x 10
-#define R_Bracket2Symbol_x 11
-#define R_BracketSymbol_x 12
-#define R_BraceSymbol_x 13
-#define R_ClassSymbol_x 14
-#define R_DeviceSymbol_x 15
-#define R_DevicesSymbol_x 16
-#define R_DimNamesSymbol_x 17
-#define R_DimSymbol_x 18
-#define R_DollarSymbol_x 19
-#define R_DotsSymbol_x 20
-#define R_DropSymbol_x 21
-#define R_LastvalueSymbol_x 22
-#define R_LevelsSymbol_x 23
-#define R_ModeSymbol_x 24
-#define R_NameSymbol_x 25
-#define R_NamesSymbol_x 26
-#define R_NaRmSymbol_x 27
-#define R_PackageSymbol_x 28
-#define R_QuoteSymbol_x 29
-#define R_RowNamesSymbol_x 30
-#define R_SeedsSymbol_x 31
-#define R_SourceSymbol_x 32
-#define R_TspSymbol_x 33
-#define R_dot_defined_x 34
-#define R_dot_Method_x 35
-#define R_dot_target_x 36
-#define R_SrcrefSymbol_x 37
-#define R_SrcfileSymbol_x 38
-#define R_NaString_x 39
-#define R_NaN_x 40
-#define R_PosInf_x 41
-#define R_NegInf_x 42
-#define R_NaReal_x 43
-#define R_NaInt_x 44
-#define R_BlankString_x 45
-#define R_BlankScalarString_x 46
-#define R_BaseSymbol_x 47
-#define R_NamespaceEnvSymbol_x 48
-#define R_RestartToken_x 49
 
 static const char *R_Home_static;
 static const char *R_TempDir_static;
 static SEXP R_EmptyEnv_static;
 static SEXP R_Srcref_static;
 static SEXP R_NilValue_static;
+static SEXP R_NilValue_static2;
 static SEXP R_UnboundValue_static;
 static SEXP R_MissingArg_static;
 static SEXP R_BaseSymbol_static;
@@ -150,6 +99,31 @@ int max_contour_segments = 25000;
 static InputHandler BasicInputHandler = {2, -1, NULL};
 InputHandler *R_InputHandlers = &BasicInputHandler;
 
+// R_GlobalEnv et al are not a variables in FASTR as they are RContext specific
+SEXP FASTR_R_GlobalEnv() {
+	return ((call_R_GlobalEnv) callbacks[R_GlobalEnv_x])();
+}
+
+SEXP FASTR_R_BaseEnv() {
+	return ((call_R_BaseEnv) callbacks[R_BaseEnv_x])();
+}
+
+SEXP FASTR_R_BaseNamespace() {
+	return ((call_R_BaseNamespace) callbacks[R_BaseNamespace_x])();
+}
+
+SEXP FASTR_R_NamespaceRegistry() {
+	return ((call_R_NamespaceRegistry) callbacks[R_NamespaceRegistry_x])();
+}
+
+CTXT FASTR_GlobalContext() {
+	return ((call_R_GlobalContext) callbacks[R_GlobalContext_x])();
+}
+
+Rboolean FASTR_R_Interactive() {
+	return (int) ((call_R_Interactive) callbacks[R_Interactive_x])();
+}
+
 char *FASTR_R_Home() {
 	return (char *) R_Home_static;
 }
@@ -347,46 +321,47 @@ void Call_initvar_string(int index, char *value) {
 }
 
 void Call_initvar_obj(int index, void* value) {
+	init_utils();
 	switch (index) {
-    case R_NilValue_x: R_NilValue_static = newObjectRef(value); break;
-    case R_UnboundValue_x: R_UnboundValue_static = newObjectRef(value); break;
-    case R_MissingArg_x: R_MissingArg_static = newObjectRef(value); break;
-//    case R_Srcref_x: R_Srcref_static = newObjectRef(value); break;
-    case R_EmptyEnv_x: R_EmptyEnv_static = newObjectRef(value); break;
-    case R_Bracket2Symbol_x: R_Bracket2Symbol_static = newObjectRef(value); break;
-    case R_BracketSymbol_x: R_BracketSymbol_static = newObjectRef(value); break;
-    case R_BraceSymbol_x: R_BraceSymbol_static = newObjectRef(value); break;
-    case R_ClassSymbol_x: R_ClassSymbol_static = newObjectRef(value); break;
-    case R_DeviceSymbol_x: R_DeviceSymbol_static = newObjectRef(value); break;
-    case R_DevicesSymbol_x: R_DevicesSymbol_static = newObjectRef(value); break;
-    case R_DimNamesSymbol_x: R_DimNamesSymbol_static = newObjectRef(value); break;
-    case R_DimSymbol_x: R_DimSymbol_static = newObjectRef(value); break;
-    case R_DollarSymbol_x: R_DollarSymbol_static = newObjectRef(value); break;
-    case R_DotsSymbol_x: R_DotsSymbol_static = newObjectRef(value); break;
-    case R_DropSymbol_x: R_DropSymbol_static = newObjectRef(value); break;
-    case R_LastvalueSymbol_x: R_LastvalueSymbol_static = newObjectRef(value); break;
-    case R_LevelsSymbol_x: R_LevelsSymbol_static = newObjectRef(value); break;
-    case R_ModeSymbol_x: R_ModeSymbol_static = newObjectRef(value); break;
-    case R_NameSymbol_x: R_NameSymbol_static = newObjectRef(value); break;
-    case R_NamesSymbol_x: R_NamesSymbol_static = newObjectRef(value); break;
-    case R_NaRmSymbol_x: R_NaRmSymbol_static = newObjectRef(value); break;
-    case R_PackageSymbol_x: R_PackageSymbol_static = newObjectRef(value); break;
-    case R_QuoteSymbol_x: R_QuoteSymbol_static = newObjectRef(value); break;
-    case R_RowNamesSymbol_x: R_RowNamesSymbol_static = newObjectRef(value); break;
-    case R_SeedsSymbol_x: R_SeedsSymbol_static = newObjectRef(value); break;
-    case R_SourceSymbol_x: R_SourceSymbol_static = newObjectRef(value); break;
-    case R_TspSymbol_x: R_TspSymbol_static = newObjectRef(value); break;
-    case R_dot_defined_x: R_dot_defined_static = newObjectRef(value); break;
-    case R_dot_Method_x: R_dot_Method_static = newObjectRef(value); break;
-    case R_dot_target_x: R_dot_target_static = newObjectRef(value); break;
-    case R_SrcrefSymbol_x: R_SrcrefSymbol_static = newObjectRef(value); break;
-    case R_SrcfileSymbol_x: R_SrcfileSymbol_static = newObjectRef(value); break;
-    case R_NaString_x: R_NaString_static = newObjectRef(value); break;
-    case R_BlankString_x: R_BlankString_static = newObjectRef(value); break;
-    case R_BlankScalarString_x: R_BlankString_static = newObjectRef(value); break;
-    case R_BaseSymbol_x: R_BaseSymbol_static = newObjectRef(value); break;
-    case R_NamespaceEnvSymbol_x: R_NamespaceEnvSymbol_static = newObjectRef(value); break;
-    // case R_RestartToken_x: R_RestartToken_static = newObjectRef(value); break;
+    case R_NilValue_x: R_NilValue_static = createGlobalRef(value, 1); break;
+    case R_UnboundValue_x: R_UnboundValue_static = createGlobalRef(value, 1); break;
+    case R_MissingArg_x: R_MissingArg_static = createGlobalRef(value, 1); break;
+    case R_Srcref_x: R_Srcref_static = createGlobalRef(value, 1); break;
+    case R_EmptyEnv_x: R_EmptyEnv_static = createGlobalRef(value, 1); break;
+    case R_Bracket2Symbol_x: R_Bracket2Symbol_static = createGlobalRef(value, 1); break;
+    case R_BracketSymbol_x: R_BracketSymbol_static = createGlobalRef(value, 1); break;
+    case R_BraceSymbol_x: R_BraceSymbol_static = createGlobalRef(value, 1); break;
+    case R_ClassSymbol_x: R_ClassSymbol_static = createGlobalRef(value, 1); break;
+    case R_DeviceSymbol_x: R_DeviceSymbol_static = createGlobalRef(value, 1); break;
+    case R_DevicesSymbol_x: R_DevicesSymbol_static = createGlobalRef(value, 1); break;
+    case R_DimNamesSymbol_x: R_DimNamesSymbol_static = createGlobalRef(value, 1); break;
+    case R_DimSymbol_x: R_DimSymbol_static = createGlobalRef(value, 1); break;
+    case R_DollarSymbol_x: R_DollarSymbol_static = createGlobalRef(value, 1); break;
+    case R_DotsSymbol_x: R_DotsSymbol_static = createGlobalRef(value, 1); break;
+    case R_DropSymbol_x: R_DropSymbol_static = createGlobalRef(value, 1); break;
+    case R_LastvalueSymbol_x: R_LastvalueSymbol_static = createGlobalRef(value, 1); break;
+    case R_LevelsSymbol_x: R_LevelsSymbol_static = createGlobalRef(value, 1); break;
+    case R_ModeSymbol_x: R_ModeSymbol_static = createGlobalRef(value, 1); break;
+    case R_NameSymbol_x: R_NameSymbol_static = createGlobalRef(value, 1); break;
+    case R_NamesSymbol_x: R_NamesSymbol_static = createGlobalRef(value, 1); break;
+    case R_NaRmSymbol_x: R_NaRmSymbol_static = createGlobalRef(value, 1); break;
+    case R_PackageSymbol_x: R_PackageSymbol_static = createGlobalRef(value, 1); break;
+    case R_QuoteSymbol_x: R_QuoteSymbol_static = createGlobalRef(value, 1); break;
+    case R_RowNamesSymbol_x: R_RowNamesSymbol_static = createGlobalRef(value, 1); break;
+    case R_SeedsSymbol_x: R_SeedsSymbol_static = createGlobalRef(value, 1); break;
+    case R_SourceSymbol_x: R_SourceSymbol_static = createGlobalRef(value, 1); break;
+    case R_TspSymbol_x: R_TspSymbol_static = createGlobalRef(value, 1); break;
+    case R_dot_defined_x: R_dot_defined_static = createGlobalRef(value, 1); break;
+    case R_dot_Method_x: R_dot_Method_static = createGlobalRef(value, 1); break;
+    case R_dot_target_x: R_dot_target_static = createGlobalRef(value, 1); break;
+    case R_SrcrefSymbol_x: R_SrcrefSymbol_static = createGlobalRef(value, 1); break;
+    case R_SrcfileSymbol_x: R_SrcfileSymbol_static = createGlobalRef(value, 1); break;
+    case R_NaString_x: R_NaString_static = createGlobalRef(value, 1); break;
+    case R_BlankString_x: R_BlankString_static = createGlobalRef(value, 1); break;
+    case R_BlankScalarString_x: R_BlankString_static = createGlobalRef(value, 1); break;
+    case R_BaseSymbol_x: R_BaseSymbol_static = createGlobalRef(value, 1); break;
+    case R_NamespaceEnvSymbol_x: R_NamespaceEnvSymbol_static = createGlobalRef(value, 1); break;
+    // case R_RestartToken_x: R_RestartToken_static = createGlobalRef(value, 1); break;
     default:
     	printf("Call_initvar_obj: unimplemented index %d\n", index);
     	exit(1);
diff --git a/com.oracle.truffle.r.native/library/tools/Makefile b/com.oracle.truffle.r.native/library/tools/Makefile
index f044af7d7e..bd62cb8515 100644
--- a/com.oracle.truffle.r.native/library/tools/Makefile
+++ b/com.oracle.truffle.r.native/library/tools/Makefile
@@ -40,6 +40,9 @@ else
 ifeq ($(FASTR_RFFI),llvm)
 XTRA_C_SOURCES += $(SRC)/truffle_llvm/gramRd_llvm.c
 XTRA_C_OBJECTS += $(OBJ)/gramRd_llvm.o
+SULONG_DIR = $(abspath $(FASTR_R_HOME)/../sulong)
+
+SULONG_INCLUDES = -I$(SULONG_DIR)/include
 else
 ifeq ($(FASTR_RFFI),jni)
 XTRA_C_SOURCES += $(SRC)/jni/gramRd_jni.c
@@ -64,6 +67,9 @@ $(OBJ)/%.o: $(GNUR_SRC)/%.c
 $(OBJ)/gramRd_nfi.o: $(SRC)/truffle_nfi/gramRd_nfi.c
 	$(CC) $(CFLAGS) $(FFI_INCLUDES) -c $< -o $@
 
+$(OBJ)/gramRd_llvm.o: $(SRC)/truffle_llvm/gramRd_llvm.c
+	$(CC) $(CFLAGS) $(FFI_INCLUDES) $(SULONG_INCLUDES) -c $< -o $@
+
 $(OBJ)/gramRd_jni.o: $(SRC)/jni/gramRd_jni.c
 	$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeIntegerArray.java b/com.oracle.truffle.r.native/library/tools/src/truffle_llvm/gramRd_llvm.c
similarity index 72%
rename from com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeIntegerArray.java
rename to com.oracle.truffle.r.native/library/tools/src/truffle_llvm/gramRd_llvm.c
index 217f5fa016..92eb70fb0d 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/NativeIntegerArray.java
+++ b/com.oracle.truffle.r.native/library/tools/src/truffle_llvm/gramRd_llvm.c
@@ -20,19 +20,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.engine.interop;
+#include "../gramRd_fastr.h"
+#include <truffle.h>
 
-import com.oracle.truffle.r.runtime.data.RTruffleObject;
+#define IMPORT_TOOLS() void *obj = truffle_import_cached("_fastr_rffi_tools")
 
-public class NativeIntegerArray extends NativeNACheck implements RTruffleObject {
-    public final int[] value;
-
-    public NativeIntegerArray(Object obj, int[] value) {
-        super(obj);
-        this.value = value;
-    }
-
-    public NativeIntegerArray(int[] value) {
-        this(null, value);
-    }
+int callGetCMethod(void *conn) {
+	IMPORT_TOOLS();
+	return (int) truffle_invoke(obj, "getC", conn);
 }
diff --git a/com.oracle.truffle.r.native/version.source b/com.oracle.truffle.r.native/version.source
index 409940768f..7273c0fa8c 100644
--- a/com.oracle.truffle.r.native/version.source
+++ b/com.oracle.truffle.r.native/version.source
@@ -1 +1 @@
-23
+25
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqFunctions.java
index b5303c48c0..3467d3e4c9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqFunctions.java
@@ -31,6 +31,8 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.ffi.impl.nodes.AsRealNode;
+import com.oracle.truffle.r.ffi.impl.nodes.AsRealNodeGen;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.SeqFunctions.SeqInt.IsIntegralNumericNode;
@@ -40,8 +42,6 @@ import com.oracle.truffle.r.nodes.builtin.base.SeqFunctionsFactory.IsNumericNode
 import com.oracle.truffle.r.nodes.builtin.base.SeqFunctionsFactory.SeqIntNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.SeqFunctionsFactory.SeqIntNodeGen.IsIntegralNumericNodeGen;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
-import com.oracle.truffle.r.nodes.ffi.AsRealNode;
-import com.oracle.truffle.r.nodes.ffi.AsRealNodeGen;
 import com.oracle.truffle.r.nodes.function.CallMatcherNode.CallMatcherGenericNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
 import com.oracle.truffle.r.nodes.function.call.RExplicitBaseEnvCallDispatcher;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUpCallMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUpCallMethod.java
deleted file mode 100644
index 218f407e58..0000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/RFFIUpCallMethod.java
+++ /dev/null
@@ -1,181 +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.nodes.ffi;
-
-import com.oracle.truffle.r.runtime.ffi.UpCallsRFFI;
-
-/**
- * Generated from {@link UpCallsRFFI}. Any native code that is dependent on the ordinal value of
- * these enums must be kept in sync. The {@link #main} method will generate the appropriate C
- * #define statements.
- */
-public enum RFFIUpCallMethod {
-    CADDR("(object) : object"),
-    CADR("(object) : object"),
-    CAR("(object) : object"),
-    CDDR("(object) : object"),
-    CDR("(object) : object"),
-    DUPLICATE_ATTRIB("(object, object) : void"),
-    ENCLOS("(object) : object"),
-    GetRNGstate("() : void"),
-    INTEGER("(object) : object"),
-    IS_S4_OBJECT("(object) : sint32"),
-    SET_S4_OBJECT("(object) : void"),
-    UNSET_S4_OBJECT("(object) : void"),
-    LENGTH("(object) : sint32"),
-    LOGICAL("(object) : object"),
-    NAMED("(object) : sint32"),
-    OBJECT("(object) : sint32"),
-    PRCODE("(object) : object"),
-    PRENV("(object) : object"),
-    PRINTNAME("(object) : object"),
-    PRSEEN("(object) : sint32"),
-    PRVALUE("(object) : object"),
-    PutRNGstate("() : void"),
-    RAW("(object) : object"),
-    RDEBUG("(object) : sint32"),
-    REAL("(object) : object"),
-    RSTEP("(object) : sint32"),
-    R_BaseEnv("() : object"),
-    R_BaseNamespace("() : object"),
-    R_BindingIsLocked("(object, object) : sint32"),
-    R_CHAR("(object) : object"),
-    R_CleanUp("(sint32, sint32, sint32) : void"),
-    R_ExternalPtrAddr("(object) : object"),
-    R_ExternalPtrProtected("(object) : object"),
-    R_ExternalPtrTag("(object) : object"),
-    R_FindNamespace("(object) : object"),
-    R_GetConnection("(sint32) : object"),
-    R_GlobalContext("() : object"),
-    R_GlobalEnv("() : object"),
-    R_HomeDir("() : object"),
-    R_Interactive("() : sint32"),
-    R_MakeExternalPtr("(object, object, object) : object"),
-    R_MethodsNamespace("() : object"),
-    R_NamespaceRegistry("() : object"),
-    R_NewHashedEnv("(object, object) : object"),
-    R_ParseVector("(object, sint32, object) : object"),
-    R_PromiseExpr("(object) : object"),
-    R_ReadConnection("(sint32, object) : sint32"),
-    R_SetExternalPtrAddr("(object, object) : void"),
-    R_SetExternalPtrProtected("(object, object) : void"),
-    R_SetExternalPtrTag("(object, object) : void"),
-    R_ToplevelExec("() : object"),
-    R_WriteConnection("(sint32, object) : sint32"),
-    R_compute_identical("(object, object, sint32) : sint32"),
-    R_do_MAKE_CLASS("(string) : object"),
-    R_do_new_object("(object) : object"),
-    R_do_slot("(object, object) : object"),
-    R_do_slot_assign("(object, object, object) : object"),
-    R_getContextCall("(object) : object"),
-    R_getContextEnv("(object) : object"),
-    R_getContextFun("(object) : object"),
-    R_getContextSrcRef("(object) : object"),
-    R_getGlobalFunctionContext("() : object"),
-    R_getParentFunctionContext("(object) : object"),
-    R_insideBrowser("() : sint32"),
-    R_isEqual("(object, object) : sint32"),
-    R_isGlobal("(object) : sint32"),
-    R_lsInternal3("(object, sint32, sint32) : object"),
-    R_new_custom_connection("(string, string, string, object) : object"),
-    R_tryEval("(object, object, object) : object"),
-    Rf_GetOption1("(object) : object"),
-    Rf_PairToVectorList("(object) : object"),
-    Rf_ScalarDouble("(double) : object"),
-    Rf_ScalarInteger("(sint32) : object"),
-    Rf_ScalarLogical("(sint32) : object"),
-    Rf_ScalarString("(object) : object"),
-    Rf_allocArray("(sint32, object) : object"),
-    Rf_allocMatrix("(sint32, sint32, sint32) : object"),
-    Rf_allocVector("(sint32, sint32) : object"),
-    Rf_any_duplicated("(object, sint32) : sint32"),
-    Rf_asChar("(object) : object"),
-    Rf_asInteger("(object) : sint32"),
-    Rf_asLogical("(object) : sint32"),
-    Rf_asReal("(object) : double"),
-    Rf_classgets("(object, object) : object"),
-    Rf_coerceVector("(object, sint32) : object"),
-    Rf_cons("(object, object) : object"),
-    Rf_copyListMatrix("(object, object, sint32) : void"),
-    Rf_copyMatrix("(object, object, sint32) : void"),
-    Rf_defineVar("(object, object, object) : void"),
-    Rf_duplicate("(object, sint32) : object"),
-    Rf_error("(string) : void"),
-    Rf_eval("(object, object) : object"),
-    Rf_findVar("(object, object) : object"),
-    Rf_findVarInFrame("(object, object) : object"),
-    Rf_findVarInFrame3("(object, object, sint32) : object"),
-    Rf_findFun("(object, object) : object"),
-    Rf_getAttrib("(object, object) : object"),
-    Rf_gsetVar("(object, object, object) : void"),
-    Rf_inherits("(string, object) : sint32"),
-    Rf_install("(string) : object"),
-    Rf_installChar("(object) : object"),
-    Rf_isNull("(object) : sint32"),
-    Rf_isString("(object) : sint32"),
-    Rf_lengthgets("(object, sint32) : object"),
-    Rf_mkCharLenCE("(pointer, sint32, sint32) : object"),
-    Rf_ncols("(object) : sint32"),
-    Rf_nrows("(object) : sint32"),
-    Rf_setAttrib("(object, object, object) : void"),
-    Rf_warning("(string) : void"),
-    Rf_warningcall("(object, string) : void"),
-    Rprintf("(string) : void"),
-    SETCADR("(object, object) : object"),
-    SETCAR("(object, object) : object"),
-    SETCDR("(object, object) : object"),
-    SET_RDEBUG("(object, sint32) : void"),
-    SET_RSTEP("(object, sint32) : void"),
-    SET_STRING_ELT("(object, sint32, object) : void"),
-    SET_SYMVALUE("(object, object) : void"),
-    SET_TAG("(object, object) : object"),
-    SET_TYPEOF_FASTR("(object, sint32) : object"),
-    SET_VECTOR_ELT("(object, sint32, object) : void"),
-    STRING_ELT("(object, sint32) : object"),
-    SYMVALUE("(object) : object"),
-    TAG("(object) : object"),
-    TYPEOF("(object) : sint32"),
-    VECTOR_ELT("(object, sint32) : object"),
-    getConnectionClassString("(object) : object"),
-    getOpenModeString("(object) : object"),
-    getSummaryDescription("(object) : object"),
-    isSeekable("(object) : object"),
-    unif_rand("() : double");
-
-    /**
-     * The signature used for the upcall in Truffle NFI.
-     */
-    public final String nfiSignature;
-
-    RFFIUpCallMethod(String signature) {
-        this.nfiSignature = signature;
-    }
-
-    public static void main(String[] args) {
-        for (RFFIUpCallMethod f : RFFIUpCallMethod.values()) {
-            System.out.printf("#define %s_x %d\n", f.name(), f.ordinal());
-        }
-        System.out.printf("\n#define CALLBACK_TABLE_SIZE %d\n", RFFIUpCallMethod.values().length);
-    }
-
-}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RForeignAccessFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RForeignAccessFactory.java
index d5732acff8..af1e792590 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RForeignAccessFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RForeignAccessFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -37,4 +37,13 @@ public interface RForeignAccessFactory {
      * Return the {@link TruffleLanguage} instance for R. (Project circularity workaround).
      */
     Class<? extends TruffleLanguage<RContext>> getTruffleLanguage();
+
+    /**
+     * Changes the interpretation of {@RNull} as {@code null} to {@code value}. This allows the
+     * {@code FFI} implementations to prevent {@RNull} being converted across the {@code FFI}
+     * interface, which would be incorrect.
+     *
+     * @return the previous setting
+     */
+    boolean setIsNull(boolean value);
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTruffleObject.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTruffleObject.java
index 89439de52f..a984127ef4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTruffleObject.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RTruffleObject.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,19 @@ import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.r.runtime.context.RContext;
 
+/**
+ * A tagging interface that indicates that a {@link TruffleObject} belongs to the R language. There
+ * are actually two sets of such types; those that are reflected at the R language level and
+ * therefore suitable for passing to other peer languages, e.g. {@link RIntVector}, and those that
+ * are used within the implementation for handling the {@code R FFI}, which uses Truffle interop
+ * internally. The latter types should not leak to other languages.
+ *
+ * As a convenience the interface provides a default implementation of
+ * {@link TruffleObject#getForeignAccess()} that indirects through
+ * {@link RContext#getRForeignAccessFactory()}. This is entirely optional and can be overridden to
+ * use an alternate implementation.
+ *
+ */
 public interface RTruffleObject extends TruffleObject {
     @Override
     default ForeignAccess getForeignAccess() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
index b6969e1e37..c83d750b9c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java
@@ -607,7 +607,6 @@ public class DLL {
         public static RFindSymbolNode create() {
             return new RFindSymbolNode();
         }
-
     }
 
     private static final class RFindSymbolRootNode extends RootNode {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFI.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFI.java
index 12c23e7e6d..6c517528b9 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFI.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFI.java
@@ -41,10 +41,9 @@ package com.oracle.truffle.r.runtime.ffi;
  * <li>{@link ZipRFFI}: interface to zip compression</li>
  * <li>{@link DLLRFFI}: interface to dll functions, e.g., {@code dlopen}</li>
  * <li>{@link REmbedRFFI}: interface to embedded support</li>
- * <li>{@link MiscRFFI}: interface to miscellaneous native functions</li>
- * <li>{@link UpCallsRFFI}: interface that defines the set of upcalls from native code (resulting
- * from {@link CallRFFI}). There is no public access to this interface as it should never be called
- * from FastR Java code and is always implemented by a specific FFI factory.
+ * <li>{@link MiscRFFI}: interface to miscellaneous native functions</li> from {@link CallRFFI}).
+ * There is no public access to this interface as it should never be called from FastR Java code and
+ * is always implemented by a specific FFI factory.
  * </ul>
  *
  * These interfaces may be implemented by one or more providers, specified either when the FastR
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
index 60d3811bdb..4ff1f85c48 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
@@ -39,10 +39,10 @@ import com.oracle.truffle.r.runtime.context.RContext.ContextState;
  */
 public abstract class RFFIFactory {
     private enum Factory {
-        JNI("com.oracle.truffle.r.runtime.ffi.jni.JNI_RFFIFactory"),
-        LLVM("com.oracle.truffle.r.engine.interop.ffi.llvm.TruffleLLVM_RFFIFactory"),
-        MANAGED("com.oracle.truffle.r.runtime.ffi.managed.Managed_RFFIFactory"),
-        NFI("com.oracle.truffle.r.engine.interop.ffi.nfi.TruffleNFI_RFFIFactory");
+        JNI("com.oracle.truffle.r.ffi.impl.jni.JNI_RFFIFactory"),
+        LLVM("com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_RFFIFactory"),
+        MANAGED("com.oracle.truffle.r.ffi.impl.managed.Managed_RFFIFactory"),
+        NFI("com.oracle.truffle.r.ffi.impl.nfi.TruffleNFI_RFFIFactory");
 
         private final String klassName;
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java
index d6b8451a81..932634dd13 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIRootNode.java
@@ -29,7 +29,7 @@ import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.runtime.context.RContext;
 
 public abstract class RFFIRootNode<T extends Node> extends RootNode {
-    @Child T rffiNode;
+    @Child protected T rffiNode;
 
     @SuppressWarnings("deprecation")
     protected RFFIRootNode(T baseRFFINode) {
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java
similarity index 89%
rename from com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java
index be05015ada..fa1fc65f0c 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIVariables.java
@@ -37,11 +37,11 @@ public enum RFFIVariables {
     R_NilValue(RNull.instance),
     R_UnboundValue(RUnboundValue.instance),
     R_MissingArg(RMissing.instance),
-    R_GlobalEnv(null),
+    R_GlobalEnv(null, true),
     R_EmptyEnv(REnvironment.emptyEnv()),
-    R_BaseEnv(null),
-    R_BaseNamespace(null),
-    R_NamespaceRegistry(null),
+    R_BaseEnv(null, true),
+    R_BaseNamespace(null, true),
+    R_NamespaceRegistry(null, true),
     R_Srcref(null),
     R_Bracket2Symbol(RDataFactory.createSymbol("[[")),
     R_BracketSymbol(RDataFactory.createSymbol("[")),
@@ -84,9 +84,15 @@ public enum RFFIVariables {
     R_RestartToken(null);
 
     private Object value;
+    public final boolean alwaysUpCall;
 
-    RFFIVariables(Object value) {
+    RFFIVariables(Object value, boolean alwaysUpCall) {
         this.value = value;
+        this.alwaysUpCall = alwaysUpCall;
+    }
+
+    RFFIVariables(Object value) {
+        this(value, false);
     }
 
     public Object getValue() {
@@ -99,6 +105,11 @@ public enum RFFIVariables {
     public static RFFIVariables[] initialize() {
         R_TempDir.value = TempPathName.tempDirPath();
         return values();
+    }
 
+    public static void main(String[] args) {
+        for (RFFIVariables var : RFFIVariables.values()) {
+            System.out.printf("#define %s_x %d\n", var.name(), var.ordinal());
+        }
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java
deleted file mode 100644
index 425f866ead..0000000000
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RFFIUpCallMethodGenerate.java
+++ /dev/null
@@ -1,133 +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.test.tools;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.Comparator;
-
-import com.oracle.truffle.r.nodes.ffi.RFFIUpCallMethod;
-import com.oracle.truffle.r.runtime.ffi.RFFICstring;
-
-/**
- * Generates the entries for {@link RFFIUpCallMethod}.
- */
-public class RFFIUpCallMethodGenerate {
-
-    public static void main(String[] args) throws Exception {
-        String klassName = "com.oracle.truffle.r.runtime.ffi.UpCallsRFFI";
-        boolean klassArg = false;
-
-        int i = 0;
-        while (i < args.length) {
-            String arg = args[i];
-            if (arg.equals("--class")) {
-                i++;
-                klassName = args[i];
-                klassArg = true;
-            }
-            i++;
-        }
-
-        Class<?> klass = Class.forName(klassName);
-        Method[] methods = klassArg ? klass.getDeclaredMethods() : klass.getMethods();
-
-        Arrays.sort(methods, new Comparator<Method>() {
-
-            @Override
-            public int compare(Method a, Method b) {
-                return a.getName().compareTo(b.getName());
-            }
-
-        });
-        for (i = 0; i < methods.length; i++) {
-            Method m = methods[i];
-            if (klassArg && (Modifier.isStatic(m.getModifiers()))) {
-                continue;
-            }
-            String sig = getNFISignature(m);
-            System.out.printf("%s(\"%s\")%s%n", m.getName(), sig, i == methods.length - 1 ? ";" : ",");
-        }
-
-    }
-
-    private static String getNFISignature(Method m) {
-        Class<?>[] paramTypes = m.getParameterTypes();
-        Annotation[][] annotations = m.getParameterAnnotations();
-        StringBuilder sb = new StringBuilder();
-        sb.append('(');
-        for (int i = 0; i < paramTypes.length; i++) {
-            Class<?> paramType = paramTypes[i];
-            String nfiParam = nfiParamName(paramType, annotations[i]);
-            sb.append(nfiParam);
-            if (i != paramTypes.length - 1) {
-                sb.append(", ");
-            }
-        }
-        sb.append(')');
-        sb.append(" : ");
-        sb.append(nfiParamName(m.getReturnType(), new Annotation[0]));
-        return sb.toString();
-    }
-
-    static String nfiParamName(Class<?> paramType, Annotation[] annotations) {
-        String paramName = paramType.getSimpleName();
-        RFFICstring rffiCstring = null;
-        if (annotations.length > 0) {
-            for (Annotation annotation : annotations) {
-                if (annotation instanceof RFFICstring) {
-                    rffiCstring = (RFFICstring) annotation;
-                    break;
-                }
-            }
-        }
-        switch (paramName) {
-            case "Object":
-                if (rffiCstring == null) {
-                    return "object";
-                } else {
-                    return rffiCstring.convert() ? "string" : "pointer";
-                }
-            case "char":
-                return "uint8";
-            case "int":
-                return "sint32";
-            case "double":
-                return "double";
-            case "void":
-                return "void";
-            case "int[]":
-                return "[sint32]";
-            case "double[]":
-                return "[double]";
-            case "byte[]":
-                return "[uint8]";
-            default:
-                return "object";
-        }
-
-    }
-
-}
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/ShowLLVMIR.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/ShowLLVMIR.java
index cb525d80db..733d2ecb48 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/ShowLLVMIR.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/ShowLLVMIR.java
@@ -26,8 +26,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import com.oracle.truffle.r.ffi.impl.llvm.LLVM_IR;
 import com.oracle.truffle.r.runtime.ProcessOutputManager;
-import com.oracle.truffle.r.runtime.ffi.truffle.LLVM_IR;
 
 public class ShowLLVMIR {
     public static void main(String[] args) {
diff --git a/documentation/dev/truffle_llvm_ffi.md b/documentation/dev/truffle_llvm_ffi.md
index 511d667d43..69e408a1ad 100644
--- a/documentation/dev/truffle_llvm_ffi.md
+++ b/documentation/dev/truffle_llvm_ffi.md
@@ -6,18 +6,7 @@ The Truffle implementation of the R FFI is based on the Truffle implementation o
 # Building
 Special setup is required to build FastR to use the Truffle R FFI implementation.
 
-## Building Sulong
-The `sulong` repository must be cloned to a sibling directory of `fastr` and built:
-
-    cd $FASTR_HOME
-    git clone https://github.com/graalvm/sulong.git
-    cd sulong
-    mx build
-    mx su-pulldragonegg
-
-The `mx build` step will clone the `compiler` repository, if necessary, and build that also. The `mx su-pulldragonegg` step is required to be able to compile Fortran code to LLVM, which is required by FastR.
-
-## Additional Pre-Requisites
+## Pre-Requisites
 
 The DragonEgg plugin requires that `gcc 4.6` and `gfortran 4.6` be available. On Mac OS, these can be installed with MacPorts (recommended) or Brew. Having installed these, set the following environment variables:
 
@@ -30,24 +19,33 @@ The above definitions assume a MacPorts installation.
 Both GNU R and FastR native code must be compiled to generate LLVM code. This is handled by special "wrapper" compiler scripts that encapsulate the required steps.
 To ensure that the wrapper compiler scripts are used in the GNU R build set:
 
-    export FASTR_TRUFFLE_RFFI=true
+    export FASTR_RFFI=llvm
 
 If you have an existing build, you must unset any definition of `GNUR_NOCLEAN` then run `mx build -c`. The wrapper scripts add quite a bit of overhead to the build process, particularly the GNU R configure step, but fortunately this only has to be done once.
 
-## Running
+## Building Sulong
+The `sulong` repository must be cloned to a sibling directory of `fastr` and built:
 
-There is no compile-time dependency between FastR and Sulong; all communication is via the Truffle Interop API. Therefore Sulong must be dynamically imported using either `mx --dynamicimport sulong` or by setting the environment variable `DEFAULT_DYNAMIC_IMPORTS=sulong`, with latter being most convenient. With this in effect, a normal `mx R` will make SuLong available. In particular, the factory class that controls which FFI implementation is in used is chosen based on whether Sulong is available.
+    cd $FASTR_HOME
+    git clone https://github.com/graalvm/sulong.git
+    cd sulong
+    mx build
+    mx su-pulldragonegg
+
+The `mx build` step will clone the `graal` repository, if necessary, and build the `truffle` suite also. The `mx su-pulldragonegg` step is required to be able to compile Fortran code to LLVM, which is required by FastR.
 
-Even then, by default, the Truffle FFI implementation is not enabled and the system defaults to the normal JNI RFFI implementation, in order to support an
-incremental approach to enabling native code for LLVM implementation. To enable one or more packages (strictly their associated dynamic libraries) for LLVM implementation, set the environment variable `FASTR_TRUFFLE_LIBS` to a comma-separated list of library names, e.g. `stats,testrffi`. If this variable is unset,and the Truffle RFFI factory class is enabled, a warning is given on every library open that the load is defaulting to the JNI implementation.
+## Building FastR
 
-At the time of writing, only the following libraries have been tested in LLVM mode:
+Both GNU R and FastR native code must be compiled to generate LLVM code. This is handled by special "wrapper" compiler scripts that encapsulate the required steps.
+To ensure that the `llvm` variant of the native build is generated, set:
 
-* `liburand`: this is not actually a package library, but a test for the user-defined random number generator feature of R. It lives in the
-`com.oracle.truffle.r.test.native/urand` directory and must be loaded explicitly using `dyn.load`. See the `TestUserRNG` class for an example of how to test it.
-* `testrffi`: This is the (evolving) RFFI test package that lives in `com.oracle.truffle.r.test.native/packages/testrffi`. It must first be installed by e.g, `bin/R CMD INSTALL com.oracle.truffle.r.test.native/packages/testrffi/lib/testrffi.tar`. As always this will install to the location specified by the `R_LIBS_USER` or `R_LIBS` environment variables or, if unset, the FastR `library` directory. An example test would then be to execute `library(testrffi); rffi.addDouble(2,3)` after running `mx R`.
-* `stats`: Most of the native code in the GNU R `stats` package has either been rewritten in Java or temporarily disabled. However, the Fast Fourier Transform code is written in C and called through the FFI. E.g. `fft(1:4)` is a simple test.
-* `digest`: This is a CRAN package that contains a significant amount of native code, relating to MD5 and SHA digest generation. The simple way to install and test this is to execute: `mx pkgtest '^digest$'`. This assumes an internet connection is available to access CRAN.
+    export FASTR_RFFI=llvm
+
+If you have an existing build, you must unset any definition of `GNUR_NOCLEAN` then run `mx build -c`. The wrapper scripts add quite a bit of overhead to the build process, particularly the GNU R configure step, but fortunately this only has to be done once.
+
+## Running
+
+There is no compile-time dependency between FastR and Sulong; all communication is via the Truffle Interop API. Therefore Sulong must be dynamically imported using either `mx --dynamicimport sulong` or by setting the environment variable `DEFAULT_DYNAMIC_IMPORTS=sulong`, with latter being most convenient. With this in effect, a normal `mx R` will make SuLong available.
 
 Note that if the `LLVM_PARSE_TIME` environment variable is set to any value, the time taken to parse each LLVM module is logged to the console, which is also an indication that the LLVM implementation variant is being used.
 
@@ -57,7 +55,11 @@ Note that if the `LLVM_PARSE_TIME` environment variable is set to any value, the
 
 The compiler wrapper scripts are simple shell scripts that first test for the existence of the `sulong` sibling directory and, if it exists and the environment variable `FASTR_SULONG_IGNORE` is not set, invoke associated `mx` commands to perform the compilation. Otherwise, the standard compiler is used. The scripts are stored in the `compilers` sub-directory of `mx.fastr` and are named: `fastr-cc`, `fastr-fc`, `fastr-c++` and `fastr-cpp`. The associated `mx` commands are in `mx.fastr/mx_fastr_compilers.py`.
 
-In order to support both LLVM and non-LLVM execution, each native source file is compiled twice, once to generate native machine code and once to generate LLVM IR. The LLVM IR is actually stored in the object file and extracted at runtime. This avoids having to disrupt the normal R package build process by allowing it to be completely unaware of the existence of LLVM.
+In order to support both LLVM and non-LLVM execution (no longer actually necessary), each native source file is compiled twice, once to generate native machine code and once to generate LLVM IR. The LLVM IR is actually stored in the object file and extracted at runtime. This avoids having to disrupt the normal R package build process by allowing it to be completely unaware of the existence of LLVM.
 
 Currently, for convenience, the Python wrappers invoke code in the Sulong `sulong/mx.sulong` directory. Eventually, they will modified to be independent of Sulong.
 
+## Limitations
+At the time of writing all the `RFFI` interfaces are implemented for LLVM. However, owing to a bug in DragonEgg, the actual Fortran code for the Lapack library is not executed under LLVM, only the wrapper.
+
+Also, not all callbacks are implemented at this time, just those necessary to run a basic system.
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index b2505b0cb6..e3f4d743c2 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -90,6 +90,7 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/tools/ToolsText.ja
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/CountFields.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Menu.java,gnu_r.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/WriteTable.java,gnu_r.copyright
+com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h,no.copyright
 com.oracle.truffle.r.native/fficall/src/common/arithmetic_fastr.c,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.native/fficall/src/common/coerce_fastr.c,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.native/fficall/src/common/errors_fastr.c,gnu_r.core.copyright
@@ -117,8 +118,10 @@ com.oracle.truffle.r.native/fficall/src/jni/Rdynload_fastr.c,gnu_r.copyright
 com.oracle.truffle.r.native/fficall/src/truffle_nfi/Rdynload_fastr.c,gnu_r.copyright
 com.oracle.truffle.r.native/fficall/src/truffle_nfi/Memory.c,gnu_r.copyright
 com.oracle.truffle.r.native/fficall/src/truffle_nfi/pcre_rffi.c,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.native/fficall/src/truffle_llvm/pcre_rffi.c,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.native/fficall/src/truffle_llvm/Rdynload_fastr.c,gnu_r.copyright
 com.oracle.truffle.r.native/fficall/src/jni/Rembedded.c,gnu_r.copyright
+com.oracle.truffle.r.native/fficall/src/truffle_llvm/Memory.c,gnu_r.copyright
 com.oracle.truffle.r.native/library/base/src/registration.c,no.copyright
 com.oracle.truffle.r.native/library/grDevices/src/gzio.c,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.native/library/methods/src/methods_dummy.c,no.copyright
@@ -133,7 +136,6 @@ com.oracle.truffle.r.native/library/stats/src/port.h,no.copyright
 com.oracle.truffle.r.native/library/stats/src/stats.h,no.copyright
 com.oracle.truffle.r.native/library/stats/src/statsR_dummy.c,no.copyright
 com.oracle.truffle.r.native/library/stats/src/ts.h,no.copyright
-com.oracle.truffle.r.native/library/tools/src/gramRd.c,no.copyright
 com.oracle.truffle.r.native/library/tools/src/tools_dummy.c,no.copyright
 com.oracle.truffle.r.native/library/utils/src/utils_dummy.c,no.copyright
 com.oracle.truffle.r.native/run/R.sh,oracle_bash.copyright
@@ -729,7 +731,7 @@ com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleV
 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/data/tree2/incx.r,no.copyright
 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/simple/data/tree2/setx.r,no.copyright
 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java,purdue.copyright
-com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/ffi/AsRealNode.java,gnu_r_gentleman_ihaka.copyright
+com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AsRealNode.java,gnu_r_gentleman_ihaka.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/AttributesPrinter.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PrintParameters.java,gnu_r_gentleman_ihaka2.copyright
diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py
index 4eac3bba91..a5ccfd0e27 100644
--- a/mx.fastr/mx_fastr.py
+++ b/mx.fastr/mx_fastr.py
@@ -152,8 +152,7 @@ def set_graal_options():
 
 def _sulong_options():
     if _mx_sulong:
-        return ['-Dfastr.ffi.factory.class=com.oracle.truffle.r.engine.interop.ffi.llvm.TruffleLLVM_RFFIFactory',
-                '-XX:-UseJVMCIClassLoader']
+        return ['-XX:-UseJVMCIClassLoader']
     else:
         return []
 
diff --git a/mx.fastr/mx_fastr_compile.py b/mx.fastr/mx_fastr_compile.py
index 94fac7e4f7..9e62f05ec9 100644
--- a/mx.fastr/mx_fastr_compile.py
+++ b/mx.fastr/mx_fastr_compile.py
@@ -170,7 +170,9 @@ def fc(args):
                 llvm_as = sulong.findLLVMProgram('llvm-as')
                 llvm_bc_file = os.path.splitext(analyzed_args.llvm_ir_file)[0] + '.bc'
                 rc = mx.run([llvm_as, analyzed_args.llvm_ir_file, '-o', llvm_bc_file])
-                rc = _embed_ir(llvm_bc_file)
+                rc = _mem2reg_opt(llvm_bc_file)
+                if rc == 0:
+                    rc = _embed_ir(llvm_bc_file)
     else:
         compiler = 'gfortran'
         rc = mx.run([compiler] + args, nonZeroIsFatal=False)
@@ -207,6 +209,7 @@ def cppcpp(args):
     return rc
 
 def _mem2reg_opt(llvm_ir_file):
+    _log('mem2reg', llvm_ir_file)
     filename = os.path.splitext(llvm_ir_file)[0]
     ext = os.path.splitext(llvm_ir_file)[1]
     opt_filename = filename + '.opt' + ext
diff --git a/mx.fastr/mx_fastr_junit.py b/mx.fastr/mx_fastr_junit.py
index 9f95551497..6e7f8f9a03 100644
--- a/mx.fastr/mx_fastr_junit.py
+++ b/mx.fastr/mx_fastr_junit.py
@@ -29,7 +29,8 @@ def junit(args, harness, parser=None, jdk_default=None):
     """run Junit tests"""
     suppliedParser = parser is not None
     parser = parser if suppliedParser else ArgumentParser(prog='mx junit')
-    parser.add_argument('--tests', action='store', help='pattern to match test classes')
+    parser.add_argument('--tests', action='store', help='pattern to match test packages')
+    parser.add_argument('--exclude', action='append', help='test classes to exclude')
     parser.add_argument('--J', dest='vm_args', action='append', help='target VM arguments (e.g. --J @-dsa)', metavar='@<args>')
     parser.add_argument('--jdk', action='store', help='jdk to use')
     if suppliedParser:
@@ -64,12 +65,18 @@ def junit(args, harness, parser=None, jdk_default=None):
 
     tests = [] if args.tests is None else [name for name in args.tests.split(',')]
     classes = []
+    excluded = args.exclude
+
     if len(tests) == 0:
         classes = candidates
     else:
         for t in tests:
             found = False
             for c in candidates:
+                if excluded:
+                    if c in excluded:
+                        print 'excluding '  + c
+                        continue
                 if t in c:
                     found = True
                     classes.append(c)
@@ -79,6 +86,8 @@ def junit(args, harness, parser=None, jdk_default=None):
     dists = ['FASTR', 'FASTR_UNIT_TESTS']
     if mx.suite('r-apptests', fatalIfMissing=False):
         dists.append('com.oracle.truffle.r.test.apps')
+    if mx_fastr._mx_sulong:
+        dists.append('SULONG')
     vmArgs += mx.get_runtime_jvm_args(dists, jdk=jdk)
 
     if len(classes) != 0:
diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py
index c3353e7fab..75d0d5c3de 100644
--- a/mx.fastr/suite.py
+++ b/mx.fastr/suite.py
@@ -223,17 +223,29 @@ suite = {
       "jacoco" : "include",
     },
 
-    "com.oracle.truffle.r.runtime.ffi" : {
+    "com.oracle.truffle.r.ffi.impl" : {
       "sourceDirs" : ["src"],
       "dependencies" : [
-        "com.oracle.truffle.r.nodes",
+         "com.oracle.truffle.r.ffi.processor",
+         "com.oracle.truffle.r.nodes"
       ],
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
+      "annotationProcessors" : [
+          "truffle:TRUFFLE_DSL_PROCESSOR",
+          "R_FFI_PROCESSOR",
+      ],
       "workingSets" : "Truffle,FastR",
       "jacoco" : "include",
     },
 
+    "com.oracle.truffle.r.ffi.processor" : {
+      "sourceDirs" : ["src"],
+      "checkstyle" : "com.oracle.truffle.r.runtime",
+      "javaCompliance" : "1.8",
+      "workingSets" : "FastR",
+    },
+
     "com.oracle.truffle.r.native" : {
       "sourceDirs" : [],
       "dependencies" : [
@@ -251,7 +263,7 @@ suite = {
     "com.oracle.truffle.r.library" : {
       "sourceDirs" : ["src"],
       "dependencies" : [
-        "com.oracle.truffle.r.runtime.ffi",
+        "com.oracle.truffle.r.ffi.impl",
       ],
       "annotationProcessors" : [
           "truffle:TRUFFLE_DSL_PROCESSOR",
@@ -274,7 +286,7 @@ suite = {
       "dependencies" : [
         "com.oracle.truffle.r.native",
         "com.oracle.truffle.r.engine",
-        "com.oracle.truffle.r.runtime.ffi"
+        "com.oracle.truffle.r.ffi.impl"
       ],
       "class" : "FastRNativeRecommendedProject",
       "native" : "true",
@@ -286,19 +298,23 @@ suite = {
   "distributions" : {
     "TRUFFLE_R_PARSER_PROCESSOR" : {
       "description" : "internal support for generating the R parser",
-      "subDir" : "truffle",
       "dependencies" : ["com.oracle.truffle.r.parser.processor"],
       "exclude" : [
         "ANTLR-3.5",
         "ANTLR-C-3.5",
        ],
        "maven" : "False",
+    },
 
+    "R_FFI_PROCESSOR" : {
+      "description" : "internal support for generating FFI classes",
+      "dependencies" : ["com.oracle.truffle.r.ffi.processor"],
+      "maven" : "False",
     },
 
     "FASTR" : {
       "description" : "class files for compiling against FastR in a separate suite",
-      "dependencies" : ["com.oracle.truffle.r.engine", "com.oracle.truffle.r.runtime.ffi"],
+      "dependencies" : ["com.oracle.truffle.r.engine", "com.oracle.truffle.r.ffi.impl"],
       "mainClass" : "com.oracle.truffle.r.engine.shell.RCommand",
       "exclude" : [
         "truffle:JLINE",
-- 
GitLab