diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pkginit/SetDotSymbolValuesCallMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pkginit/SetDotSymbolValuesCallMR.java
index a6ee9008c16eae9a81e4704f44217ed89ed3ed3f..ce76b9870d2ec52f83cae85f83445971d7ac9573 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pkginit/SetDotSymbolValuesCallMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/pkginit/SetDotSymbolValuesCallMR.java
@@ -25,7 +25,7 @@ package com.oracle.truffle.r.ffi.impl.interop.pkginit;
 import com.oracle.truffle.api.interop.MessageResolution;
 import com.oracle.truffle.api.interop.Resolve;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.r.ffi.impl.common.UpCallUnwrap;
+import com.oracle.truffle.r.ffi.impl.upcalls.UpCallUnwrap;
 import com.oracle.truffle.r.runtime.ffi.DLL.DLLInfo;
 
 @MessageResolution(receiverType = SetDotSymbolValuesCall.class)
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java
index 42d3e293c6d2bfacf3ca147241961a59faf19293..cb72d9283acff60d5c7563922f843e324f2caad5 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Call.java
@@ -34,12 +34,12 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.ffi.impl.common.RFFIUtils;
-import com.oracle.truffle.r.ffi.impl.common.UpCallUnwrap;
 import com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_CallFactory.ToNativeNodeGen;
 import com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_CallFactory.TruffleLLVM_InvokeCallNodeGen;
 import com.oracle.truffle.r.ffi.impl.llvm.upcalls.BytesToNativeCharArrayCall;
 import com.oracle.truffle.r.ffi.impl.llvm.upcalls.CharSXPToNativeArrayCall;
 import com.oracle.truffle.r.ffi.impl.upcalls.Callbacks;
+import com.oracle.truffle.r.ffi.impl.upcalls.UpCallUnwrap;
 import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ATTRIB.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ATTRIB.java
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java
index 3f675ef3eca28cb4df8dd056b4731c6127405d6e..0bce74744dc62ef55b09ffbd5568067ce7084672 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ListAccessNodes.java
@@ -28,6 +28,11 @@ 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.dsl.TypeSystemReference;
+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.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -83,6 +88,10 @@ public final class ListAccessNodes {
         protected Object car(@SuppressWarnings("unused") Object obj) {
             throw RInternalError.unimplemented("CAR only works on pair lists, language objects, argument lists, and symbols");
         }
+
+        public static CARNode create() {
+            return CARNodeGen.create();
+        }
     }
 
     @TypeSystemReference(RTypes.class)
@@ -123,7 +132,10 @@ public final class ListAccessNodes {
         @Fallback
         protected Object cdr(@SuppressWarnings("unused") Object obj) {
             throw RInternalError.unimplemented("CDR only works on pair lists, language objects, and argument lists");
+        }
 
+        public static CDRNode create() {
+            return CDRNodeGen.create();
         }
     }
 
@@ -143,6 +155,10 @@ public final class ListAccessNodes {
         protected Object cadr(@SuppressWarnings("unused") Object obj) {
             throw RInternalError.unimplemented("CADR only works on pair lists and language objects");
         }
+
+        public static CADRNode create() {
+            return CADRNodeGen.create();
+        }
     }
 
     @TypeSystemReference(RTypes.class)
@@ -161,6 +177,10 @@ public final class ListAccessNodes {
         protected Object caddr(@SuppressWarnings("unused") Object obj) {
             throw RInternalError.unimplemented("CADDR only works on pair lists and language objects");
         }
+
+        public static CADDRNode create() {
+            return CADDRNodeGen.create();
+        }
     }
 
     @TypeSystemReference(RTypes.class)
@@ -179,7 +199,10 @@ public final class ListAccessNodes {
         @Fallback
         protected Object cddr(@SuppressWarnings("unused") Object obj) {
             throw RInternalError.unimplemented("CDDR only works on pair lists and language objects");
+        }
 
+        public static CDDRNode create() {
+            return CDDRNodeGen.create();
         }
     }
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java
index 918352aba3a2b7ee6660ef9c5ad7fd41e976f82a..b4e1b39d568d6586c7c2ebcb9ae251cb0039956a 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MiscNodes.java
@@ -26,6 +26,10 @@ import com.oracle.truffle.api.CompilerDirectives;
 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.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.nodes.access.AccessSlotNode;
 import com.oracle.truffle.r.nodes.access.AccessSlotNodeGen;
 import com.oracle.truffle.r.nodes.access.UpdateSlotNode;
@@ -102,6 +106,10 @@ public final class MiscNodes {
             CompilerDirectives.transferToInterpreter();
             throw RError.error(RError.SHOW_CALLER2, RError.Message.LENGTH_MISAPPLIED, SEXPTYPE.gnuRTypeForObject(obj).name());
         }
+
+        public static LENGTHNode create() {
+            return LENGTHNodeGen.create();
+        }
     }
 
     @TypeSystemReference(RTypes.class)
@@ -122,6 +130,10 @@ public final class MiscNodes {
         Object doSlot(@SuppressWarnings("unused") Object o, Object name) {
             throw RError.error(RError.SHOW_CALLER2, RError.Message.INVALID_ARGUMENT_OF_TYPE, "name", SEXPTYPE.gnuRTypeForObject(name).name());
         }
+
+        public static RDoSlotNode create() {
+            return RDoSlotNodeGen.create();
+        }
     }
 
     @TypeSystemReference(RTypes.class)
@@ -142,6 +154,10 @@ public final class MiscNodes {
         Object doSlot(@SuppressWarnings("unused") Object o, Object name, @SuppressWarnings("unused") Object value) {
             throw RError.error(RError.SHOW_CALLER2, RError.Message.INVALID_ARGUMENT_OF_TYPE, "name", SEXPTYPE.gnuRTypeForObject(name).name());
         }
+
+        public static RDoSlotAssignNode create() {
+            return RDoSlotAssignNodeGen.create();
+        }
     }
 
     @TypeSystemReference(RTypes.class)
@@ -157,6 +173,10 @@ public final class MiscNodes {
         Object doNewObject(Object classDef) {
             return newObjectNode.execute(classDef);
         }
+
+        public static RDoNewObjectNode create() {
+            return RDoNewObjectNodeGen.create();
+        }
     }
 
     @TypeSystemReference(RTypes.class)
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/AbstractDowncallForeign.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/AbstractDowncallForeign.java
new file mode 100644
index 0000000000000000000000000000000000000000..dcb23038b56cc7afc6ea9a8f5b14103efcbdcd2c
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/AbstractDowncallForeign.java
@@ -0,0 +1,112 @@
+/*
+ * 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.upcalls;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.interop.ForeignAccess.Factory;
+import com.oracle.truffle.api.interop.ForeignAccess.Factory26;
+import com.oracle.truffle.api.interop.Message;
+import com.oracle.truffle.api.nodes.RootNode;
+
+public abstract class AbstractDowncallForeign implements Factory26, Factory {
+    @Override
+    public CallTarget accessIsNull() {
+        return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(false));
+    }
+
+    @Override
+    public CallTarget accessIsExecutable() {
+        return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(true));
+    }
+
+    @Override
+    public CallTarget accessIsBoxed() {
+        return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(false));
+    }
+
+    @Override
+    public CallTarget accessHasSize() {
+        return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(false));
+    }
+
+    @Override
+    public CallTarget accessGetSize() {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessUnbox() {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessRead() {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessWrite() {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessInvoke(int argumentsLength) {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessNew(int argumentsLength) {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessKeyInfo() {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessKeys() {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessIsPointer() {
+        return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(false));
+    }
+
+    @Override
+    public CallTarget accessAsPointer() {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessToNative() {
+        return null;
+    }
+
+    @Override
+    public CallTarget accessMessage(Message unknown) {
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java
index 6226535d30b75aa31f47e75d6d361b23f2eb8cb8..b569971b0250b159f2b9275e4b4157c5a17a0201 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java
@@ -22,7 +22,19 @@
  */
 package com.oracle.truffle.r.ffi.impl.upcalls;
 
+import com.oracle.truffle.r.ffi.impl.nodes.ATTRIB;
+import com.oracle.truffle.r.ffi.impl.nodes.AsCharNode;
+import com.oracle.truffle.r.ffi.impl.nodes.AsIntegerNode;
+import com.oracle.truffle.r.ffi.impl.nodes.AsLogicalNode;
+import com.oracle.truffle.r.ffi.impl.nodes.AsRealNode;
+import com.oracle.truffle.r.ffi.impl.nodes.CoerceVectorNode;
+import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CADDRNode;
+import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CADRNode;
+import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CARNode;
+import com.oracle.truffle.r.ffi.impl.nodes.ListAccessNodes.CDRNode;
+import com.oracle.truffle.r.ffi.impl.nodes.MiscNodes.LENGTHNode;
 import com.oracle.truffle.r.ffi.processor.RFFICstring;
+import com.oracle.truffle.r.ffi.processor.RFFIUpCallNode;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -60,14 +72,19 @@ public interface StdUpCallsRFFI {
 
     RStringVector Rf_ScalarString(Object value);
 
+    @RFFIUpCallNode(AsIntegerNode.class)
     int Rf_asInteger(Object x);
 
+    @RFFIUpCallNode(AsRealNode.class)
     double Rf_asReal(Object x);
 
+    @RFFIUpCallNode(AsLogicalNode.class)
     int Rf_asLogical(Object x);
 
+    @RFFIUpCallNode(AsCharNode.class)
     Object Rf_asChar(Object x);
 
+    @RFFIUpCallNode(CoerceVectorNode.class)
     Object Rf_coerceVector(Object x, int mode);
 
     Object Rf_mkCharLenCE(@RFFICstring(convert = false) Object bytes, int len, int encoding);
@@ -89,6 +106,7 @@ public interface StdUpCallsRFFI {
 
     Object Rf_findVarInFrame3(Object envArg, Object symbolArg, int doGet);
 
+    @RFFIUpCallNode(ATTRIB.class)
     Object ATTRIB(Object obj);
 
     Object Rf_getAttrib(Object obj, Object name);
@@ -127,6 +145,7 @@ public interface StdUpCallsRFFI {
 
     int Rf_ncols(Object x);
 
+    @RFFIUpCallNode(LENGTHNode.class)
     int LENGTH(Object x);
 
     int /* void */ SET_STRING_ELT(Object x, long i, Object v);
@@ -163,12 +182,16 @@ public interface StdUpCallsRFFI {
 
     Object TAG(Object e);
 
+    @RFFIUpCallNode(CARNode.class)
     Object CAR(Object e);
 
+    @RFFIUpCallNode(CDRNode.class)
     Object CDR(Object e);
 
+    @RFFIUpCallNode(CADRNode.class)
     Object CADR(Object e);
 
+    @RFFIUpCallNode(CADDRNode.class)
     Object CADDR(Object e);
 
     Object CDDR(Object e);
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/UpCallUnwrap.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/UpCallUnwrap.java
similarity index 98%
rename from com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/UpCallUnwrap.java
rename to com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/UpCallUnwrap.java
index cbaa000be27e05852271586265fab913b3d6bba5..65f03708e12c29ecfbe6aa08b61dbdc51f519731 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/UpCallUnwrap.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/UpCallUnwrap.java
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.ffi.impl.common;
+package com.oracle.truffle.r.ffi.impl.upcalls;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
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
index 355660afa1b638fc4c773423a0a20d8d31711a2c..b40b966fbe08676165a700c175e0fcb90abe95b5 100644
--- 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
@@ -40,6 +40,7 @@ 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.MirroredTypeException;
 import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.util.Types;
@@ -157,55 +158,27 @@ public final class FFIProcessor extends AbstractProcessor {
         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.close();
-    }
-
-    private void generateMessageClass(ExecutableElement m) throws IOException {
-        String name = m.getSimpleName().toString();
-        String callName = name + "Call";
-        String returnType = getTypeName(m.getReturnType());
+        RFFIUpCallNode nodeAnnotation = m.getAnnotation(RFFIUpCallNode.class);
+        String node = null;
+        if (nodeAnnotation != null) {
+            try {
+                nodeAnnotation.value();
+            } catch (MirroredTypeException e) {
+                node = ((TypeElement) processingEnv.getTypeUtils().asElement(e.getTypeMirror())).getQualifiedName().toString();
+            }
+        }
+        // process arguments first to see if unwrap is necessary
         List<? extends VariableElement> params = m.getParameters();
-
         StringBuilder arguments = new StringBuilder();
-        boolean usesUnwrap = false;
-
-        // process arguments first to see if unwrap is necessary
-        int lparams = params.size();
-        for (int i = 0; i < lparams; i++) {
-            String is = Integer.toString(i);
+        int unwrapCount = 0;
+        for (int i = 0; i < params.size(); i++) {
+            if (i != 0) {
+                arguments.append(", ");
+            }
             String paramTypeName = getTypeName(params.get(i).asType());
             boolean isScalar = true;
             boolean needCast = !paramTypeName.equals("java.lang.Object");
@@ -213,60 +186,91 @@ public final class FFIProcessor extends AbstractProcessor {
                 arguments.append('(').append(paramTypeName).append(") ");
             }
             if (isScalar) {
-                usesUnwrap = true;
-                arguments.append("unwrap.unwrap(");
+                arguments.append("unwrap").append(unwrapCount).append(".unwrap(");
+                unwrapCount++;
             }
-            arguments.append("arguments[").append(is).append("]");
+            arguments.append("arguments.get(").append(i).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");
+        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.MessageResolution;\n");
-        w.append("import com.oracle.truffle.api.interop.Resolve;\n");
-        w.append("import com.oracle.truffle.api.nodes.Node;\n");
-        if (usesUnwrap) {
-            w.append("import com.oracle.truffle.r.ffi.impl.common.UpCallUnwrap;\n");
+        w.append("\n");
+        w.append("package com.oracle.truffle.r.ffi.impl.upcalls;\n");
+        w.append("\n");
+        w.append("import java.util.List;\n");
+        w.append("\n");
+        w.append("import com.oracle.truffle.api.CallTarget;\n");
+        w.append("import com.oracle.truffle.api.Truffle;\n");
+        w.append("import com.oracle.truffle.api.frame.VirtualFrame;\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.api.nodes.RootNode;\n");
+        w.append("import com.oracle.truffle.r.ffi.impl.upcalls.UpCallsRFFI;\n");
+        w.append("import com.oracle.truffle.r.runtime.data.RTruffleObject;\n");
+        w.append("\n");
+        w.append("// Checkstyle: stop method name check\n");
+        w.append("\n");
+        w.append("final class ").append(callName).append(" implements RTruffleObject {\n");
+        w.append('\n');
+        if (node == null) {
+            w.append("    private final UpCallsRFFI upCallsImpl;\n");
+            w.append('\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");
-        if (usesUnwrap) {
-            w.append("\n        @Child private UpCallUnwrap unwrap = new UpCallUnwrap();\n\n");
+        w.append("    ").append(callName).append("(UpCallsRFFI upCallsImpl) {\n");
+        w.append("        assert upCallsImpl != null;\n");
+        if (node == null) {
+            w.append("        this.upCallsImpl = upCallsImpl;\n");
+        }
+        w.append("    }\n");
+        w.append('\n');
+        w.append("    private static final class ").append(callName).append("Factory extends AbstractDowncallForeign {\n");
+        w.append("        @Override\n");
+        w.append("        public boolean canHandle(TruffleObject obj) {\n");
+        w.append("            return obj instanceof ").append(callName).append(";\n");
+        w.append("        }\n");
+        w.append("\n");
+        w.append("        @Override\n");
+        w.append("        public CallTarget accessExecute(int argumentsLength) {\n");
+        w.append("            return Truffle.getRuntime().createCallTarget(new RootNode(null) {\n");
+        w.append("\n");
+        if (unwrapCount > 0) {
+            for (int i = 0; i < unwrapCount; i++) {
+                w.append("                @Child private UpCallUnwrap unwrap").append(Integer.toString(i)).append(" = new UpCallUnwrap();\n");
+            }
+            w.append("\n");
         }
-        w.append("        protected ").append(returnType).append(" access(").append(callName).append(" receiver, ");
-        if (params.size() == 0) {
-            w.append("@SuppressWarnings(\"unused\") ");
+        if (node != null) {
+            w.append("                @Child private ").append(node).append(" node").append(" = ").append(node).append(".create();\n");
+            w.append("\n");
         }
-        w.append("Object[] arguments) {\n");
-        w.append("            ").append("return").append(" receiver.upCallsImpl.").append(name).append("(");
-
-        w.append(arguments);
-
-        w.append(");\n");
+        w.append("                @Override\n");
+        w.append("                public Object execute(VirtualFrame frame) {\n");
+        w.append("                    List<Object> arguments = ForeignAccess.getArguments(frame);\n");
+        w.append("                    assert arguments.size() == ").append(Integer.toString(params.size())).append(" : \"wrong number of arguments passed to ").append(name).append("\";\n");
+        if (node != null) {
+            w.append("                    return node.executeObject(").append(arguments).append(");\n");
+        } else {
+            w.append("                    return ((").append(callName).append(") ForeignAccess.getReceiver(frame)).upCallsImpl.").append(name).append("(").append(arguments).append(");\n");
+        }
+        w.append("                }\n");
+        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("    private static final ForeignAccess ACCESS = ForeignAccess.create(new ").append(callName).append("Factory(), null);\n");
+        w.append("\n");
+        w.append("    @Override\n");
+        w.append("    public ForeignAccess getForeignAccess() {\n");
+        w.append("        return ACCESS;\n");
         w.append("    }\n");
         w.append("}\n");
         w.close();
-
     }
 
     private void generateCallbacks(ExecutableElement[] methods) throws IOException {
diff --git a/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFIUpCallNode.java b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFIUpCallNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..d41af7c487beae6947807b2e41b4646576dd8a3e
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/RFFIUpCallNode.java
@@ -0,0 +1,34 @@
+/*
+ * 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.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Tags upcall functions that have a corresponding node implementation.
+ */
+@Target({ElementType.METHOD})
+public @interface RFFIUpCallNode {
+    Class<?> value();
+}