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 39d41c413493129185eccdefefcc773adf41516b..7d0999d6da905a374068760941ef99bbc8b9be45 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
@@ -442,6 +442,12 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
         ((RBaseNode) node).serialize(state);
     }
 
+    @Override
+    public void serializeFunctionDefinitionNode(RSerialize.State state, RFunction fn) {
+        FunctionDefinitionNode fdn = (FunctionDefinitionNode) fn.getRootNode();
+        fdn.serializeImpl(state);
+    }
+
     @Override
     public ArgumentsSignature getArgumentsSignature(RFunction f) {
         return ((RRootNode) f.getRootNode()).getSignature();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Assign.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Assign.java
index 1af8e0ba9aa15ae52e70af657e5b4e8f90e20ea0..a185e05d9c6da216f8ae526be748986133e9fc36 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Assign.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Assign.java
@@ -39,6 +39,7 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RShareable;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
@@ -107,7 +108,9 @@ public abstract class Assign extends RBuiltinNode {
      */
     @Specialization
     protected Object assign(RAbstractStringVector xVec, Object value, REnvironment envir, byte inherits, //
-                    @Cached("createBinaryProfile()") ConditionProfile inheritsProfile) {
+                    @Cached("createBinaryProfile()") ConditionProfile inheritsProfile,
+                    @Cached("createBinaryProfile()") ConditionProfile isShareableProfile,
+                    @Cached("createBinaryProfile()") ConditionProfile isRefCountUpdateable) {
         String x = checkVariable(xVec);
         REnvironment env = envir;
         if (inheritsProfile.profile(RRuntime.fromLogical(inherits))) {
@@ -129,6 +132,12 @@ public abstract class Assign extends RBuiltinNode {
                 throw RError.error(errorContext(), RError.Message.CANNOT_ASSIGN_IN_EMPTY_ENV);
             }
         }
+        if (isShareableProfile.profile(value instanceof RShareable)) {
+            RShareable shareable = (RShareable) value;
+            if (isRefCountUpdateable.profile(!shareable.isSharedPermanent())) {
+                shareable.incRefCount();
+            }
+        }
         try {
             env.put(x, value);
         } catch (PutException ex) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java
index aa454c0002d8a29000c90055b468fb1ee87c8f77..eac4155fc6ef9de92adcbeb290f9d4c86f671876 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java
@@ -37,6 +37,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerAsserts;
 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.dsl.Cached;
 import com.oracle.truffle.api.dsl.NodeChild;
@@ -44,6 +45,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.CombineNodeGen.CombineInputCastNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastComplexNodeGen;
@@ -55,7 +57,6 @@ import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.nodes.unary.CastRawNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
-import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
 import com.oracle.truffle.r.nodes.unary.PrecedenceNode;
 import com.oracle.truffle.r.nodes.unary.PrecedenceNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
@@ -86,10 +87,19 @@ public abstract class Combine extends RBuiltinNode {
 
     protected static final int COMBINE_CACHED_LIMIT = PrecedenceNode.NUMBER_OF_PRECEDENCES;
 
+    private static final int MAX_PROFILES = 4;
+
     @Child private PrecedenceNode precedenceNode = PrecedenceNodeGen.create();
     @Child private CombineInputCast inputCast = CombineInputCastNodeGen.create(null);
     @Child private CastToVectorNode castVector;
+
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    private final BranchProfile naBranch = BranchProfile.create();
+    private final NACheck naCheck = NACheck.create();
+    private final ConditionProfile fastNamesMerge = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile isAbstractVectorProfile = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile hasNewNamesProfile = ConditionProfile.createBinaryProfile();
+    @CompilationFinal private final ValueProfile[] argProfiles = new ValueProfile[MAX_PROFILES];
 
     public abstract Object executeCombine(Object value);
 
@@ -107,9 +117,7 @@ public abstract class Combine extends RBuiltinNode {
                     @Cached("args.getSignature()") ArgumentsSignature cachedSignature, //
                     @Cached("precedence( args, cachedSignature.getLength())") int cachedPrecedence, //
                     @Cached("createCast(cachedPrecedence)") CastNode cast, //
-                    @Cached("create()") BranchProfile naBranch, //
                     @Cached("create()") BranchProfile naNameBranch, //
-                    @Cached("create()") NACheck naCheck, //
                     @Cached("create()") NACheck naNameCheck, //
                     @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile) {
         CompilerAsserts.partialEvaluationConstant(cachedSignature);
@@ -121,13 +129,15 @@ public abstract class Combine extends RBuiltinNode {
 
         // perform all the casts
         Object[] elements = new Object[cachedSignature.getLength()];
-        int size = prepareElements(args.getArguments(), cachedSignature, cast, cachedPrecedence, elements);
+        int size = prepareElements(args.getArguments(), cast, cachedPrecedence, elements);
 
         // prepare the names (if there are any)
-        RStringVector namesVector = hasNamesProfile.profile(hasNames(elements)) ? foldNames(naNameBranch, naNameCheck, elements, size) : null;
+        boolean signatureHasNames = signatureHasNames(cachedSignature);
+        CompilerAsserts.partialEvaluationConstant(signatureHasNames);
+        RStringVector namesVector = hasNamesProfile.profile(signatureHasNames || hasNames(elements)) ? foldNames(naNameBranch, naNameCheck, elements, size, cachedSignature) : null;
 
         // get the actual contents of the result
-        RVector result = foldContents(cachedPrecedence, naBranch, naCheck, elements, size, namesVector);
+        RVector result = foldContents(cachedPrecedence, elements, size, namesVector);
 
         RNode.reportWork(this, size);
 
@@ -139,13 +149,17 @@ public abstract class Combine extends RBuiltinNode {
     }
 
     @ExplodeLoop
-    private int prepareElements(Object[] args, ArgumentsSignature signature, CastNode cast, int precedence, Object[] elements) {
-        boolean signatureHasNames = signatureHasNames(signature);
-        CompilerAsserts.partialEvaluationConstant(signatureHasNames);
-
+    private int prepareElements(Object[] args, CastNode cast, int precedence, Object[] elements) {
         int size = 0;
         for (int i = 0; i < elements.length; i++) {
-            Object element = readAndCast(cast, args, signature, i, precedence, signatureHasNames);
+            Object element = readAndCast(cast, args[i], precedence);
+            if (i < argProfiles.length) {
+                if (argProfiles[i] == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    argProfiles[i] = ValueProfile.createClassProfile();
+                }
+                element = argProfiles[i].profile(element);
+            }
             elements[i] = element;
             size += getElementSize(element);
         }
@@ -165,7 +179,11 @@ public abstract class Combine extends RBuiltinNode {
 
     @ExplodeLoop
     private boolean hasNames(Object[] elements) {
-        for (Object element : elements) {
+        for (int i = 0; i < elements.length; i++) {
+            Object element = elements[i];
+            if (i < argProfiles.length) {
+                element = argProfiles[i].profile(element);
+            }
             if (element instanceof RAbstractVector) {
                 RAbstractVector vector = (RAbstractVector) element;
                 if (vector.getNames(attrProfiles) != null) {
@@ -177,22 +195,36 @@ public abstract class Combine extends RBuiltinNode {
     }
 
     @ExplodeLoop
-    private RStringVector foldNames(BranchProfile naNameBranch, NACheck naNameCheck, Object[] elements, int size) {
+    private RStringVector foldNames(BranchProfile naNameBranch, NACheck naNameCheck, Object[] elements, int size, ArgumentsSignature signature) {
         RStringVector result = RDataFactory.createStringVector(new String[size], true);
         result.incRefCount();
         int pos = 0;
-        for (Object element : elements) {
-            pos += processNamesElement(naNameBranch, naNameCheck, result, pos, element);
+        for (int i = 0; i < elements.length; i++) {
+            Object element = elements[i];
+            if (i < argProfiles.length) {
+                element = argProfiles[i].profile(element);
+            }
+            pos += processNamesElement(naNameBranch, naNameCheck, result, pos, element, i, signature);
         }
         return result;
     }
 
-    private int processNamesElement(BranchProfile naNameBranch, NACheck naNameCheck, RStringVector result, int pos, Object element) {
+    private int processNamesElement(BranchProfile naNameBranch, NACheck naNameCheck, RStringVector result, int pos, Object element, int index, ArgumentsSignature signature) {
+        String signatureName = signature.getName(index);
         if (element instanceof RAbstractVector) {
             RAbstractVector v = (RAbstractVector) element;
+            int length = v.getLength();
+
             RStringVector newNames = v.getNames(attrProfiles);
-            if (newNames != null) {
-                for (int i1 = 0; i1 < newNames.getLength(); i1++) {
+            if (signatureName != null && length > 0) {
+                if (fastNamesMerge.profile(length == 1 && newNames == null)) {
+                    newNames = RDataFactory.createStringVector(new String[]{signatureName}, true);
+                } else {
+                    newNames = RDataFactory.createStringVector(mergeNamesSlow(length, signatureName, newNames), true);
+                }
+            }
+            if (hasNewNamesProfile.profile(newNames != null)) {
+                for (int i1 = 0; i1 < length; i1++) {
                     result.transferElementSameType(pos + i1, newNames, i1);
                 }
                 if (!newNames.isComplete()) {
@@ -200,7 +232,7 @@ public abstract class Combine extends RBuiltinNode {
                     result.setComplete(false);
                 }
             } else {
-                for (int i1 = 0; i1 < v.getLength(); i1++) {
+                for (int i1 = 0; i1 < length; i1++) {
                     result.updateDataAt(pos + i1, RRuntime.NAMES_ATTR_EMPTY_VALUE, naNameCheck);
                 }
             }
@@ -209,23 +241,24 @@ public abstract class Combine extends RBuiltinNode {
             // nothing to do - NULL elements are skipped
             return 0;
         } else {
-            result.updateDataAt(pos, RRuntime.NAMES_ATTR_EMPTY_VALUE, naNameCheck);
+            String name = signatureName != null ? signatureName : RRuntime.NAMES_ATTR_EMPTY_VALUE;
+            result.updateDataAt(pos, name, naNameCheck);
             return 1;
         }
     }
 
     @ExplodeLoop
-    private static RVector foldContents(int cachedPrecedence, BranchProfile naBranch, NACheck naCheck, Object[] elements, int size, RStringVector namesVector) {
+    private RVector foldContents(int cachedPrecedence, Object[] elements, int size, RStringVector namesVector) {
         RVector result = createResultVector(cachedPrecedence, size, namesVector);
         int pos = 0;
         for (Object element : elements) {
-            pos += processContentElement(naBranch, naCheck, result, pos, element);
+            pos += processContentElement(result, pos, element);
         }
         return result;
     }
 
-    private static int processContentElement(BranchProfile naBranch, NACheck naCheck, RVector result, int pos, Object element) {
-        if (element instanceof RAbstractVector) {
+    private int processContentElement(RVector result, int pos, Object element) {
+        if (isAbstractVectorProfile.profile(element instanceof RAbstractVector)) {
             RAbstractVector v = (RAbstractVector) element;
             for (int i = 0; i < v.getLength(); i++) {
                 result.transferElementSameType(pos + i, v, i);
@@ -254,12 +287,10 @@ public abstract class Combine extends RBuiltinNode {
     protected Object combine(RArgsValuesAndNames args, //
                     @Cached("precedence(args, args.getLength())") int cachedPrecedence, //
                     @Cached("createCast(cachedPrecedence)") CastNode cast, //
-                    @Cached("create()") BranchProfile naBranch, //
                     @Cached("create()") BranchProfile naNameBranch, //
-                    @Cached("create()") NACheck naCheck, //
                     @Cached("create()") NACheck naNameCheck, //
                     @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile) {
-        return combineCached(args, args.getSignature(), cachedPrecedence, cast, naBranch, naNameBranch, naCheck, naNameCheck, hasNamesProfile);
+        return combineCached(args, args.getSignature(), cachedPrecedence, cast, naNameBranch, naNameCheck, hasNamesProfile);
     }
 
     @Specialization(guards = "!isArguments(args)")
@@ -267,39 +298,11 @@ public abstract class Combine extends RBuiltinNode {
         return combine.executeCombine(new RArgsValuesAndNames(new Object[]{args}, EMPTY_SIGNATURE));
     }
 
-    private Object readAndCast(CastNode castNode, Object[] values, ArgumentsSignature signature, int index, int precedence, boolean hasNames) {
-        Object value = inputCast.execute(values[index]);
-        if (hasNames) {
-            value = namesMerge(castVector(value), signature.getName(index));
-        }
+    private Object readAndCast(CastNode castNode, Object arg, int precedence) {
+        Object value = inputCast.execute(arg);
         return (precedence == EXPRESSION_PRECEDENCE && value instanceof RLanguage) ? value : castNode.execute(value);
     }
 
-    private RAbstractVector namesMerge(RAbstractVector vector, String name) {
-        RStringVector orgNamesObject = vector.getNames(attrProfiles);
-        if (name == null) {
-            return vector;
-        }
-        if (vector.getLength() == 0) {
-            return vector;
-        }
-        return mergeNamesSlow(vector, name, orgNamesObject);
-    }
-
-    private RAbstractVector castVector(Object value) {
-        if (castVector == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            castVector = insert(CastToVectorNodeGen.create(false));
-        }
-        RVector resultVector = ((RAbstractVector) castVector.execute(value)).materialize();
-        // need to copy if vector is shared in case the same variable is used in combine, e.g. :
-        // x <- 1:2 ; names(x) <- c("A",NA) ; c(x,test=x)
-        if (resultVector.isShared()) {
-            resultVector = resultVector.copy();
-        }
-        return resultVector;
-    }
-
     protected int precedence(RArgsValuesAndNames args) {
         int precedence = NO_PRECEDENCE;
         Object[] array = args.getArguments();
@@ -376,37 +379,30 @@ public abstract class Combine extends RBuiltinNode {
     }
 
     @TruffleBoundary
-    private static RAbstractVector mergeNamesSlow(RAbstractVector vector, String name, RStringVector orgNames) {
-        /*
-         * TODO (chumer) we should reuse some node for this to concat a RStringVector with a String.
-         */
-        RVector v = vector.materialize();
-        RStringVector newNames;
+    private static String[] mergeNamesSlow(int length, String name, RStringVector orgNames) {
+        String[] names = new String[length];
         if (orgNames == null) {
             assert (name != null);
             assert (!name.equals(RRuntime.NAMES_ATTR_EMPTY_VALUE));
-            if (v.getLength() == 1) {
+            if (length == 1) {
                 // single value - just use the name
-                newNames = RDataFactory.createStringVector(new String[]{name}, true);
+                names[0] = name;
             } else {
                 // multiple values - prepend name to the index of a given value
-                String[] names = new String[v.getLength()];
                 for (int i = 0; i < names.length; i++) {
                     names[i] = name + (i + 1);
                 }
-                newNames = RDataFactory.createStringVector(names, true);
+                return names;
             }
         } else {
-            if (vector.getLength() == 1) {
-                // single value
-                // prepend name to the original name
+            if (length == 1) {
+                // single value - prepend name to the original name
                 assert (!name.equals(RRuntime.NAMES_ATTR_EMPTY_VALUE));
                 String orgName = orgNames.getDataAt(0);
-                newNames = RDataFactory.createStringVector(new String[]{orgName.equals(RRuntime.NAMES_ATTR_EMPTY_VALUE) ? name : name + "." + orgName}, true);
+                names[0] = orgName.equals(RRuntime.NAMES_ATTR_EMPTY_VALUE) ? name : name + "." + orgName;
             } else {
                 // multiple values - prepend name to the index of a given value or to the original
                 // name
-                String[] names = new String[v.getLength()];
                 for (int i = 0; i < names.length; i++) {
                     if (name == null) {
                         names[i] = orgNames.getDataAt(i);
@@ -416,11 +412,9 @@ public abstract class Combine extends RBuiltinNode {
                         names[i] = orgName.equals(RRuntime.NAMES_ATTR_EMPTY_VALUE) ? name + (i + 1) : name + "." + orgName;
                     }
                 }
-                newNames = RDataFactory.createStringVector(names, true);
             }
         }
-        v.setNames(newNames);
-        return v;
+        return names;
     }
 
     @NodeChild
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LogFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LogFunctions.java
index e5e14eebc3563ef6b13e184a9e06a5cc0a02d9dd..5bf1d9ac7265203e81e5ecf582c584a5c72d04de 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LogFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LogFunctions.java
@@ -22,8 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.complexValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
 import static com.oracle.truffle.r.runtime.RDispatch.MATH_GROUP_GENERIC;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
@@ -43,13 +43,13 @@ import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.ops.na.NACheck;
+import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
 public class LogFunctions {
     @RBuiltin(name = "log", kind = PRIMITIVE, parameterNames = {"x", "base"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Log extends RBuiltinNode {
 
-        private final NACheck naCheck = NACheck.create();
+        private final NAProfile naProfile = NAProfile.create();
         private final BranchProfile nanProfile = BranchProfile.create();
 
         @Override
@@ -79,7 +79,7 @@ public class LogFunctions {
             for (int i = 0; i < vector.getLength(); i++) {
                 int inputValue = vector.getDataAt(i);
                 double result = RRuntime.DOUBLE_NA;
-                if (!RRuntime.isNA(inputValue)) {
+                if (!naProfile.isNA(inputValue)) {
                     result = logb(inputValue, base);
                 }
                 resultVector[i] = result;
@@ -101,8 +101,7 @@ public class LogFunctions {
         }
 
         private double logb(double x, double base) {
-            naCheck.enable(true);
-            if (naCheck.check(base)) {
+            if (naProfile.isNA(base)) {
                 return RRuntime.DOUBLE_NA;
             }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java
index ba423dae3cbafc423b481033a8fb9b51cba8348a..fc26c8e3365e813b31cdb70fa6f5e27f2844690e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.size;
 import static com.oracle.truffle.r.runtime.RDispatch.INTERNAL_GENERIC;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
@@ -30,9 +32,12 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import java.util.Arrays;
 import java.util.function.Function;
 
+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.attributes.InitAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.PutAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -142,39 +147,44 @@ public abstract class Repeat extends RBuiltinNode {
     }
 
     @Specialization(guards = {"each > 1", "hasNames(x)"})
-    protected RAbstractVector repEachNames(RAbstractVector x, RAbstractIntVector times, int lengthOut, int each) {
+    protected RAbstractVector repEachNames(RAbstractVector x, RAbstractIntVector times, int lengthOut, int each,
+                    @Cached("create()") InitAttributesNode initAttributes,
+                    @Cached("createNames()") PutAttributeNode putNames) {
         if (times.getLength() > 1) {
             errorBranch.enter();
             throw invalidTimes();
         }
         RAbstractVector input = handleEach(x, each);
         RStringVector names = (RStringVector) handleEach(x.getNames(attrProfiles), each);
+        RVector r;
         if (lengthOutOrTimes.profile(!RRuntime.isNA(lengthOut))) {
             names = (RStringVector) handleLengthOut(names, lengthOut, false);
-            RVector r = handleLengthOut(input, lengthOut, false);
-            r.setNames(names);
-            return r;
+            r = handleLengthOut(input, lengthOut, false);
         } else {
             names = (RStringVector) handleTimes(names, times, false);
-            RVector r = handleTimes(input, times, false);
-            r.setNames(names);
-            return r;
+            r = handleTimes(input, times, false);
         }
+        putNames.execute(initAttributes.execute(r), names);
+        r.setInternalNames(names);
+        return r;
     }
 
     @Specialization(guards = {"each <= 1", "hasNames(x)"})
-    protected RAbstractVector repNoEachNames(RAbstractVector x, RAbstractIntVector times, int lengthOut, @SuppressWarnings("unused") int each) {
+    protected RAbstractVector repNoEachNames(RAbstractVector x, RAbstractIntVector times, int lengthOut, @SuppressWarnings("unused") int each,
+                    @Cached("create()") InitAttributesNode initAttributes,
+                    @Cached("createNames()") PutAttributeNode putNames) {
+        RStringVector names;
+        RVector r;
         if (lengthOutOrTimes.profile(!RRuntime.isNA(lengthOut))) {
-            RStringVector names = (RStringVector) handleLengthOut(x.getNames(attrProfiles), lengthOut, true);
-            RVector r = handleLengthOut(x, lengthOut, true);
-            r.setNames(names);
-            return r;
+            names = (RStringVector) handleLengthOut(x.getNames(attrProfiles), lengthOut, true);
+            r = handleLengthOut(x, lengthOut, true);
         } else {
-            RStringVector names = (RStringVector) handleTimes(x.getNames(attrProfiles), times, true);
-            RVector r = handleTimes(x, times, true);
-            r.setNames(names);
-            return r;
+            names = (RStringVector) handleTimes(x.getNames(attrProfiles), times, true);
+            r = handleTimes(x, times, true);
         }
+        putNames.execute(initAttributes.execute(r), names);
+        r.setInternalNames(names);
+        return r;
     }
 
     /**
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
index 0cac8ae1caf25989e975c4cf4876315e0741de98..c2d61bfb227fca501542427026c13c971b3838d3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
@@ -31,12 +31,14 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.runtime.JumpToTopLevelException;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
+import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.RSrcref;
 import com.oracle.truffle.r.runtime.ReturnException;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.ConsoleHandler;
+import com.oracle.truffle.r.runtime.context.Engine.IncompleteSourceException;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -77,7 +79,6 @@ public abstract class BrowserInteractNode extends RNode {
         ConsoleHandler ch = RContext.getInstance().getConsoleHandler();
         BrowserState browserState = RContext.getInstance().stateInstrumentation.getBrowserState();
         String savedPrompt = ch.getPrompt();
-        ch.setPrompt(browserPrompt(RArguments.getDepth(frame)));
         RFunction callerFunction = RArguments.getFunction(frame);
         // we may be at top level where there is not caller
         boolean callerIsDebugged = callerFunction == null ? false : DebugHandling.isDebugged(callerFunction);
@@ -90,6 +91,7 @@ public abstract class BrowserInteractNode extends RNode {
         try {
             browserState.setInBrowser(browserCaller);
             LW: while (true) {
+                ch.setPrompt(browserPrompt(RArguments.getDepth(frame)));
                 String input = ch.readLine();
                 if (input != null) {
                     input = input.trim();
@@ -144,15 +146,27 @@ public abstract class BrowserInteractNode extends RNode {
                     }
 
                     default:
-                        try {
-                            RContext.getEngine().parseAndEval(RSource.fromTextInternal(input, RSource.Internal.BROWSER_INPUT), mFrame, true);
-                        } catch (ReturnException e) {
-                            exitMode = NEXT;
-                            break LW;
-                        } catch (ParseException e) {
-                            throw e.throwAsRError();
+                        StringBuffer sb = new StringBuffer(input);
+                        while (true) {
+                            try {
+                                RContext.getEngine().parseAndEval(RSource.fromTextInternal(sb.toString(), RSource.Internal.BROWSER_INPUT), mFrame, true);
+                            } catch (IncompleteSourceException e) {
+                                // read another line of input
+                                ch.setPrompt("+ ");
+                                sb.append(ch.readLine());
+                                // The only continuation in the while loop
+                                continue;
+                            } catch (ParseException e) {
+                                e.report(ch);
+                                continue LW;
+                            } catch (RError e) {
+                                continue LW;
+                            } catch (ReturnException e) {
+                                exitMode = NEXT;
+                                break LW;
+                            }
+                            continue LW;
                         }
-                        break;
                 }
             }
         } finally {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
index 02dbbaf855144c918ced61076866063bb38648c5..15d687d6799438879e7d85a67c0a1d1ff06a3f96 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
@@ -138,10 +138,7 @@ public final class AccessArgumentNode extends RNode {
 
     private Object doArgument(VirtualFrame frame, Object arg) {
         if (hasDefaultArg) {
-            if (isMissingProfile.profile(arg == RMissing.instance)) {
-                return doArgumentInternal(frame);
-            }
-            if (isEmptyProfile.profile(arg == REmpty.instance)) {
+            if (isMissingProfile.profile(arg == RMissing.instance) || isEmptyProfile.profile(arg == REmpty.instance)) {
                 return doArgumentInternal(frame);
             }
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
index b880d98d8649d7eb23de404bc596058810cec6e5..98ed65cc09722ad911098ba83ea80dae38fd6c05 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/BaseWriteVariableNode.java
@@ -48,10 +48,11 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  */
 abstract class BaseWriteVariableNode extends WriteVariableNode {
 
+    private final ConditionProfile isObjectProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile isCurrentProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile isShareableProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile isSharedProfile = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile isRefCountUpdateable = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile isSharedPermanent = ConditionProfile.createBinaryProfile();
 
     private final BranchProfile initialSetKindProfile = BranchProfile.create();
 
@@ -89,18 +90,18 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
 
                 // this comparison does not work consistently for boxing objects, so it's important
                 // to do the RShareable check first.
-                if (isCurrentProfile.profile(isCurrentValue(frame, frameSlot, value))) {
+                if (isCurrentValue(frame, frameSlot, value)) {
                     return value;
                 }
                 RShareable rShareable = (RShareable) shareableProfile.profile(value);
                 if (mode == Mode.COPY) {
                     return rShareable.copy();
                 } else {
-                    if (isRefCountUpdateable.profile(!rShareable.isSharedPermanent())) {
+                    if (!isSharedPermanent.profile(rShareable.isSharedPermanent())) {
                         if (isSuper) {
                             // if non-local assignment, increment conservatively
                             rShareable.incRefCount();
-                        } else if (isSharedProfile.profile(!rShareable.isShared())) {
+                        } else if (!isSharedProfile.profile(rShareable.isShared())) {
                             // don't increment if already shared - will not get "unshared" until
                             // this function exits anyway
                             rShareable.incRefCount();
@@ -112,9 +113,9 @@ abstract class BaseWriteVariableNode extends WriteVariableNode {
         return value;
     }
 
-    private static boolean isCurrentValue(Frame frame, FrameSlot frameSlot, Object value) {
+    private boolean isCurrentValue(Frame frame, FrameSlot frameSlot, Object value) {
         try {
-            return frame.isObject(frameSlot) && frame.getObject(frameSlot) == value;
+            return isObjectProfile.profile(frame.isObject(frameSlot)) && isCurrentProfile.profile(frame.getObject(frameSlot) == value);
         } catch (FrameSlotTypeException ex) {
             throw RInternalError.shouldNotReachHere();
         }
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 6a304222baf4f22bbbf5296aebfe8f0c4d54ef28..8fc53aa127bc467060511104f8de4db53746de28 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
@@ -28,7 +28,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 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.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.access.vector.CachedExtractVectorNodeFactory.SetNamesNodeGen;
@@ -274,6 +273,8 @@ final class CachedExtractVectorNode extends CachedVectorNode {
     private final ConditionProfile dimNamesNull = ConditionProfile.createBinaryProfile();
     private final ValueProfile foundDimNamesProfile = ValueProfile.createClassProfile();
     private final ConditionProfile selectPositionsProfile = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile originalDimNamesPRofile = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile foundNamesProfile = ConditionProfile.createBinaryProfile();
 
     @ExplodeLoop
     private void applyDimensions(RAbstractContainer originalTarget, RVector extractedTarget, int extractedTargetLength, PositionProfile[] positionProfile, Object[] positions) {
@@ -311,9 +312,9 @@ final class CachedExtractVectorNode extends CachedVectorNode {
             if (newDimNames != null) {
                 extractedTarget.setDimNames(RDataFactory.createList(newDimNames));
             }
-        } else if (newDimNames != null && originalDimNames.getLength() > 0) {
+        } else if (newDimNames != null && originalDimNamesPRofile.profile(originalDimNames.getLength() > 0)) {
             RAbstractStringVector foundNames = translateDimNamesToNames(positionProfile, originalDimNames, extractedTargetLength, positions);
-            if (foundNames != null) {
+            if (foundNamesProfile.profile(foundNames != null)) {
                 foundNames = foundDimNamesProfile.profile(foundNames);
                 if (foundNames.getLength() > 0) {
                     metadataApplied.enter();
@@ -323,7 +324,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         }
     }
 
-    private final BranchProfile droppedDimensionProfile = BranchProfile.create();
+    private final ConditionProfile droppedDimensionProfile = ConditionProfile.createBinaryProfile();
 
     @ExplodeLoop
     private int countDimensions(PositionProfile[] boundsProfile) {
@@ -331,8 +332,7 @@ final class CachedExtractVectorNode extends CachedVectorNode {
             int dimCount = numberOfDimensions;
             for (int i = 0; i < numberOfDimensions; i++) {
                 int selectedPositionsCount = boundsProfile[i].selectedPositionsCount;
-                if (selectedPositionsCount == 1) {
-                    droppedDimensionProfile.enter();
+                if (droppedDimensionProfile.profile(selectedPositionsCount == 1)) {
                     dimCount--;
                 }
             }
@@ -342,6 +342,8 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         }
     }
 
+    private final ConditionProfile srcNamesProfile = ConditionProfile.createBinaryProfile();
+    private final ValueProfile srcNamesValueProfile = ValueProfile.createClassProfile();
     private final ConditionProfile newNamesProfile = ConditionProfile.createBinaryProfile();
 
     @ExplodeLoop
@@ -353,8 +355,8 @@ final class CachedExtractVectorNode extends CachedVectorNode {
                 continue;
             }
 
-            Object srcNames = originalDimNames.getDataAt(currentDimIndex);
-            if (srcNames != RNull.instance) {
+            Object srcNames = srcNamesValueProfile.profile(originalDimNames.getDataAt(currentDimIndex));
+            if (srcNamesProfile.profile(srcNames != RNull.instance)) {
                 Object position = positions[currentDimIndex];
 
                 Object newNames = extractNames((RAbstractStringVector) RRuntime.asAbstractVector(srcNames), new Object[]{position}, new PositionProfile[]{profile}, currentDimIndex,
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractListElement.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractListElement.java
index 3a292f4988bbddeb41e4e53d532c29db1c538625..15e5733176ca6cb894f83abed38731c4bff57c76 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractListElement.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractListElement.java
@@ -23,10 +23,10 @@
 package com.oracle.truffle.r.nodes.access.vector;
 
 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.nodes.Node;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.access.vector.ExtractListElementNodeGen.UpdateStateOfListElementNodeGen;
 import com.oracle.truffle.r.runtime.data.RListBase;
 import com.oracle.truffle.r.runtime.data.RShareable;
@@ -84,10 +84,12 @@ public abstract class ExtractListElement extends Node {
         }
 
         @Specialization
-        protected void doShareableValues(RListBase owner, RShareable value, //
-                        @Cached("createBinaryProfile()") ConditionProfile sharedValue, //
+        protected void doShareableValues(RListBase owner, RShareable value,
+                        @Cached("createClassProfile()") ValueProfile valueProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile sharedValue,
                         @Cached("createBinaryProfile()") ConditionProfile temporaryOwner) {
-            if (sharedValue.profile(value.isShared())) {
+            RShareable profiledValue = valueProfile.profile(value);
+            if (sharedValue.profile(profiledValue.isShared())) {
                 // it is already shared, not need to do anything
                 return;
             }
@@ -99,21 +101,25 @@ public abstract class ExtractListElement extends Node {
                 return;
             }
 
-            if (value.isTemporary()) {
+            if (profiledValue.isTemporary()) {
                 // make it at least non-shared (parent list must be also at least non-shared)
-                value.incRefCount();
+                profiledValue.incRefCount();
             }
             if (owner.isShared()) {
                 // owner is shared, make the value shared too
-                value.incRefCount();
+                profiledValue.incRefCount();
             }
         }
 
-        @Fallback
+        @Specialization(guards = "isFallback(owner, value)")
         protected void doFallback(Object owner, Object value) {
             assert !(value instanceof RShareable && owner instanceof RAbstractVector && !(owner instanceof RListBase)) : "RShareables can only live inside lists and no other vectors.";
             // nop: either value is not RShareable, or the owner is "list" like structure with
             // reference semantics (e.g. REnvironment)
         }
+
+        protected final boolean isFallback(Object owner, Object value) {
+            return !(value instanceof RShareable) || !(owner instanceof RListBase);
+        }
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java
index c83e56efcc76dd8284a1139f085bf9acf6bece68..3ab96e71be61213a05440038eba694b6d25bac6b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionsCheckNode.java
@@ -44,6 +44,7 @@ final class PositionsCheckNode extends Node {
     private final VectorLengthProfile selectedPositionsCountProfile = VectorLengthProfile.create();
     private final VectorLengthProfile maxOutOfBoundsProfile = VectorLengthProfile.create();
     private final ConditionProfile containsNAProfile = ConditionProfile.createBinaryProfile();
+    private final BranchProfile unsupportedProfile = BranchProfile.create();
     private final boolean replace;
     private final int positionsLength;
 
@@ -64,10 +65,12 @@ final class PositionsCheckNode extends Node {
     @ExplodeLoop
     public boolean isSupported(Object[] positions) {
         if (positionsCheck.length != positions.length) {
+            unsupportedProfile.enter();
             return false;
         }
         for (int i = 0; i < positionsCheck.length; i++) {
             if (!positionsCheck[i].isSupported(positions[i])) {
+                unsupportedProfile.enter();
                 return false;
             }
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyAttributesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyAttributesNode.java
index bfca64f94be16909d1fb4165563a0acb693e9a32..79c34d928574bbcfd5679d9990cc84cbb7e5746c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyAttributesNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyAttributesNode.java
@@ -107,19 +107,20 @@ public abstract class CopyAttributesNode extends RBaseNode {
 
     @Specialization(guards = {"leftLength == rightLength", "containsMetadata(left, attrLeftProfiles) || containsMetadata(right, attrRightProfiles)"})
     protected RAbstractVector copySameLength(RAbstractVector target, RAbstractVector left, @SuppressWarnings("unused") int leftLength, RAbstractVector right,
-                    @SuppressWarnings("unused") int rightLength, //
-                    @Cached("create()") CopyOfRegAttributesNode copyOfRegLeft, //
-                    @Cached("create()") CopyOfRegAttributesNode copyOfRegRight, //
-                    @Cached("createDim()") RemoveAttributeNode removeDim, //
-                    @Cached("createDimNames()") RemoveAttributeNode removeDimNames, //
-                    @Cached("create()") InitAttributesNode initAttributes, //
-                    @Cached("createNames()") PutAttributeNode putNames, //
-                    @Cached("createDim()") PutAttributeNode putDim, //
-                    @Cached("create()") BranchProfile leftHasDimensions, //
-                    @Cached("create()") BranchProfile rightHasDimensions, //
-                    @Cached("create()") BranchProfile noDimensions, //
-                    @Cached("createBinaryProfile()") ConditionProfile hasNamesLeft, //
-                    @Cached("createBinaryProfile()") ConditionProfile hasNamesRight, //
+                    @SuppressWarnings("unused") int rightLength,
+                    @Cached("create()") CopyOfRegAttributesNode copyOfRegLeft,
+                    @Cached("create()") CopyOfRegAttributesNode copyOfRegRight,
+                    @Cached("createBinaryProfile()") ConditionProfile hasAttributes,
+                    @Cached("createDim()") RemoveAttributeNode removeDim,
+                    @Cached("createDimNames()") RemoveAttributeNode removeDimNames,
+                    @Cached("create()") InitAttributesNode initAttributes,
+                    @Cached("createNames()") PutAttributeNode putNames,
+                    @Cached("createDim()") PutAttributeNode putDim,
+                    @Cached("create()") BranchProfile leftHasDimensions,
+                    @Cached("create()") BranchProfile rightHasDimensions,
+                    @Cached("create()") BranchProfile noDimensions,
+                    @Cached("createBinaryProfile()") ConditionProfile hasNamesLeft,
+                    @Cached("createBinaryProfile()") ConditionProfile hasNamesRight,
                     @Cached("createBinaryProfile()") ConditionProfile hasDimNames) {
         if (LOG) {
             log("copyAttributes: ==");
@@ -141,7 +142,7 @@ public abstract class CopyAttributesNode extends RBaseNode {
             if (newDimensions == null) {
                 noDimensions.enter();
                 RAttributes attributes = result.getAttributes();
-                if (attributes != null) {
+                if (hasAttributes.profile(attributes != null)) {
                     removeDim.execute(attributes);
                     removeDimNames.execute(attributes);
                     result.setInternalDimNames(null);
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 b066be22af2ab3675b97b82ef6d48ff6029d2223..de60064a28a74110e61b45178f375df86982aeb0 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
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.nodes.attributes;
 
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RAttributes;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
@@ -37,6 +38,8 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
  */
 public abstract class CopyOfRegAttributesNode extends RBaseNode {
 
+    private final ConditionProfile sizeOneProfile = ConditionProfile.createBinaryProfile();
+
     public abstract void execute(RAbstractVector source, RVector target);
 
     public static CopyOfRegAttributesNode create() {
@@ -60,9 +63,9 @@ public abstract class CopyOfRegAttributesNode extends RBaseNode {
         // nothing to do
     }
 
-    protected static final boolean onlyDimAttribute(RAbstractVector source) {
+    protected final boolean onlyDimAttribute(RAbstractVector source) {
         RAttributes attributes = source.getAttributes();
-        return attributes != null && attributes.size() == 1 && attributes.getNameAtIndex(0) == RRuntime.DIM_ATTR_KEY;
+        return attributes != null && sizeOneProfile.profile(attributes.size() == 1) && attributes.getNameAtIndex(0) == RRuntime.DIM_ATTR_KEY;
     }
 
     @SuppressWarnings("unused")
@@ -71,9 +74,9 @@ public abstract class CopyOfRegAttributesNode extends RBaseNode {
         // nothing to do
     }
 
-    protected static final boolean onlyNamesAttribute(RAbstractVector source) {
+    protected final boolean onlyNamesAttribute(RAbstractVector source) {
         RAttributes attributes = source.getAttributes();
-        return attributes != null && attributes.size() == 1 && attributes.getNameAtIndex(0) == RRuntime.NAMES_ATTR_KEY;
+        return attributes != null && sizeOneProfile.profile(attributes.size() == 1) && attributes.getNameAtIndex(0) == RRuntime.NAMES_ATTR_KEY;
     }
 
     @SuppressWarnings("unused")
@@ -82,9 +85,9 @@ public abstract class CopyOfRegAttributesNode extends RBaseNode {
         // nothing to do
     }
 
-    protected static final boolean onlyClassAttribute(RAbstractVector source) {
+    protected final boolean onlyClassAttribute(RAbstractVector source) {
         RAttributes attributes = source.getAttributes();
-        return attributes != null && attributes.size() == 1 && attributes.getNameAtIndex(0) == RRuntime.CLASS_ATTR_KEY;
+        return attributes != null && sizeOneProfile.profile(attributes.size() == 1) && attributes.getNameAtIndex(0) == RRuntime.CLASS_ATTR_KEY;
     }
 
     @Specialization(guards = "onlyClassAttribute(source)")
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 ee4d814f981084f26fe209474fdbb1a721e4da9b..b26a5aca8397d77862132affd87663c5c274bbe6 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
@@ -839,7 +839,7 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
             FormalArguments formals = root.getFormalArguments();
             if (root instanceof RBuiltinRootNode) {
                 RBuiltinRootNode builtinRoot = (RBuiltinRootNode) root;
-                return new BuiltinCallNode(RBuiltinNode.inline(builtinRoot.getBuiltin(), null), builtinRoot.getBuiltin(), formals, originalCall);
+                return new BuiltinCallNode(RBuiltinNode.inline(builtinRoot.getBuiltin(), null), builtinRoot.getBuiltin(), formals, originalCall, explicitArgs);
             } else {
                 return new DispatchedCallNode(cachedTarget, originalCall);
             }
@@ -928,36 +928,54 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
     private static final class BuiltinCallNode extends LeafCallNode {
 
         @Child private RBuiltinNode builtin;
-        @Child private PromiseCheckHelperNode promiseHelper;
+        @Child private PromiseCheckHelperNode varArgsPromiseHelper;
+        @Children private final PromiseHelperNode[] promiseHelpers;
         @Children private final CastNode[] casts;
         @Child private SetVisibilityNode visibility = SetVisibilityNode.create();
 
-        private final BranchProfile emptyProfile = BranchProfile.create();
-        private final BranchProfile varArgsProfile = BranchProfile.create();
-        private final ConditionProfile wrapProfile = ConditionProfile.createBinaryProfile();
+        // not using profiles to save overhead
+        @CompilationFinal private final boolean[] argEmptySeen;
+        @CompilationFinal private final boolean[] varArgSeen;
+        @CompilationFinal private final boolean[] nonWrapSeen;
+        @CompilationFinal private final boolean[] wrapSeen;
+
         private final FormalArguments formals;
         private final RBuiltinDescriptor builtinDescriptor;
+        private final boolean explicitArgs;
 
-        BuiltinCallNode(RBuiltinNode builtin, RBuiltinDescriptor builtinDescriptor, FormalArguments formalArguments, RCallNode originalCall) {
+        BuiltinCallNode(RBuiltinNode builtin, RBuiltinDescriptor builtinDescriptor, FormalArguments formalArguments, RCallNode originalCall, boolean explicitArgs) {
             super(originalCall);
             this.builtin = builtin;
             this.builtinDescriptor = builtinDescriptor;
+            this.explicitArgs = explicitArgs;
             this.casts = builtin.getCasts();
             this.formals = formalArguments;
+            promiseHelpers = new PromiseHelperNode[formals.getLength()];
+            argEmptySeen = new boolean[formals.getLength()];
+            varArgSeen = new boolean[formals.getLength()];
+            nonWrapSeen = new boolean[formals.getLength()];
+            wrapSeen = new boolean[formals.getLength()];
         }
 
         @ExplodeLoop
         public Object[] castArguments(VirtualFrame frame, Object[] args) {
             int argCount = formals.getLength();
+            int varArgIndex = formals.getSignature().getVarArgIndex();
             Object[] result = new Object[argCount];
             for (int i = 0; i < argCount; i++) {
                 Object arg = args[i];
-                if (arg == REmpty.instance) {
-                    emptyProfile.enter();
+                if (explicitArgs && arg == REmpty.instance) {
+                    if (!argEmptySeen[i]) {
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                        argEmptySeen[i] = true;
+                    }
                     arg = formals.getInternalDefaultArgumentAt(i);
                 }
-                if (arg instanceof RArgsValuesAndNames) {
-                    varArgsProfile.enter();
+                if (varArgIndex == i && arg instanceof RArgsValuesAndNames) {
+                    if (!varArgSeen[i]) {
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                        varArgSeen[i] = true;
+                    }
                     RArgsValuesAndNames varArgs = (RArgsValuesAndNames) arg;
                     if (builtinDescriptor.evaluatesArg(i)) {
                         forcePromises(frame, varArgs);
@@ -967,12 +985,11 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
                 } else {
                     if (builtinDescriptor.evaluatesArg(i)) {
                         if (arg instanceof RPromise) {
-                            if (promiseHelper == null) {
+                            if (promiseHelpers[i] == null) {
                                 CompilerDirectives.transferToInterpreterAndInvalidate();
-                                promiseHelper = insert(new PromiseCheckHelperNode());
+                                promiseHelpers[i] = insert(new PromiseHelperNode());
                             }
-                            arg = promiseHelper.checkEvaluate(frame, arg);
-
+                            arg = promiseHelpers[i].evaluate(frame, (RPromise) arg);
                         }
                         if (i < casts.length && casts[i] != null) {
                             assert builtinDescriptor.evaluatesArg(i);
@@ -980,7 +997,16 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
                         }
                     } else {
                         assert casts.length <= i || casts[i] == null : "no casts allowed on non-evaluated arguments";
-                        if (wrapProfile.profile(!(arg instanceof RPromise || arg instanceof RMissing))) {
+                        if (arg instanceof RPromise || arg instanceof RMissing) {
+                            if (!nonWrapSeen[i]) {
+                                CompilerDirectives.transferToInterpreterAndInvalidate();
+                                nonWrapSeen[i] = true;
+                            }
+                        } else {
+                            if (!wrapSeen[i]) {
+                                CompilerDirectives.transferToInterpreterAndInvalidate();
+                                wrapSeen[i] = true;
+                            }
                             arg = createPromise(arg);
                         }
                     }
@@ -993,11 +1019,11 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         private void forcePromises(VirtualFrame frame, RArgsValuesAndNames varArgs) {
             Object[] array = varArgs.getArguments();
             for (int i = 0; i < array.length; i++) {
-                if (promiseHelper == null) {
+                if (varArgsPromiseHelper == null) {
                     CompilerDirectives.transferToInterpreterAndInvalidate();
-                    promiseHelper = insert(new PromiseCheckHelperNode());
+                    varArgsPromiseHelper = insert(new PromiseCheckHelperNode());
                 }
-                array[i] = promiseHelper.checkEvaluate(frame, array[i]);
+                array[i] = varArgsPromiseHelper.checkEvaluate(frame, array[i]);
             }
         }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java
index 9cb4f0d2939053290bf23227eea5ed16281774fb..90437a0a43800871bbf1f9b5a22fe67c73bbe10d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java
@@ -22,6 +22,7 @@
  */
 package com.oracle.truffle.r.nodes.primitive;
 
+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;
@@ -73,7 +74,7 @@ public final class BinaryMapNode extends RBaseNode {
     private final ConditionProfile maxLengthProfile;
     private final ConditionProfile leftIsNAProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile rightIsNAProfile = ConditionProfile.createBinaryProfile();
-    private final BranchProfile seenEmpty = BranchProfile.create();
+    private final ConditionProfile seenEmpty = ConditionProfile.createBinaryProfile();
     private final ConditionProfile shareLeft;
     private final ConditionProfile shareRight;
     private final RType argumentType;
@@ -229,16 +230,16 @@ public final class BinaryMapNode extends RBaseNode {
     private Object applyVectorized(RAbstractVector left, RAbstractVector leftCast, int leftLength, RAbstractVector right, RAbstractVector rightCast, int rightLength) {
         if (mayContainMetadata && (dimensionsProfile.profile(left.hasDimensions() && right.hasDimensions()))) {
             if (differentDimensions(left, right)) {
+                CompilerDirectives.transferToInterpreter();
                 throw RError.error(this, RError.Message.NON_CONFORMABLE_ARRAYS);
             }
         }
 
-        if (leftLength == 0 || rightLength == 0) {
+        if (seenEmpty.profile(leftLength == 0 || rightLength == 0)) {
             /*
              * It is safe to skip attribute handling here as they are never copied if length is 0 of
              * either side. Note that dimension check still needs to be performed.
              */
-            seenEmpty.enter();
             return resultType.getEmpty();
         }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java
index 1c779089684ab3f4405a4289a2279be02d557295..98bfef344407cdf0d8a635dadf0742f77f547966 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java
@@ -175,8 +175,12 @@ public final class UnaryMapNode extends RBaseNode {
         return result;
     }
 
+    private final ConditionProfile hasDimensionsProfile = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile hasNamesProfile = ConditionProfile.createBinaryProfile();
+
     private boolean containsMetadata(RAbstractVector vector) {
-        return vector instanceof RVector && (vector.hasDimensions() || vector.getAttributes() != null || vector.getNames(attrProfiles) != null || vector.getDimNames(attrProfiles) != null);
+        return vector instanceof RVector && (hasDimensionsProfile.profile(vector.hasDimensions()) || vector.getAttributes() != null || hasNamesProfile.profile(vector.getNames(attrProfiles) != null) ||
+                        vector.getDimNames(attrProfiles) != null);
     }
 
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java
index 9132ef16228f3fa65e5b00ad550e40a32032a94e..7c3def09aa07d436b25b8cdbfdc7a4c5f13bff1a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java
@@ -46,8 +46,6 @@ public final class ArgumentsSignature implements Iterable<String> {
     public static final String VARARG_NAME = "...";
     public static final int NO_VARARG = -1;
 
-    private static final String VARARG_GETTER_PREFIX = "..";
-
     @CompilationFinal private static final ArgumentsSignature[] EMPTY_SIGNATURES = new ArgumentsSignature[32];
     public static final ArgumentsSignature INVALID_SIGNATURE = new ArgumentsSignature(new String[]{"<<invalid>>"});
 
@@ -78,7 +76,7 @@ public final class ArgumentsSignature implements Iterable<String> {
     @CompilationFinal private final String[] names;
     @CompilationFinal private final int[] varArgIndexes;
     @CompilationFinal private final boolean[] isVarArg;
-    @CompilationFinal private final boolean[] isVarArgGetter;
+    private final int varArgIndex;
     private final int nonNullCount;
 
     private ArgumentsSignature(String[] names) {
@@ -88,7 +86,6 @@ public final class ArgumentsSignature implements Iterable<String> {
         int index = NO_VARARG;
         int count = 0;
         this.isVarArg = new boolean[names.length];
-        this.isVarArgGetter = new boolean[names.length];
         for (int i = 0; i < names.length; i++) {
             String name = names[i];
             if (name != null) {
@@ -98,8 +95,6 @@ public final class ArgumentsSignature implements Iterable<String> {
                     if (index != NO_VARARG) {
                         index = i;
                     }
-                } else if (name.startsWith(VARARG_GETTER_PREFIX)) {
-                    this.isVarArgGetter[i] = true;
                 }
             }
         }
@@ -110,6 +105,7 @@ public final class ArgumentsSignature implements Iterable<String> {
                 varArgIndexes[pos++] = i;
             }
         }
+        this.varArgIndex = varArgIndexes.length == 0 ? NO_VARARG : varArgIndexes[0];
     }
 
     public boolean isEmpty() {
@@ -126,7 +122,7 @@ public final class ArgumentsSignature implements Iterable<String> {
 
     public int getVarArgIndex() {
         assert varArgIndexes.length <= 1 : "cannot ask for _the_ vararg index if there are multiple varargs";
-        return varArgIndexes.length == 0 ? NO_VARARG : varArgIndexes[0];
+        return varArgIndex;
     }
 
     public int getVarArgCount() {
@@ -207,7 +203,7 @@ public final class ArgumentsSignature implements Iterable<String> {
      * methods {@link #isVarArgsIndex(long)}, {@link #extractVarArgsArgumentIndex(long)} and
      * {@link #extractVarArgsArgumentIndex(long)} to access the data packed in the {@code long}
      * value. This method also removes arguments that are marked as 'unmatched' in the signature.
-     * 
+     *
      * @param argListSize length of the result -- sum of lengths of all varargs contained within
      *            varArgSignatures minus any unmatched arguments.
      */
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java
index e73a6cd2d823f5592126a3502995b7364b1069a9..0fec763e0e56036ee7fb48ae964ffcfafebab476 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java
@@ -29,6 +29,7 @@ import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.Semaphore;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -42,12 +43,13 @@ import com.oracle.truffle.r.runtime.data.RFunction;
 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.RPromise;
 import com.oracle.truffle.r.runtime.data.RShareable;
 import com.oracle.truffle.r.runtime.data.RUnboundValue;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.frame.REnvTruffleFrameAccess;
+import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
 
 /**
  * Implementation of a channel abstraction used for communication between parallel contexts in
@@ -202,7 +204,7 @@ public class RChannel {
     private static class TransmitterCommon extends RSerialize.RefCounter {
 
         protected static class SerializedRef {
-            private int index;
+            private final int index;
 
             public SerializedRef(int index) {
                 this.index = index;
@@ -215,7 +217,7 @@ public class RChannel {
 
         protected static class SerializedList {
 
-            private RList list;
+            private final RList list;
 
             SerializedList(RList list) {
                 this.list = list;
@@ -229,8 +231,8 @@ public class RChannel {
         protected static class SerializedEnv {
 
             public static class Bindings {
-                private String[] names;
-                private Object[] values;
+                private final String[] names;
+                private final Object[] values;
 
                 Bindings(String[] names, Object[] values) {
                     this.names = names;
@@ -268,9 +270,9 @@ public class RChannel {
 
         protected static class SerializedPromise {
 
-            private Object env;
-            private Object value;
-            private byte[] serializedExpr;
+            private final Object env;
+            private final Object value;
+            private final byte[] serializedExpr;
 
             public SerializedPromise(Object env, Object value, byte[] serializedExpr) {
                 this.env = env;
@@ -292,10 +294,34 @@ public class RChannel {
 
         }
 
+        protected static class SerializedFunction {
+            private final RAttributes attributes;
+            private final Object env;
+            private final byte[] serializedDef;
+
+            public SerializedFunction(RAttributes attributes, Object env, byte[] serializedDef) {
+                this.attributes = attributes;
+                this.env = env;
+                this.serializedDef = serializedDef;
+            }
+
+            public RAttributes getAttributes() {
+                return attributes;
+            }
+
+            public Object getEnv() {
+                return env;
+            }
+
+            public byte[] getSerializedDef() {
+                return serializedDef;
+            }
+        }
+
         protected static class SerializedAttributable {
 
-            private RAttributes attributes;
-            private byte[] serializedAttributable;
+            private final RAttributes attributes;
+            private final byte[] serializedAttributable;
 
             public SerializedAttributable(RAttributes attributes, byte[] serializedAttributable) {
                 this.attributes = attributes;
@@ -401,6 +427,14 @@ public class RChannel {
 
         }
 
+        private SerializedFunction convertPrivateFunction(Object msg) throws IOException {
+            RFunction fn = (RFunction) msg;
+            byte[] serializedFunctionDef = RSerialize.serializeFunctionNonEnv(fn);
+            Object env = convertPrivate(REnvironment.frameToEnvironment(fn.getEnclosingFrame()));
+            RAttributes attributes = fn.getAttributes();
+            return new SerializedFunction(attributes == null ? null : createShareableSlow(attributes, true), env, serializedFunctionDef);
+        }
+
         private Object convertPrivateAttributable(Object msg) throws IOException {
             // do full serialization but handle attributes separately (no reason to serialize them
             // unconditionally)
@@ -435,7 +469,7 @@ public class RChannel {
         }
 
         private static boolean serializeObject(Object o) {
-            return o instanceof RFunction || o instanceof REnvironment || o instanceof RConnection || o instanceof RLanguage;
+            return o instanceof REnvironment || o instanceof RConnection || o instanceof RLanguage;
         }
 
         private Object convertPrivate(Object o) throws IOException {
@@ -445,6 +479,8 @@ public class RChannel {
                 return convertPrivateEnv(o);
             } else if (o instanceof RPromise) {
                 return convertPrivatePromise(o);
+            } else if (o instanceof RFunction) {
+                return convertPrivateFunction(o);
             } else if (!serializeObject(o)) {
                 // we need to make internal values (permanently) shared to avoid updates to ref
                 // count by different threads
@@ -524,51 +560,11 @@ public class RChannel {
         }
 
         public Object processOutgoingMessage(Object data) {
-            Object msg = data;
-            if (msg instanceof RList) {
-                try {
-                    msg = convertPrivateList(msg);
-                } catch (IOException x) {
-                    throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "error creating shareable list");
-                }
-            } else if (shareableEnv(msg)) {
-                try {
-                    msg = convertPrivateEnv(msg);
-                } catch (IOException x) {
-                    throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "error creating shareable environment");
-                }
-            } else if (msg instanceof RPromise) {
-                try {
-                    msg = convertPrivatePromise(msg);
-                } catch (IOException x) {
-                    throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "error creating shareable promise");
-                }
-            } else if (!serializeObject(msg)) {
-                // make sure that what's passed through the channel will be copied on the first
-                // update
-                makeShared(msg);
-                try {
-                    if (msg instanceof RAttributable && ((RAttributable) msg).getAttributes() != null) {
-                        Object newMsg = convertObjectAttributesToPrivate(msg);
-                        if (newMsg == msg) {
-                            makeShared(msg);
-                        } // otherwise a copy has been created to store new attributes
-                        return newMsg;
-                    } else {
-                        return makeShared(msg);
-                    }
-                } catch (IOException x) {
-                    throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "error creating channel message");
-                }
-            } else {
-                assert msg instanceof RAttributable;
-                try {
-                    msg = convertPrivateAttributable(msg);
-                } catch (IOException x) {
-                    throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "error creating shareable attributable");
-                }
+            try {
+                return convertPrivate(data);
+            } catch (IOException x) {
+                throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "error serializing message for channel transmission");
             }
-            return msg;
         }
 
     }
@@ -588,6 +584,8 @@ public class RChannel {
                     ret = unserializeEnv((SerializedEnv) el);
                 } else if (el instanceof SerializedPromise) {
                     ret = unserializePromise((SerializedPromise) el);
+                } else if (el instanceof SerializedFunction) {
+                    ret = unserializeFunction((SerializedFunction) el);
                 } else if (el instanceof SerializedAttributable) {
                     ret = unserializeAttributable((SerializedAttributable) el);
                 }
@@ -642,6 +640,25 @@ public class RChannel {
             return RSerialize.unserializePromise(expr, env, p.getValue());
         }
 
+        @TruffleBoundary
+        private RFunction unserializeFunction(SerializedFunction f) throws IOException {
+            Map<String, Object> constants = new HashMap<>();
+            RPairList l = (RPairList) RSerialize.unserialize(f.getSerializedDef(), null, null, null);
+            // seems like the best (only) way to make deparser see a correct pair list type here
+            RPairList closxpList = RDataFactory.createPairList(l.car(), l.cdr(), RNull.instance, SEXPTYPE.CLOSXP);
+            String deparse = RDeparse.deparseDeserialize(constants, closxpList);
+            REnvironment env = (REnvironment) unserializeObject(f.getEnv());
+            MaterializedFrame enclosingFrame = env.getFrame();
+            RFunction fn = RContext.getEngine().parseFunction(constants, null, RSource.fromTextInternal(deparse, RSource.Internal.PAIRLIST_DEPARSE), enclosingFrame);
+            RAttributes attributes = f.getAttributes();
+            if (attributes != null) {
+                assert fn.getAttributes() == null;
+                // attributes unserialized in caller methods
+                fn.initAttributes(attributes);
+            }
+            return fn;
+        }
+
         @TruffleBoundary
         private static RAttributable unserializeAttributable(SerializedAttributable a) throws IOException {
             RAttributes attributes = a.getAttributes();
@@ -668,9 +685,11 @@ public class RChannel {
                 if (newVal != val) {
                     // class attribute is a string vector which should be always shared
                     assert !a.getName().equals(RRuntime.CLASS_ATTR_KEY);
-                    // TODO: this is a bit brittle as it relies on the iterator to work correctly in
+                    // TODO: this is a bit brittle as it relies on the iterator to work
+                    // correctly in
                     // the
-                    // face of updates (which it does under current implementation of attributes)
+                    // face of updates (which it does under current implementation of
+                    // attributes)
                     attributable.setAttr(a.getName(), newVal);
                 }
             }
@@ -678,24 +697,7 @@ public class RChannel {
 
         public Object processedReceivedMessage(Object msg) {
             try {
-                Object ret = msg;
-                if (msg instanceof SerializedList) {
-                    RList list = ((SerializedList) msg).getList();
-                    // list and attributes are already private (shallow copies - do the appropriate
-                    // changes in place)
-                    unserializeList(list);
-                    ret = list;
-                } else if (msg instanceof SerializedEnv) {
-                    ret = unserializeEnv((SerializedEnv) msg);
-                } else if (msg instanceof SerializedPromise) {
-                    ret = unserializePromise((SerializedPromise) msg);
-                } else if (msg instanceof SerializedAttributable) {
-                    ret = unserializeAttributable((SerializedAttributable) msg);
-                }
-                if (ret instanceof RAttributable && ((RAttributable) ret).getAttributes() != null) {
-                    unserializeAttributes((RAttributable) ret);
-                }
-                return ret;
+                return unserializeObject(msg);
             } catch (IOException x) {
                 throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, "error unserializing msg from the channel");
             }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
index a4c2666b45d703bbe24068e2ddda4450b4d9c57d..0827b88505e7236f32f1e265b16d5a62fe59968c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
@@ -94,6 +94,11 @@ public interface RRuntimeASTAccess {
      */
     void serializeNode(RSerialize.State state, Object node);
 
+    /**
+     * Helper function for {@code serialize} working around cyclic dependency.
+     */
+    void serializeFunctionDefinitionNode(RSerialize.State state, RFunction fn);
+
     /**
      * Returns the real caller associated with {@code rl}, by locating the {@code RSyntaxNode}
      * associated with the node stored with {@code rl}.
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
index cf63c459a0801639fa0e09200f7ce1938970d867..0f70a3f2a23cbfe9e1e33e7396581cc08f6f3c44 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
@@ -2262,6 +2262,24 @@ public class RSerialize {
         }
     }
 
+    @TruffleBoundary
+    public static byte[] serializeFunctionNonEnv(RFunction fn) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        try {
+            Output output = new Output(out, XDR, DEFAULT_VERSION, null);
+            State state = new PLState(output);
+            state.openPairList(SEXPTYPE.CLOSXP);
+            RContext.getRRuntimeASTAccess().serializeFunctionDefinitionNode(state, fn);
+            Object res = state.closePairList();
+            // CLOSXP-type ensures that the list is not shrunk
+            state.convertUnboundValues((RPairList) res);
+            output.serialize(state, res);
+            return out.toByteArray();
+        } catch (IOException ex) {
+            throw RInternalError.shouldNotReachHere();
+        }
+    }
+
     @TruffleBoundary
     public static void serialize(RConnection conn, Object obj, int type, int version, Object refhook) throws IOException {
         Output output = new Output(conn, type, version, (CallHook) refhook);
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 f14f81f0e2490e343fce14310a77e725cd894dc4..55b970ddc97b6a285dda59fc42380e4b08aca143 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
@@ -7011,6 +7011,16 @@ raw(0)
 #argv <- list(list());as.raw(argv[[1]]);
 raw(0)
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_assign.testassign
+#x <- c(1,2,4); e <- new.env(); assign('foo', x, e); x[[1]] <- 5; x; get('foo', e)
+[1] 5 2 4
+[1] 1 2 4
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_assign.testassign
+#x <- c(1,2,4); e <- new.env(); assign('foo', x, e, inherits=0); x[[1]] <- 5; x; get('foo', e)
+[1] 5 2 4
+[1] 1 2 4
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_assign.testassign1
 #argv <- structure(list(x = '`', value = TRUE), .Names = c('x',     'value'));do.call('assign', argv)
 
@@ -108830,6 +108840,10 @@ a b c d e
 #{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/channels/R/channels16.R") }
 [1] 7 7
 
+##com.oracle.truffle.r.test.library.fastr.TestChannels.runRSourceTests
+#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/channels/R/channels17.R") }
+[1] 42  7
+
 ##com.oracle.truffle.r.test.library.fastr.TestChannels.runRSourceTests
 #{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/channels/R/channels2.R") }
 [[1]]
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_assign.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_assign.java
index 5f58a4207b4a06023d731072559ddc46e703c28b..56b2634e7bd148379b854990670f847e107d8261 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_assign.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_assign.java
@@ -22,4 +22,10 @@ public class TestBuiltin_assign extends TestBase {
     public void testassign1() {
         assertEval("argv <- structure(list(x = '`', value = TRUE), .Names = c('x',     'value'));do.call('assign', argv)");
     }
+
+    @Test
+    public void testassign() {
+        assertEval("x <- c(1,2,4); e <- new.env(); assign('foo', x, e); x[[1]] <- 5; x; get('foo', e)");
+        assertEval("x <- c(1,2,4); e <- new.env(); assign('foo', x, e, inherits=0); x[[1]] <- 5; x; get('foo', e)");
+    }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels17.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels17.R
new file mode 100644
index 0000000000000000000000000000000000000000..68cc2c1f8467af241a592f689a0420c43fe56fc8
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels17.R
@@ -0,0 +1,18 @@
+# test passing a function (environment must stay private)
+
+if (length(grep("FastR", R.Version()$version.string)) == 1) {
+    ch <- .fastr.channel.create(1L)
+    code <- "ch <- .fastr.channel.get(1L); f<-.fastr.channel.receive(ch); env <- environment(f); env[['x']] <- 7; .fastr.channel.send(ch, env[['x']])"
+    cx <- .fastr.context.spawn(code)
+    env <- new.env()
+    env[["x"]] <- 42
+    f <- function() 42
+    environment(f) <- env
+   .fastr.channel.send(ch, f)
+    y <- .fastr.channel.receive(ch)
+    .fastr.context.join(cx)
+    .fastr.channel.close(ch)
+    print(c(env[["x"]], y))
+} else {
+    print(c(42, 7))
+}