diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index f20257ce3144f3e3fbd1d52c0d878da62d8dc4fb..a439a0b7db36dcd248ecc5f690dbac5da2861088 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -455,7 +455,8 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
         } else if (call == RError.SHOW_CALLER2) {
             Frame frame = Utils.getActualCurrentFrame();
             if (frame != null && RArguments.isRFrame(frame)) {
-                frame = Utils.getCallerFrame(frame, FrameAccess.READ_ONLY);
+                RCaller parent = RArguments.getCall(frame);
+                frame = Utils.getCallerFrame(parent, FrameAccess.READ_ONLY);
             }
             return findCallerFromFrame(frame);
         } else {
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
index ed489a3e28ac1dd64e86b4f372b20fe4b19a3c66..f004349cc8abf9fbaeb4f8a6c510b8b3f742de6d 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
@@ -68,7 +68,6 @@ import com.oracle.truffle.r.runtime.conn.NativeConnections.NativeRConnection;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -110,7 +109,6 @@ import com.oracle.truffle.r.runtime.ffi.CharSXPWrapper;
 import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle;
 import com.oracle.truffle.r.runtime.gnur.SA_TYPE;
 import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
-import com.oracle.truffle.r.runtime.nmath.distr.Unif;
 import com.oracle.truffle.r.runtime.nodes.DuplicationHelper;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
@@ -684,19 +682,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
 
     @Override
     public Object TAG(Object e) {
-        if (e instanceof RPairList) {
-            return ((RPairList) e).getTag();
-        } else if (e instanceof RArgsValuesAndNames) {
-            ArgumentsSignature signature = ((RArgsValuesAndNames) e).getSignature();
-            if (signature.getLength() > 0 && signature.getName(0) != null) {
-                return signature.getName(0);
-            }
-            return RNull.instance;
-        } else {
-            guaranteeInstanceOf(e, RExternalPtr.class);
-            // at the moment, this can only be used to null out the pointer
-            return ((RExternalPtr) e).getTag();
-        }
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.TAG).call(e);
     }
 
     @Override
@@ -1608,4 +1594,20 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
         return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.Rf_namesgets).call(x, y);
     }
 
+    @Override
+    public int Rf_copyMostAttrib(Object x, Object y) {
+        FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.Rf_copyMostAttrib).call(x, y);
+        return 0;
+    }
+
+    @Override
+    public Object Rf_VectorToPairList(Object x) {
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.Rf_VectorToPairList).call(x);
+    }
+
+    @Override
+    public Object Rf_asCharacterFactor(Object x) {
+        return FFIUpCallRootNode.getCallTarget(RFFIUpCallTable.Rf_asCharacterFactor).call(x);
+    }
+
 }
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/TracingUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/TracingUpCallsRFFIImpl.java
index 8b5fc1e7b73848a4d471312f981c1bc9350174b9..d4287407b14c8166a7062551c212a8d296298b53 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/TracingUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/TracingUpCallsRFFIImpl.java
@@ -878,4 +878,22 @@ final class TracingUpCallsRFFIImpl implements UpCallsRFFI {
         return delegate.Rf_namesgets(vec, val);
     }
 
+    @Override
+    public int Rf_copyMostAttrib(Object x, Object y) {
+        RFFIUtils.traceUpCall("Rf_copyMostAttrib", x, y);
+        return delegate.Rf_copyMostAttrib(x, y);
+    }
+
+    @Override
+    public Object Rf_VectorToPairList(Object x) {
+        RFFIUtils.traceUpCall("Rf_VectorToPairlist", x);
+        return delegate.Rf_VectorToPairList(x);
+    }
+
+    @Override
+    public Object Rf_asCharacterFactor(Object x) {
+        RFFIUtils.traceUpCall("Rf_asCharacterFactor", x);
+        return delegate.Rf_asCharacterFactor(x);
+    }
+
 }
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
deleted file mode 100644
index 07d6a98681272787228e27e46d13c0f2a60d901f..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/ATTRIB.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.ffi.impl.nodes;
-
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.nodes.attributes.GetAttributesNode;
-import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RError.Message;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RNull;
-
-public abstract class ATTRIB extends FFIUpCallNode.Arg1 {
-    @Child private GetAttributesNode getAttributesNode = GetAttributesNode.create();
-
-    @Specialization
-    public Object doAttributable(RAttributable obj) {
-        return getAttributesNode.execute(obj);
-    }
-
-    @Fallback
-    public RNull doOthers(Object obj) {
-        if (obj == RNull.instance || RRuntime.isForeignObject(obj)) {
-            return RNull.instance;
-        } else {
-            CompilerDirectives.transferToInterpreter();
-            String type = obj == null ? "null" : obj.getClass().getSimpleName();
-            throw RError.error(RError.NO_CALLER, Message.GENERIC, "object of type '" + type + "' cannot be attributed");
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AttributesAccessNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AttributesAccessNodes.java
new file mode 100644
index 0000000000000000000000000000000000000000..49276644184fddf03db94bec6cb3f64ed8c4e63c
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/AttributesAccessNodes.java
@@ -0,0 +1,131 @@
+/*
+ * 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.nodes;
+
+import com.oracle.truffle.api.CompilerDirectives;
+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.nodes.attributes.CopyOfRegAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.GetAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+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.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
+import com.oracle.truffle.r.runtime.data.RExternalPtr;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+
+public final class AttributesAccessNodes {
+
+    abstract static class ATTRIB extends FFIUpCallNode.Arg1 {
+        @Child private GetAttributesNode getAttributesNode;
+
+        @Specialization
+        public Object doAttributable(RAttributable obj) {
+            if (getAttributesNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getAttributesNode = GetAttributesNode.create();
+            }
+            return getAttributesNode.execute(obj);
+        }
+
+        @Fallback
+        public RNull doOthers(Object obj) {
+            if (obj == RNull.instance || RRuntime.isForeignObject(obj)) {
+                return RNull.instance;
+            } else {
+                CompilerDirectives.transferToInterpreter();
+                String type = obj == null ? "null" : obj.getClass().getSimpleName();
+                throw RError.error(RError.NO_CALLER, Message.GENERIC, "object of type '" + type + "' cannot be attributed");
+            }
+        }
+    }
+
+    abstract static class TAG extends FFIUpCallNode.Arg1 {
+
+        @Specialization
+        public Object doPairlist(RPairList obj) {
+            return obj.getTag();
+        }
+
+        @Specialization
+        public Object doArgs(RArgsValuesAndNames obj) {
+            ArgumentsSignature signature = obj.getSignature();
+            if (signature.getLength() > 0 && signature.getName(0) != null) {
+                return signature.getName(0);
+            }
+            return RNull.instance;
+        }
+
+        @Specialization
+        public Object doExternalPtr(RExternalPtr obj) {
+            return obj.getTag();
+        }
+
+        @Specialization
+        public Object doList(RList obj,
+                        @Cached("create()") GetNamesAttributeNode getNamesAttributeNode) {
+            RStringVector names = getNamesAttributeNode.getNames(obj);
+            if (names != null && names.getLength() > 0) {
+                return names.getDataAt(0);
+            }
+            return RNull.instance;
+        }
+
+        @Fallback
+        @TruffleBoundary
+        public RNull doOthers(Object obj) {
+            throw RInternalError.unimplemented("TAG is not implemented for type " + obj.getClass().getSimpleName());
+        }
+    }
+
+    abstract static class CopyMostAttrib extends FFIUpCallNode.Arg2 {
+
+        @Child protected CopyOfRegAttributesNode copyRegAttributes;
+
+        @Specialization
+        public Object doList(RAttributeStorage x, RAttributeStorage y) {
+            if (copyRegAttributes == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                copyRegAttributes = CopyOfRegAttributesNode.create();
+            }
+            copyRegAttributes.execute(x, y);
+            return null;
+        }
+
+        @Fallback
+        @SuppressWarnings("unused")
+        public Void doOthers(Object x, Object y) {
+            throw RInternalError.unimplemented("Rf_copyMostAttrib only works with atrributables.");
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/CoerceNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/CoerceNodes.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad332c706566981cc979622af8111f525e3d1045
--- /dev/null
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/CoerceNodes.java
@@ -0,0 +1,285 @@
+/*
+ * 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.nodes;
+
+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.nodes.attributes.CopyOfRegAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.GetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
+import com.oracle.truffle.r.nodes.helpers.RFactorNodes;
+import com.oracle.truffle.r.nodes.unary.CastComplexNode;
+import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
+import com.oracle.truffle.r.nodes.unary.CastExpressionNode;
+import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
+import com.oracle.truffle.r.nodes.unary.CastListNode;
+import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.nodes.unary.CastRawNode;
+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.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.RShareable;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
+
+public final class CoerceNodes {
+
+    abstract static class VectorToPairListNode extends FFIUpCallNode.Arg1 {
+
+        @Child private CopyOfRegAttributesNode copyRegAttributesNode;
+        @Child private GetNamesAttributeNode getNamesAttributeNode;
+
+        @Specialization
+        protected Object convert(RAbstractVector v) {
+
+            RStringVector names = getNamesAttributeNode.getNames(v);
+            SEXPTYPE gnurType = SEXPTYPE.gnuRTypeForObject(v);
+
+            RPairList head = null;
+            RPairList prev = null;
+            assert names == null || names.getLength() == v.getLength();
+            for (int i = 0; i < v.getLength(); i++) {
+                Object element = v.getDataAtAsObject(i);
+                adjustSharing(v, element);
+                RPairList cur = RDataFactory.createPairList(element, RNull.instance, names != null ? names.getDataAt(i) : RNull.instance, gnurType);
+
+                if (prev == null) {
+                    assert head == null;
+                    head = cur;
+                } else {
+                    prev.setCdr(cur);
+                }
+                prev = cur;
+            }
+            if (head != null) {
+                // also copy regular attributes
+                copyRegAttributesNode.execute(v, head);
+                return head;
+            }
+            return RNull.instance;
+        }
+
+        private static void adjustSharing(RAbstractVector origin, Object element) {
+            if (origin instanceof RShareable) {
+                int v = getSharingLevel((RShareable) origin);
+                if (element instanceof RShareable) {
+                    RShareable r = (RShareable) element;
+                    if (v == 2) {
+                        // we play it safe: if the caller wants this instance to be shared, they may
+                        // expect it to never become non-shared again, which could happen in FastR
+                        r.makeSharedPermanent();
+                    }
+                    if (v == 1 && r.isTemporary()) {
+                        r.incRefCount();
+                    }
+                }
+            }
+        }
+
+        public static VectorToPairListNode create() {
+            return CoerceNodesFactory.VectorToPairListNodeGen.create();
+        }
+
+        private static int getSharingLevel(RShareable r) {
+            return r.isTemporary() ? 0 : r.isShared() ? 2 : 1;
+        }
+    }
+
+    abstract static class AsCharacterFactor extends FFIUpCallNode.Arg1 {
+
+        @Child private InheritsCheckNode inheritsFactorNode = InheritsCheckNode.createFactor();
+        @Child private GetAttributeNode getAttributeNode = GetAttributeNode.create();
+        @Child private RFactorNodes.GetLevels getLevels = RFactorNodes.GetLevels.create();
+
+        @Specialization
+        protected Object doFactor(RAbstractIntVector o) {
+            if (!inheritsFactorNode.execute(o)) {
+                throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "attempting to coerce non-factor");
+            }
+
+            RStringVector levels = getLevels.execute(o);
+            if (levels == null) {
+                throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "malformed factor");
+            }
+
+            String[] data = new String[o.getLength()];
+            boolean isComplete = true;
+            int nl = levels.getLength();
+            for (int i = 0; i < o.getLength(); i++) {
+                assert !o.isComplete() || o.getDataAt(i) != RRuntime.INT_NA;
+                int idx = o.getDataAt(i);
+                if (idx == RRuntime.INT_NA) {
+                    data[i] = RRuntime.STRING_NA;
+                    isComplete = false;
+                } else if (idx >= 1 && idx <= nl) {
+                    data[i] = levels.getDataAt(idx - 1);
+                } else {
+                    throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "malformed factor");
+                }
+            }
+
+            return RDataFactory.createStringVector(data, isComplete);
+        }
+
+        public static AsCharacterFactor create() {
+            return CoerceNodesFactory.AsCharacterFactorNodeGen.create();
+        }
+
+    }
+
+    /**
+     * Implements Rf_coerceVector.
+     */
+    abstract static class CoerceVectorNode extends FFIUpCallNode.Arg2 {
+
+        public static CoerceVectorNode create() {
+            return CoerceNodesFactory.CoerceVectorNodeGen.create();
+        }
+
+        @Specialization(guards = "value.isS4()")
+        Object doS4Object(@SuppressWarnings("unused") RTypedValue value, @SuppressWarnings("unused") int mode) {
+            throw RError.nyi(RError.NO_CALLER, "Rf_coerceVector for S4 objects.");
+        }
+
+        // Note: caches should cover all valid possibilities
+        @Specialization(guards = {"!isS4Object(value)", "isNotList(value)", "isValidMode(mode)", "cachedMode == mode"}, limit = "99")
+        Object doCached(Object value, @SuppressWarnings("unused") int mode,
+                        @Cached("mode") @SuppressWarnings("unused") int cachedMode,
+                        @Cached("createCastNode(cachedMode)") CastNode castNode) {
+            return castNode.doCast(value);
+        }
+
+        // Lists are coerced with only preserved names unlike other types
+        @Specialization(guards = {"!isS4Object(value)", "isValidMode(mode)", "cachedMode == mode"}, limit = "99")
+        Object doCached(RList value, @SuppressWarnings("unused") int mode,
+                        @Cached("mode") @SuppressWarnings("unused") int cachedMode,
+                        @Cached("createCastNodeForList(cachedMode)") CastNode castNode) {
+            return castNode.doCast(value);
+        }
+
+        @Fallback
+        @TruffleBoundary
+        Object doFallback(Object value, Object mode) {
+            String type = value != null ? value.getClass().getSimpleName() : "null";
+            throw RInternalError.unimplemented(String.format("Rf_coerceVector unimplemented for type %s or mode %s.", type, mode));
+        }
+
+        static boolean isS4Object(Object obj) {
+            return obj instanceof RTypedValue && ((RTypedValue) obj).isS4();
+        }
+
+        static boolean isNotList(Object obj) {
+            return !(obj instanceof RList);
+        }
+
+        static boolean isValidMode(int mode) {
+            return mode >= SEXPTYPE.NILSXP.code && mode <= SEXPTYPE.RAWSXP.code;
+        }
+
+        static CastNode createCastNode(int mode) {
+            return createCastNode(mode, false);
+        }
+
+        static CastNode createCastNodeForList(int mode) {
+            return createCastNode(mode, true);
+        }
+
+        private static CastNode createCastNode(int mode, boolean forList) {
+            SEXPTYPE type = SEXPTYPE.mapInt(mode);
+            boolean preserveDims = !forList;
+            boolean preserveAttrs = !forList;
+            switch (type) {
+                case SYMSXP:
+                    return CastSymbolNode.createForRFFI(false, false, false);
+                case NILSXP:
+                    return new CastNullNode();
+                case LISTSXP:
+                    throw RInternalError.unimplemented("Rf_coerceVector unimplemented for PairLists.");
+                case LANGSXP:
+                    throw RInternalError.unimplemented("Rf_coerceVector unimplemented for RLanguage.");
+                case ENVSXP:
+                    return new EnvironmentCast();
+                case VECSXP:
+                    return CastListNode.createForRFFI(true, forList, forList);
+                case EXPRSXP:
+                    return CastExpressionNode.createForRFFI();
+                case INTSXP:
+                    return CastIntegerNode.createForRFFI(true, preserveDims, preserveAttrs);
+                case REALSXP:
+                    return CastDoubleNode.createForRFFI(true, preserveDims, preserveAttrs);
+                case LGLSXP:
+                    return CastLogicalNode.createForRFFI(true, preserveDims, preserveAttrs);
+                case STRSXP:
+                    return CastStringNode.createForRFFI(true, preserveDims, preserveAttrs);
+                case CPLXSXP:
+                    return CastComplexNode.createForRFFI(true, preserveDims, preserveAttrs);
+                case RAWSXP:
+                    return CastRawNode.createForRFFI(true, preserveDims, preserveAttrs);
+                default:
+                    throw RInternalError.unimplemented(String.format("Rf_coerceVector called with unimplemented mode %d (type %s).", mode, type));
+            }
+        }
+
+        private static final class CastNullNode extends CastNode {
+            @Override
+            @TruffleBoundary
+            public Object execute(Object value) {
+                if (value instanceof RList) {
+                    throw RError.error(RError.NO_CALLER, Message.UNIMPLEMENTED_TYPE_IN_FUNCTION, "list", "coerceVectorList");
+                } else {
+                    throw RError.error(RError.NO_CALLER, Message.CANNOT_COERCE, getTypeName(value), "NULL");
+                }
+            }
+
+            private static String getTypeName(Object val) {
+                Object value = RRuntime.convertScalarVectors(val);
+                if (value == null) {
+                    return "null";
+                }
+                return value instanceof RTypedValue ? ((RTypedValue) value).getRType().getName() : value.getClass().getSimpleName();
+            }
+        }
+
+        private static final class EnvironmentCast extends CastNode {
+            @Override
+            @TruffleBoundary
+            public Object execute(Object value) {
+                throw RError.error(RError.NO_CALLER, Message.ENVIRONMENTS_COERCE);
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/CoerceVectorNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/CoerceVectorNode.java
deleted file mode 100644
index 32da1b5c50147f250972a2dba8c2d3efa6f85a81..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/CoerceVectorNode.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.r.ffi.impl.nodes;
-
-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.nodes.unary.CastComplexNode;
-import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
-import com.oracle.truffle.r.nodes.unary.CastExpressionNode;
-import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
-import com.oracle.truffle.r.nodes.unary.CastListNode;
-import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
-import com.oracle.truffle.r.nodes.unary.CastNode;
-import com.oracle.truffle.r.nodes.unary.CastRawNode;
-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;
-import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
-
-/**
- * Implements Rf_coerceVector.
- */
-public abstract class CoerceVectorNode extends FFIUpCallNode.Arg2 {
-
-    public static CoerceVectorNode create() {
-        return CoerceVectorNodeGen.create();
-    }
-
-    @Specialization(guards = "value.isS4()")
-    Object doS4Object(@SuppressWarnings("unused") RTypedValue value, @SuppressWarnings("unused") int mode) {
-        throw RError.nyi(RError.NO_CALLER, "Rf_coerceVector for S4 objects.");
-    }
-
-    // Note: caches should cover all valid possibilities
-    @Specialization(guards = {"!isS4Object(value)", "isNotList(value)", "isValidMode(mode)", "cachedMode == mode"}, limit = "99")
-    Object doCached(Object value, @SuppressWarnings("unused") int mode,
-                    @Cached("mode") @SuppressWarnings("unused") int cachedMode,
-                    @Cached("createCastNode(cachedMode)") CastNode castNode) {
-        return castNode.doCast(value);
-    }
-
-    // Lists are coerced with only preserved names unlike other types
-    @Specialization(guards = {"!isS4Object(value)", "isValidMode(mode)", "cachedMode == mode"}, limit = "99")
-    Object doCached(RList value, @SuppressWarnings("unused") int mode,
-                    @Cached("mode") @SuppressWarnings("unused") int cachedMode,
-                    @Cached("createCastNodeForList(cachedMode)") CastNode castNode) {
-        return castNode.doCast(value);
-    }
-
-    @Fallback
-    @TruffleBoundary
-    Object doFallback(Object value, Object mode) {
-        String type = value != null ? value.getClass().getSimpleName() : "null";
-        throw RInternalError.unimplemented(String.format("Rf_coerceVector unimplemented for type %s or mode %s.", type, mode));
-    }
-
-    static boolean isS4Object(Object obj) {
-        return obj instanceof RTypedValue && ((RTypedValue) obj).isS4();
-    }
-
-    static boolean isNotList(Object obj) {
-        return !(obj instanceof RList);
-    }
-
-    static boolean isValidMode(int mode) {
-        return mode >= SEXPTYPE.NILSXP.code && mode <= SEXPTYPE.RAWSXP.code;
-    }
-
-    static CastNode createCastNode(int mode) {
-        return createCastNode(mode, false);
-    }
-
-    static CastNode createCastNodeForList(int mode) {
-        return createCastNode(mode, true);
-    }
-
-    private static CastNode createCastNode(int mode, boolean forList) {
-        SEXPTYPE type = SEXPTYPE.mapInt(mode);
-        boolean preserveDims = !forList;
-        boolean preserveAttrs = !forList;
-        switch (type) {
-            case SYMSXP:
-                return CastSymbolNode.createForRFFI(false, false, false);
-            case NILSXP:
-                return new CastNullNode();
-            case LISTSXP:
-                throw RInternalError.unimplemented("Rf_coerceVector unimplemented for PairLists.");
-            case LANGSXP:
-                throw RInternalError.unimplemented("Rf_coerceVector unimplemented for RLanguage.");
-            case ENVSXP:
-                return new EnvironmentCast();
-            case VECSXP:
-                return CastListNode.createForRFFI(true, forList, forList);
-            case EXPRSXP:
-                return CastExpressionNode.createForRFFI();
-            case INTSXP:
-                return CastIntegerNode.createForRFFI(true, preserveDims, preserveAttrs);
-            case REALSXP:
-                return CastDoubleNode.createForRFFI(true, preserveDims, preserveAttrs);
-            case LGLSXP:
-                return CastLogicalNode.createForRFFI(true, preserveDims, preserveAttrs);
-            case STRSXP:
-                return CastStringNode.createForRFFI(true, preserveDims, preserveAttrs);
-            case CPLXSXP:
-                return CastComplexNode.createForRFFI(true, preserveDims, preserveAttrs);
-            case RAWSXP:
-                return CastRawNode.createForRFFI(true, preserveDims, preserveAttrs);
-            default:
-                throw RInternalError.unimplemented(String.format("Rf_coerceVector called with unimplemented mode %d (type %s).", mode, type));
-        }
-    }
-
-    private static final class CastNullNode extends CastNode {
-        @Override
-        @TruffleBoundary
-        public Object execute(Object value) {
-            if (value instanceof RList) {
-                throw RError.error(RError.NO_CALLER, Message.UNIMPLEMENTED_TYPE_IN_FUNCTION, "list", "coerceVectorList");
-            } else {
-                throw RError.error(RError.NO_CALLER, Message.CANNOT_COERCE, getTypeName(value), "NULL");
-            }
-        }
-
-        private static String getTypeName(Object val) {
-            Object value = RRuntime.convertScalarVectors(val);
-            if (value == null) {
-                return "null";
-            }
-            return value instanceof RTypedValue ? ((RTypedValue) value).getRType().getName() : value.getClass().getSimpleName();
-        }
-    }
-
-    private static final class EnvironmentCast extends CastNode {
-        @Override
-        @TruffleBoundary
-        public Object execute(Object value) {
-            throw RError.error(RError.NO_CALLER, Message.ENVIRONMENTS_COERCE);
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java
index 4470a37a76c32fce8da819194007efa910eedea4..de10716282dd189766cd122c954d30a2cca6e652 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/FFIUpCallRootNode.java
@@ -94,12 +94,12 @@ public final class FFIUpCallRootNode extends RootNode {
     }
 
     public static void register() {
-        FFIUpCallRootNode.add(RFFIUpCallTable.ATTRIB, ATTRIBNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.ATTRIB, AttributesAccessNodesFactory.ATTRIBNodeGen::create);
         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.Rf_coerceVector, CoerceNodes.CoerceVectorNode::create);
         FFIUpCallRootNode.add(RFFIUpCallTable.CAR, CARNodeGen::create);
         FFIUpCallRootNode.add(RFFIUpCallTable.CDR, CDRNodeGen::create);
         FFIUpCallRootNode.add(RFFIUpCallTable.CADR, CADRNodeGen::create);
@@ -114,5 +114,9 @@ public final class FFIUpCallRootNode extends RootNode {
         FFIUpCallRootNode.add(RFFIUpCallTable.Rf_qunif, () -> RandFunction3_2NodeGen.create(new Unif.QUnif()));
         FFIUpCallRootNode.add(RFFIUpCallTable.Rf_punif, () -> RandFunction3_2NodeGen.create(new Unif.PUnif()));
         FFIUpCallRootNode.add(RFFIUpCallTable.Rf_namesgets, MiscNodesFactory.NamesGetsNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.TAG, AttributesAccessNodesFactory.TAGNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.Rf_copyMostAttrib, AttributesAccessNodesFactory.CopyMostAttribNodeGen::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.Rf_VectorToPairList, CoerceNodes.VectorToPairListNode::create);
+        FFIUpCallRootNode.add(RFFIUpCallTable.Rf_asCharacterFactor, CoerceNodes.AsCharacterFactor::create);
     }
 }
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 bd0c3374713aabd8bfe24182e7de4ab285183b3c..3f675ef3eca28cb4df8dd056b4731c6127405d6e 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
@@ -22,14 +22,22 @@
  */
 package com.oracle.truffle.r.ffi.impl.nodes;
 
+import java.util.Arrays;
+
+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.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLanguage;
+import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTypes;
 
@@ -61,6 +69,11 @@ public final class ListAccessNodes {
             return sym;
         }
 
+        @Specialization
+        protected Object car(RList list) {
+            return list.getDataAt(0);
+        }
+
         @Specialization
         protected Object car(@SuppressWarnings("unused") RNull nil) {
             return RNull.instance;
@@ -68,7 +81,7 @@ public final class ListAccessNodes {
 
         @Fallback
         protected Object car(@SuppressWarnings("unused") Object obj) {
-            throw RInternalError.unimplemented("CAR only works on pair lists and language objects");
+            throw RInternalError.unimplemented("CAR only works on pair lists, language objects, argument lists, and symbols");
         }
     }
 
@@ -90,9 +103,26 @@ public final class ListAccessNodes {
             return args.toPairlist().cdr();
         }
 
+        @Specialization
+        protected Object cdr(RList list,
+                        @Cached("create()") GetNamesAttributeNode getNamesNode,
+                        @Cached("create()") SetNamesAttributeNode setNamesNode) {
+            if (list.getLength() == 1) {
+                return RNull.instance;
+            }
+            Object[] dataCopy = list.getDataWithoutCopying();
+            RStringVector names = getNamesNode.getNames(list);
+            RList copy = RDataFactory.createList(Arrays.copyOfRange(dataCopy, 1, list.getLength()));
+            if (names != null) {
+                String[] dataWithoutCopying = names.getDataWithoutCopying();
+                setNamesNode.setNames(copy, RDataFactory.createStringVector(Arrays.copyOfRange(dataWithoutCopying, 1, names.getLength()), true));
+            }
+            return copy;
+        }
+
         @Fallback
         protected Object cdr(@SuppressWarnings("unused") Object obj) {
-            throw RInternalError.unimplemented("CDR only works on pair lists and language objects");
+            throw RInternalError.unimplemented("CDR only works on pair lists, language objects, and argument lists");
 
         }
     }
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 76c10d6ac9e1285218e7e2774872b3375cc1ac2a..6226535d30b75aa31f47e75d6d361b23f2eb8cb8 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
@@ -305,4 +305,9 @@ public interface StdUpCallsRFFI {
 
     Object Rf_namesgets(Object vec, Object val);
 
+    int Rf_copyMostAttrib(Object x, Object y);
+
+    Object Rf_VectorToPairList(Object x);
+
+    Object Rf_asCharacterFactor(Object x);
 }
diff --git a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h
index 44b8bf7102dac472baa11a2dbcc4d41c3aa2842f..3452fe945c59beacfd0054e3ab02dad68c8249db 100644
--- a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h
+++ b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcalls.h
@@ -58,7 +58,7 @@ typedef SEXP (*call_Rf_coerceVector)(SEXP x, SEXPTYPE mode);
 typedef R_xlen_t (*call_Rf_any_duplicated)(SEXP x, Rboolean from_last);
 typedef SEXP (*call_Rf_duplicated)(SEXP x, Rboolean y);
 typedef SEXP (*call_Rf_applyClosure)(SEXP x, SEXP y, SEXP z, SEXP a, SEXP b);
-typedef void (*call_Rf_copyMostAttrib)(SEXP x, SEXP y);
+typedef int (*call_Rf_copyMostAttrib)(SEXP x, SEXP y);
 typedef void (*call_Rf_copyVector)(SEXP x, SEXP y);
 typedef int (*call_Rf_countContexts)(int x, int y);
 typedef Rboolean (*call_Rf_inherits)(SEXP x, const char * klass);
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
index f17d3f1c1952664e5635140c1d46dbd2461cf3a9..e414fb91e6d9dd51d3e716fd0e908e96c1b28055 100644
--- a/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h
+++ b/com.oracle.truffle.r.native/fficall/src/common/rffi_upcallsindex.h
@@ -78,73 +78,76 @@
 #define Rf_ScalarInteger_x 73
 #define Rf_ScalarLogical_x 74
 #define Rf_ScalarString_x 75
-#define Rf_allocArray_x 76
-#define Rf_allocMatrix_x 77
-#define Rf_allocVector_x 78
-#define Rf_any_duplicated_x 79
-#define Rf_asChar_x 80
-#define Rf_asInteger_x 81
-#define Rf_asLogical_x 82
-#define Rf_asReal_x 83
-#define Rf_classgets_x 84
-#define Rf_coerceVector_x 85
-#define Rf_cons_x 86
-#define Rf_copyListMatrix_x 87
-#define Rf_copyMatrix_x 88
-#define Rf_defineVar_x 89
-#define Rf_dunif_x 90
-#define Rf_duplicate_x 91
-#define Rf_error_x 92
-#define Rf_errorcall_x 93
-#define Rf_eval_x 94
-#define Rf_findFun_x 95
-#define Rf_findVar_x 96
-#define Rf_findVarInFrame_x 97
-#define Rf_findVarInFrame3_x 98
-#define Rf_getAttrib_x 99
-#define Rf_gsetVar_x 100
-#define Rf_inherits_x 101
-#define Rf_install_x 102
-#define Rf_installChar_x 103
-#define Rf_isNull_x 104
-#define Rf_isString_x 105
-#define Rf_lengthgets_x 106
-#define Rf_mkCharLenCE_x 107
-#define Rf_namesgets_x 108
-#define Rf_ncols_x 109
-#define Rf_nrows_x 110
-#define Rf_punif_x 111
-#define Rf_qunif_x 112
-#define Rf_runif_x 113
-#define Rf_setAttrib_x 114
-#define Rf_str2type_x 115
-#define Rf_warning_x 116
-#define Rf_warningcall_x 117
-#define Rprintf_x 118
-#define SETCADR_x 119
-#define SETCAR_x 120
-#define SETCDR_x 121
-#define SET_NAMED_FASTR_x 122
-#define SET_RDEBUG_x 123
-#define SET_RSTEP_x 124
-#define SET_S4_OBJECT_x 125
-#define SET_STRING_ELT_x 126
-#define SET_SYMVALUE_x 127
-#define SET_TAG_x 128
-#define SET_TYPEOF_FASTR_x 129
-#define SET_VECTOR_ELT_x 130
-#define STRING_ELT_x 131
-#define SYMVALUE_x 132
-#define TAG_x 133
-#define TYPEOF_x 134
-#define UNSET_S4_OBJECT_x 135
-#define VECTOR_ELT_x 136
-#define getConnectionClassString_x 137
-#define getOpenModeString_x 138
-#define getSummaryDescription_x 139
-#define isSeekable_x 140
-#define unif_rand_x 141
+#define Rf_VectorToPairList_x 76
+#define Rf_allocArray_x 77
+#define Rf_allocMatrix_x 78
+#define Rf_allocVector_x 79
+#define Rf_any_duplicated_x 80
+#define Rf_asChar_x 81
+#define Rf_asCharacterFactor_x 82
+#define Rf_asInteger_x 83
+#define Rf_asLogical_x 84
+#define Rf_asReal_x 85
+#define Rf_classgets_x 86
+#define Rf_coerceVector_x 87
+#define Rf_cons_x 88
+#define Rf_copyListMatrix_x 89
+#define Rf_copyMatrix_x 90
+#define Rf_copyMostAttrib_x 91
+#define Rf_defineVar_x 92
+#define Rf_dunif_x 93
+#define Rf_duplicate_x 94
+#define Rf_error_x 95
+#define Rf_errorcall_x 96
+#define Rf_eval_x 97
+#define Rf_findFun_x 98
+#define Rf_findVar_x 99
+#define Rf_findVarInFrame_x 100
+#define Rf_findVarInFrame3_x 101
+#define Rf_getAttrib_x 102
+#define Rf_gsetVar_x 103
+#define Rf_inherits_x 104
+#define Rf_install_x 105
+#define Rf_installChar_x 106
+#define Rf_isNull_x 107
+#define Rf_isString_x 108
+#define Rf_lengthgets_x 109
+#define Rf_mkCharLenCE_x 110
+#define Rf_namesgets_x 111
+#define Rf_ncols_x 112
+#define Rf_nrows_x 113
+#define Rf_punif_x 114
+#define Rf_qunif_x 115
+#define Rf_runif_x 116
+#define Rf_setAttrib_x 117
+#define Rf_str2type_x 118
+#define Rf_warning_x 119
+#define Rf_warningcall_x 120
+#define Rprintf_x 121
+#define SETCADR_x 122
+#define SETCAR_x 123
+#define SETCDR_x 124
+#define SET_NAMED_FASTR_x 125
+#define SET_RDEBUG_x 126
+#define SET_RSTEP_x 127
+#define SET_S4_OBJECT_x 128
+#define SET_STRING_ELT_x 129
+#define SET_SYMVALUE_x 130
+#define SET_TAG_x 131
+#define SET_TYPEOF_FASTR_x 132
+#define SET_VECTOR_ELT_x 133
+#define STRING_ELT_x 134
+#define SYMVALUE_x 135
+#define TAG_x 136
+#define TYPEOF_x 137
+#define UNSET_S4_OBJECT_x 138
+#define VECTOR_ELT_x 139
+#define getConnectionClassString_x 140
+#define getOpenModeString_x 141
+#define getSummaryDescription_x 142
+#define isSeekable_x 143
+#define unif_rand_x 144
 
-#define UPCALLS_TABLE_SIZE 142
+#define UPCALLS_TABLE_SIZE 145
 
 #endif // RFFI_UPCALLSINDEX_H
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 87605b88e859b232b52c5273435005c89ddd3549..5dcff7874a0572fb6df31c8bfb4e9d27f2c7e1f2 100644
--- a/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
+++ b/com.oracle.truffle.r.native/fficall/src/jni/Rinternals.c
@@ -105,6 +105,8 @@ static jmethodID Rf_coerceVectorMethodID;
 static jmethodID Rf_mkCharLenCEMethodID;
 static jmethodID Rf_asLogicalMethodID;
 static jmethodID Rf_PairToVectorListMethodID;
+static jmethodID Rf_VectorToPairListMethodID;
+static jmethodID Rf_asCharacterFactorMethodID;
 static jmethodID gnuRCodeForObjectMethodID;
 static jmethodID NAMED_MethodID;
 static jmethodID SET_TYPEOF_FASTR_MethodID;
@@ -147,6 +149,7 @@ static jmethodID Rf_copyMatrixMethodID;
 static jmethodID Rf_nrowsMethodID;
 static jmethodID Rf_ncolsMethodID;
 static jmethodID Rf_namesgetsMethodID;
+static jmethodID Rf_copyMostAttribMethodID;
 
 static jclass CharSXPWrapperClass;
 jclass JNIUpCallsRFFIImplClass;
@@ -227,6 +230,8 @@ void init_internals(JNIEnv *env) {
         Rf_coerceVectorMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_coerceVector", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
 	Rf_asLogicalMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_asLogical", "(Ljava/lang/Object;)I", 0);
 	Rf_PairToVectorListMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_PairToVectorList", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_VectorToPairListMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_VectorToPairList", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
+	Rf_asCharacterFactorMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_asCharacterFactor", "(Ljava/lang/Object;)Ljava/lang/Object;", 0);
 	NAMED_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "NAMED", "(Ljava/lang/Object;)I", 0);
 	SET_TYPEOF_FASTR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_TYPEOF_FASTR", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
 	SET_NAMED_FASTR_MethodID = checkGetMethodID(env, UpCallsRFFIClass, "SET_NAMED_FASTR", "(Ljava/lang/Object;I)Ljava/lang/Object;", 0);
@@ -265,6 +270,7 @@ void init_internals(JNIEnv *env) {
     Rf_nrowsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_nrows", "(Ljava/lang/Object;)I", 0);
     Rf_ncolsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_ncols", "(Ljava/lang/Object;)I", 0);
     Rf_namesgetsMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_namesgets", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 0);
+    Rf_copyMostAttribMethodID = checkGetMethodID(env, UpCallsRFFIClass, "Rf_copyMostAttrib", "(Ljava/lang/Object;Ljava/lang/Object;)I", 0);
 
     // static JNI-specific methods
 	JNIUpCallsRFFIImplClass = checkFindClass(env, "com/oracle/truffle/r/ffi/impl/jni/JNIUpCallsRFFIImpl");
@@ -446,7 +452,9 @@ SEXP Rf_applyClosure(SEXP x, SEXP y, SEXP z, SEXP a, SEXP b) {
 }
 
 void Rf_copyMostAttrib(SEXP x, SEXP y) {
-	unimplemented("Rf_copyMostAttrib");
+	TRACE(TARGpp, x, y);
+	JNIEnv *thisenv = getEnv();
+    (*thisenv)->CallIntMethod(thisenv, UpCallsRFFIObject, Rf_copyMostAttribMethodID, x, y);
 }
 
 void Rf_copyVector(SEXP x, SEXP y) {
@@ -1186,13 +1194,17 @@ SEXP Rf_PairToVectorList(SEXP x){
 }
 
 SEXP Rf_VectorToPairList(SEXP x){
-	unimplemented("Rf_VectorToPairList");
-	return NULL;
+	TRACE(TARGp, x);
+	JNIEnv *thisenv = getEnv();
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_VectorToPairListMethodID, x);
+	return checkRef(thisenv, result);
 }
 
 SEXP Rf_asCharacterFactor(SEXP x){
-	unimplemented("Rf_VectorToPairList");
-	return NULL;
+	TRACE(TARGp, x);
+	JNIEnv *thisenv = getEnv();
+	SEXP result = (*thisenv)->CallObjectMethod(thisenv, UpCallsRFFIObject, Rf_asCharacterFactorMethodID, x);
+	return checkRef(thisenv, result);
 }
 
 int Rf_asLogical(SEXP x){
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h
index 916a6d0a4be19b603150d698a09baca6a51243b6..767572ed9dcd0d6a4fa390d50702bfef2615f0b6 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h
@@ -311,7 +311,7 @@ SEXP Rf_applyClosure(SEXP x, SEXP y, SEXP z, SEXP a, SEXP b) {
 }
 
 void Rf_copyMostAttrib(SEXP x, SEXP y) {
-	unimplemented("Rf_copyMostAttrib");
+	((call_Rf_copyMostAttrib) callbacks[Rf_copyMostAttrib_x])(x, y);
 }
 
 void Rf_copyVector(SEXP x, SEXP y) {
@@ -763,12 +763,11 @@ SEXP Rf_PairToVectorList(SEXP x){
 }
 
 SEXP Rf_VectorToPairList(SEXP x){
-	return unimplemented("Rf_VectorToPairList");
+	return checkRef(((call_Rf_VectorToPairList) callbacks[Rf_VectorToPairList_x])(x));
 }
 
 SEXP Rf_asCharacterFactor(SEXP x){
-	unimplemented("Rf_VectorToPairList");
-	return NULL;
+	return checkRef(((call_Rf_asCharacterFactor) callbacks[Rf_asCharacterFactor_x])(x));
 }
 
 int Rf_asLogical(SEXP x){
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/APerm.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/APerm.java
index 39146e8235b4014f1139a75ad00cb489a2347732..c8bdd10f11b20611384f9ebe538b69db8e76fa49 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/APerm.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/APerm.java
@@ -18,21 +18,25 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.access.vector.ExtractListElement;
+import com.oracle.truffle.r.nodes.attributes.RemoveRegAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.function.opt.ReuseNonSharedNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -51,11 +55,12 @@ public abstract class APerm extends RBuiltinNode.Arg3 {
 
     @Child private SetDimNamesAttributeNode setDimNames;
     @Child private ExtractListElement extractListElement;
+    @Child private ReuseNonSharedNode reuseNonSharedNode;
 
     static {
         Casts casts = new Casts(APerm.class);
         casts.arg("a").mustNotBeNull(RError.Message.FIRST_ARG_MUST_BE_ARRAY);
-        casts.arg("perm").allowNull().mustBe(numericValue().or(stringValue()).or(complexValue())).mapIf(numericValue().or(complexValue()), asIntegerVectorClosure());
+        casts.arg("perm").allowNull().mustBe(numericValue().or(stringValue()).or(complexValue())).mapIf(numericValue().or(complexValue()), asIntegerVectorClosure(true, true, false));
         casts.arg("resize").mustBe(numericValue().or(logicalValue()), Message.INVALID_LOGICAL, "resize").asLogicalVector().findFirst();
     }
 
@@ -107,7 +112,30 @@ public abstract class APerm extends RBuiltinNode.Arg3 {
         return result;
     }
 
-    @Specialization
+    @Specialization(guards = "isIdentityPermutation(vector, permVector, getDimsNode)")
+    protected RAbstractVector doIdentity(RAbstractVector vector, @SuppressWarnings("unused") RAbstractIntVector permVector, @SuppressWarnings("unused") byte resize,
+                    @Cached("create()") RemoveRegAttributesNode removeClassAttrNode,
+                    @Cached("create()") GetDimAttributeNode getDimsNode) {
+
+        if (reuseNonSharedNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            reuseNonSharedNode = insert(ReuseNonSharedNode.create());
+        }
+
+        int[] dim = getDimsNode.getDimensions(vector);
+        checkErrorConditions(dim);
+
+        RVector<?> reused = reuseNonSharedNode.execute(vector);
+
+        // we have to remove some attributes
+        // remove all regular attributes (including the class attribute)
+        removeClassAttrNode.execute(reused);
+
+        // also ensures that we do not give a closure away
+        return reused;
+    }
+
+    @Specialization(guards = "!isIdentityPermutation(vector, permVector, getDimsNode)")
     protected RAbstractVector aPerm(RAbstractVector vector, RAbstractIntVector permVector, byte resize,
                     @Cached("create()") GetDimAttributeNode getDimsNode,
                     @Cached("create()") SetDimAttributeNode setDimsNode,
@@ -153,10 +181,26 @@ public abstract class APerm extends RBuiltinNode.Arg3 {
         return result;
     }
 
+    protected boolean isIdentityPermutation(RAbstractVector v, RAbstractIntVector permVector, GetDimAttributeNode getDimAttributeNode) {
+        int[] dimensions = getDimAttributeNode.getDimensions(v);
+        if (dimensions != null) {
+            int[] perm = getPermute(dimensions, permVector);
+            for (int i = 0; i < dimensions.length; i++) {
+                if (i != perm[i]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
     @Specialization
     protected RAbstractVector aPerm(RAbstractVector vector, RAbstractStringVector permVector, byte resize,
+                    @Cached("createBinaryProfile()") ConditionProfile isIdentityProfile,
                     @Cached("create()") GetDimAttributeNode getDimsNode,
                     @Cached("create()") SetDimAttributeNode setDimsNode,
+                    @Cached("create()") RemoveRegAttributesNode removeClassAttrNode,
                     @Cached("create()") GetDimNamesAttributeNode getDimNamesNode) {
         RList dimNames = getDimNamesNode.getDimNames(vector);
         if (dimNames == null) {
@@ -175,8 +219,13 @@ public abstract class APerm extends RBuiltinNode.Arg3 {
             // TODO: not found dimname error
         }
 
+        RIntVector permIntVector = RDataFactory.createIntVector(perm, true);
+        if (isIdentityProfile.profile(isIdentityPermutation(vector, permIntVector, getDimsNode))) {
+            return doIdentity(vector, permIntVector, resize, removeClassAttrNode, getDimsNode);
+        }
+
         // Note: if this turns out to be slow, we can cache the permutation
-        return aPerm(vector, RDataFactory.createIntVector(perm, true), resize, getDimsNode, setDimsNode, getDimNamesNode);
+        return aPerm(vector, permIntVector, resize, getDimsNode, setDimsNode, getDimNamesNode);
     }
 
     private static int[] getReverse(int[] dim) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
index 794229c9aae8f269919166366dfcb42e346ef626..33b9f53eb2bcc6da41a102539f92b1e8d6ca6f2e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
@@ -77,6 +77,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RTypes;
 import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
@@ -304,7 +305,7 @@ public abstract class Bind extends RBaseNode {
             }
         }
         if (firstDimNames != RNull.instance) {
-            RStringVector names = (RStringVector) firstDimNames;
+            RAbstractStringVector names = (RAbstractStringVector) firstDimNames;
             if (names != null && names.getLength() == dimLength) {
                 firstDimResultNames = names;
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
index a6e10e9ec1e2dca9b7b13a612b0706f55c51be26..7baf39bb7d472663a413ba1c8264c101bf0df70c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FrameFunctions.java
@@ -55,6 +55,7 @@ import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgNode;
 import com.oracle.truffle.r.nodes.function.PromiseNode.VarArgsPromiseNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.HasSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -159,29 +160,25 @@ public class FrameFunctions {
                 if (RArguments.getDepth(frame) - actualFrame <= ITERATE_LEVELS) {
                     Frame current = frame;
                     for (int i = 0; i < ITERATE_LEVELS; i++) {
-                        current = current == null ? null : RArguments.getCallerFrame(current);
+                        current = current == null ? null : getCallerFrame(current);
                         if (current != null && RArguments.getDepth(current) == actualFrame) {
                             return current;
                         }
                     }
-                    notifyRCallNodes(actualFrame, RArguments.getCall(frame));
                 }
                 return Utils.getStackFrame(access, actualFrame);
             }
         }
 
-        @TruffleBoundary
-        private static void notifyRCallNodes(int actualFrame, RCaller caller) {
-            RCaller currentCaller = caller;
-            for (int i = 0; i < ITERATE_LEVELS; i++) {
-                if (currentCaller == null || currentCaller.getDepth() <= actualFrame) {
-                    break;
-                }
-                if (currentCaller.isValidCaller() && !currentCaller.isPromise() && currentCaller.getSyntaxNode() instanceof RCallNode) {
-                    ((RCallNode) currentCaller.getSyntaxNode()).setNeedsCallerFrame();
-                }
-                currentCaller = currentCaller.getParent();
+        private static Frame getCallerFrame(Frame current) {
+            Object callerFrame = RArguments.getCallerFrame(current);
+            if (callerFrame instanceof CallerFrameClosure) {
+                CallerFrameClosure closure = (CallerFrameClosure) callerFrame;
+                closure.setNeedsCallerFrame();
+                return closure.getMaterializedCallerFrame();
             }
+            assert callerFrame instanceof Frame;
+            return (Frame) callerFrame;
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
index 3e5b3d2fb9bbfc7022715e181b1ca6b59e44ca68..e8547dc985cbad602368033b7b425c51b9b78a82 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetFunctions.java
@@ -37,7 +37,6 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
@@ -257,8 +256,6 @@ public class GetFunctions {
         @Child private TypeFromModeNode typeFromMode = TypeFromModeNodeGen.create();
         @Child private CallRFunctionCachedNode callCache = CallRFunctionCachedNodeGen.create(2);
 
-        @CompilationFinal private boolean needsCallerFrame;
-
         static {
             Casts casts = new Casts(MGet.class);
             casts.arg("x").mustBe(stringValue()).asStringVector();
@@ -375,14 +372,12 @@ public class GetFunctions {
         }
 
         private Object call(VirtualFrame frame, RFunction ifnFunc, String x) {
-            if (!needsCallerFrame && ((RRootNode) ifnFunc.getRootNode()).containsDispatch()) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                needsCallerFrame = true;
+            if (((RRootNode) ifnFunc.getRootNode()).containsDispatch()) {
+                callCache.setNeedsCallerFrame();
             }
-            MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
             FormalArguments formals = ((RRootNode) ifnFunc.getRootNode()).getFormalArguments();
             RArgsValuesAndNames args = new RArgsValuesAndNames(new Object[]{x}, ArgumentsSignature.empty(1));
-            return callCache.execute(frame, ifnFunc, RCaller.create(frame, RCallerHelper.createFromArguments(ifnFunc, args)), callerFrame, new Object[]{x}, formals.getSignature(),
+            return callCache.execute(frame, ifnFunc, RCaller.create(frame, RCallerHelper.createFromArguments(ifnFunc, args)), new Object[]{x}, formals.getSignature(),
                             ifnFunc.getEnclosingFrame(), null);
         }
 
@@ -400,5 +395,6 @@ public class GetFunctions {
             }
             return value;
         }
+
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
index 965159a003e27909512544a134e49257fdb42e48..7163f0f9bdce97762ca7b848cc3bb87265f5e467 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
@@ -253,7 +253,7 @@ public class HiddenInternalFunctions {
                 RSerialize.CallHook callHook = new RSerialize.CallHook() {
                     @Override
                     public Object eval(Object arg) {
-                        return callCache.execute(SubstituteVirtualFrame.create(frame), envhook, RCaller.create(frame, getOriginalCall()), null, new Object[]{arg}, null);
+                        return callCache.execute(SubstituteVirtualFrame.create(frame), envhook, RCaller.create(frame, getOriginalCall()), new Object[]{arg}, null);
                     }
                 };
                 String functionName = ReadVariableNode.getSlowPathEvaluationName();
@@ -385,7 +385,7 @@ public class HiddenInternalFunctions {
             RSerialize.CallHook callHook = new RSerialize.CallHook() {
                 @Override
                 public Object eval(Object arg) {
-                    return callCache.execute(SubstituteVirtualFrame.create(frame), hook, RCaller.create(frame, getOriginalCall()), null, new Object[]{arg}, null);
+                    return callCache.execute(SubstituteVirtualFrame.create(frame), hook, RCaller.create(frame, getOriginalCall()), new Object[]{arg}, null);
                 }
             };
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MakeUnique.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MakeUnique.java
index eddadcd858b150cc4ee5544107aed1931bcb23dd..79ba2deff626e7d39f093471d4326bae64af5500 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MakeUnique.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MakeUnique.java
@@ -27,12 +27,20 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.function.opt.ReuseNonSharedNode;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RStringSequence;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
@@ -40,8 +48,9 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 @RBuiltin(name = "make.unique", kind = INTERNAL, parameterNames = {"names", "sep"}, behavior = PURE)
 public abstract class MakeUnique extends RBuiltinNode.Arg2 {
 
-    private final ConditionProfile namesProfile = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile duplicatesProfile = ConditionProfile.createBinaryProfile();
+    @Child private ReuseNonSharedNode reuseNonSharedNode;
+
+    private final ConditionProfile trivialSizeProfile = ConditionProfile.createBinaryProfile();
     private final NACheck dummyCheck = NACheck.create(); // never triggered (used for vector update)
 
     static {
@@ -52,49 +61,74 @@ public abstract class MakeUnique extends RBuiltinNode.Arg2 {
     }
 
     @Specialization
-    protected RAbstractStringVector makeUnique(RAbstractStringVector names, String sep) {
-        if (namesProfile.profile(names.getLength() == 0 || names.getLength() == 1)) {
+    protected RAbstractStringVector makeUnique(String names, @SuppressWarnings("unused") String sep) {
+        // a single string cannot have duplicates
+        return RDataFactory.createStringVectorFromScalar(names);
+    }
+
+    @Specialization
+    protected RAbstractStringVector makeUnique(RStringVector names, String sep) {
+        if (trivialSizeProfile.profile(names.getLength() == 0 || names.getLength() == 1)) {
             return names;
-        } else {
-            // TODO: perhaps for longer vectors there is a faster algorithm using hash maps, but
-            // then it would probably have to be put on the slow path even for cases when no
-            // duplicates actually exist
-            int[] duplicates = new int[names.getLength()];
-            boolean duplicatesExist = false;
-            for (int i = 0; i < duplicates.length; i++) {
-                if (duplicates[i] > 0) {
-                    // already processed
-                    continue;
-                }
-                String current = names.getDataAt(i);
-                int duplicatesCount = 0;
-                for (int j = i + 1; j < duplicates.length; j++) {
-                    if (current.equals(names.getDataAt(j))) {
-                        duplicatesExist = true;
-                        duplicates[j] = ++duplicatesCount;
-                    }
+        }
+        if (reuseNonSharedNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            reuseNonSharedNode = insert(ReuseNonSharedNode.create());
+        }
+
+        RStringVector reused = (RStringVector) reuseNonSharedNode.execute(names);
+        return doLargeVector(reused, sep);
+    }
+
+    @Specialization
+    protected RAbstractStringVector makeUnique(RStringSequence names, @SuppressWarnings("unused") String sep) {
+        // a string sequence cannot have duplicates if stride is not zero
+        if (names.getStride() != 0) {
+            return names;
+        }
+        throw RInternalError.unimplemented("make.unique for string sequence with zero stride is not implemented");
+    }
+
+    @TruffleBoundary
+    protected RStringVector doLargeVector(RStringVector names, String sep) {
+        HashMap<String, AtomicInteger> keys = new HashMap<>(names.getLength());
+        boolean containsDuplicates = false;
+        boolean containsClashes = true;
+        for (int i = 0; i < names.getLength(); i++) {
+            AtomicInteger value = new AtomicInteger(0);
+            String element = names.getDataAt(i);
+            AtomicInteger prev = keys.put(element, value);
+            if (prev != null) {
+                containsDuplicates = true;
+                value.incrementAndGet();
+                if (!containsClashes) {
+                    int lastIndexOf = element.lastIndexOf(sep);
+                    // If an element contains the separator string followed by a digit, we may
+                    // encounter clashes.
+                    containsClashes = lastIndexOf != -1 && lastIndexOf + 1 < element.length() && Character.isDigit(element.charAt(lastIndexOf + 1));
                 }
             }
-            if (duplicatesProfile.profile(!duplicatesExist)) {
-                return names;
-            } else {
-                RStringVector newNames = names.materialize();
-                if (newNames.isShared()) {
-                    newNames = (RStringVector) newNames.copy();
-                }
-                // start with 1 as the first one is never the duplicate
-                for (int i = 1; i < duplicates.length; i++) {
-                    if (duplicates[i] > 0) {
-                        newNames.updateDataAt(i, concat(newNames.getDataAt(i), sep, duplicates[i]), dummyCheck);
-                    }
+        }
+        if (containsDuplicates) {
+            for (int i = 0; i < names.getLength(); i++) {
+                AtomicInteger atomicInteger = keys.get(names.getDataAt(i));
+                int curCnt = atomicInteger.getAndIncrement() - 1;
+                if (curCnt > 0) {
+                    String updatedElement;
+                    do {
+                        updatedElement = names.getDataAt(i) + sep + curCnt;
+
+                        // The generated string may already be in the vector.
+                        if (containsClashes && keys.containsKey(updatedElement)) {
+                            curCnt = atomicInteger.getAndIncrement() - 1;
+                        } else {
+                            break;
+                        }
+                    } while (true);
+                    names.updateDataAt(i, updatedElement, dummyCheck);
                 }
-                return newNames;
             }
         }
-    }
-
-    @TruffleBoundary
-    private static String concat(String s1, String sep, int index) {
-        return s1 + sep + index;
+        return names;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java
index afaa35a46f4ee13c6dd42ce27f0755e5700d3660..55a76080cb08370639afb5555aaf5e0a806f9f3f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste.java
@@ -31,6 +31,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
@@ -45,8 +46,11 @@ import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RIntSequence;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RScalar;
+import com.oracle.truffle.r.runtime.data.RStringSequence;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -101,14 +105,19 @@ public abstract class Paste extends RBuiltinNode.Arg3 {
     }
 
     @Specialization
-    protected RStringVector pasteListNullSep(VirtualFrame frame, RAbstractListVector values, String sep, @SuppressWarnings("unused") RNull collapse) {
+    protected RAbstractStringVector pasteListNullSep(VirtualFrame frame, RAbstractListVector values, String sep, @SuppressWarnings("unused") RNull collapse) {
         int length = lengthProfile.profile(values.getLength());
         if (hasNonNullElements(values, length)) {
-            String[] result = pasteListElements(frame, values, sep, length);
-            if (result == ONE_EMPTY_STRING) {
-                return RDataFactory.createEmptyStringVector();
+            int seqPos = isStringSequence(values, length);
+            if (seqPos != -1) {
+                return createStringSequence(values, length, seqPos, sep);
             } else {
-                return RDataFactory.createStringVector(result, RDataFactory.COMPLETE_VECTOR);
+                String[] result = pasteListElements(frame, values, sep, length);
+                if (result == ONE_EMPTY_STRING) {
+                    return RDataFactory.createEmptyStringVector();
+                } else {
+                    return RDataFactory.createStringVector(result, RDataFactory.COMPLETE_VECTOR);
+                }
             }
         } else {
             return RDataFactory.createEmptyStringVector();
@@ -253,4 +262,46 @@ public abstract class Paste extends RBuiltinNode.Arg3 {
         }
         return asCharacterNode;
     }
+
+    /**
+     * Tests for pattern = { scalar } intSequence { scalar }.
+     */
+    private static int isStringSequence(RAbstractListVector values, int length) {
+        int i = 0;
+        // consume prefix
+        while (i < length && isScalar(values.getDataAt(i))) {
+            i++;
+        }
+        if (i < length && values.getDataAt(i) instanceof RIntSequence) {
+            // consume suffix
+            int j = i + 1;
+            while (j < length && isScalar(values.getDataAt(j))) {
+                j++;
+            }
+            if (j == length) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private static boolean isScalar(Object dataAt) {
+        return dataAt instanceof RScalar || dataAt instanceof String || dataAt instanceof Double || dataAt instanceof Integer || dataAt instanceof Byte;
+    }
+
+    @TruffleBoundary
+    private static RStringSequence createStringSequence(RAbstractListVector values, int length, int seqPos, String sep) {
+        assert isStringSequence(values, length) != -1;
+
+        StringBuilder prefix = new StringBuilder();
+        for (int i = 0; i < seqPos; i++) {
+            prefix.append(values.getDataAt(i)).append(sep);
+        }
+        RIntSequence seq = (RIntSequence) values.getDataAt(seqPos);
+        StringBuilder suffix = new StringBuilder();
+        for (int i = seqPos + 1; i < length; i++) {
+            suffix.append(values.getDataAt(i)).append(sep);
+        }
+        return RDataFactory.createStringSequence(prefix.toString(), suffix.toString(), seq.getStart(), seq.getStride(), seq.getLength());
+    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java
index 650ab72158da049cc6119b59bdd5d7a7f471fade..b0a0f0e3cbbeeeb74c422358e005a9f5a822f3a9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java
@@ -17,7 +17,6 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.SUBSTITUTE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
@@ -27,6 +26,7 @@ import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.CallMatcherNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
+import com.oracle.truffle.r.nodes.function.GetCallerFrameNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode;
@@ -41,7 +41,6 @@ import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.ReturnException;
-import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -59,24 +58,11 @@ public abstract class S3DispatchFunctions {
         @Child private S3FunctionLookupNode methodLookup;
         @Child private CallMatcherNode callMatcher;
 
-        private final ConditionProfile callerFrameSlowPath = ConditionProfile.createBinaryProfile();
-        private final ConditionProfile topLevelFrameProfile = ConditionProfile.createBinaryProfile();
-
         protected Helper(boolean nextMethod) {
             methodLookup = S3FunctionLookupNode.create(true, nextMethod);
             callMatcher = CallMatcherNode.create(false);
         }
 
-        protected MaterializedFrame getCallerFrame(VirtualFrame frame) {
-            MaterializedFrame funFrame = RArguments.getCallerFrame(frame);
-            if (callerFrameSlowPath.profile(funFrame == null)) {
-                funFrame = Utils.getCallerFrame(frame, FrameAccess.MATERIALIZE).materialize();
-                RError.performanceWarning("slow caller frame access in UseMethod dispatch");
-            }
-            // S3 method can be dispatched from top-level where there is no caller frame
-            return topLevelFrameProfile.profile(funFrame == null) ? frame.materialize() : funFrame;
-        }
-
         protected Object dispatch(VirtualFrame frame, String generic, RStringVector type, String group, MaterializedFrame callerFrame, MaterializedFrame genericDefFrame,
                         ArgumentsSignature suppliedSignature, Object[] suppliedArguments) {
             Result lookupResult = methodLookup.execute(frame, generic, type, group, callerFrame, genericDefFrame);
@@ -97,6 +83,7 @@ public abstract class S3DispatchFunctions {
 
         @Child private ClassHierarchyNode classHierarchyNode = ClassHierarchyNode.createForDispatch(true);
         @Child private PromiseCheckHelperNode promiseCheckHelper;
+        @Child private GetCallerFrameNode getCallerFrameNode = new GetCallerFrameNode();
         @Child private Helper helper = new Helper(false);
 
         private final BranchProfile firstArgMissing = BranchProfile.create();
@@ -118,7 +105,7 @@ public abstract class S3DispatchFunctions {
             }
 
             RStringVector type = dispatchedObject == null ? RDataFactory.createEmptyStringVector() : classHierarchyNode.execute(dispatchedObject);
-            MaterializedFrame callerFrame = helper.getCallerFrame(frame);
+            MaterializedFrame callerFrame = getCallerFrameNode.execute(frame);
             MaterializedFrame genericDefFrame = RArguments.getEnclosingFrame(frame);
 
             ArgumentsSignature suppliedSignature = RArguments.getSuppliedSignature(frame);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java
index ea2f8107dc40567fefc4e7bff43368127a0524d7..3a89f202b0f44b142a66a6364591ffe1948487cd 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SysFunctions.java
@@ -55,6 +55,7 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinPackages;
 import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.REnvVars;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -146,8 +147,9 @@ public class SysFunctions {
 
     @TruffleBoundary
     private static void doCheckNSLoad(MaterializedFrame frame, RAbstractStringVector values, boolean setting) {
-        Frame caller = Utils.getCallerFrame(frame, FrameAccess.READ_ONLY);
-        RFunction func = RArguments.getFunction(caller);
+        RCaller caller = RArguments.getCall(frame);
+        Frame callerFrame = Utils.getCallerFrame(caller, FrameAccess.READ_ONLY);
+        RFunction func = RArguments.getFunction(callerFrame);
         if (func.toString().equals(LOADNAMESPACE)) {
             if (setting) {
                 RContext.getInstance().setNamespaceName(values.getDataAt(0));
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unlist.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unlist.java
index 626821a38aa55082fb3aaa99c9751aa448a45d17..5483988770fdc65ac8914b97e0e5e77558197827 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unlist.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unlist.java
@@ -35,6 +35,7 @@ import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.UnlistNodeGen.RecursiveLengthNodeGen;
+import com.oracle.truffle.r.nodes.builtin.base.UnlistNodeGen.UnlistLengthNodeGen;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
 import com.oracle.truffle.r.nodes.unary.PrecedenceNode;
 import com.oracle.truffle.r.nodes.unary.PrecedenceNodeGen;
@@ -75,12 +76,37 @@ public abstract class Unlist extends RBuiltinNode.Arg3 {
     }
 
     @Child private PrecedenceNode precedenceNode = PrecedenceNodeGen.create();
-    @Child private RLengthNode lengthNode;
+    @Child private UnlistLength lengthNode;
     @Child private RecursiveLength recursiveLengthNode;
     @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create();
     @Child private Node hasSizeNode;
     @Child private ForeignArray2R foreignArray2RNode;
 
+    @ImportStatic({Message.class, RRuntime.class, ForeignArray2R.class})
+    @TypeSystemReference(RTypes.class)
+    protected abstract static class UnlistLength extends Node {
+
+        public abstract int execute(Object vector);
+
+        @Child private RLengthNode lengthNode;
+
+        @Specialization
+        protected int getLength(@SuppressWarnings("unused") RLanguage l) {
+            // language object do not get expanded - as such their length for the purpose of unlist
+            // is 1
+            return 1;
+        }
+
+        @Fallback
+        protected int getLength(Object operand) {
+            if (lengthNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                lengthNode = insert(RLengthNode.create());
+            }
+            return lengthNode.executeInteger(operand);
+        }
+    }
+
     @ImportStatic({Message.class, RRuntime.class, ForeignArray2R.class})
     @TypeSystemReference(RTypes.class)
     protected abstract static class RecursiveLength extends Node {
@@ -216,13 +242,13 @@ public abstract class Unlist extends RBuiltinNode.Arg3 {
 
     private int getLength(Object operand) {
         initLengthNode();
-        return lengthNode.executeInteger(operand);
+        return lengthNode.execute(operand);
     }
 
     private void initLengthNode() {
         if (lengthNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            lengthNode = insert(RLengthNode.create());
+            lengthNode = insert(UnlistLengthNodeGen.create());
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java
index 135fea29b3990a92cf73aa9ca525e24c8f2eb8b0..4e58a26827823130d05262542f7a91929bce0238 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java
@@ -106,7 +106,7 @@ public abstract class UpdateSlot extends RBuiltinNode.Arg3 {
             if (cached.profile(currentFunction == checkSlotAssignFunction)) {
                 // TODO: technically, someone could override checkAtAssignment function and access
                 // the caller, but it's rather unlikely
-                checkAtAssignmentCall.execute(frame, checkSlotAssignFunction, RCaller.createInvalid(frame), null, new Object[]{objClass, name, valClass}, SIGNATURE,
+                checkAtAssignmentCall.execute(frame, checkSlotAssignFunction, RCaller.createInvalid(frame), new Object[]{objClass, name, valClass}, SIGNATURE,
                                 checkSlotAssignFunction.getEnclosingFrame(), null);
             } else {
                 // slow path
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/WriteTable.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/WriteTable.java
index 9bef765b6372169ec18ff6220aa0238e414ef12d..06ab7c3d948fc06a2985e605e2f33bb0f0e16cec 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/WriteTable.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/WriteTable.java
@@ -31,10 +31,6 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -162,7 +158,7 @@ public abstract class WriteTable extends RExternalBuiltinNode.Arg11 {
             // if (i % 1000 == 999)
             // R_CheckUserInterrupt();
             if (!(rnames instanceof RNull)) {
-                tmp.append(encodeElement2((RStringVector) rnames, i, quoteRn, qmethod, cdec)).append(csep);
+                tmp.append(encodeElement2((RAbstractStringVector) rnames, i, quoteRn, qmethod, cdec)).append(csep);
             }
             for (int j = 0; j < nc; j++) {
                 Object xjObj = x.getDataAtAsObject(j);
@@ -214,8 +210,8 @@ public abstract class WriteTable extends RExternalBuiltinNode.Arg11 {
         if (indx < 0 || indx >= x.getLength()) {
             throw new IllegalArgumentException("index out of range");
         }
-        if (x instanceof RStringVector) {
-            RStringVector sx = (RStringVector) x;
+        if (x instanceof RAbstractStringVector) {
+            RAbstractStringVector sx = (RAbstractStringVector) x;
             String p0 = /* translateChar */sx.getDataAt(indx);
             return encodeStringElement(p0, quote, qmethod);
         }
@@ -246,16 +242,16 @@ public abstract class WriteTable extends RExternalBuiltinNode.Arg11 {
     }
 
     private static boolean isna(RAbstractContainer x, int indx) {
-        if (x instanceof RLogicalVector) {
-            return RRuntime.isNA(((RLogicalVector) x).getDataAt(indx));
-        } else if (x instanceof RDoubleVector) {
-            return RRuntime.isNA(((RDoubleVector) x).getDataAt(indx));
-        } else if (x instanceof RIntVector) {
-            return RRuntime.isNA(((RIntVector) x).getDataAt(indx));
-        } else if (x instanceof RStringVector) {
-            return RRuntime.isNA(((RStringVector) x).getDataAt(indx));
-        } else if (x instanceof RComplexVector) {
-            RComplexVector cvec = (RComplexVector) x;
+        if (x instanceof RAbstractLogicalVector) {
+            return RRuntime.isNA(((RAbstractLogicalVector) x).getDataAt(indx));
+        } else if (x instanceof RAbstractDoubleVector) {
+            return RRuntime.isNA(((RAbstractDoubleVector) x).getDataAt(indx));
+        } else if (x instanceof RAbstractIntVector) {
+            return RRuntime.isNA(((RAbstractIntVector) x).getDataAt(indx));
+        } else if (x instanceof RAbstractStringVector) {
+            return RRuntime.isNA(((RAbstractStringVector) x).getDataAt(indx));
+        } else if (x instanceof RAbstractComplexVector) {
+            RAbstractComplexVector cvec = (RAbstractComplexVector) x;
             RComplex c = cvec.getDataAt(indx);
             return c.isNA();
         } else {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
index 0ce9148bd595ef13b9a3a6a175f26ab6590cea02..7ce6f47b092713c0e5503f85b28e24f883381c98 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
@@ -25,11 +25,13 @@ package com.oracle.truffle.r.nodes.access.vector;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.nodes.access.vector.CachedExtractVectorNodeFactory.ExtractDimNamesNodeGen;
 import com.oracle.truffle.r.nodes.access.vector.CachedExtractVectorNodeFactory.SetNamesNodeGen;
 import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
 import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
@@ -278,7 +280,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
     private Object extract(int dimensionIndex, RAbstractStringVector vector, Object pos, PositionProfile profile) {
         if (extractDimNames == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            extractDimNames = insert(new ExtractDimNamesNode(numberOfDimensions));
+            extractDimNames = insert(ExtractDimNamesNodeGen.create(numberOfDimensions));
         }
         return extractDimNames.extract(dimensionIndex, vector, pos, profile);
     }
@@ -498,24 +500,45 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         }
     }
 
-    private static class ExtractDimNamesNode extends Node {
+    abstract static class ExtractDimNamesNode extends Node {
 
-        @Children private final CachedExtractVectorNode[] extractNodes;
+        protected final int limit;
+
+        @Child protected ExtractVectorNode fallbackExtractNode;
 
         ExtractDimNamesNode(int dimensions) {
-            this.extractNodes = new CachedExtractVectorNode[dimensions];
+            // Support at most 2 different kinds of cached extract nodes per dimension.
+            limit = dimensions * 2;
         }
 
-        public Object extract(int dimensionIndex, RAbstractStringVector vector, Object position, PositionProfile profile) {
-            Object[] positions = new Object[]{position};
+        protected abstract Object execute(int dimensionIndex, RAbstractStringVector vector, Object position, PositionProfile profile);
+
+        protected boolean isSupported(CachedExtractVectorNode cachedExtractNode, RAbstractStringVector vector, Object position) {
+            return cachedExtractNode.isSupported(vector, new Object[]{position}, RLogical.TRUE, RLogical.TRUE);
+        }
+
+        protected CachedExtractVectorNode createCached(RAbstractStringVector vector, Object position) {
+            return new CachedExtractVectorNode(ElementAccessMode.SUBSET, vector, new Object[]{position}, RLogical.TRUE, RLogical.TRUE, true);
+        }
+
+        @Specialization(limit = "limit", guards = {"dimensionIndex == cachedIndex", "isSupported(cachedExtractNode, vector, position)"})
+        public Object extractDimNamesCached(int dimensionIndex, RAbstractStringVector vector, Object position, PositionProfile profile,
+                        @Cached("createCached(vector, position)") CachedExtractVectorNode cachedExtractNode,
+                        @SuppressWarnings("unused") @Cached("dimensionIndex") int cachedIndex) {
             PositionProfile[] profiles = new PositionProfile[]{profile};
-            if (extractNodes[dimensionIndex] == null) {
+            CompilerAsserts.partialEvaluationConstant(dimensionIndex);
+            return cachedExtractNode.apply(vector, new Object[]{position}, profiles, RLogical.TRUE, RLogical.TRUE);
+        }
+
+        @Specialization(replaces = "extractDimNamesCached")
+        public Object extract(int dimensionIndex, RAbstractStringVector vector, Object position, @SuppressWarnings("unused") PositionProfile profile) {
+            if (fallbackExtractNode == null) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
-                extractNodes[dimensionIndex] = insert(new CachedExtractVectorNode(ElementAccessMode.SUBSET, vector, positions, RLogical.TRUE, RLogical.TRUE, false));
+                fallbackExtractNode = insert(ExtractVectorNode.createRecursive(ElementAccessMode.SUBSET));
             }
             CompilerAsserts.partialEvaluationConstant(dimensionIndex);
-            assert extractNodes[dimensionIndex].isSupported(vector, positions, RLogical.TRUE, RLogical.TRUE);
-            return extractNodes[dimensionIndex].apply(vector, positions, profiles, RLogical.TRUE, RLogical.TRUE);
+            Object[] positions = new Object[]{position};
+            return fallbackExtractNode.apply(vector, positions, RLogical.TRUE, RLogical.TRUE);
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyOfRegAttributesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyOfRegAttributesNode.java
index 7118f008d8b60b50368ebf25865099e9680f6e10..bfe2fa60ef92b7a2a3388bbf4bdd56cb3ffb923c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyOfRegAttributesNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyOfRegAttributesNode.java
@@ -30,9 +30,10 @@ import com.oracle.truffle.api.object.Property;
 import com.oracle.truffle.api.object.Shape;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
 import com.oracle.truffle.r.runtime.data.RVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 /**
@@ -52,60 +53,60 @@ public abstract class CopyOfRegAttributesNode extends RBaseNode {
     @Child private GetFixedAttributeNode namesAttrGetter = GetFixedAttributeNode.createNames();
     @Child private GetFixedAttributeNode classAttrGetter = GetFixedAttributeNode.createClass();
 
-    public abstract void execute(RAbstractVector source, RVector<?> target);
+    public abstract void execute(RAttributable source, RAttributable target);
 
     public static CopyOfRegAttributesNode create() {
         return CopyOfRegAttributesNodeGen.create();
     }
 
     @Specialization(guards = "source.getAttributes() == null")
-    protected void copyNoAttributes(@SuppressWarnings("unused") RAbstractVector source, @SuppressWarnings("unused") RVector<?> target) {
+    protected void copyNoAttributes(@SuppressWarnings("unused") RAttributeStorage source, @SuppressWarnings("unused") RAttributeStorage target) {
         // nothing to do
     }
 
-    protected static final boolean emptyAttributes(RAbstractVector source) {
+    protected static final boolean emptyAttributes(RAttributeStorage source) {
         DynamicObject attributes = source.getAttributes();
         return attributes == null || attributes.isEmpty();
     }
 
     @Specialization(guards = "emptyAttributes(source)", replaces = "copyNoAttributes")
-    protected void copyEmptyAttributes(@SuppressWarnings("unused") RAbstractVector source, @SuppressWarnings("unused") RVector<?> target) {
+    protected void copyEmptyAttributes(@SuppressWarnings("unused") RAttributeStorage source, @SuppressWarnings("unused") RAttributeStorage target) {
         // nothing to do
     }
 
-    protected final boolean onlyDimAttribute(RAbstractVector source) {
+    protected final boolean onlyDimAttribute(RAttributeStorage source) {
         DynamicObject attributes = source.getAttributes();
         return attributes != null && sizeOneProfile.profile(attributes.size() == 1) && dimAttrGetter.execute(attributes) != null;
     }
 
     @Specialization(guards = "onlyDimAttribute(source)")
-    protected void copyDimOnly(@SuppressWarnings("unused") RAbstractVector source, @SuppressWarnings("unused") RVector<?> target) {
+    protected void copyDimOnly(@SuppressWarnings("unused") RAttributeStorage source, @SuppressWarnings("unused") RAttributeStorage target) {
         // nothing to do
     }
 
-    protected final boolean onlyNamesAttribute(RAbstractVector source) {
+    protected final boolean onlyNamesAttribute(RAttributeStorage source) {
         DynamicObject attributes = source.getAttributes();
         return attributes != null && sizeOneProfile.profile(attributes.size() == 1) && namesAttrGetter.execute(attributes) != null;
     }
 
     @Specialization(guards = "onlyNamesAttribute(source)")
-    protected void copyNamesOnly(@SuppressWarnings("unused") RAbstractVector source, @SuppressWarnings("unused") RVector<?> target) {
+    protected void copyNamesOnly(@SuppressWarnings("unused") RAttributeStorage source, @SuppressWarnings("unused") RAttributeStorage target) {
         // nothing to do
     }
 
-    protected final boolean onlyClassAttribute(RAbstractVector source) {
+    protected final boolean onlyClassAttribute(RAttributeStorage source) {
         DynamicObject attributes = source.getAttributes();
         return attributes != null && sizeOneProfile.profile(attributes.size() == 1) && classAttrGetter.execute(attributes) != null;
     }
 
     @Specialization(guards = "onlyClassAttribute(source)")
-    protected void copyClassOnly(RAbstractVector source, RVector<?> target) {
+    protected void copyClassOnly(RAttributeStorage source, RVector<?> target) {
         Object classAttr = classAttrGetter.execute(source.getAttributes());
         target.initAttributes(RAttributesLayout.createClass(classAttr));
     }
 
     @Specialization
-    protected void copyGeneric(RAbstractVector source, RVector<?> target) {
+    protected void copyGeneric(RAttributeStorage source, RAttributeStorage target) {
         DynamicObject orgAttributes = source.getAttributes();
         if (orgAttributes != null) {
             Shape shape = orgAttributes.getShape();
@@ -120,4 +121,14 @@ public abstract class CopyOfRegAttributesNode extends RBaseNode {
             }
         }
     }
+
+    @Specialization(guards = "!isAttributeStorage(source)")
+    @SuppressWarnings("unused")
+    protected void copyNothing(RAttributable source, RAttributable target) {
+        // do nothing, just pass
+    }
+
+    protected static boolean isAttributeStorage(Object o) {
+        return o instanceof RAttributeStorage;
+    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveRegAttributesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveRegAttributesNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8e270b43dfe05a2c478d401644809f3c75f1422
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveRegAttributesNode.java
@@ -0,0 +1,140 @@
+/*
+ * 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.nodes.attributes;
+
+import java.util.List;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.object.DynamicObject;
+import com.oracle.truffle.api.object.Property;
+import com.oracle.truffle.api.object.Shape;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.RemoveClassAttributeNode;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
+
+/**
+ * Remove all regular attributes. This node is in particular useful if we reuse containers with
+ * attributes but the reusing builtin should actually return a fresh container with no regular
+ * attributes.
+ */
+public abstract class RemoveRegAttributesNode extends AttributeAccessNode {
+
+    private final ConditionProfile sizeOneProfile = ConditionProfile.createBinaryProfile();
+
+    @Child private GetFixedAttributeNode dimAttrGetter = GetFixedAttributeNode.createDim();
+    @Child private GetFixedAttributeNode namesAttrGetter = GetFixedAttributeNode.createNames();
+    @Child private GetFixedAttributeNode classAttrGetter = GetFixedAttributeNode.createClass();
+    @Child private RemoveClassAttributeNode removeClassAttributeNode;
+
+    protected RemoveRegAttributesNode() {
+    }
+
+    public static RemoveRegAttributesNode create() {
+        return RemoveRegAttributesNodeGen.create();
+    }
+
+    public abstract void execute(RAttributable attrs);
+
+    @Specialization(guards = "source.getAttributes() == null")
+    protected void copyNoAttributes(@SuppressWarnings("unused") RAttributeStorage source) {
+        // nothing to do
+    }
+
+    protected static final boolean emptyAttributes(RAttributeStorage source) {
+        DynamicObject attributes = source.getAttributes();
+        return attributes == null || attributes.isEmpty();
+    }
+
+    @Specialization(guards = "emptyAttributes(source)", replaces = "copyNoAttributes")
+    protected void copyEmptyAttributes(@SuppressWarnings("unused") RAttributeStorage source) {
+        // nothing to do
+    }
+
+    protected final boolean onlyDimAttribute(RAttributeStorage source) {
+        DynamicObject attributes = source.getAttributes();
+        return attributes != null && sizeOneProfile.profile(attributes.size() == 1) && dimAttrGetter.execute(attributes) != null;
+    }
+
+    @Specialization(guards = "onlyDimAttribute(source)")
+    protected void copyDimOnly(@SuppressWarnings("unused") RAttributeStorage source) {
+        // nothing to do
+    }
+
+    protected final boolean onlyNamesAttribute(RAttributeStorage source) {
+        DynamicObject attributes = source.getAttributes();
+        return attributes != null && sizeOneProfile.profile(attributes.size() == 1) && namesAttrGetter.execute(attributes) != null;
+    }
+
+    @Specialization(guards = "onlyNamesAttribute(source)")
+    protected void copyNamesOnly(@SuppressWarnings("unused") RAttributeStorage source) {
+        // nothing to do
+    }
+
+    protected final boolean onlyClassAttribute(RAttributeStorage source) {
+        DynamicObject attributes = source.getAttributes();
+        return attributes != null && sizeOneProfile.profile(attributes.size() == 1) && classAttrGetter.execute(attributes) != null;
+    }
+
+    @Specialization(guards = "onlyClassAttribute(source)")
+    protected void copyClassOnly(RAttributeStorage source) {
+        if (removeClassAttributeNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            removeClassAttributeNode = insert(RemoveClassAttributeNode.create());
+        }
+        removeClassAttributeNode.execute(source);
+    }
+
+    @Specialization
+    protected void removeGeneric(RAttributeStorage source) {
+        DynamicObject orgAttributes = source.getAttributes();
+        assert orgAttributes != null;
+        Shape shape = orgAttributes.getShape();
+        List<Property> properties = shape.getPropertyList();
+        for (int i = 0; i < properties.size(); i++) {
+            Property p = properties.get(i);
+            String name = (String) p.getKey();
+            if (name != RRuntime.DIM_ATTR_KEY && name != RRuntime.DIMNAMES_ATTR_KEY && name != RRuntime.NAMES_ATTR_KEY) {
+                removeAttrFallback(orgAttributes, name);
+            }
+        }
+    }
+
+    @TruffleBoundary
+    protected void removeAttrFallback(DynamicObject attrs, String name) {
+        attrs.delete(name);
+    }
+
+    @Specialization(guards = "!isAttributeStorage(source)")
+    @SuppressWarnings("unused")
+    protected void copyNothing(RAttributable source) {
+        // do nothing, just pass
+    }
+
+    protected static boolean isAttributeStorage(RAttributable o) {
+        return o instanceof RAttributeStorage;
+    }
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
index 88f0ec886749048b8b13f2d7ba6eb64f664ab8e2..fa01f31ea9879cd1422a07ccad89f66df1d9a40a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java
@@ -16,6 +16,7 @@ import java.util.function.Supplier;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.NodeCost;
@@ -32,6 +33,7 @@ import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
+import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RVisibility;
@@ -263,8 +265,9 @@ public abstract class CallMatcherNode extends RBaseNode {
                     String genFunctionName = functionName == null ? function.getName() : functionName;
                     Supplier<RSyntaxElement> argsSupplier = RCallerHelper.createFromArguments(genFunctionName, preparePermutation, suppliedArguments, suppliedSignature);
                     RCaller caller = genFunctionName == null ? RCaller.createInvalid(frame, parent) : RCaller.create(frame, parent, argsSupplier);
+                    MaterializedFrame callerFrame = dispatchArgs instanceof S3Args ? ((S3Args) dispatchArgs).callEnv : null;
                     try {
-                        return call.execute(frame, cachedFunction, caller, null, reorderedArgs, matchedArgs.getSignature(), cachedFunction.getEnclosingFrame(), dispatchArgs);
+                        return call.execute(frame, cachedFunction, caller, callerFrame, reorderedArgs, matchedArgs.getSignature(), cachedFunction.getEnclosingFrame(), dispatchArgs);
                     } finally {
                         visibility.executeAfterCall(frame, caller);
                     }
@@ -343,8 +346,9 @@ public abstract class CallMatcherNode extends RBaseNode {
             RCaller caller = genFunctionName == null ? RCaller.createInvalid(frame, parent)
                             : RCaller.create(frame, RCallerHelper.createFromArguments(genFunctionName,
                                             new RArgsValuesAndNames(reorderedArgs.getArguments(), ArgumentsSignature.empty(reorderedArgs.getLength()))));
+            MaterializedFrame callerFrame = (dispatchArgs instanceof S3Args) ? ((S3Args) dispatchArgs).callEnv : null;
             try {
-                return call.execute(frame, function, caller, null, reorderedArgs.getArguments(), reorderedArgs.getSignature(), function.getEnclosingFrame(), dispatchArgs);
+                return call.execute(frame, function, caller, callerFrame, reorderedArgs.getArguments(), reorderedArgs.getSignature(), function.getEnclosingFrame(), dispatchArgs);
             } finally {
                 visibility.executeAfterCall(frame, caller);
             }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java
index 226573d72f40586343af33015ea00f34d9b2e53b..bf283b26035874d27723ac6863c20439a148c5bd 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GetCallerFrameNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.nodes.function;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.Frame;
@@ -30,6 +29,7 @@ import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
@@ -38,7 +38,8 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 public final class GetCallerFrameNode extends RBaseNode {
 
-    private final BranchProfile topLevelProfile = BranchProfile.create();
+    private final BranchProfile frameAvailableProfile = BranchProfile.create();
+    private final BranchProfile closureProfile = BranchProfile.create();
     @CompilationFinal private boolean slowPathInitialized;
 
     @Override
@@ -47,38 +48,46 @@ public final class GetCallerFrameNode extends RBaseNode {
     }
 
     public MaterializedFrame execute(Frame frame) {
-        MaterializedFrame funFrame = RArguments.getCallerFrame(frame);
-        if (funFrame == null) {
-            if (!slowPathInitialized) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                slowPathInitialized = true;
-            }
-            notifyCallers(RArguments.getCall(frame));
-            if (slowPathInitialized) {
-                RError.performanceWarning("slow caller frame access");
-            }
-            Frame callerFrame = Utils.getCallerFrame(frame, FrameAccess.MATERIALIZE);
-            if (callerFrame != null) {
-                return callerFrame.materialize();
-            } else {
-                topLevelProfile.enter();
-                // S3 method can be dispatched from top-level where there is no caller frame
-                return frame.materialize();
+        Object callerFrameObject = RArguments.getCallerFrame(frame);
+        if (callerFrameObject instanceof MaterializedFrame) {
+            frameAvailableProfile.enter();
+            return (MaterializedFrame) callerFrameObject;
+        }
+        if (callerFrameObject instanceof CallerFrameClosure) {
+            closureProfile.enter();
+            CallerFrameClosure closure = (CallerFrameClosure) callerFrameObject;
+            RCaller parent = RArguments.getCall(frame);
+            MaterializedFrame slowPathFrame = notifyCallers(closure, parent);
+            if (slowPathFrame != null) {
+                return slowPathFrame;
             }
         }
-        return funFrame;
+        assert callerFrameObject == null;
+
+        // S3 method can be dispatched from top-level where there is no caller frame
+        // Since RArguments does not allow to create arguments with a 'null' caller frame, this
+        // must be the top level case.
+        return frame.materialize();
     }
 
     @TruffleBoundary
-    private void notifyCallers(RCaller call) {
-        RCaller current = call;
-        while (current != null && current.isPromise()) {
-            current = current.getParent();
+    private static MaterializedFrame notifyCallers(CallerFrameClosure closure, RCaller parent) {
+
+        // inform the responsible call node to create a caller frame
+        closure.setNeedsCallerFrame();
+
+        // if interpreted, we will have a materialized frame in the closure
+        MaterializedFrame materializedCallerFrame = closure.getMaterializedCallerFrame();
+        if (materializedCallerFrame != null) {
+            return materializedCallerFrame;
         }
-        if (current != null && current.isValidCaller() && current.getSyntaxNode() instanceof RCallNode) {
-            if (!((RCallNode) current.getSyntaxNode()).setNeedsCallerFrame()) {
-                slowPathInitialized = false;
-            }
+        RError.performanceWarning("slow caller frame access");
+        // for now, get it on the very slow path
+        Frame callerFrame = Utils.getCallerFrame(parent, FrameAccess.MATERIALIZE);
+        if (callerFrame != null) {
+            return callerFrame.materialize();
         }
+        return null;
     }
+
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
index b2d5ebe471e2af85b928e377576af4bf6ee3c6d8..c3a0a12254405c034ad0cbd1c12137c5ebe61d76 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/PromiseNode.java
@@ -521,8 +521,40 @@ public abstract class PromiseNode extends RNode {
             }
         }
 
-        @ExplodeLoop
         private int evaluateArguments(VirtualFrame frame, Object[] evaluatedArgs) {
+            if (evaluatedArgs.length <= 32) {
+                return evaluateArgumentsExplode(frame, evaluatedArgs);
+            }
+            return evaluateArgumentsLoop(frame, evaluatedArgs);
+        }
+
+        @ExplodeLoop
+        private int evaluateArgumentsExplode(VirtualFrame frame, Object[] evaluatedArgs) {
+            int size = 0;
+            boolean containsVarargs = false;
+            for (int i = 0; i < varargs.length; i++) {
+                Object argValue = varargs[i].execute(frame);
+                if (argsValueAndNamesProfile.profile(argValue instanceof RArgsValuesAndNames)) {
+                    containsVarargs = true;
+                    size += ((RArgsValuesAndNames) argValue).getLength();
+                    evaluatedArgs[i] = argValue;
+                } else {
+                    size++;
+                    evaluatedArgs[i] = promiseCheckHelper.checkEvaluate(frame, argValue);
+                }
+                if (evaluatedArgs[i] == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    throw RInternalError.shouldNotReachHere("evaluated argument must not be null");
+                }
+            }
+            if (containsVarargProfile.profile(containsVarargs)) {
+                return size;
+            } else {
+                return -1;
+            }
+        }
+
+        private int evaluateArgumentsLoop(VirtualFrame frame, Object[] evaluatedArgs) {
             int size = 0;
             boolean containsVarargs = false;
             for (int i = 0; i < varargs.length; i++) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
index 58a993f8712f6aac3c59d263b5e5aacc19866943..b44c463f465a08c5b3f99e4c3a0fa5af461edfaf 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
@@ -31,7 +31,6 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.RootCallTarget;
-import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
@@ -70,8 +69,7 @@ import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.interop.Foreign2R;
-import com.oracle.truffle.r.runtime.interop.R2Foreign;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RArguments.S3DefaultArguments;
@@ -95,13 +93,14 @@ import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RMissing;
-import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RPromise.Closure;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
+import com.oracle.truffle.r.runtime.interop.Foreign2R;
 import com.oracle.truffle.r.runtime.interop.Foreign2RNodeGen;
+import com.oracle.truffle.r.runtime.interop.R2Foreign;
 import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RFastPathNode;
@@ -154,14 +153,6 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
     // needed for INTERNAL_GENERIC calls:
     @Child private FunctionDispatch internalDispatchCall;
 
-    private final Assumption needsNoCallerFrame = Truffle.getRuntime().createAssumption("no caller frame");
-
-    public boolean setNeedsCallerFrame() {
-        boolean value = !needsNoCallerFrame.isValid();
-        needsNoCallerFrame.invalidate();
-        return value;
-    }
-
     protected RCaller createCaller(VirtualFrame frame, RFunction function) {
         if (explicitArgs == null) {
             return RCaller.create(frame, this);
@@ -956,6 +947,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         private final RootCallTarget cachedTarget;
         private final FastPathFactory fastPathFactory;
         private final RVisibility fastPathVisibility;
+        private final boolean containsDispatch;
 
         DispatchedCallNode(RootCallTarget cachedTarget, RCallNode originalCall) {
             super(originalCall);
@@ -965,9 +957,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
             this.fastPath = fastPathFactory == null ? null : fastPathFactory.create();
             this.fastPathVisibility = fastPathFactory == null ? null : fastPathFactory.getVisibility();
             this.visibility = fastPathFactory == null ? null : SetVisibilityNode.create();
-            if (root.containsDispatch()) {
-                originalCall.setNeedsCallerFrame();
-            }
+            this.containsDispatch = root.containsDispatch();
         }
 
         @Override
@@ -990,14 +980,48 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
                 if (needsSplitting(cachedTarget)) {
                     call.getCallNode().cloneCallTarget();
                 }
+                if (containsDispatch) {
+                    call.setNeedsCallerFrame();
+                }
             }
-            MaterializedFrame callerFrame = /* CompilerDirectives.inInterpreter() || */originalCall.needsNoCallerFrame.isValid() ? null : frame.materialize();
+            MaterializedFrame callerFrame = s3Args != null ? s3Args.callEnv : null;
+            RCaller caller = originalCall.createCaller(frame, function);
 
-            return call.execute(frame, function, originalCall.createCaller(frame, function), callerFrame, orderedArguments.getArguments(), orderedArguments.getSignature(),
-                            function.getEnclosingFrame(), s3Args);
+            return call.execute(frame, function, caller, callerFrame, orderedArguments.getArguments(), orderedArguments.getSignature(), function.getEnclosingFrame(), s3Args);
         }
     }
 
+    public static final class InvalidateNoCallerFrame extends CallerFrameClosure {
+
+        private final Assumption needsNoCallerFrame;
+        private final MaterializedFrame frame;
+
+        protected InvalidateNoCallerFrame(Assumption needsNoCallerFrame) {
+            this.needsNoCallerFrame = needsNoCallerFrame;
+            this.frame = null;
+        }
+
+        protected InvalidateNoCallerFrame(Assumption needsNoCallerFrame, MaterializedFrame frame) {
+            this.needsNoCallerFrame = needsNoCallerFrame;
+            this.frame = frame;
+        }
+
+        @Override
+        public boolean setNeedsCallerFrame() {
+            if (needsNoCallerFrame.isValid()) {
+                needsNoCallerFrame.invalidate();
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public MaterializedFrame getMaterializedCallerFrame() {
+            return frame;
+        }
+
+    }
+
     @Override
     public RSyntaxElement getSyntaxLHS() {
         return getFunction() == null ? RSyntaxLookup.createDummyLookup(RSyntaxNode.LAZY_DEPARSE, "FUN", true) : getFunction().asRSyntaxNode();
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionBaseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionBaseNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..10aee1f08cfcc1f2abd4849015d564442c39037e
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionBaseNode.java
@@ -0,0 +1,121 @@
+/*
+ * 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.nodes.function.call;
+
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.CallerFrameClosure;
+
+public abstract class CallRFunctionBaseNode extends Node {
+
+    protected final Assumption needsNoCallerFrame = Truffle.getRuntime().createAssumption("no caller frame");
+    protected final CallerFrameClosure invalidateNoCallerFrame = new InvalidateNoCallerFrame(needsNoCallerFrame);
+    private static final CallerFrameClosure DUMMY = new DummyCallerFrameClosure();
+
+    public boolean setNeedsCallerFrame() {
+        boolean value = !needsNoCallerFrame.isValid();
+        needsNoCallerFrame.invalidate();
+        return value;
+    }
+
+    private Object getCallerFrameClosure(MaterializedFrame callerFrame) {
+        if (CompilerDirectives.inInterpreter()) {
+            return new InvalidateNoCallerFrame(needsNoCallerFrame, callerFrame);
+        }
+        return invalidateNoCallerFrame;
+    }
+
+    private Object getCallerFrameClosure(VirtualFrame callerFrame) {
+        if (CompilerDirectives.inInterpreter()) {
+            return new InvalidateNoCallerFrame(needsNoCallerFrame, callerFrame != null ? callerFrame.materialize() : null);
+        }
+        return invalidateNoCallerFrame;
+    }
+
+    protected final Object getCallerFrameObject(VirtualFrame curFrame, MaterializedFrame callerFrame, boolean topLevel) {
+        if (needsNoCallerFrame.isValid()) {
+            return getCallerFrameClosure(callerFrame);
+        } else {
+            if (callerFrame != null) {
+                return callerFrame;
+            } else if (topLevel) {
+                return DUMMY;
+            }
+            return curFrame.materialize();
+        }
+    }
+
+    protected final Object getCallerFrameObject(VirtualFrame callerFrame) {
+        return needsNoCallerFrame.isValid() ? getCallerFrameClosure(callerFrame) : callerFrame.materialize();
+    }
+
+    private static final class DummyCallerFrameClosure extends CallerFrameClosure {
+
+        @Override
+        public boolean setNeedsCallerFrame() {
+            return false;
+        }
+
+        @Override
+        public MaterializedFrame getMaterializedCallerFrame() {
+            return null;
+        }
+
+    }
+
+    public static final class InvalidateNoCallerFrame extends CallerFrameClosure {
+
+        private final Assumption needsNoCallerFrame;
+        private final MaterializedFrame frame;
+
+        protected InvalidateNoCallerFrame(Assumption needsNoCallerFrame) {
+            this.needsNoCallerFrame = needsNoCallerFrame;
+            this.frame = null;
+        }
+
+        protected InvalidateNoCallerFrame(Assumption needsNoCallerFrame, MaterializedFrame frame) {
+            this.needsNoCallerFrame = needsNoCallerFrame;
+            this.frame = frame;
+        }
+
+        @Override
+        public boolean setNeedsCallerFrame() {
+            if (needsNoCallerFrame.isValid()) {
+                needsNoCallerFrame.invalidate();
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public MaterializedFrame getMaterializedCallerFrame() {
+            return frame;
+        }
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionCachedNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionCachedNode.java
index d319b9f8c05f15928c8e2d3def5fb1b8d14fc6b6..2f0010a56085aff14a2f76677eddb6bffa089ed4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionCachedNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionCachedNode.java
@@ -30,7 +30,6 @@ import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.nodes.IndirectCallNode;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
@@ -41,7 +40,7 @@ import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.data.RFunction;
 
 @NodeInfo(cost = NodeCost.NONE)
-public abstract class CallRFunctionCachedNode extends Node {
+public abstract class CallRFunctionCachedNode extends CallRFunctionBaseNode {
 
     @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
@@ -51,14 +50,21 @@ public abstract class CallRFunctionCachedNode extends Node {
         this.cacheLimit = cacheLimit;
     }
 
-    public final Object execute(VirtualFrame frame, RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs, DispatchArgs dispatchArgs) {
-        Object[] callArgs = RArguments.create(function, call, callerFrame, evaluatedArgs, dispatchArgs);
+    public final Object execute(VirtualFrame frame, RFunction function, RCaller call, Object[] evaluatedArgs, DispatchArgs dispatchArgs) {
+        Object[] callArgs = RArguments.create(function, call, getCallerFrameObject(frame), evaluatedArgs, dispatchArgs);
+        return execute(frame, function.getTarget(), callArgs, call);
+    }
+
+    public final Object execute(VirtualFrame frame, RFunction function, RCaller call, Object[] evaluatedArgs,
+                    ArgumentsSignature suppliedSignature, MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
+        Object[] callArgs = RArguments.create(function, call, getCallerFrameObject(frame), evaluatedArgs, suppliedSignature, enclosingFrame, dispatchArgs);
         return execute(frame, function.getTarget(), callArgs, call);
     }
 
     public final Object execute(VirtualFrame frame, RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs,
                     ArgumentsSignature suppliedSignature, MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
-        Object[] callArgs = RArguments.create(function, call, callerFrame, evaluatedArgs, suppliedSignature, enclosingFrame, dispatchArgs);
+        boolean topLevel = call == null || call.getDepth() == 0;
+        Object[] callArgs = RArguments.create(function, call, getCallerFrameObject(frame, callerFrame, topLevel), evaluatedArgs, suppliedSignature, enclosingFrame, dispatchArgs);
         return execute(frame, function.getTarget(), callArgs, call);
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java
index ee1b587d0e3ccb584dd31d81d7bd75f9d612986d..5e92e7411b9f0abff7864fc5ea9b737c0dbf1ccb 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/CallRFunctionNode.java
@@ -27,7 +27,6 @@ import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.DirectCallNode;
-import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
@@ -38,7 +37,7 @@ import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.data.RFunction;
 
 @NodeInfo(cost = NodeCost.NONE)
-public final class CallRFunctionNode extends Node {
+public final class CallRFunctionNode extends CallRFunctionBaseNode {
 
     @Child private DirectCallNode callNode;
     @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
@@ -51,9 +50,21 @@ public final class CallRFunctionNode extends Node {
         return new CallRFunctionNode(callTarget);
     }
 
-    public Object execute(VirtualFrame frame, RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature,
+    public Object execute(VirtualFrame frame, RFunction function, RCaller caller, MaterializedFrame candidate, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature,
                     MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
-        Object[] callArgs = RArguments.create(function, caller, callerFrame, evaluatedArgs, suppliedSignature, enclosingFrame, dispatchArgs);
+
+        boolean topLevel = caller == null || caller.getDepth() == 0;
+        Object[] callArgs = RArguments.create(function, caller, getCallerFrameObject(frame, candidate, topLevel), evaluatedArgs, suppliedSignature, enclosingFrame, dispatchArgs);
+        try {
+            return callNode.call(callArgs);
+        } finally {
+            visibility.executeAfterCall(frame, caller);
+        }
+    }
+
+    public Object execute(VirtualFrame frame, RFunction function, RCaller caller, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature,
+                    MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
+        Object[] callArgs = RArguments.create(function, caller, getCallerFrameObject(frame), evaluatedArgs, suppliedSignature, enclosingFrame, dispatchArgs);
         try {
             return callNode.call(callArgs);
         } finally {
@@ -66,11 +77,13 @@ public final class CallRFunctionNode extends Node {
     }
 
     public static Object executeSlowpath(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, ArgumentsSignature suppliedSignature, DispatchArgs dispatchArgs) {
+        assert callerFrame != null;
         Object[] callArgs = RArguments.create(function, caller, callerFrame, evaluatedArgs, suppliedSignature, function.getEnclosingFrame(), dispatchArgs);
         return executeSlowpath(function, caller, callerFrame, callArgs);
     }
 
     public static Object executeSlowpath(RFunction function, RCaller caller, MaterializedFrame callerFrame, Object[] evaluatedArgs, DispatchArgs dispatchArgs) {
+        assert callerFrame != null;
         Object[] callArgs = RArguments.create(function, caller, callerFrame, evaluatedArgs, dispatchArgs);
         return executeSlowpath(function, caller, callerFrame, callArgs);
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java
index 6a7d5fa17632485aa4bf2f7d75f8284435146ab3..28fe8a13b4c821b874880fe25407165efb736743 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/RFactorNodes.java
@@ -36,6 +36,8 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 /**
  * Contains helper nodes related to factors, special R class of {@link RAbstractIntVector}. Note:
@@ -86,11 +88,11 @@ public final class RFactorNodes {
             Object attr = attrAccess.execute(factor.getAttributes());
 
             // Convert scalars to vector if necessary
-            RVector<?> vec;
+            RAbstractVector vec;
             if (nonScalarLevels.profile(attr instanceof RVector)) {
                 vec = (RVector<?>) attr;
             } else if (attr != null) {
-                vec = (RVector<?>) RRuntime.asAbstractVector(attr);   // scalar to vector
+                vec = (RAbstractVector) RRuntime.asAbstractVector(attr);   // scalar to vector
             } else {
                 notVectorBranch.enter();
                 // N.B: when a factor is lacking the 'levels' attribute, GNU R uses range 1:14331272
@@ -100,14 +102,14 @@ public final class RFactorNodes {
             }
 
             // Convert to string vector if necessary
-            if (stringVectorLevels.profile(vec instanceof RStringVector)) {
-                return (RStringVector) vec;
+            if (stringVectorLevels.profile(vec instanceof RAbstractStringVector)) {
+                return ((RAbstractStringVector) vec).materialize();
             } else {
                 if (castString == null) {
                     CompilerDirectives.transferToInterpreterAndInvalidate();
                     castString = insert(CastStringNodeGen.create(false, false, false));
                 }
-                RStringVector slevels = (RStringVector) castString.executeString(vec);
+                RStringVector slevels = ((RAbstractStringVector) castString.executeString(vec)).materialize();
                 return RDataFactory.createStringVector(slevels.getDataWithoutCopying(), RDataFactory.COMPLETE_VECTOR);
             }
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java
index a9912feba90ea092a00e3a2f725d2b12e3ab4aaf..1803e88efbfda4b92f6f6c4b95f1097ec6477f8b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/LoadMethod.java
@@ -131,7 +131,7 @@ abstract class LoadMethod extends RBaseNode {
             if (cached.profile(currentFunction == loadMethodFunction)) {
                 // TODO: technically, someone could override loadMethod function and access the
                 // caller, but it's rather unlikely
-                ret = (RFunction) loadMethodCall.execute(frame, loadMethodFunction, caller, null, new Object[]{fdef, fname, REnvironment.frameToEnvironment(frame.materialize())}, SIGNATURE,
+                ret = (RFunction) loadMethodCall.execute(frame, loadMethodFunction, caller, new Object[]{fdef, fname, REnvironment.frameToEnvironment(frame.materialize())}, SIGNATURE,
                                 loadMethodFunction.getEnclosingFrame(), null);
             } else {
                 // slow path
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java
index ebec5af6e485d4e7016ec3162bad1b624eaaeb59..ccdeba3324a6a9a8afe7cdc0d8bf40746d32054a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringNode.java
@@ -33,7 +33,9 @@ import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RIntSequence;
 import com.oracle.truffle.r.runtime.data.RLanguage;
+import com.oracle.truffle.r.runtime.data.RStringSequence;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
@@ -70,12 +72,26 @@ public abstract class CastStringNode extends CastStringBaseNode {
         return ret;
     }
 
+    protected boolean isIntSequence(RAbstractContainer c) {
+        return c instanceof RIntSequence;
+    }
+
     @Specialization
     protected RStringVector doStringVector(RStringVector vector) {
         return vector;
     }
 
     @Specialization
+    protected RStringSequence doStringSequence(RStringSequence vector) {
+        return vector;
+    }
+
+    @Specialization
+    protected RStringSequence doIntSequence(RIntSequence vector) {
+        return RDataFactory.createStringSequence("", "", vector.getStart(), vector.getStride(), vector.getLength());
+    }
+
+    @Specialization(guards = "!isIntSequence(operandIn)")
     protected RStringVector doAbstractContainer(RAbstractContainer operandIn,
                     @Cached("createClassProfile()") ValueProfile operandProfile,
                     @Cached("createBinaryProfile()") ConditionProfile isLanguageProfile) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java
index 4b8964a8834e3e73e9d8f2bf510c0b36efca0797..c0d2eab3b9722d69e76595be79f602093daf553d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/PrecedenceNode.java
@@ -55,6 +55,7 @@ import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RS4Object;
+import com.oracle.truffle.r.runtime.data.RStringSequence;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -157,6 +158,11 @@ public abstract class PrecedenceNode extends RBaseNode {
         return STRING_PRECEDENCE;
     }
 
+    @Specialization
+    protected int doString(RStringSequence val, boolean recursive) {
+        return STRING_PRECEDENCE;
+    }
+
     @Specialization
     protected int doFunction(RFunction func, boolean recursive) {
         return LIST_PRECEDENCE;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/CallerFrameClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/CallerFrameClosure.java
new file mode 100644
index 0000000000000000000000000000000000000000..10c963766c8cc8b6cd7d88249711c1263a2b60b2
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/CallerFrameClosure.java
@@ -0,0 +1,39 @@
+/*
+ * 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.runtime;
+
+import com.oracle.truffle.api.frame.MaterializedFrame;
+
+public abstract class CallerFrameClosure {
+
+    /**
+     * Inform the call node to subsequently provide the caller frame.
+     */
+    public abstract boolean setNeedsCallerFrame();
+
+    /**
+     * Retrieve the materialized caller frame if available (i.e. interpreter only).
+     */
+    public abstract MaterializedFrame getMaterializedCallerFrame();
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
index 1f41cbf04d1065e45362ffe5744d36e0c2a93e65..3c6ffeaa58399b08bf11b16a0cd183b45ebdf039 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
@@ -170,10 +170,10 @@ public final class RArguments {
         return frame.getArguments().length - INDEX_ARGUMENTS;
     }
 
-    public static Object[] create(RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs, DispatchArgs dispatchArgs) {
+    public static Object[] create(RFunction function, RCaller call, Object callerFrameObject, Object[] evaluatedArgs, DispatchArgs dispatchArgs) {
         ArgumentsSignature formalSignature = ((HasSignature) function.getRootNode()).getSignature();
         CompilerAsserts.neverPartOfCompilation();
-        return create(function, call, callerFrame, evaluatedArgs, ArgumentsSignature.empty(formalSignature.getLength()), function.getEnclosingFrame(), dispatchArgs);
+        return create(function, call, callerFrameObject, evaluatedArgs, ArgumentsSignature.empty(formalSignature.getLength()), function.getEnclosingFrame(), dispatchArgs);
     }
 
     /**
@@ -187,11 +187,12 @@ public final class RArguments {
      *         function as well as additional information like the parent frame or supplied
      *         signature.
      */
-    public static Object[] create(RFunction function, RCaller call, MaterializedFrame callerFrame, Object[] evaluatedArgs,
+    public static Object[] create(RFunction function, RCaller call, Object callerFrameObject, Object[] evaluatedArgs,
                     ArgumentsSignature suppliedSignature, MaterializedFrame enclosingFrame, DispatchArgs dispatchArgs) {
         assert suppliedSignature.getLength() == evaluatedArgs.length : "suppliedSignature should match the evaluatedArgs (see Java docs).";
         assert evaluatedArgs != null : "RArguments.create evaluatedArgs is null";
         assert call != null : "RArguments.create call is null";
+        assert callerFrameObject != null : "RArguments.create callerFrameObject is null";
         // Eventually we want to have this invariant
         // assert call != null || REnvironment.isGlobalEnvFrame(callerFrame);
 
@@ -199,7 +200,7 @@ public final class RArguments {
         a[INDEX_ENVIRONMENT] = null;
         a[INDEX_FUNCTION] = function;
         a[INDEX_CALL] = call;
-        a[INDEX_CALLER_FRAME] = callerFrame;
+        a[INDEX_CALLER_FRAME] = callerFrameObject;
         a[INDEX_ENCLOSING_FRAME] = enclosingFrame;
         a[INDEX_DISPATCH_ARGS] = dispatchArgs;
         a[INDEX_IS_IRREGULAR] = false;
@@ -226,9 +227,11 @@ public final class RArguments {
         return a;
     }
 
-    public static MaterializedFrame getCallerFrame(Frame frame) {
+    public static Object getCallerFrame(Frame frame) {
         Object[] args = frame.getArguments();
-        return (MaterializedFrame) args[INDEX_CALLER_FRAME];
+        // a 'null' caller frame is still allowed in case of environments
+        assert args[INDEX_CALLER_FRAME] != null || args[INDEX_ENVIRONMENT] != null;
+        return args[INDEX_CALLER_FRAME];
     }
 
     public static DispatchArgs getDispatchArgs(Frame frame) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index f44a3cfa50fda6a71b4f6d89a442bfbc7e241c2c..5dc6ed526481acc3228f4795854c88f5fa73af48 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -448,8 +448,8 @@ public final class Utils {
      * TODO Calls to this method should be validated with respect to whether promise evaluation is
      * in progress and replaced with use of {@code FrameDepthNode}.
      */
-    public static Frame getCallerFrame(Frame frame, FrameAccess fa) {
-        RCaller parent = RArguments.getCall(frame);
+    public static Frame getCallerFrame(RCaller caller, FrameAccess fa) {
+        RCaller parent = caller;
         while (parent != null && parent.isPromise()) {
             parent = parent.getParent();
         }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
index 06f3c97902603be00b2dd49044ba68de9b30138f..619bc2a75a2caff43fd491cb19def9571921693b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDataFactory.java
@@ -297,6 +297,10 @@ public final class RDataFactory {
         return createStringVector(new String[]{RRuntime.STRING_NA}, false);
     }
 
+    public static RStringSequence createStringSequence(String prefix, String suffix, int start, int stride, int length) {
+        return traceDataCreated(new RStringSequence(prefix, suffix, start, stride, length));
+    }
+
     public static RComplexVector createEmptyComplexVector() {
         return createComplexVector(new double[0], true);
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringSequence.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringSequence.java
new file mode 100644
index 0000000000000000000000000000000000000000..03c11fa316b416042f33958257930f2e024c2fee
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RStringSequence.java
@@ -0,0 +1,148 @@
+/*
+ * 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.runtime.data;
+
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.data.closures.RClosures;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public class RStringSequence extends RSequence implements RAbstractStringVector {
+
+    private final int start;
+    private final int stride;
+    private final String prefix;
+    private final String suffix;
+
+    protected RStringSequence(String prefix, String suffix, int start, int stride, int length) {
+        super(length);
+        this.start = start;
+        this.stride = stride;
+        this.prefix = prefix != null ? prefix : "";
+        this.suffix = suffix != null ? suffix : "";
+    }
+
+    private static void resizeData(String[] newData, String[] data, int oldDataLength, String fill) {
+        if (newData.length > oldDataLength) {
+            if (fill != null) {
+                for (int i = data.length; i < oldDataLength; i++) {
+                    newData[i] = fill;
+                }
+            } else {
+                for (int i = oldDataLength, j = 0; i < newData.length; ++i, j = Utils.incMod(j, oldDataLength)) {
+                    newData[i] = data[j];
+                }
+            }
+        }
+    }
+
+    @Override
+    public RStringVector copyResized(int size, boolean fillNA) {
+        String[] data = new String[size];
+        populateVectorData(data);
+        resizeData(data, data, getLength(), fillNA ? RRuntime.STRING_NA : null);
+        return RDataFactory.createStringVector(data, !(fillNA && size > getLength()));
+    }
+
+    @Override
+    public RVector<?> copyResizedWithDimensions(int[] newDimensions, boolean fillNA) {
+        int size = newDimensions[0] * newDimensions[1];
+        String[] data = new String[size];
+        populateVectorData(data);
+        resizeData(data, data, getLength(), fillNA ? RRuntime.STRING_NA : null);
+        return RDataFactory.createStringVector(data, !(fillNA && size > getLength()), newDimensions);
+    }
+
+    @Override
+    public RDoubleVector createEmptySameType(int newLength, boolean newIsComplete) {
+        return RDataFactory.createDoubleVector(new double[newLength], newIsComplete);
+    }
+
+    @Override
+    public String getDataAt(int index) {
+        assert index >= 0 && index < getLength();
+        return prefix + (start + stride * index) + suffix;
+    }
+
+    private void populateVectorData(String[] result) {
+        int current = start;
+        for (int i = 0; i < result.length && i < getLength(); i++) {
+            result[i] = prefix + current + suffix;
+            current += stride;
+        }
+    }
+
+    @Override
+    public RStringVector materialize() {
+        return internalCreateVector();
+    }
+
+    public int getStart() {
+        return start;
+    }
+
+    public int getEnd() {
+        return start + (getLength() - 1) * stride;
+    }
+
+    public int getStride() {
+        return stride;
+    }
+
+    @Override
+    public Object getStartObject() {
+        return prefix + start + suffix;
+    }
+
+    @Override
+    public Object getStrideObject() {
+        return Integer.toString(stride);
+    }
+
+    @Override
+    protected RStringVector internalCreateVector() {
+        return copyResized(getLength(), false);
+    }
+
+    @Override
+    public RAbstractVector castSafe(RType type, ConditionProfile isNAProfile, boolean keepAttributes) {
+        switch (type) {
+            case Character:
+                return this;
+            case List:
+                return RClosures.createToListVector(this, keepAttributes);
+            default:
+                return null;
+        }
+    }
+
+    @Override
+    public String toString() {
+        CompilerAsserts.neverPartOfCompilation();
+        return "[\"" + getStartObject() + "\" - \"" + prefix + getEnd() + suffix + "\"]";
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
index ad2756524f8c0ca8b4ea1a7677fea16f55ab2c33..846193104cbe95021aa0b18859b180ce2e6c6235 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
@@ -37,6 +37,7 @@ import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.SuppressFBWarnings;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
@@ -302,7 +303,7 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
                             throw invokingNode.error(RError.Message.DIMNAMES_DONT_MATCH_EXTENT, i + 1);
                         }
                     } else {
-                        RStringVector dimVector = (RStringVector) dimObject;
+                        RAbstractStringVector dimVector = (RAbstractStringVector) dimObject;
                         if (dimVector == null) {
                             newDimNames.updateDataAt(i, RNull.instance, null);
                         } else if (dimVector.getLength() != dimensions[i]) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java
index 1cc4294efa9d58eb83760855b4e1a146a83c62dd..1c9527d80690831ccb3b65296d0be34deb133d50 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RClosures.java
@@ -31,6 +31,7 @@ import com.oracle.truffle.r.runtime.data.RIntSequence;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RRawVector;
+import com.oracle.truffle.r.runtime.data.RStringSequence;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
@@ -225,6 +226,10 @@ public class RClosures {
         return new RStringToListVectorClosure(vector, keepAttributes);
     }
 
+    public static RAbstractListVector createToListVector(RStringSequence vector, boolean keepAttributes) {
+        return new RStringSequenceToListVectorClosure(vector, keepAttributes);
+    }
+
     // Factor to vector
 
     public static RAbstractVector createFactorToVector(RAbstractIntVector factor, boolean withNames, RAbstractVector levels) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToListVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToListVectorClosure.java
index f6ef8f334c63b9c3fde8cf8f676d23d21fb9352f..80ede7abf8eeed21598c3597dc9d817ccd629f58 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToListVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToListVectorClosure.java
@@ -34,6 +34,7 @@ import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.data.RRawVector;
+import com.oracle.truffle.r.runtime.data.RStringSequence;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
@@ -224,6 +225,26 @@ final class RStringToListVectorClosure extends RToListVectorClosure {
     }
 }
 
+final class RStringSequenceToListVectorClosure extends RToListVectorClosure {
+
+    private final RStringSequence vector;
+
+    RStringSequenceToListVectorClosure(RStringSequence vector, boolean keepAttributes) {
+        super(keepAttributes);
+        this.vector = vector;
+    }
+
+    @Override
+    public RStringSequence getVector() {
+        return vector;
+    }
+
+    @Override
+    public String getDataAt(int index) {
+        return vector.getDataAt(index);
+    }
+}
+
 final class RRawToListVectorClosure extends RToListVectorClosure {
 
     private final RRawVector vector;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java
index 146301963b7405eaf7e9c06776afcd32f287cfea..977cc6c1a7a9fabd532de93fb4957c0cd5e142f7 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/gnur/SEXPTYPE.java
@@ -34,6 +34,7 @@ import com.oracle.truffle.r.runtime.data.RPromise.EagerPromise;
 import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RS4Object;
+import com.oracle.truffle.r.runtime.data.RStringSequence;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.RUnboundValue;
@@ -57,7 +58,7 @@ public enum SEXPTYPE {
     INTSXP(13, RIntVector.class, RIntSequence.class, Integer.class), /* integer vectors */
     REALSXP(14, RDoubleVector.class, RDoubleSequence.class, Double.class), /* real variables */
     CPLXSXP(15, RComplexVector.class, RComplex.class), /* complex variables */
-    STRSXP(16, RStringVector.class, String.class), /* string vectors */
+    STRSXP(16, RStringVector.class, RStringSequence.class, String.class), /* string vectors */
     DOTSXP(17, RArgsValuesAndNames.class), /* dot-dot-dot object */
     ANYSXP(18), /* make "any" args work */
     VECSXP(19, RList.class), /* generic vectors */
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index 5288c6fc4ac7f00ba3f6e2bf05cd1327feefb666..23aeeb5945798641fbd90e60e94ccec19d95c57e 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -6351,6 +6351,14 @@ complex(0)
 #argv <- list(1L);as.complex(argv[[1]]);
 [1] 1+0i
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_asdataframe.testWithDimnames#
+#{ v1 <- matrix(rep(1.1, 16), 4, 4, dimnames=list(c('a', 'b', 'c', 'd'), c('e', 'f', 'g', 'h'))); v0 <- matrix(rep(1.2, 16), 4, 4, dimnames=list(1L:4L, c('e', 'f', 'g', 'h'))); as.data.frame(v1); as.data.frame(v0) }
+    e   f   g   h
+1 1.2 1.2 1.2 1.2
+2 1.2 1.2 1.2 1.2
+3 1.2 1.2 1.2 1.2
+4 1.2 1.2 1.2 1.2
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_asdataframe.testasdataframe1#
 #argv <- structure(list(x = structure(c(3.5, 2, 1.7, 0.40625,     0.5, 0.882, 4, 2, 2, 4, 2, 3, 0.625, 0.5, 0.444444444444444,     0, 0, 0.333333333333333, 0.833333333333333, 1, 0.333333333333333,     0.5, 0.666666666666667, 0.666666666666667, 0.166666666666667,     0, 0.5), .Dim = c(3L, 9L), .Dimnames = list(c('q1.csv', 'q2.csv',     'q3.csv'), c('effsize', 'constraint', 'outdegree', 'indegree',     'efficiency', 'hierarchy', 'centralization', 'gden', 'ego.gden')))),     .Names = 'x');do.call('as.data.frame', argv)
        effsize constraint outdegree indegree efficiency hierarchy
@@ -34667,6 +34675,38 @@ Error in make.unique(1) : 'names' must be a character vector
 #{ make.unique(character()) }
 character(0)
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_makeunique.testMakeUnique5#
+#{ make.unique(paste0('a', 1:10)) }
+ [1] "a1"  "a2"  "a3"  "a4"  "a5"  "a6"  "a7"  "a8"  "a9"  "a10"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_makeunique.testMakeUnique5#
+#{ make.unique(rep('a', 10)) }
+ [1] "a"   "a.1" "a.2" "a.3" "a.4" "a.5" "a.6" "a.7" "a.8" "a.9"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_makeunique.testMakeUnique6#
+#{ make.unique(paste('a', 1:10, sep = '.')) }
+ [1] "a.1"  "a.2"  "a.3"  "a.4"  "a.5"  "a.6"  "a.7"  "a.8"  "a.9"  "a.10"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_makeunique.testMakeUnique6#
+#{ make.unique(paste0('a', 1:10)) }
+ [1] "a1"  "a2"  "a3"  "a4"  "a5"  "a6"  "a7"  "a8"  "a9"  "a10"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_makeunique.testMakeUnique7#
+#{ make.unique(c('a', 'a', 'a.2', 'a'), sep = '.') }
+[1] "a"   "a.1" "a.2" "a.3"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_makeunique.testMakeUnique7#
+#{ make.unique(c('a.1', 'a.2', 'a', 'a'), sep = '.') }
+[1] "a.1" "a.2" "a"   "a.3"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_makeunique.testMakeUnique7#
+#{ make.unique(c('a.2', 'a.2', 'a', 'a', 'a'), sep = '.') }
+[1] "a.2"   "a.2.1" "a"     "a.1"   "a.3"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_makeunique.testMakeUnique7#
+#{ make.unique(c('a.2', 'a.2', 'a.3', 'a.3', 'a', 'a', 'a'), sep = '.') }
+[1] "a.2"   "a.2.1" "a.3"   "a.3.1" "a"     "a.1"   "a.4"
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_makeunique.testmakeunique1#
 #argv <- list(c('A', 'B', 'C', 'D', 'E', 'F'), '.'); .Internal(make.unique(argv[[1]], argv[[2]]))
 [1] "A" "B" "C" "D" "E" "F"
@@ -72372,6 +72412,12 @@ a.b   a
 a.b  a2  a3
 "1" "2" "3"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_unlist.testUnlist#
+#{ unlist(list(quote(for(i in seq(1)) print(i))), recursive=F) }
+[[1]]
+for (i in seq(1)) print(i)
+
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_unlist.testUnlist#
 #{ x <- list("a", c("b", "c"), list("d", list("e"))) ; unlist(x) }
 [1] "a" "b" "c" "d" "e"
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_asdataframe.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_asdataframe.java
index 477c36e02f7447cea98b3c6d66c5501a337b5813..ed681dbc29804f4f272d4561df777e5787915c90 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_asdataframe.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_asdataframe.java
@@ -4,7 +4,7 @@
  * http://www.gnu.org/licenses/gpl-2.0.html
  *
  * Copyright (c) 2014, Purdue University
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -22,5 +22,11 @@ public class TestBuiltin_asdataframe extends TestBase {
     public void testasdataframe1() {
         assertEval("argv <- structure(list(x = structure(c(3.5, 2, 1.7, 0.40625,     0.5, 0.882, 4, 2, 2, 4, 2, 3, 0.625, 0.5, 0.444444444444444,     0, 0, 0.333333333333333, 0.833333333333333, 1, 0.333333333333333,     0.5, 0.666666666666667, 0.666666666666667, 0.166666666666667,     0, 0.5), .Dim = c(3L, 9L), .Dimnames = list(c('q1.csv', 'q2.csv',     'q3.csv'), c('effsize', 'constraint', 'outdegree', 'indegree',     'efficiency', 'hierarchy', 'centralization', 'gden', 'ego.gden')))),     .Names = 'x');" +
                         "do.call('as.data.frame', argv)");
+
+    }
+
+    @Test
+    public void testWithDimnames() {
+        assertEval("{ v1 <- matrix(rep(1.1, 16), 4, 4, dimnames=list(c('a', 'b', 'c', 'd'), c('e', 'f', 'g', 'h'))); v0 <- matrix(rep(1.2, 16), 4, 4, dimnames=list(1L:4L, c('e', 'f', 'g', 'h'))); as.data.frame(v1); as.data.frame(v0) }");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_makeunique.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_makeunique.java
index db726d9db92d7e40ed5f7b2dce447fbd9a5ff9d8..a6263d3959e3fe2a2ffea6d0c2385e9a6cb3377d 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_makeunique.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_makeunique.java
@@ -4,7 +4,7 @@
  * http://www.gnu.org/licenses/gpl-2.0.html
  *
  * Copyright (c) 2012-2014, Purdue University
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -37,6 +37,28 @@ public class TestBuiltin_makeunique extends TestBase {
         assertEval("argv <- list(character(0), '.'); .Internal(make.unique(argv[[1]], argv[[2]]))");
     }
 
+    @Test
+    public void testMakeUnique5() {
+        assertEval("{ make.unique(rep('a', 10)) }");
+        assertEval("{ make.unique(paste0('a', 1:10)) }");
+    }
+
+    @Test
+    public void testMakeUnique6() {
+        // test string sequences
+        assertEval("{ make.unique(paste0('a', 1:10)) }");
+        assertEval("{ make.unique(paste('a', 1:10, sep = '.')) }");
+    }
+
+    @Test
+    public void testMakeUnique7() {
+        // test clashes
+        assertEval("{ make.unique(c('a', 'a', 'a.2', 'a'), sep = '.') }");
+        assertEval("{ make.unique(c('a.1', 'a.2', 'a', 'a'), sep = '.') }");
+        assertEval("{ make.unique(c('a.2', 'a.2', 'a', 'a', 'a'), sep = '.') }");
+        assertEval("{ make.unique(c('a.2', 'a.2', 'a.3', 'a.3', 'a', 'a', 'a'), sep = '.') }");
+    }
+
     @Test
     public void testMakeUnique() {
         assertEval("{ make.unique(\"a\") }");
@@ -54,6 +76,6 @@ public class TestBuiltin_makeunique extends TestBase {
         assertEval("{ .Internal(make.unique(c(\"7\", \"42\"), character())) }");
         assertEval("{ .Internal(make.unique(c(\"7\", \"42\"), c(\".\", \".\"))) }");
         assertEval("{ .Internal(make.unique(c(\"7\", \"42\"), NULL)) }");
-
     }
+
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_unlist.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_unlist.java
index 7beb5839d2c2f90561a96c652fe6e333d68da693..ea57dc309ce7add318414dc3c97e44faaea48977 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_unlist.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_unlist.java
@@ -338,6 +338,8 @@ public class TestBuiltin_unlist extends TestBase {
         assertEval("{ x <- list(1,list(2,3),4) ; z <- list(x,x) ; u <- list(z,z) ; u[[c(2,2,3)]] <- 6 ; unlist(u) }");
 
         assertEval("{ x<-quote(f(1,2)); y<-function(z) 42; l<-list(x, y, NULL); y<-unlist(l); c(length(y), typeof(y)) }");
+
+        assertEval("{ unlist(list(quote(for(i in seq(1)) print(i))), recursive=F) }");
     }
 
     @Test