diff --git a/ci.hocon b/ci.hocon
index f7f43acc45bd34c8355142f48df9af8bbee677a9..8060d69d637e98a826a688dd697e37d944610c3d 100644
--- a/ci.hocon
+++ b/ci.hocon
@@ -15,6 +15,7 @@ common : {
   environment : {
   }
   logs : [
+    "fastr_errors.log"
     "com.oracle.truffle.r.native/gnur/R-*/gnur_configure.log"
     "com.oracle.truffle.r.native/gnur/R-*/gnur_make.log"
     "com.oracle.truffle.r.native/gnur/R-*/Makeconf"
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
index 62a3e4bb2b873db16ebc06b620aced31690a5563..479dbaca13b04118744e13dd78c39f673ca27dad 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java
@@ -51,13 +51,16 @@ import com.oracle.truffle.r.nodes.RASTBuilder;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinPackages;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNode;
+import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinterNode;
 import com.oracle.truffle.r.nodes.control.BreakException;
 import com.oracle.truffle.r.nodes.control.NextException;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
+import com.oracle.truffle.r.nodes.function.CallMatcherNode.CallMatcherGenericNode;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
-import com.oracle.truffle.r.runtime.JumpToTopLevelException;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
+import com.oracle.truffle.r.runtime.JumpToTopLevelException;
+import com.oracle.truffle.r.runtime.ExitException;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
@@ -66,8 +69,8 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RParserFactory;
 import com.oracle.truffle.r.runtime.RProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RStartParams.SA_TYPE;
 import com.oracle.truffle.r.runtime.RSource;
+import com.oracle.truffle.r.runtime.RStartParams.SA_TYPE;
 import com.oracle.truffle.r.runtime.ReturnException;
 import com.oracle.truffle.r.runtime.SubstituteVirtualFrame;
 import com.oracle.truffle.r.runtime.ThreadTimings;
@@ -76,6 +79,7 @@ import com.oracle.truffle.r.runtime.Utils.DebugExitException;
 import com.oracle.truffle.r.runtime.VirtualEvalFrame;
 import com.oracle.truffle.r.runtime.context.Engine;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RExpression;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -84,6 +88,7 @@ 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.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.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -247,7 +252,7 @@ final class REngine implements Engine, Engine.Timings {
             return lastValue;
         } catch (ReturnException ex) {
             return ex.getResult();
-        } catch (DebugExitException | JumpToTopLevelException e) {
+        } catch (DebugExitException | JumpToTopLevelException | ExitException e) {
             throw e;
         } catch (RError e) {
             // RError prints the correct result on the console during construction
@@ -322,12 +327,11 @@ final class REngine implements Engine, Engine.Timings {
                 return lastValue;
             } catch (ReturnException ex) {
                 return ex.getResult();
-            } catch (DebugExitException | JumpToTopLevelException | ThreadDeath e) {
+            } catch (DebugExitException | JumpToTopLevelException | ExitException | ThreadDeath e) {
                 throw e;
             } catch (RError e) {
-                // TODO normal error reporting is done by the runtime
-                RInternalError.reportError(e);
-                return null;
+                CompilerDirectives.transferToInterpreter();
+                throw e;
             } catch (Throwable t) {
                 throw t;
             } finally {
@@ -381,7 +385,7 @@ final class REngine implements Engine, Engine.Timings {
 
     @Override
     @TruffleBoundary
-    public Object evalFunction(RFunction func, MaterializedFrame frame, RCaller caller, Object... args) {
+    public Object evalFunction(RFunction func, MaterializedFrame frame, RCaller caller, RStringVector names, Object... args) {
         assert frame == null || caller != null;
         MaterializedFrame actualFrame = frame;
         if (actualFrame == null) {
@@ -393,7 +397,17 @@ final class REngine implements Engine, Engine.Timings {
                 actualFrame = current.materialize();
             }
         }
-        Object[] rArgs = RArguments.create(func, caller == null ? RArguments.getCall(actualFrame) : caller, actualFrame, args, null);
+        RArgsValuesAndNames reorderedArgs = CallMatcherGenericNode.reorderArguments(args, func,
+                        names == null ? ArgumentsSignature.empty(args.length) : ArgumentsSignature.get(names.getDataWithoutCopying()), false,
+                        RError.NO_CALLER);
+        Object[] newArgs = reorderedArgs.getArguments();
+        for (int i = 0; i < newArgs.length; i++) {
+            Object arg = newArgs[i];
+            if (arg instanceof RPromise) {
+                newArgs[i] = PromiseHelperNode.evaluateSlowPath(null, (RPromise) arg);
+            }
+        }
+        Object[] rArgs = RArguments.create(func, caller == null ? RArguments.getCall(actualFrame) : caller, actualFrame, newArgs, null);
         return func.getTarget().call(rArgs);
     }
 
@@ -507,7 +521,7 @@ final class REngine implements Engine, Engine.Timings {
                     // there can be an outer loop
                     throw cfe;
                 }
-            } catch (DebugExitException | JumpToTopLevelException e) {
+            } catch (DebugExitException | JumpToTopLevelException | ExitException e) {
                 CompilerDirectives.transferToInterpreter();
                 throw e;
             } catch (Throwable e) {
@@ -570,7 +584,7 @@ final class REngine implements Engine, Engine.Timings {
         result = RRuntime.asAbstractVector(result);
         // this supports printing of non-R values (via toString for now)
         if (result instanceof RTypedValue) {
-            return PrettyPrinterNode.prettyPrintDefault(result);
+            return ValuePrinterNode.prettyPrint(result);
         } else if (result == null) {
             return "[external object (null)]";
         } else if (result instanceof TruffleObject) {
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 332c07f468c46aae09577bbafddd57228f7d614c..8c6219e025259da327baa3ef5e0c2db103981188 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
@@ -47,7 +47,6 @@ import com.oracle.truffle.r.nodes.control.IfNode;
 import com.oracle.truffle.r.nodes.control.ReplacementNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.FunctionExpressionNode;
-import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
@@ -368,7 +367,7 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
     public Object callback(RFunction f, Object[] args) {
         boolean gd = RContext.getInstance().stateInstrumentation.setDebugGloballyDisabled(true);
         try {
-            return RContext.getEngine().evalFunction(f, null, null, args);
+            return RContext.getEngine().evalFunction(f, null, null, null, args);
         } catch (ReturnException ex) {
             // cannot throw return exceptions further up.
             return ex.getResult();
@@ -378,9 +377,9 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
     }
 
     @Override
-    public Object forcePromise(Object val) {
+    public Object forcePromise(String identifier, Object val) {
         if (val instanceof RPromise) {
-            return PromiseHelperNode.evaluateSlowPath(null, (RPromise) val);
+            return ReadVariableNode.evalPromiseSlowPathWithName(identifier, null, (RPromise) val);
         } else {
             return val;
         }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java
index e1cee8110744a8b7a5ce61a369499790c44c4af8..dc9ba058337ef8f48d23465a6b0f6085c07ffc49 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java
@@ -45,6 +45,7 @@ import com.oracle.truffle.r.runtime.RPerfStats;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RVersionInfo;
 import com.oracle.truffle.r.runtime.TempPathName;
+import com.oracle.truffle.r.runtime.Utils;
 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;
@@ -76,9 +77,7 @@ public final class TruffleRLanguage extends TruffleLanguage<RContext> {
             RPackageSource.initialize();
             RContext.initialize(new RASTBuilder(), new RRuntimeASTAccessImpl(), RBuiltinPackages.getInstance(), new RForeignAccessFactoryImpl());
         } catch (Throwable t) {
-            System.err.println("error during engine initialization: " + t);
-            t.printStackTrace();
-            System.exit(-1);
+            Utils.rSuicide("error during R language initialization");
         }
     }
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java
index 0369266b8df75d613c28c63de810f3a5037f745a..dc3db25f769838496fc5eb497fa461721ed4e104 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java
@@ -48,7 +48,7 @@ class JLineConsoleHandler implements ConsoleHandler {
             console.setHandleUserInterrupt(true);
             console.setExpandEvents(false);
         } catch (IOException ex) {
-            throw Utils.fail("unexpected error opening console reader");
+            throw Utils.rSuicide("unexpected error opening console reader");
         }
         // long start = System.currentTimeMillis();
         printWriter = new PrintWriter(console.getOutput());
@@ -82,7 +82,7 @@ class JLineConsoleHandler implements ConsoleHandler {
         } catch (UserInterruptException e) {
             throw e;
         } catch (Exception ex) {
-            throw Utils.fail("unexpected error reading console input: " + ex);
+            throw Utils.rSuicide("unexpected error reading console input: " + ex);
         }
     }
 
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
index 5452a324b63f0696df3c7fa271254cf108344825..04c66c9f1e98f0a38d1966e699a4142dfe2392ad 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
@@ -36,8 +36,10 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
 import com.oracle.truffle.r.nodes.builtin.base.Quit;
+import com.oracle.truffle.r.runtime.ExitException;
 import com.oracle.truffle.r.runtime.JumpToTopLevelException;
 import com.oracle.truffle.r.runtime.RCmdOptions;
+import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RStartParams;
@@ -180,6 +182,7 @@ public class RCommand {
      * exiting. So,in either case, we never return.
      */
     static void readEvalPrint(PolyglotEngine vm) {
+        int lastStatus = 0;
         ConsoleHandler consoleHandler = ContextInfo.getContextInfo(vm).getConsoleHandler();
         try {
             // console.println("initialize time: " + (System.currentTimeMillis() - start));
@@ -206,6 +209,7 @@ public class RCommand {
                          * exceptions that are <: Exception are converted to IOException, Error
                          * subclasses pass through.
                          */
+                        lastStatus = 0;
                         try {
                             vm.eval(source);
                             emitIO();
@@ -224,26 +228,24 @@ public class RCommand {
                             e.report(consoleHandler);
                         } catch (IOException e) {
                             /*
-                             * We have to extract QuitException and DebugExitException and rethrow
-                             * them explicitly
+                             * We have to extract the underlying cause and handle the special cases
+                             * appropriately.
                              */
+                            lastStatus = 1;
                             Throwable cause = e.getCause();
-                            if (cause instanceof JumpToTopLevelException) {
+                            if (cause instanceof RError) {
+                                // drop through to continue REPL and remember last eval was an error
+                            } else if (cause instanceof JumpToTopLevelException) {
                                 // drop through to continue REPL
                             } else if (cause instanceof DebugExitException) {
                                 throw (RuntimeException) cause;
-                            } else if (cause instanceof RInternalError) {
-                                /*
-                                 * Placing this here makes it a non-fatal error. With default error
-                                 * logging the report will go to a file, so we print a message on
-                                 * the console as well.
-                                 */
-                                RInternalError.reportError(e);
-                                consoleHandler.println("internal error: " + e.getMessage() + " (see fastr_errors.log)");
+                            } else if (cause instanceof ExitException) {
+                                // usually from quit
+                                vm.dispose();
+                                Utils.systemExit(((ExitException) cause).getStatus());
                             } else {
-                                // Something else, e.g. NPE
-                                RInternalError.reportError(e);
-                                consoleHandler.println("unexpected internal error (" + e.getClass().getSimpleName() + "); " + e.getMessage());
+                                RInternalError.reportErrorAndConsoleLog(cause, consoleHandler, 0);
+                                // We continue the repl even though the system may be broken
                             }
                         }
                         continue REPL;
@@ -258,6 +260,10 @@ public class RCommand {
             try {
                 vm.eval(QUIT_EOF);
             } catch (Throwable e) {
+                if (e.getCause() instanceof ExitException) {
+                    // normal quit, but with exit code based on lastStatus
+                    Utils.systemExit(lastStatus);
+                }
                 throw RInternalError.shouldNotReachHere(e);
             }
         } finally {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
index ef6a876f10150a83939e57140cd3f8ff2e60ae45..c4a0903de44e7231885b14bfc2dcfb98a58a58e2 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
@@ -74,7 +74,7 @@ public class REmbedded {
         try {
             vm.eval(INIT);
         } catch (IOException ex) {
-            Utils.rSuicide("initializeR");
+            Utils.rSuicideDefault("initializeR");
         }
         return vm;
     }
@@ -116,11 +116,14 @@ public class REmbedded {
     // Checkstyle: stop method name check
 
     /**
-     * Upcalled from embedded mode to commit suicide.
+     * Upcalled from embedded mode to (really) commit suicide. This provides the default
+     * implementation of the {@code R_Suicide} function in the {@code Rinterface} API. If an
+     * embeddee overrides it, it typically will save this value and invoke it after its own
+     * customization.
      */
     @SuppressWarnings("unused")
     private static void R_Suicide(String msg) {
-        Utils.exit(2);
+        Utils.rSuicideDefault(msg);
     }
 
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java
index 5b4d80c96f3f3d8b085cf7f2a706ebb056a7eba9..b952a0da7a3b74f43725f163a7221b7d57d48379 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/ObjectSize.java
@@ -22,199 +22,56 @@
  */
 package com.oracle.truffle.r.library.utils;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.library.utils.ObjectSizeNodeGen.RecursiveObjectSizeNodeGen;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.nodes.profile.TruffleBoundaryNode;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributes;
-import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RExpression;
+import com.oracle.truffle.r.runtime.data.RObjectSize;
 import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-import com.oracle.truffle.r.runtime.data.RList;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
-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.RStringVector;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
 
-/*
- * Similarly to GNU R's version, this is very approximate
- * (e.g. overhead related to Java object headers is not included)
- * and is only (semi) accurate for atomic vectors.
+/**
+ * Similarly to GNU R's version, this is approximate and based, for {@link RTypedValue} instances on
+ * {@link RObjectSize#getObjectSize}. As per GNU R the AST size for a closure is included. TODO AST
+ * size not included owing to problems sizing it automatically.
  */
+@SuppressWarnings("unused")
 public abstract class ObjectSize extends RExternalBuiltinNode.Arg1 {
 
-    protected abstract int executeInt(Object o);
-
-    @Child RecursiveObjectSize recursiveObjectSize;
-
-    protected int recursiveObjectSize(Object o) {
-        if (recursiveObjectSize == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            recursiveObjectSize = insert(RecursiveObjectSizeNodeGen.create());
-        }
-        return recursiveObjectSize.executeInt(o);
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RNull o) {
-        return 64; // pointer?
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") int o) {
-        return 32;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") double o) {
-        return 64;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") byte o) {
-        return 8;
-    }
-
-    @Specialization
-    protected int objectSize(String o) {
-        return o.length() * 16;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RRaw o) {
-        return 8;
-    }
-
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RComplex o) {
-        return 128;
-    }
-
-    @Specialization
-    protected int objectSize(RIntSequence o) {
-        int res = 96; // int length + int start + int stride
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RDoubleSequence o) {
-        int res = 160; // int length + double start + double stride
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RIntVector o) {
-        return o.getLength() * 32 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RDoubleVector o) {
-        return o.getLength() * 64 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RStringVector o) {
-        int res = 0;
-        for (int i = 0; i < o.getLength(); i++) {
-            res += o.getLength() * 16;
-        }
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RLogicalVector o) {
-        return o.getLength() * 8 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RComplexVector o) {
-        return o.getLength() * 128 + attrSize(o);
-    }
-
-    @Specialization
-    protected int objectSize(RRawVector o) {
-        return o.getLength() * 8 + attrSize(o);
-    }
-
-    @Specialization
-    @TruffleBoundary
-    protected int objectSize(RList o) {
-        int res = 0;
-        for (int i = 0; i < o.getLength(); i++) {
-            res += recursiveObjectSize(o.getDataAt(i));
-        }
-        return res + attrSize(o);
-    }
-
-    @Specialization
-    @TruffleBoundary
-    protected int objectSize(RPairList o) {
-        RPairList list = o;
-        Object car = list.car();
-        int res = 0;
-        while (true) {
-            res += recursiveObjectSize(car);
-            Object cdr = list.cdr();
-            if (cdr == RNull.instance) {
-                break;
+    private static class MyIgnoreObjectHandler implements RObjectSize.IgnoreObjectHandler {
+        @Override
+        public boolean ignore(Object rootObject, Object obj) {
+            if (obj == RNull.instance) {
+                return true;
             } else {
-                list = (RPairList) cdr;
-                car = list.car();
+                return false;
             }
         }
-        return res + attrSize(o);
-    }
 
-    @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RFunction o) {
-        return 256; // arbitrary, but does it really matter?
     }
 
+    private static final MyIgnoreObjectHandler ignoreObjectHandler = new MyIgnoreObjectHandler();
+
     @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RLanguage o) {
-        return 256; // arbitrary, but does it really matter?
+    protected int objectSize(int o) {
+        return RObjectSize.INT_SIZE;
     }
 
     @Specialization
-    protected int objectSize(@SuppressWarnings("unused") RExpression o) {
-        return 256; // arbitrary, but does it really matter?
+    protected int objectSize(double o) {
+        return RObjectSize.DOUBLE_SIZE;
     }
 
-    protected int attrSize(RAttributable o) {
-        return o.getAttributes() == null ? 0 : attrSizeInternal(o.getAttributes());
+    @Specialization
+    protected int objectSize(byte o) {
+        return RObjectSize.BYTE_SIZE;
     }
 
+    @Fallback
     @TruffleBoundary
-    protected int attrSizeInternal(RAttributes attributes) {
-        int size = 0;
-        for (RAttribute attr : attributes) {
-            size += attr.getName().length() * 16;
-            size += recursiveObjectSize(attr.getValue());
-        }
-        return size;
-    }
-
-    protected abstract static class RecursiveObjectSize extends TruffleBoundaryNode {
-
-        protected abstract int executeInt(Object o);
-
-        @Child ObjectSize objectSize = ObjectSizeNodeGen.create();
-
-        @Specialization
-        protected int objectSize(Object o) {
-            return objectSize.executeInt(o);
-        }
+    protected int objectSize(Object o) {
+        return (int) RObjectSize.getObjectSize(o, ignoreObjectHandler);
     }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java
index d03f663b1151eaae9a39bdecede4f62e9996a80e..db38227316cc29e6eec6064ae342363e887c6018 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprof.java
@@ -50,12 +50,36 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RObjectSize;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState;
+import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofState.MemoryQuad;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
+/**
+ * Implements the {@code Rprof} external.
+ *
+ * The output is basically a sequence of call stacks, output at each sample interval, with entries
+ * in the stack identified by quoted function names. If memory profiling, the stack is preceded by a
+ * auad of numbers {@code :smallv:bigv:nodes:duplicate_counter:} allocated in the interval. If line
+ * profiling is enabled source files are listed as
+ *
+ * <pre>
+ * #File N: path
+ * </pre>
+ *
+ * and then the {@code N} is used in line number references of the form {@code N#L},which precede
+ * the function name.
+ *
+ */
+public abstract class Rprof extends RExternalBuiltinNode.Arg8 implements RDataFactory.Listener, MemoryCopyTracer.Listener {
+
+    private RprofState profState;
 
     @SuppressWarnings("unused")
     @Specialization
@@ -64,13 +88,13 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
         if (!RContext.getInstance().isInitial()) {
             throw RError.error(this, RError.Message.GENERIC, "profiling not supported in created contexts");
         }
-        RprofState profState = RContext.getInstance().stateInstrumentation.getRprof();
+        profState = RContext.getInstance().stateInstrumentation.getRprof();
         String filename = filenameVec.getDataAt(0);
         if (filename.length() == 0) {
             // disable
             endProfiling();
         } else {
-            // enable
+            // enable after ending any previous session
             if (profState.out() != null) {
                 endProfiling();
             }
@@ -79,18 +103,21 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
             boolean gcProfiling = RRuntime.fromLogical(gcProfilingL);
             try {
                 PrintWriter out = new PrintWriter(new FileWriter(filename, append));
-                if (memProfiling) {
-                    RError.warning(this, RError.Message.GENERIC, "Rprof: memory profiling not supported");
-                }
                 if (gcProfiling) {
                     RError.warning(this, RError.Message.GENERIC, "Rprof: gc profiling not supported");
                 }
+                if (memProfiling) {
+                    RDataFactory.addListener(this);
+                    RDataFactory.setAllocationTracing(true);
+                    MemoryCopyTracer.addListener(this);
+                    MemoryCopyTracer.setTracingState(true);
+                }
                 // interval is in seconds, we convert to millis
                 long intervalInMillis = (long) (1E3 * intervalD);
                 StatementListener statementListener = new StatementListener();
                 ProfileThread profileThread = new ProfileThread(intervalInMillis, statementListener);
                 profileThread.setDaemon(true);
-                profState.initialize(out, profileThread, statementListener, intervalInMillis, RRuntime.fromLogical(lineProfilingL));
+                profState.initialize(out, profileThread, statementListener, intervalInMillis, RRuntime.fromLogical(lineProfilingL), memProfiling);
                 profileThread.start();
             } catch (IOException ex) {
                 throw RError.error(this, RError.Message.GENERIC, String.format("Rprof: cannot open profile file '%s'", filename));
@@ -99,13 +126,37 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
         return RNull.instance;
     }
 
-    private static void endProfiling() {
-        RprofState profState = RContext.getInstance().stateInstrumentation.getRprof();
+    @Override
+    @TruffleBoundary
+    public void reportAllocation(RTypedValue data) {
+        long size = RObjectSize.getObjectSize(data, Rprofmem.myIgnoreObjectHandler);
+        if (data instanceof RAbstractVector) {
+            if (size >= Rprofmem.LARGE_VECTOR) {
+                profState.memoryQuad().largeV += size;
+            } else {
+                profState.memoryQuad().smallV += size;
+            }
+        } else {
+            profState.memoryQuad().nodes += size;
+        }
+
+    }
+
+    @Override
+    @TruffleBoundary
+    public void reportCopying(RAbstractVector source, RAbstractVector dest) {
+        profState.memoryQuad().copied += RObjectSize.getObjectSize(source, Rprofmem.myIgnoreObjectHandler);
+    }
+
+    private void endProfiling() {
         ProfileThread profileThread = (ProfileThread) profState.profileThread();
         profileThread.running = false;
         HashMap<String, Integer> fileMap = null;
         PrintWriter out = profState.out();
         StatementListener statementListener = (StatementListener) profState.statementListener();
+        if (profState.memoryProfiling()) {
+            out.print("memory profiling: ");
+        }
         if (profState.lineProfiling()) {
             out.print("line profiling: ");
         }
@@ -124,7 +175,12 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
                 }
             }
         }
+        int index = 0;
         for (ArrayList<RSyntaxNode> intervalStack : statementListener.intervalStacks) {
+            if (profState.memoryProfiling()) {
+                MemoryQuad mq = statementListener.intervalMemory.get(index);
+                out.printf(":%d:%d:%d:%d:", mq.largeV, mq.smallV, mq.nodes, mq.copied);
+            }
             for (RSyntaxNode node : intervalStack) {
                 RootNode rootNode = node.asRNode().getRootNode();
                 if (rootNode instanceof FunctionDefinitionNode) {
@@ -139,8 +195,15 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
                 }
             }
             out.println();
+            index++;
         }
         out.close();
+        profState.setOut(null);
+        if (profState.memoryProfiling()) {
+            RDataFactory.setAllocationTracing(false);
+            MemoryCopyTracer.setTracingState(false);
+        }
+
     }
 
     private static String getPath(RSyntaxNode node) {
@@ -177,8 +240,9 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
      * Emulates a sampling timer by checking when the sample interval rolls over and at that point
      * collects the stack of functions.
      */
-    private static final class StatementListener implements ExecutionEventListener {
+    private final class StatementListener implements ExecutionEventListener {
         private ArrayList<ArrayList<RSyntaxNode>> intervalStacks = new ArrayList<>();
+        private ArrayList<MemoryQuad> intervalMemory = new ArrayList<>();
         private volatile boolean newInterval;
 
         private StatementListener() {
@@ -200,12 +264,16 @@ public abstract class Rprof extends RExternalBuiltinNode.Arg8 {
                 stack.add((RSyntaxNode) context.getInstrumentedNode());
                 collectStack(stack);
                 intervalStacks.add(stack);
+                if (profState.memoryProfiling()) {
+                    intervalMemory.add(profState.memoryQuad().copyAndClear());
+                }
+
                 newInterval = false;
             }
         }
 
         @TruffleBoundary
-        private static void collectStack(final ArrayList<RSyntaxNode> stack) {
+        private void collectStack(final ArrayList<RSyntaxNode> stack) {
             Utils.iterateRFrames(FrameAccess.READ_ONLY, new Function<Frame, Object>() {
 
                 @Override
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fa5c672e50bcf65325c39085cb018110c0dc822
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/Rprofmem.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016, 2016, 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.library.utils;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RObjectSize;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.instrument.InstrumentationState.RprofmemState;
+
+public abstract class Rprofmem extends RExternalBuiltinNode.Arg3 implements RDataFactory.Listener {
+
+    private RprofmemState profmemState;
+
+    @Specialization
+    @TruffleBoundary
+    public Object doRprofmem(RAbstractStringVector filenameVec, byte appendL, RAbstractDoubleVector thresholdVec) {
+        if (!RContext.getInstance().isInitial()) {
+            throw RError.error(this, RError.Message.GENERIC, "profiling not supported in created contexts");
+        }
+        String filename = filenameVec.getDataAt(0);
+        if (filename.length() == 0) {
+            // disable
+            endProfiling();
+        } else {
+            // enable after ending any previous session
+            profmemState = RContext.getInstance().stateInstrumentation.getRprofmem();
+            if (profmemState.out() != null) {
+                endProfiling();
+            }
+            boolean append = RRuntime.fromLogical(appendL);
+            try {
+                PrintWriter out = new PrintWriter(new FileWriter(filename, append));
+                profmemState.initialize(out, thresholdVec.getDataAt(0));
+                RDataFactory.addListener(this);
+                RDataFactory.setAllocationTracing(true);
+            } catch (IOException ex) {
+                throw RError.error(this, RError.Message.GENERIC, String.format("Rprofmem: cannot open profile file '%s'", filename));
+            }
+        }
+        return RNull.instance;
+    }
+
+    private void endProfiling() {
+        if (profmemState != null) {
+            RDataFactory.setAllocationTracing(false);
+            profmemState.out().flush();
+            profmemState.out().close();
+            profmemState.setOut(null);
+        }
+    }
+
+    private static final int PAGE_SIZE = 2000;
+    static final int LARGE_VECTOR = 128;
+
+    /**
+     * We ignore nested {@link RTypedValue} instances as these will have been counted already. We
+     * also ignore {@link Node} instances, except in {@link RFunction} objects.
+     */
+    private static class MyIgnoreObjectHandler implements RObjectSize.IgnoreObjectHandler {
+        @Override
+        public boolean ignore(Object rootObject, Object obj) {
+            if (obj == RNull.instance) {
+                return true;
+            } else {
+                Class<?> klass = obj.getClass();
+                if (RTypedValue.class.isAssignableFrom(klass)) {
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+        }
+
+    }
+
+    static final RObjectSize.IgnoreObjectHandler myIgnoreObjectHandler = new MyIgnoreObjectHandler();
+
+    @Override
+    @TruffleBoundary
+    public void reportAllocation(RTypedValue data) {
+        // We could do some in memory buffering
+        // TODO write out full stack
+        Frame frame = Utils.getActualCurrentFrame();
+        if (frame == null) {
+            // not an R evaluation, some internal use
+            return;
+        }
+        RFunction func = RArguments.getFunction(frame);
+        if (func == null) {
+            return;
+        }
+        String name = func.getRootNode().getName();
+
+        long size = RObjectSize.getObjectSize(data, myIgnoreObjectHandler);
+        if (data instanceof RAbstractVector && size >= LARGE_VECTOR) {
+            profmemState.out().printf("%d: %s\n", size, name);
+        } else {
+            int pageCount = profmemState.pageCount();
+            long pcs = pageCount + size;
+            if (pcs > PAGE_SIZE) {
+                profmemState.out().printf("new page: %s\n", name);
+                profmemState.setPageCount((int) (pcs - PAGE_SIZE));
+            } else {
+                profmemState.setPageCount((int) pcs);
+            }
+        }
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java
index aeaa156a31e54db5d95aac70ec9fab51e13a2caa..874e70cffb9c4222c2b0e437b78c80d7cf1c51ad 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java
@@ -35,10 +35,10 @@ import java.util.function.Function;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ResourceHandlerFactory;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -126,7 +126,7 @@ public abstract class RBuiltinPackage {
                 }
             }
         } catch (IOException ex) {
-            Utils.fail("error loading R code from " + pkgName + " : " + ex);
+            Utils.rSuicide("error loading R code from " + pkgName + " : " + ex);
         }
         return componentList;
     }
@@ -162,6 +162,6 @@ public abstract class RBuiltinPackage {
         ArgumentsSignature signature = ArgumentsSignature.get(parameterNames);
 
         putBuiltin(new RBuiltinFactory(annotation.name(), builtinClass, annotation.visibility(), annotation.aliases(), annotation.kind(), signature, annotation.nonEvalArgs(), annotation.splitCaller(),
-                        annotation.alwaysSplit(), annotation.dispatch(), constructor));
+                        annotation.alwaysSplit(), annotation.dispatch(), constructor, annotation.behavior()));
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
index 830fc564fd81ce9a8041df08081b1ba435e01a39..453fd92c7902277f2826426105276790ebf97eef 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackages.java
@@ -35,17 +35,17 @@ import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.nodes.builtin.base.BasePackage;
 import com.oracle.truffle.r.nodes.builtin.base.BaseVariables;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RBuiltinLookup;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.REnvVars;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinLookup;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.env.REnvironment;
@@ -84,7 +84,7 @@ public final class RBuiltinPackages implements RBuiltinLookup {
                     baseEnv.put(methodName, function);
                     baseEnv.lockBinding(methodName);
                 } catch (PutException ex) {
-                    Utils.fail("failed to install builtin function: " + methodName);
+                    Utils.rSuicide("failed to install builtin function: " + methodName);
                 }
             }
         }
@@ -95,13 +95,13 @@ public final class RBuiltinPackages implements RBuiltinLookup {
         try {
             baseSource = RSource.fromFileName(basePathbase.toString());
         } catch (IOException ex) {
-            Utils.fail(String.format("unable to open the base package %s", basePathbase));
+            Utils.rSuicide(String.format("unable to open the base package %s", basePathbase));
         }
         // Load the (stub) DLL for base
         try {
             DLL.loadPackageDLL(baseDirPath.resolve("libs").resolve("base.so").toString(), true, true);
         } catch (DLLException ex) {
-            Utils.fail(ex.getMessage());
+            Utils.rSuicide(ex.getMessage());
         }
         // Any RBuiltinKind.SUBSTITUTE functions installed above should not be overridden
         try {
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 b52b9448e8bb482464f6dc82fd25e85f1e640fca..bac47c0d9e2e6790b2b6dbec2659903aa73661fe 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
@@ -10,7 +10,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -18,16 +19,16 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RNull;
 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.RAbstractVector;
 
 //TODO: Implement permuting with DimNames
-@RBuiltin(name = "aperm", kind = INTERNAL, parameterNames = {"a", "perm", "resize"})
+@RBuiltin(name = "aperm", kind = INTERNAL, parameterNames = {"a", "perm", "resize"}, behavior = PURE)
 public abstract class APerm extends RBuiltinNode {
 
     private final BranchProfile errorProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/All.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/All.java
index 8159aeb5faecf54ae02af6f62653f4c90a1657d7..aeae231f1a6706aa88f4b15024a01292f69372e6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/All.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/All.java
@@ -22,7 +22,10 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RDispatch.SUMMARY_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -30,9 +33,8 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -44,7 +46,7 @@ import com.oracle.truffle.r.runtime.data.RVector;
  * TODO: Added primitive {@code na.rm} support, but this code needs rewriting in the same manner as
  * {@link Any} and there is opportunity to share code.
  */
-@RBuiltin(name = "all", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = RDispatch.SUMMARY_GROUP_GENERIC)
+@RBuiltin(name = "all", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = SUMMARY_GROUP_GENERIC, behavior = PURE)
 public abstract class All extends RBuiltinNode {
 
     @Child private CastLogicalNode castLogicalNode;
@@ -56,31 +58,32 @@ public abstract class All extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.toLogical(0);
+        // casts.arg("...").mustBe(integerValue().or(logicalValue())).asLogicalVector();
+        casts.arg("na.rm").asLogicalVector().findFirst(RRuntime.LOGICAL_NA).map(toBoolean());
     }
 
     @Specialization
-    protected byte all(byte value, @SuppressWarnings("unused") byte naRm) {
+    protected byte all(byte value, @SuppressWarnings("unused") boolean naRm) {
         return value;
     }
 
     @Specialization
-    protected byte all(RLogicalVector vector, byte naRm) {
+    protected byte all(RLogicalVector vector, boolean naRm) {
         return accumulate(vector, naRm);
     }
 
     @Specialization
-    protected byte all(@SuppressWarnings("unused") RNull vector, @SuppressWarnings("unused") byte naRm) {
+    protected byte all(@SuppressWarnings("unused") RNull vector, @SuppressWarnings("unused") boolean naRm) {
         return RRuntime.LOGICAL_TRUE;
     }
 
     @Specialization
-    protected byte all(@SuppressWarnings("unused") RMissing vector, @SuppressWarnings("unused") byte naRm) {
+    protected byte all(@SuppressWarnings("unused") RMissing vector, @SuppressWarnings("unused") boolean naRm) {
         return RRuntime.LOGICAL_TRUE;
     }
 
     @Specialization
-    protected byte all(RArgsValuesAndNames args, byte naRm) {
+    protected byte all(RArgsValuesAndNames args, boolean naRm) {
         if (castLogicalNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             castLogicalNode = insert(CastLogicalNodeGen.create(true, false, false));
@@ -94,7 +97,7 @@ public abstract class All extends RBuiltinNode {
                 result = RRuntime.LOGICAL_TRUE;
             } else {
                 result = (byte) castLogicalNode.execute(argValue);
-                if (result == RRuntime.LOGICAL_NA && naRm != RRuntime.LOGICAL_FALSE) {
+                if (result == RRuntime.LOGICAL_NA && naRm) {
                     continue;
                 }
             }
@@ -105,10 +108,10 @@ public abstract class All extends RBuiltinNode {
         return RRuntime.LOGICAL_TRUE;
     }
 
-    private static byte accumulate(RLogicalVector vector, byte naRm) {
+    private static byte accumulate(RLogicalVector vector, boolean naRm) {
         for (int i = 0; i < vector.getLength(); i++) {
             byte b = vector.getDataAt(i);
-            if (b == RRuntime.LOGICAL_NA && naRm != RRuntime.LOGICAL_FALSE) {
+            if (b == RRuntime.LOGICAL_NA && naRm) {
                 continue;
             }
             if (b != RRuntime.LOGICAL_TRUE) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java
index e82ee5be8bdca3d441fd853caceb5e9b393d0fb8..905fb5e0d0e90b19380f9fc56fe0ee2f68a8d9fc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AllNames.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -33,8 +34,8 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RExpression;
 import com.oracle.truffle.r.runtime.data.RLanguage;
@@ -47,14 +48,14 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxFunction;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
 
-@RBuiltin(name = "all.names", kind = INTERNAL, parameterNames = {"expr", "function", "max.names", "unique"})
+@RBuiltin(name = "all.names", kind = INTERNAL, parameterNames = {"expr", "functions", "max.names", "unique"}, behavior = PURE)
 public abstract class AllNames extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.toLogical(1);
-        casts.toInteger(2);
-        casts.toLogical(3);
+        casts.arg("functions").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).notNA(RRuntime.LOGICAL_FALSE);
+        casts.arg("max.names").asIntegerVector().findFirst(0).notNA(0);
+        casts.arg("unique").asLogicalVector().findFirst(RRuntime.LOGICAL_TRUE).notNA(RRuntime.LOGICAL_TRUE);
     }
 
     @Specialization
@@ -143,7 +144,7 @@ public abstract class AllNames extends RBuiltinNode {
     @TruffleBoundary
     protected Object doAllNames(RSymbol symbol, @SuppressWarnings("unused") byte functions, int maxNames, @SuppressWarnings("unused") byte unique) {
         if (maxNames > 0 || maxNames == -1) {
-            return RDataFactory.createStringVector(0);
+            return RDataFactory.createEmptyStringVector();
         } else {
             return RDataFactory.createStringVectorFromScalar(symbol.getName());
         }
@@ -152,6 +153,6 @@ public abstract class AllNames extends RBuiltinNode {
     @SuppressWarnings("unused")
     @Fallback
     protected Object doAllNames(Object expr, Object functions, Object maxNames, Object unique) {
-        return RDataFactory.createStringVector(0);
+        return RDataFactory.createEmptyStringVector();
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Any.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Any.java
index e5cb5fea4d3b207a6a16fbd7ab32b604923d3260..669b8bab732c5aea14e4c8ae877008c611ef0e6a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Any.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Any.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RDispatch.SUMMARY_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Cached;
@@ -34,15 +36,14 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "any", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = RDispatch.SUMMARY_GROUP_GENERIC)
+@RBuiltin(name = "any", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = SUMMARY_GROUP_GENERIC, behavior = PURE)
 public abstract class Any extends RBuiltinNode {
 
     protected static final int MAX_CACHED_LENGTH = 10;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AnyNA.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AnyNA.java
index dd747a2a285a309d3dd442318eeae1ffe8ace078..65de607dba4d9a9dbec7644b0f3b59d72d49bd5b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AnyNA.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AnyNA.java
@@ -22,16 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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;
+
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -45,7 +47,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "anyNA", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.INTERNAL_GENERIC)
+@RBuiltin(name = "anyNA", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class AnyNA extends RBuiltinNode {
 
     private final NACheck naCheck = NACheck.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Arg.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Arg.java
index 0e48c883acee07a8d0c79f340c1647ed530317db..618c7c4b5f99ed26bab4713d06855088ad30c6ab 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Arg.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Arg.java
@@ -22,16 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RDispatch.COMPLEX_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
-@RBuiltin(name = "Arg", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = RDispatch.COMPLEX_GROUP_GENERIC)
+@RBuiltin(name = "Arg", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = COMPLEX_GROUP_GENERIC, behavior = PURE)
 public abstract class Arg extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Args.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Args.java
index 8d243a04a70b0620f9e6f36dafd8c486e89aa4c0..fbddf40edc89997b91ceba3ffd47500849c63733 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Args.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Args.java
@@ -22,6 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+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.Truffle;
@@ -35,10 +38,9 @@ import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.SaveArgumentsNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -53,7 +55,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  * here.
  *
  */
-@RBuiltin(name = "args", kind = RBuiltinKind.INTERNAL, parameterNames = {"name"})
+@RBuiltin(name = "args", kind = INTERNAL, parameterNames = {"name"}, behavior = COMPLEX)
 public abstract class Args extends RBuiltinNode {
 
     @Child private GetFunctions.Get getNode;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Array.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Array.java
index b8e87f257228377f9efb18dbc058b78f834b4e45..31c17451af606214a9552bd2cf6d5a7487a458f8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Array.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Array.java
@@ -22,14 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
+import java.util.function.Function;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -40,10 +44,12 @@ import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -58,7 +64,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
  *
  * TODO complete. This is sufficient for the b25 benchmark use.
  */
-@RBuiltin(name = "array", kind = INTERNAL, parameterNames = {"data", "dim", "dimnames"})
+@RBuiltin(name = "array", kind = INTERNAL, parameterNames = {"data", "dim", "dimnames"}, behavior = PURE)
 public abstract class Array extends RBuiltinNode {
 
     @Child private UpdateDimNames updateDimNames;
@@ -72,9 +78,20 @@ public abstract class Array extends RBuiltinNode {
         updateDimNames.executeRAbstractContainer(container, o);
     }
 
+    private String argType(Object arg) {
+        return ((RTypedValue) arg).getRType().getName();
+    }
+
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.toInteger(1);
+        Function<Object, Object> argType = this::argType;
+        casts.arg("data").mustBe(instanceOf(RAbstractListVector.class).or(numericValue()).or(stringValue()).or(complexValue().or(rawValue())),
+                        RError.SHOW_CALLER, RError.Message.MUST_BE_VECTOR_BUT_WAS, "data",
+                        argType);
+        casts.arg("dim").asIntegerVector().mustBe(notEmpty(), RError.SHOW_CALLER, RError.Message.CANNOT_BE_LENGTH, "dims", 0);
+        casts.arg("dimnames").shouldBe(instanceOf(RList.class).or(nullValue()), RError.SHOW_CALLER,
+                        RError.Message.GENERIC, "non-list dimnames are disregarded; will be an error in R 3.3.0").mapIf(
+                                        instanceOf(RList.class).not(), nullConstant());
     }
 
     private int dimDataHelper(RAbstractIntVector dim, int[] dimData) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java
index e71bd1880e9f82bb717eb9d7adae9009fe9aece7..8d86ad3188d61287b99c0ff165393607945d8a24 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java
@@ -22,16 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
+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.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RExpression;
@@ -42,7 +45,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "as.call", kind = PRIMITIVE, parameterNames = {"x"})
+@RBuiltin(name = "as.call", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
 public abstract class AsCall extends RBuiltinNode {
 
     private final ConditionProfile nullNamesProfile = ConditionProfile.createBinaryProfile();
@@ -100,4 +103,9 @@ public abstract class AsCall extends RBuiltinNode {
 
         return new RArgsValuesAndNames(values, signature);
     }
+
+    @Fallback
+    protected Object asCallFunction(@SuppressWarnings("unused") Object x) {
+        throw RError.error(this, RError.Message.GENERIC, "invalid argument list");
+    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
index 3aff0b6e173b266892df371993290c7820ce51e0..7c29e7beb057ffa40802f9bf1c38f497b40fc028 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacter.java
@@ -22,106 +22,50 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
 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;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RDeparse;
 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.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RRaw;
 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;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "as.character", kind = PRIMITIVE, parameterNames = {"x", "..."}, dispatch = INTERNAL_GENERIC)
+@RBuiltin(name = "as.character", kind = PRIMITIVE, parameterNames = {"x", "..."}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class AsCharacter extends RBuiltinNode {
 
-    @Child private CastStringNode castStringNode;
+    private final ConditionProfile noAttributes = ConditionProfile.createBinaryProfile();
 
-    public abstract Object execute(Object obj);
-
-    public static AsCharacter create() {
-        return AsCharacterNodeGen.create(null);
-    }
-
-    private void initCast() {
-        if (castStringNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
-        }
-    }
-
-    private String castString(int o) {
-        initCast();
-        return (String) castStringNode.executeString(o);
-    }
-
-    private String castString(double o) {
-        initCast();
-        return (String) castStringNode.executeString(o);
-    }
-
-    private String castString(byte o) {
-        initCast();
-        return (String) castStringNode.executeString(o);
-    }
-
-    private RStringVector castStringVector(Object o) {
-        initCast();
-        return (RStringVector) ((RStringVector) castStringNode.executeString(o)).copyDropAttributes();
-    }
-
-    @Specialization
-    protected String doInt(int value) {
-        return castString(value);
-    }
-
-    @Specialization
-    protected String doDouble(double value) {
-        return castString(value);
-    }
-
-    @Specialization
-    protected String doLogical(byte value) {
-        return castString(value);
-    }
-
-    @Specialization
-    protected String doRaw(RRaw value) {
-        initCast();
-        return (String) castStringNode.executeString(value);
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg("x").mapIf(instanceOf(RAbstractListVector.class).not(), asStringVector());
     }
 
     @Specialization
-    protected String doString(String value) {
-        return value;
+    protected RAbstractStringVector asCharacter(@SuppressWarnings("unused") RNull n) {
+        return RDataFactory.createEmptyStringVector();
     }
 
     @Specialization
-    protected String doSymbol(RSymbol value) {
-        return value.getName();
-    }
-
-    @Specialization
-    protected RStringVector doNull(@SuppressWarnings("unused") RNull value) {
-        return RDataFactory.createStringVector(0);
-    }
-
-    @Specialization
-    protected RStringVector doStringVector(RStringVector vector) {
-        return RDataFactory.createStringVector(vector.getDataCopy(), vector.isComplete());
+    protected RAbstractStringVector asCharacter(RAbstractStringVector v) {
+        if (noAttributes.profile(v.getAttributes() == null)) {
+            return v;
+        } else {
+            return (RAbstractStringVector) v.copyDropAttributes();
+        }
     }
 
     @Specialization
-    protected RStringVector doList(RList list) {
+    protected RStringVector asCharacter(RAbstractListVector list) {
         int len = list.getLength();
         boolean complete = RDataFactory.COMPLETE_VECTOR;
         String[] data = new String[len];
@@ -141,8 +85,4 @@ public abstract class AsCharacter extends RBuiltinNode {
         return RDataFactory.createStringVector(data, complete);
     }
 
-    @Specialization(guards = "!isRList(container)")
-    protected RStringVector doVector(RAbstractContainer container) {
-        return castStringVector(container);
-    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsComplex.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsComplex.java
index 6c3623f6cc2954fa5354d63a1dfa9f811ff67e5a..964c8bceec1b1198a9d4b2d4a4cc554e722f92a3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsComplex.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsComplex.java
@@ -22,74 +22,40 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastComplexNode;
-import com.oracle.truffle.r.nodes.unary.CastComplexNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 
-@RBuiltin(name = "as.complex", kind = PRIMITIVE, parameterNames = {"x", "..."})
+@RBuiltin(name = "as.complex", kind = PRIMITIVE, parameterNames = {"x", "..."}, behavior = PURE)
 public abstract class AsComplex extends RBuiltinNode {
 
-    @Child private CastComplexNode castComplexNode;
+    private final ConditionProfile noAttributes = ConditionProfile.createBinaryProfile();
 
-    private void initCast() {
-        if (castComplexNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            castComplexNode = insert(CastComplexNodeGen.create(false, false, false));
-        }
-    }
-
-    @Specialization
-    protected RComplex doComplex(RComplex value) {
-        return value;
-    }
-
-    @Specialization
-    protected RComplex doInt(int value) {
-        initCast();
-        return (RComplex) castComplexNode.executeComplex(value);
-    }
-
-    @Specialization
-    protected RComplex doDouble(double value) {
-        initCast();
-        return (RComplex) castComplexNode.executeComplex(value);
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg("x").asComplexVector();
     }
 
     @Specialization
-    protected RComplex doLogical(byte value) {
-        initCast();
-        return (RComplex) castComplexNode.executeComplex(value);
+    protected RAbstractComplexVector asComplex(@SuppressWarnings("unused") RNull n) {
+        return RDataFactory.createEmptyComplexVector();
     }
 
     @Specialization
-    protected RComplex doString(String value) {
-        initCast();
-        return (RComplex) castComplexNode.executeComplex(value);
-    }
-
-    @Specialization
-    protected RComplexVector doNull(@SuppressWarnings("unused") RNull value) {
-        return RDataFactory.createComplexVector(0);
-    }
-
-    @Specialization
-    protected RComplexVector doComplexVector(RComplexVector vector) {
-        return RDataFactory.createComplexVector(vector.getDataCopy(), vector.isComplete());
+    protected RAbstractComplexVector asComplex(RAbstractComplexVector v) {
+        if (noAttributes.profile(v.getAttributes() == null)) {
+            return v;
+        } else {
+            return (RAbstractComplexVector) v.copyDropAttributes();
+        }
     }
 
-    @Specialization
-    protected RComplexVector doIntVector(RAbstractVector vector) {
-        initCast();
-        return (RComplexVector) castComplexNode.executeComplex(vector);
-    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsDouble.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsDouble.java
index 8f577b5e6ecba01b82b38d0b25895b0dc1f17976..39780bb2a6ad5b9880a96d7d39422154be6673fe 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsDouble.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsDouble.java
@@ -22,86 +22,39 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
-import com.oracle.truffle.r.nodes.unary.CastDoubleNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RComplex;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 
-@RBuiltin(name = "as.double", aliases = {"as.numeric"}, kind = PRIMITIVE, parameterNames = {"x", "..."})
+@RBuiltin(name = "as.double", aliases = {"as.numeric"}, kind = PRIMITIVE, parameterNames = {"x", "..."}, behavior = PURE)
 public abstract class AsDouble extends RBuiltinNode {
 
-    @Child private CastDoubleNode castDoubleNode;
+    private final ConditionProfile noAttributes = ConditionProfile.createBinaryProfile();
 
-    private void initCast() {
-        if (castDoubleNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            castDoubleNode = insert(CastDoubleNodeGen.create(false, false, false));
-        }
-    }
-
-    @Specialization
-    protected double asDouble(double value) {
-        return value;
-    }
-
-    @Specialization
-    protected double asDoubleInt(int value) {
-        initCast();
-        return (double) castDoubleNode.executeDouble(value);
-    }
-
-    @Specialization
-    protected double asDouble(byte value) {
-        initCast();
-        return (double) castDoubleNode.executeDouble(value);
-    }
-
-    @Specialization
-    protected double asDouble(RComplex value) {
-        initCast();
-        return (double) castDoubleNode.executeDouble(value);
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg("x").asDoubleVector();
     }
 
     @Specialization
-    protected double asDouble(String value) {
-        initCast();
-        return (double) castDoubleNode.executeDouble(value);
+    protected RAbstractDoubleVector asDouble(@SuppressWarnings("unused") RNull n) {
+        return RDataFactory.createEmptyDoubleVector();
     }
 
     @Specialization
-    protected RDoubleVector asDouble(@SuppressWarnings("unused") RNull vector) {
-        return RDataFactory.createDoubleVector(0);
-    }
-
-    @Specialization
-    protected RDoubleVector asDouble(RDoubleVector vector) {
-        return RDataFactory.createDoubleVector(vector.getDataCopy(), vector.isComplete());
-    }
-
-    @Specialization
-    protected RDoubleSequence asDouble(RDoubleSequence sequence) {
-        return sequence;
-    }
-
-    @Specialization
-    protected RDoubleSequence asDouble(RIntSequence sequence) {
-        return RDataFactory.createDoubleSequence(sequence.getStart(), sequence.getStride(), sequence.getLength());
-    }
-
-    @Specialization
-    protected RDoubleVector asDouble(RAbstractVector vector) {
-        initCast();
-        return (RDoubleVector) castDoubleNode.executeDouble(vector);
+    protected RAbstractDoubleVector asDouble(RAbstractDoubleVector v) {
+        if (noAttributes.profile(v.getAttributes() == null)) {
+            return v;
+        } else {
+            return (RAbstractDoubleVector) v.copyDropAttributes();
+        }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsFunction.java
index 8a0024a79a1c3b1ab282a19b30c7ee5a99da060f..7afbe27d40577b1c9665fb2ad129a876f53815b4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsFunction.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsFunction.java
@@ -22,6 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.TruffleBoundary;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.Truffle;
@@ -37,13 +40,12 @@ import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.SaveArgumentsNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RExpression;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -60,7 +62,7 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-@RBuiltin(name = "as.function.default", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "envir"})
+@RBuiltin(name = "as.function.default", kind = INTERNAL, parameterNames = {"x", "envir"}, behavior = PURE)
 public abstract class AsFunction extends RBuiltinNode {
     @Specialization
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
index bedbe5a585fd06ee45257822ffc5fdb83e429369..c6a78959fa4142db09ac12146080ef1d05d2f176 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsInteger.java
@@ -22,94 +22,46 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
-import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
-import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "as.integer", kind = PRIMITIVE, parameterNames = {"x", "..."})
+@RBuiltin(name = "as.integer", kind = PRIMITIVE, parameterNames = {"x", "..."}, behavior = PURE)
 public abstract class AsInteger extends RBuiltinNode {
 
-    @Child private CastIntegerNode castIntNode;
+    private final ConditionProfile noAttributes = ConditionProfile.createBinaryProfile();
 
-    private void initCast() {
-        if (castIntNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            castIntNode = insert(CastIntegerNodeGen.create(false, false, false));
-        }
-    }
-
-    @Specialization
-    protected int asInteger(int value) {
-        return value;
-    }
-
-    @Specialization
-    protected int asInteger(double value) {
-        initCast();
-        return (int) castIntNode.executeInt(value);
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg("x").asIntegerVector();
     }
 
     @Specialization
-    protected int asInteger(byte value) {
-        initCast();
-        return (int) castIntNode.executeInt(value);
-    }
-
-    @Specialization
-    protected int asInteger(RComplex value) {
-        initCast();
-        return (int) castIntNode.executeInt(value);
-    }
-
-    @Specialization
-    protected int asInteger(RRaw value) {
-        initCast();
-        return (int) castIntNode.executeInt(value);
-    }
-
-    @Specialization
-    protected int asInteger(String value) {
-        initCast();
-        return (int) castIntNode.executeInt(value);
-    }
-
-    @Specialization
-    protected RIntVector asInteger(@SuppressWarnings("unused") RNull value) {
+    protected RAbstractIntVector asInteger(@SuppressWarnings("unused") RNull n) {
         return RDataFactory.createEmptyIntVector();
     }
 
     @Specialization
-    protected RIntVector asInteger(RIntVector vector) {
-        return RDataFactory.createIntVector(vector.getDataCopy(), vector.isComplete());
-    }
-
-    @Specialization
-    protected RIntVector asInteger(RIntSequence sequence) {
-        return (RIntVector) sequence.createVector();
-    }
-
-    @Specialization
-    protected RAbstractIntVector asInteger(RAbstractVector vector) {
-        initCast();
-        return (RAbstractIntVector) castIntNode.executeInt(vector);
+    protected RAbstractIntVector asInteger(RAbstractIntVector v) {
+        if (noAttributes.profile(v.getAttributes() == null)) {
+            return v;
+        } else {
+            return (RAbstractIntVector) v.copyDropAttributes();
+        }
     }
 
     @Specialization
     protected int asInteger(RConnection conn) {
         return conn.getDescriptor();
     }
+
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsLogical.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsLogical.java
index 65c091df97352dd9bb0d3c214f9bd77b767b0b00..d92ed1e1a988451b5bbc6501fb6d855bcb10d436 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsLogical.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsLogical.java
@@ -22,78 +22,39 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
-import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RComplex;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 
-@RBuiltin(name = "as.logical", kind = PRIMITIVE, parameterNames = {"x", "..."})
+@RBuiltin(name = "as.logical", kind = PRIMITIVE, parameterNames = {"x", "..."}, behavior = PURE)
 public abstract class AsLogical extends RBuiltinNode {
 
-    @Child private CastLogicalNode castLogicalNode;
+    private final ConditionProfile noAttributes = ConditionProfile.createBinaryProfile();
 
-    private byte castLogical(Object o) {
-        if (castLogicalNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            castLogicalNode = insert(CastLogicalNodeGen.create(false, false, false));
-        }
-        return (byte) castLogicalNode.execute(o);
-    }
-
-    private RLogicalVector castLogicalVector(Object o) {
-        if (castLogicalNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            castLogicalNode = insert(CastLogicalNodeGen.create(false, false, false));
-        }
-        return (RLogicalVector) castLogicalNode.execute(o);
-    }
-
-    @Specialization
-    protected byte asLogical(byte value) {
-        return value;
-    }
-
-    @Specialization
-    protected byte asLogical(int value) {
-        return castLogical(value);
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg("x").asLogicalVector();
     }
 
     @Specialization
-    protected byte asLogical(double value) {
-        return castLogical(value);
+    protected RAbstractLogicalVector asLogicaleger(@SuppressWarnings("unused") RNull n) {
+        return RDataFactory.createEmptyLogicalVector();
     }
 
     @Specialization
-    protected byte asLogical(RComplex value) {
-        return castLogical(value);
-    }
-
-    @Specialization
-    protected byte asLogical(String value) {
-        return castLogical(value);
-    }
-
-    @Specialization
-    protected RLogicalVector asLogical(@SuppressWarnings("unused") RNull vector) {
-        return RDataFactory.createLogicalVector(0);
-    }
-
-    @Specialization
-    protected RLogicalVector asLogical(RLogicalVector vector) {
-        return RDataFactory.createLogicalVector(vector.getDataCopy(), vector.isComplete());
-    }
-
-    @Specialization
-    protected RLogicalVector asLogical(RAbstractContainer container) {
-        return castLogicalVector(container);
+    protected RAbstractLogicalVector asLogicaleger(RAbstractLogicalVector v) {
+        if (noAttributes.profile(v.getAttributes() == null)) {
+            return v;
+        } else {
+            return (RAbstractLogicalVector) v.copyDropAttributes();
+        }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsRaw.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsRaw.java
index 077de464fed979af82e6efe76db0d29f27a763ed..1300bfa38b2addff31d700096f857731d464eda8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsRaw.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsRaw.java
@@ -22,95 +22,39 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
-import com.oracle.truffle.r.nodes.unary.CastRawNode;
-import com.oracle.truffle.r.nodes.unary.CastRawNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RComplex;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 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.RRaw;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
 
-@RBuiltin(name = "as.raw", kind = PRIMITIVE, parameterNames = {"x"})
+@RBuiltin(name = "as.raw", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
 public abstract class AsRaw extends RBuiltinNode {
 
-    @Child private CastIntegerNode castInteger;
-    @Child private CastRawNode castRawNode;
+    private final ConditionProfile noAttributes = ConditionProfile.createBinaryProfile();
 
-    private void initCast() {
-        if (castRawNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            castRawNode = insert(CastRawNodeGen.create(false, false, false));
-        }
-    }
-
-    @Specialization
-    protected RRawVector asRaw(@SuppressWarnings("unused") RNull vector) {
-        return RDataFactory.createRawVector(0);
-    }
-
-    @Specialization
-    protected RRaw asRaw(byte logical) {
-        initCast();
-        return (RRaw) castRawNode.executeRaw(logical);
-    }
-
-    @Specialization
-    protected RRaw asRaw(int value) {
-        initCast();
-        return (RRaw) castRawNode.executeRaw(value);
-    }
-
-    @Specialization
-    protected RRaw asRaw(double value) {
-        initCast();
-        return (RRaw) castRawNode.executeRaw(value);
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg("x").asRawVector();
     }
 
     @Specialization
-    protected RRaw asRaw(RComplex value) {
-        initCast();
-        return (RRaw) castRawNode.executeRaw(value);
+    protected RAbstractRawVector asRaw(@SuppressWarnings("unused") RNull n) {
+        return RDataFactory.createEmptyRawVector();
     }
 
     @Specialization
-    protected RRaw asRaw(String value) {
-        initCast();
-        return (RRaw) castRawNode.executeRaw(value);
-    }
-
-    @Specialization
-    protected RRaw asRaw(RRaw value) {
-        return value;
-    }
-
-    @Specialization
-    protected RRawVector asRaw(RRawVector value) {
-        return RDataFactory.createRawVector(value.getDataCopy());
-    }
-
-    @Specialization
-    protected RRawVector asRaw(RList value) {
-        initCast();
-        int length = value.getLength();
-        RRawVector result = RDataFactory.createRawVector(length);
-        for (int i = 0; i < length; i++) {
-            result.updateDataAt(i, (RRaw) castRawNode.executeRaw(value.getDataAt(i)));
+    protected RAbstractRawVector asRaw(RAbstractRawVector v) {
+        if (noAttributes.profile(v.getAttributes() == null)) {
+            return v;
+        } else {
+            return (RAbstractRawVector) v.copyDropAttributes();
         }
-        return result;
-    }
-
-    @Specialization
-    protected RRawVector asRaw(RAbstractVector vector) {
-        initCast();
-        return (RRawVector) castRawNode.executeRaw(vector);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsVector.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsVector.java
index 90def9d1d66e671944f5d9db7dbce3a4ab77cf64..0b2ccbc14eccadf9815d60c7143b773489712d2e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsVector.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsVector.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -32,6 +34,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.TypeSystemReference;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
@@ -48,12 +51,14 @@ import com.oracle.truffle.r.nodes.unary.CastListNode;
 import com.oracle.truffle.r.nodes.unary.CastListNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
 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.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -70,7 +75,7 @@ import com.oracle.truffle.r.runtime.data.RTypes;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "as.vector", kind = INTERNAL, parameterNames = {"x", "mode"})
+@RBuiltin(name = "as.vector", kind = INTERNAL, parameterNames = {"x", "mode"}, behavior = COMPLEX)
 public abstract class AsVector extends RBuiltinNode {
 
     @Child private AsVectorInternal internal = AsVectorInternalNodeGen.create();
@@ -81,7 +86,7 @@ public abstract class AsVector extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.firstStringWithError(1, RError.Message.INVALID_ARGUMENT, "mode");
+        casts.arg("mode").mustBe(scalarStringValue(), RError.Message.INVALID_ARGUMENT, "mode");
     }
 
     protected static AsVectorInternal createInternal() {
@@ -115,41 +120,54 @@ public abstract class AsVector extends RBuiltinNode {
         public abstract Object execute(Object x, String mode);
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        private final BranchProfile hasAttributes = BranchProfile.create();
+
+        private Object dropAttributesIfNeeded(Object o) {
+            Object res = o;
+            if (res instanceof RAttributable && ((RAttributable) res).getAttributes() != null) {
+                // the assertion should hold because of how cast works and it's only used for
+                // vectors (as per as.vector docs)
+                assert res instanceof RAbstractVector;
+                hasAttributes.enter();
+                res = ((RAbstractVector) res).copyDropAttributes();
+            }
+            return res;
+        }
 
         @Specialization(guards = "castToString(mode)")
         protected Object asVectorString(Object x, @SuppressWarnings("unused") String mode, //
-                        @Cached("create()") AsCharacter asCharacter) {
-            return asCharacter.execute(x);
+                        @Cached("createNonPreserving()") CastStringNode cast) {
+            return dropAttributesIfNeeded(cast.execute(x));
         }
 
         @Specialization(guards = "castToInt(x, mode)")
         protected Object asVectorInt(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
                         @Cached("createNonPreserving()") CastIntegerNode cast) {
-            return cast.execute(x);
+            return dropAttributesIfNeeded(cast.execute(x));
         }
 
         @Specialization(guards = "castToDouble(x, mode)")
         protected Object asVectorDouble(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
                         @Cached("createNonPreserving()") CastDoubleNode cast) {
-            return cast.execute(x);
+            return dropAttributesIfNeeded(cast.execute(x));
         }
 
         @Specialization(guards = "castToComplex(x, mode)")
         protected Object asVectorComplex(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
                         @Cached("createNonPreserving()") CastComplexNode cast) {
-            return cast.execute(x);
+            return dropAttributesIfNeeded(cast.execute(x));
         }
 
         @Specialization(guards = "castToLogical(x, mode)")
         protected Object asVectorLogical(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
                         @Cached("createNonPreserving()") CastLogicalNode cast) {
-            return cast.execute(x);
+            return dropAttributesIfNeeded(cast.execute(x));
         }
 
         @Specialization(guards = "castToRaw(x, mode)")
         protected Object asVectorRaw(RAbstractContainer x, @SuppressWarnings("unused") String mode, //
                         @Cached("createNonPreserving()") CastRawNode cast) {
-            return cast.execute(x);
+            return dropAttributesIfNeeded(cast.execute(x));
         }
 
         protected static CastListNode createListCast() {
@@ -207,12 +225,20 @@ public abstract class AsVector extends RBuiltinNode {
         }
 
         @Specialization(guards = "modeIsPairList(mode)")
+        @TruffleBoundary
         protected Object asVectorPairList(RList x, @SuppressWarnings("unused") String mode) {
             // TODO implement non-empty element list conversion; this is a placeholder for type test
             if (x.getLength() == 0) {
                 return RNull.instance;
             } else {
-                throw RError.nyi(RError.SHOW_CALLER, "non-empty lists");
+                Object list = RNull.instance;
+                RStringVector names = x.getNames();
+                for (int i = x.getLength() - 1; i >= 0; i--) {
+                    Object name = names == null ? RNull.instance : RDataFactory.createSymbolInterned(names.getDataAt(i));
+                    Object data = x.getDataAt(i);
+                    list = RDataFactory.createPairList(data, list, name);
+                }
+                return list;
             }
         }
 
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 713e74d6bd41b5172af860d9a7191bd1b587146e..f0db2b54703a95e4e9f0ed6e19fd5af7cbd31acb 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
@@ -22,8 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Cached;
@@ -33,10 +36,9 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 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;
@@ -51,7 +53,7 @@ import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
  * </ul>
  *
  */
-@RBuiltin(name = "assign", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"x", "value", "envir", "inherits"})
+@RBuiltin(name = "assign", visibility = OFF, kind = INTERNAL, parameterNames = {"x", "value", "envir", "inherits"}, behavior = COMPLEX)
 public abstract class Assign extends RBuiltinNode {
 
     private final BranchProfile errorProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AttachFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AttachFunctions.java
index d36683f74e81a4a3762c95530a66f17fc7bd6541..2b02daeba067b2adcd8bd163961271a7858f2e46 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AttachFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AttachFunctions.java
@@ -22,16 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -43,7 +44,7 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.REnvironment.DetachException;
 
 public class AttachFunctions {
-    @RBuiltin(name = "attach", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"what", "pos", "name"})
+    @RBuiltin(name = "attach", visibility = OFF, kind = INTERNAL, parameterNames = {"what", "pos", "name"}, behavior = COMPLEX)
     public abstract static class Attach extends RBuiltinNode {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
@@ -105,7 +106,7 @@ public class AttachFunctions {
         }
     }
 
-    @RBuiltin(name = "detach", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"pos"})
+    @RBuiltin(name = "detach", visibility = OFF, kind = INTERNAL, parameterNames = {"pos"}, behavior = COMPLEX)
     public abstract static class Detach extends RBuiltinNode {
 
         @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java
index c685475df731712b12a7f41d22df26357438b07f..ff4f6c27ec13c932a6ed6dd1d7b6462a1ee6054a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -32,9 +33,9 @@ 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.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RAttributes;
@@ -47,7 +48,7 @@ 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.RAbstractVector;
 
-@RBuiltin(name = "attr", kind = PRIMITIVE, parameterNames = {"x", "which", "exact"})
+@RBuiltin(name = "attr", kind = PRIMITIVE, parameterNames = {"x", "which", "exact"}, behavior = PURE)
 public abstract class Attr extends RBuiltinNode {
 
     private final ConditionProfile searchPartialProfile = ConditionProfile.createBinaryProfile();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
index a3ad15593847ee6d3da152e2505624ec12fd88b4..06fdbe2a5ff5f45c92792f427e7fe3c69796078f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import java.util.Arrays;
 
@@ -33,9 +34,9 @@ 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.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributes;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
@@ -46,7 +47,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "attributes", kind = PRIMITIVE, parameterNames = {"obj"})
+@RBuiltin(name = "attributes", kind = PRIMITIVE, parameterNames = {"obj"}, behavior = PURE)
 public abstract class Attributes extends RBuiltinNode {
 
     private final BranchProfile rownamesBranch = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BaseGammaFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BaseGammaFunctions.java
index 98ac9575c76ee9b487ed6688156f14ef8f5787c6..9c39c508526239c6362bd5a201b28acca420f4e5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BaseGammaFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BaseGammaFunctions.java
@@ -19,7 +19,9 @@ import static com.oracle.truffle.r.library.stats.StatsUtil.DBL_MIN_EXP;
 import static com.oracle.truffle.r.library.stats.StatsUtil.M_LOG10_2;
 import static com.oracle.truffle.r.library.stats.StatsUtil.M_PI;
 import static com.oracle.truffle.r.library.stats.StatsUtil.fmax2;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -31,10 +33,9 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.library.stats.GammaFunctions;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.BaseGammaFunctionsFactory.DpsiFnCalcNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.closures.RClosures;
@@ -47,7 +48,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 public class BaseGammaFunctions {
 
-    @RBuiltin(name = "gamma", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "gamma", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Gamma extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -56,7 +57,7 @@ public class BaseGammaFunctions {
         }
     }
 
-    @RBuiltin(name = "trigamma", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "trigamma", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class TriGamma extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -65,7 +66,7 @@ public class BaseGammaFunctions {
         }
     }
 
-    @RBuiltin(name = "lgamma", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "lgamma", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Lgamma extends RBuiltinNode {
 
         private final NACheck naValCheck = NACheck.create();
@@ -105,7 +106,7 @@ public class BaseGammaFunctions {
         }
     }
 
-    @RBuiltin(name = "digamma", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "digamma", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class DiGamma extends RBuiltinNode {
 
         @Child private DpsiFnCalc dpsiFnCalc;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index 87d627da486027bc7e0850cc681e71bc99b6fb8f..8170e1d1b19f680b8335398e890fe242f5ef98cb 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -74,8 +74,8 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrls;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrlsNodeGen;
 import com.oracle.truffle.r.nodes.unary.UnaryNotNode;
 import com.oracle.truffle.r.nodes.unary.UnaryNotNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.data.FastPathFactory;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.nodes.RFastPathNode;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
@@ -277,7 +277,6 @@ public class BasePackage extends RBuiltinPackage {
         add(Exists.class, ExistsNodeGen::create);
         add(Expression.class, ExpressionNodeGen::create);
         add(FastRContext.CloseChannel.class, FastRContextFactory.CloseChannelNodeGen::create);
-        add(FastRContext.Create.class, FastRContextFactory.CreateNodeGen::create);
         add(FastRContext.CreateChannel.class, FastRContextFactory.CreateChannelNodeGen::create);
         add(FastRContext.Eval.class, FastRContextFactory.EvalNodeGen::create);
         add(FastRContext.Get.class, FastRContextFactory.GetNodeGen::create);
@@ -575,8 +574,8 @@ public class BasePackage extends RBuiltinPackage {
         add(Tabulate.class, TabulateNodeGen::create);
         add(TempDir.class, TempDirNodeGen::create);
         add(TempFile.class, TempFileNodeGen::create);
-        add(ToLowerOrUpper.ToLower.class, ToLowerOrUpper::createToLower);
-        add(ToLowerOrUpper.ToUpper.class, ToLowerOrUpper::createToUpper);
+        add(ToLowerOrUpper.ToLower.class, ToLowerOrUpperFactory.ToLowerNodeGen::create);
+        add(ToLowerOrUpper.ToUpper.class, ToLowerOrUpperFactory.ToUpperNodeGen::create);
         add(Traceback.class, TracebackNodeGen::create);
         add(TraceFunctions.PrimTrace.class, TraceFunctionsFactory.PrimTraceNodeGen::create);
         add(TraceFunctions.PrimUnTrace.class, TraceFunctionsFactory.PrimUnTraceNodeGen::create);
@@ -625,13 +624,13 @@ public class BasePackage extends RBuiltinPackage {
         add(Vector.class, VectorNodeGen::create);
         add(Warning.class, WarningNodeGen::create);
         add(WhichFunctions.Which.class, WhichFunctionsFactory.WhichNodeGen::create);
-        add(WhichFunctions.WhichMax.class, WhichFunctionsFactory.WhichMaxNodeGen::create);
-        add(WhichFunctions.WhichMin.class, WhichFunctionsFactory.WhichMinNodeGen::create);
+        add(WhichFunctions.WhichMax.class, WhichFunctions.WhichMax::create);
+        add(WhichFunctions.WhichMin.class, WhichFunctions.WhichMin::create);
         add(Xtfrm.class, XtfrmNodeGen::create);
     }
 
     private static void addFastPath(MaterializedFrame baseFrame, String name, FastPathFactory factory) {
-        RFunction function = ReadVariableNode.lookupFunction(name, baseFrame, false);
+        RFunction function = ReadVariableNode.lookupFunction(name, baseFrame);
         ((RRootNode) function.getRootNode()).setFastPath(factory);
     }
 
@@ -663,7 +662,7 @@ public class BasePackage extends RBuiltinPackage {
 
     private static void setContainsDispatch(MaterializedFrame baseFrame, String... functions) {
         for (String name : functions) {
-            RFunction function = ReadVariableNode.lookupFunction(name, baseFrame, false);
+            RFunction function = ReadVariableNode.lookupFunction(name, baseFrame);
             ((RRootNode) function.getRootNode()).setContainsDispatch(true);
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bincode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bincode.java
index 740bf0f3b6644227ce35b5cac9bc03f8526aac97..feb0ae5f278c8bfaea909c862ae52cafb7910c97 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bincode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bincode.java
@@ -12,21 +12,22 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "bincode", kind = INTERNAL, parameterNames = {"x", "breaks", "right", "include.lowest"})
+@RBuiltin(name = "bincode", kind = INTERNAL, parameterNames = {"x", "breaks", "right", "include.lowest"}, behavior = PURE)
 public abstract class Bincode extends RBuiltinNode {
 
     private final BranchProfile errorProfile = BranchProfile.create();
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 8aea4bf0f6e8f4bd6dae3bd29ece426ecac1a99d..eed6487e7d200d01f870fc30c9c3217d6c11d3d1 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
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Arrays;
 
@@ -53,11 +54,11 @@ import com.oracle.truffle.r.nodes.unary.PrecedenceNode;
 import com.oracle.truffle.r.nodes.unary.PrecedenceNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -397,7 +398,7 @@ public abstract class Bind extends RBaseNode {
         return false;
     }
 
-    @RBuiltin(name = "cbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."})
+    @RBuiltin(name = "cbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."}, behavior = COMPLEX)
     public abstract static class CbindInternal extends RBuiltinNode {
 
         @Child private Bind bind = BindNodeGen.create(BindType.cbind);
@@ -511,7 +512,7 @@ public abstract class Bind extends RBaseNode {
         return result;
     }
 
-    @RBuiltin(name = "rbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."})
+    @RBuiltin(name = "rbind", kind = INTERNAL, parameterNames = {"deparse.level", "..."}, behavior = COMPLEX)
     public abstract static class RbindInternal extends RBuiltinNode {
 
         @Child private Bind bind = BindNodeGen.create(BindType.rbind);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BitwiseFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BitwiseFunctions.java
index 2aad58eac63aadb179b8c5a8cc46544998fcbb8d..2661b83fdd58b015a5f6e1a1757614aa112ffd72 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BitwiseFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BitwiseFunctions.java
@@ -11,6 +11,9 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
@@ -19,12 +22,11 @@ import com.oracle.truffle.r.nodes.binary.CastTypeNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -169,7 +171,7 @@ public class BitwiseFunctions {
         }
     }
 
-    @RBuiltin(name = "bitwiseAnd", kind = RBuiltinKind.INTERNAL, parameterNames = {"a", "b"})
+    @RBuiltin(name = "bitwiseAnd", kind = INTERNAL, parameterNames = {"a", "b"}, behavior = PURE)
     public abstract static class BitwiseAnd extends BasicBitwise {
 
         @Specialization
@@ -178,7 +180,7 @@ public class BitwiseFunctions {
         }
     }
 
-    @RBuiltin(name = "bitwiseOr", kind = RBuiltinKind.INTERNAL, parameterNames = {"a", "b"})
+    @RBuiltin(name = "bitwiseOr", kind = INTERNAL, parameterNames = {"a", "b"}, behavior = PURE)
     public abstract static class BitwiseOr extends BasicBitwise {
 
         @Specialization
@@ -187,7 +189,7 @@ public class BitwiseFunctions {
         }
     }
 
-    @RBuiltin(name = "bitwiseXor", kind = RBuiltinKind.INTERNAL, parameterNames = {"a", "b"})
+    @RBuiltin(name = "bitwiseXor", kind = INTERNAL, parameterNames = {"a", "b"}, behavior = PURE)
     public abstract static class BitwiseXor extends BasicBitwise {
 
         @Specialization
@@ -196,7 +198,7 @@ public class BitwiseFunctions {
         }
     }
 
-    @RBuiltin(name = "bitwiseShiftR", kind = RBuiltinKind.INTERNAL, parameterNames = {"a", "n"})
+    @RBuiltin(name = "bitwiseShiftR", kind = INTERNAL, parameterNames = {"a", "n"}, behavior = PURE)
     public abstract static class BitwiseShiftR extends BasicBitwise {
 
         @Specialization(guards = {"!shiftByCharacter(n)"})
@@ -212,7 +214,7 @@ public class BitwiseFunctions {
         }
     }
 
-    @RBuiltin(name = "bitwiseShiftL", kind = RBuiltinKind.INTERNAL, parameterNames = {"a", "n"})
+    @RBuiltin(name = "bitwiseShiftL", kind = INTERNAL, parameterNames = {"a", "n"}, behavior = PURE)
     public abstract static class BitwiseShiftL extends BasicBitwise {
 
         @Specialization(guards = {"!shiftByCharacter(n)"})
@@ -228,7 +230,7 @@ public class BitwiseFunctions {
         }
     }
 
-    @RBuiltin(name = "bitwiseNot", kind = RBuiltinKind.INTERNAL, parameterNames = {"a"})
+    @RBuiltin(name = "bitwiseNot", kind = INTERNAL, parameterNames = {"a"}, behavior = PURE)
     public abstract static class BitwiseNot extends BasicBitwise {
 
         @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Body.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Body.java
index bdd5ffd060c663c89905f40ea43a97bdf22e7bd6..7533186dd55b6c228eaab8c3fd92e954277aca6f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Body.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Body.java
@@ -22,17 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
 
-@RBuiltin(name = "body", kind = INTERNAL, parameterNames = {"fun"})
+@RBuiltin(name = "body", kind = INTERNAL, parameterNames = {"fun"}, behavior = PURE)
 public abstract class Body extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java
index f544f9d7be967824a5b1e782f1120aece6eb1aa2..ce2427e8087414c5b0958463e42b31a028008c09 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java
@@ -22,6 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -33,13 +38,11 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.helpers.BrowserInteractNode;
 import com.oracle.truffle.r.nodes.builtin.helpers.BrowserInteractNodeGen;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RCaller;
 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.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RNull;
 
@@ -58,7 +61,7 @@ public class BrowserFunctions {
 
     private static final ArrayList<HelperState> helperState = new ArrayList<>();
 
-    @RBuiltin(name = "browser", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"text", "condition", "expr", "skipCalls"})
+    @RBuiltin(name = "browser", visibility = OFF, kind = PRIMITIVE, parameterNames = {"text", "condition", "expr", "skipCalls"}, behavior = COMPLEX)
     public abstract static class BrowserNode extends RBuiltinNode {
 
         @Child private BrowserInteractNode browserInteractNode = BrowserInteractNodeGen.create();
@@ -115,7 +118,7 @@ public class BrowserFunctions {
         }
     }
 
-    @RBuiltin(name = "browserText", kind = RBuiltinKind.INTERNAL, parameterNames = {"n"})
+    @RBuiltin(name = "browserText", kind = INTERNAL, parameterNames = {"n"}, behavior = COMPLEX)
     public abstract static class BrowserText extends RetrieveAdapter {
 
         @Specialization
@@ -131,7 +134,7 @@ public class BrowserFunctions {
         }
     }
 
-    @RBuiltin(name = "browserCondition", kind = RBuiltinKind.INTERNAL, parameterNames = {"n"})
+    @RBuiltin(name = "browserCondition", kind = INTERNAL, parameterNames = {"n"}, behavior = COMPLEX)
     public abstract static class BrowserCondition extends RetrieveAdapter {
 
         @Specialization
@@ -147,7 +150,7 @@ public class BrowserFunctions {
         }
     }
 
-    @RBuiltin(name = "browserSetDebug", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"n"})
+    @RBuiltin(name = "browserSetDebug", visibility = OFF, kind = INTERNAL, parameterNames = {"n"}, behavior = COMPLEX)
     public abstract static class BrowserSetDebug extends RetrieveAdapter {
 
         @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CacheClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CacheClass.java
index 7ede6b4e673a15926acbadcef7329f9a42020665..9cf229dc3349778e0d333973263f9b1f6f128fd9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CacheClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CacheClass.java
@@ -13,18 +13,19 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = ".cache_class", kind = PRIMITIVE, parameterNames = {"class", "extends"})
+@RBuiltin(name = ".cache_class", kind = PRIMITIVE, parameterNames = {"class", "extends"}, behavior = COMPLEX)
 public abstract class CacheClass extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java
index ed7e87edb2e586f2f863dda24acc486d091afb84..a1c627dc33ffa1fbd12b72ecd1a47ad6f8c70738 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Call.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
@@ -31,8 +32,8 @@ import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -46,7 +47,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  *
  * Does not perform argument matching for first parameter "name".
  */
-@RBuiltin(name = "call", kind = PRIMITIVE, parameterNames = {"", "..."})
+@RBuiltin(name = "call", kind = PRIMITIVE, parameterNames = {"", "..."}, behavior = PURE)
 public abstract class Call extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CapabilitiesFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CapabilitiesFunctions.java
index 9747ccc12e44080a9ec23d3b90fb4b82eeea13ce..32cd5b8d6140411a6b000c9773b08c697218489d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CapabilitiesFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CapabilitiesFunctions.java
@@ -22,11 +22,13 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
@@ -34,7 +36,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 
 public class CapabilitiesFunctions {
 
-    @RBuiltin(name = "capabilities", kind = RBuiltinKind.INTERNAL, parameterNames = {})
+    @RBuiltin(name = "capabilities", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class Capabilities extends RBuiltinNode {
         private enum Capability {
             jpeg(false, null),
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java
index aab602aa90555030e154749dfcbdf72b8dcd8fd6..820ebe68374842f7955c7060ed0fea3dffdbd603 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Cat.java
@@ -33,6 +33,9 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.scalarLogica
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -44,12 +47,10 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.ToStringNode;
 import com.oracle.truffle.r.nodes.unary.ToStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -62,7 +63,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 /**
  * The {@code cat .Internal}.
  */
-@RBuiltin(name = "cat", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"arglist", "file", "sep", "fill", "labels", "append"})
+@RBuiltin(name = "cat", visibility = OFF, kind = INTERNAL, parameterNames = {"arglist", "file", "sep", "fill", "labels", "append"}, behavior = IO)
 public abstract class Cat extends RBuiltinNode {
 
     @Child private ToStringNode toString;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Ceiling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Ceiling.java
index 0d6fb7280528edfee85d9ce6fa21756a11f42e67..1d60402d1004bbcfec766753ad8b3155ed768797 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Ceiling.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Ceiling.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNode;
@@ -30,13 +32,12 @@ import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.ops.UnaryArithmeticFactory;
 
-@RBuiltin(name = "ceiling", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+@RBuiltin(name = "ceiling", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
 public abstract class Ceiling extends RBuiltinNode {
 
     public static final UnaryArithmeticFactory CEILING = CeilingArithmetic::new;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CharMatch.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CharMatch.java
index f70e8111bfafb0212cd9efe05f86456bb5482dc7..b8d013e51094dd52e3e76d33acc38b666c2f07a9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CharMatch.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CharMatch.java
@@ -10,18 +10,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.model.RAbstractStringVector;
 
-@RBuiltin(name = "charmatch", kind = INTERNAL, parameterNames = {"x", "table", "noMatch"})
+@RBuiltin(name = "charmatch", kind = INTERNAL, parameterNames = {"x", "table", "noMatch"}, behavior = PURE)
 public abstract class CharMatch extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Choose.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Choose.java
index ab57ab2e28e85247d28652b13e3ca47ce6818e93..cac9ba5f4c1832f2195a3452f63d3a9dcf84c150 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Choose.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Choose.java
@@ -22,17 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import java.util.function.IntToDoubleFunction;
 import java.util.function.IntUnaryOperator;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -40,7 +42,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 /**
  * Binomial coefficients (n, k) for real n and integral k (rounded with warning).
  */
-@RBuiltin(name = "choose", kind = RBuiltinKind.INTERNAL, parameterNames = {"n", "k"})
+@RBuiltin(name = "choose", kind = INTERNAL, parameterNames = {"n", "k"}, behavior = PURE)
 public abstract class Choose extends RBuiltinNode {
 
     // TODO: cast of logicals to integers
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Col.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Col.java
index 570c5588a46b48e5af0eb3910f00ae8717c8d2c6..fdfb7fc3a832adea1877761167956f9836bae5e4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Col.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Col.java
@@ -23,20 +23,21 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Arrays;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+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.model.RAbstractIntVector;
 
-@RBuiltin(name = "col", kind = INTERNAL, parameterNames = {"dims"})
+@RBuiltin(name = "col", kind = INTERNAL, parameterNames = {"dims"}, behavior = PURE)
 public abstract class Col extends RBuiltinNode {
 
     @Override
@@ -55,5 +56,4 @@ public abstract class Col extends RBuiltinNode {
         }
         return RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR, new int[]{nrows, ncols});
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ColMeans.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ColMeans.java
index 017c9e9b76c4a628f7dff8b3a3461b22fac66f8f..518c42f361639a3b9265921b49be991dd66606ae 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ColMeans.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ColMeans.java
@@ -12,41 +12,144 @@ 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.toBoolean;
+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.dsl.Specialization;
+import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDataFactory;
 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.model.RAbstractDoubleVector;
+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.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 //Implements .colMeans
-@RBuiltin(name = "colMeans", kind = RBuiltinKind.INTERNAL, parameterNames = {"X", "m", "n", "na.rm"})
+@RBuiltin(name = "colMeans", kind = INTERNAL, parameterNames = {"X", "m", "n", "na.rm"}, behavior = PURE)
 public abstract class ColMeans extends RBuiltinNode {
 
     @Child private BinaryArithmetic add = BinaryArithmetic.ADD.create();
+
     private final NACheck na = NACheck.create();
+    private final ConditionProfile vectorLengthProfile = ConditionProfile.createBinaryProfile();
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.arg("X").mustBe(numericValue(), RError.Message.X_NUMERIC);
+        casts.arg("X").mustBe(numericValue(), RError.NO_CALLER, RError.Message.X_NUMERIC);
+
+        casts.arg("m").defaultError(RError.NO_CALLER, RError.Message.INVALID_ARGUMENT, "n").asIntegerVector().findFirst().notNA(RError.NO_CALLER, RError.Message.VECTOR_SIZE_NA);
+
+        casts.arg("n").defaultError(RError.NO_CALLER, RError.Message.INVALID_ARGUMENT, "p").asIntegerVector().findFirst().notNA(RError.NO_CALLER, RError.Message.VECTOR_SIZE_NA);
+
+        casts.arg("na.rm").defaultError(RError.NO_CALLER, RError.Message.INVALID_ARGUMENT, "na.rm").asLogicalVector().findFirst().notNA().map(toBoolean());
+    }
+
+    private void checkVectorLength(RAbstractVector x, int rowNum, int colNum) {
+        if (vectorLengthProfile.profile(x.getLength() < rowNum * colNum)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+    }
+
+    protected boolean isEmptyMatrix(int rowNum, int colNum) {
+        return rowNum == 0 && colNum == 0;
+    }
+
+    @Specialization(guards = "isEmptyMatrix(rowNum, colNum)")
+    @SuppressWarnings("unused")
+    protected RDoubleVector colMeansEmptyMatrix(Object x, int rowNum, int colNum, boolean naRm) {
+        return RDataFactory.createEmptyDoubleVector();
+    }
+
+    @Specialization(guards = "!naRm")
+    protected RDoubleVector colMeansScalarNaRmFalse(double x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
 
-        casts.arg("m").asIntegerVector().findFirst().notNA();
+        return RDataFactory.createDoubleVectorFromScalar(x);
+    }
 
-        casts.arg("n").asIntegerVector().findFirst().notNA();
+    @Specialization(guards = "naRm")
+    protected RDoubleVector colMeansScalarNaRmTrue(double x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
 
-        casts.arg("na.rm").asLogicalVector().findFirst().map(toBoolean());
+        na.enable(x);
+        if (!na.check(x) && !Double.isNaN(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(Double.NaN);
+        }
     }
 
     @Specialization(guards = "!naRm")
-    protected RDoubleVector colMeansNaRmFalse(RDoubleVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+    protected RDoubleVector colMeansScalarNaRmFalse(int x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+
+        na.enable(x);
+        if (!na.check(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(RRuntime.DOUBLE_NA);
+        }
+    }
+
+    @Specialization(guards = "naRm")
+    protected RDoubleVector colMeansScalarNaRmTrue(int x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+
+        na.enable(x);
+        if (!na.check(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(Double.NaN);
+        }
+    }
+
+    @Specialization(guards = "!naRm")
+    protected RDoubleVector colMeansScalarNaRmFalse(byte x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+
+        na.enable(x);
+        if (!na.check(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(RRuntime.DOUBLE_NA);
+        }
+    }
+
+    @Specialization(guards = "naRm")
+    protected RDoubleVector colMeansScalarNaRmTrue(byte x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+
+        na.enable(x);
+        if (!na.check(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(Double.NaN);
+        }
+    }
+
+    @Specialization(guards = "!naRm")
+    protected RDoubleVector colMeansNaRmFalse(RAbstractDoubleVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        checkVectorLength(x, rowNum, colNum);
+
         double[] result = new double[colNum];
         boolean isComplete = true;
         na.enable(x);
@@ -71,7 +174,9 @@ public abstract class ColMeans extends RBuiltinNode {
     }
 
     @Specialization(guards = "naRm")
-    protected RDoubleVector colMeansNaRmTrue(RDoubleVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+    protected RDoubleVector colMeansNaRmTrue(RAbstractDoubleVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        checkVectorLength(x, rowNum, colNum);
+
         double[] result = new double[colNum];
         boolean isComplete = true;
         na.enable(x);
@@ -96,7 +201,9 @@ public abstract class ColMeans extends RBuiltinNode {
     }
 
     @Specialization(guards = "!naRm")
-    protected RDoubleVector colMeansNaRmFalse(RLogicalVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+    protected RDoubleVector colMeansNaRmFalse(RAbstractLogicalVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        checkVectorLength(x, rowNum, colNum);
+
         double[] result = new double[colNum];
         na.enable(x);
         nextCol: for (int c = 0; c < colNum; c++) {
@@ -115,7 +222,9 @@ public abstract class ColMeans extends RBuiltinNode {
     }
 
     @Specialization(guards = "naRm")
-    protected RDoubleVector colMeansNaRmTrue(RLogicalVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+    protected RDoubleVector colMeansNaRmTrue(RAbstractLogicalVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        checkVectorLength(x, rowNum, colNum);
+
         double[] result = new double[colNum];
         boolean isComplete = true;
         na.enable(x);
@@ -140,7 +249,9 @@ public abstract class ColMeans extends RBuiltinNode {
     }
 
     @Specialization(guards = "!naRm")
-    protected RDoubleVector colMeansNaRmFalse(RIntVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+    protected RDoubleVector colMeansNaRmFalse(RAbstractIntVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        checkVectorLength(x, rowNum, colNum);
+
         double[] result = new double[colNum];
         na.enable(x);
         nextCol: for (int c = 0; c < colNum; c++) {
@@ -159,7 +270,9 @@ public abstract class ColMeans extends RBuiltinNode {
     }
 
     @Specialization(guards = "naRm")
-    protected RDoubleVector colMeansNaRmTrue(RIntVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+    protected RDoubleVector colMeansNaRmTrue(RAbstractIntVector x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        checkVectorLength(x, rowNum, colNum);
+
         double[] result = new double[colNum];
         boolean isComplete = true;
         na.enable(x);
@@ -182,5 +295,4 @@ public abstract class ColMeans extends RBuiltinNode {
         }
         return RDataFactory.createDoubleVector(result, isComplete);
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ColSums.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ColSums.java
index b10161d7cf90343e0fea8dc82c8282fb1002dbb5..23564e03eae97ff55fa53bd7554005217f87738a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ColSums.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ColSums.java
@@ -24,54 +24,157 @@ 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.toBoolean;
+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.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDataFactory;
 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.model.RAbstractDoubleVector;
+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.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "colSums", kind = RBuiltinKind.INTERNAL, parameterNames = {"X", "m", "n", "na.rm"})
+@RBuiltin(name = "colSums", kind = INTERNAL, parameterNames = {"X", "m", "n", "na.rm"}, behavior = PURE)
 public abstract class ColSums extends RBuiltinNode {
 
     @Child private BinaryArithmetic add = BinaryArithmetic.ADD.create();
 
     private final NACheck na = NACheck.create();
-
     private final ConditionProfile removeNA = ConditionProfile.createBinaryProfile();
+    private final ValueProfile concreteVectorProfile = ValueProfile.createClassProfile();
+    private final ConditionProfile vectorLengthProfile = ConditionProfile.createBinaryProfile();
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.arg("X").mustBe(numericValue(), RError.Message.X_NUMERIC);
+        casts.arg("X").mustBe(numericValue(), RError.NO_CALLER, RError.Message.X_NUMERIC);
+
+        casts.arg("m").defaultError(RError.NO_CALLER, RError.Message.INVALID_ARGUMENT, "n").asIntegerVector().findFirst().notNA(RError.NO_CALLER, RError.Message.VECTOR_SIZE_NA);
+
+        casts.arg("n").defaultError(RError.NO_CALLER, RError.Message.INVALID_ARGUMENT, "p").asIntegerVector().findFirst().notNA(RError.NO_CALLER, RError.Message.VECTOR_SIZE_NA);
+
+        casts.arg("na.rm").defaultError(RError.NO_CALLER, RError.Message.INVALID_ARGUMENT, "na.rm").asLogicalVector().findFirst().notNA().map(toBoolean());
+    }
+
+    private void checkVectorLength(RAbstractVector x, int rowNum, int colNum) {
+        if (vectorLengthProfile.profile(x.getLength() < rowNum * colNum)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+    }
+
+    protected boolean isEmptyMatrix(int rowNum, int colNum) {
+        return rowNum == 0 && colNum == 0;
+    }
+
+    @Specialization(guards = "isEmptyMatrix(rowNum, colNum)")
+    @SuppressWarnings("unused")
+    protected RDoubleVector colSumsEmptyMatrix(Object x, int rowNum, int colNum, boolean naRm) {
+        return RDataFactory.createEmptyDoubleVector();
+    }
 
-        casts.arg("m").asIntegerVector().findFirst().notNA();
+    @Specialization(guards = "!naRm")
+    protected RDoubleVector colSumsScalarNaRmFalse(double x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+
+        return RDataFactory.createDoubleVectorFromScalar(x);
+    }
+
+    @Specialization(guards = "naRm")
+    protected RDoubleVector colSumsScalarNaRmTrue(double x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+
+        na.enable(x);
+        if (!na.check(x) && !Double.isNaN(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(Double.NaN);
+        }
+    }
 
-        casts.arg("n").asIntegerVector().findFirst().notNA();
+    @Specialization(guards = "!naRm")
+    protected RDoubleVector colSumsScalarNaRmFalse(int x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
 
-        casts.arg("na.rm").asLogicalVector().findFirst().map(toBoolean());
+        na.enable(x);
+        if (!na.check(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(RRuntime.DOUBLE_NA);
+        }
+    }
+
+    @Specialization(guards = "naRm")
+    protected RDoubleVector colSumsScalarNaRmTrue(int x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+
+        na.enable(x);
+        if (!na.check(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(Double.NaN);
+        }
+    }
+
+    @Specialization(guards = "!naRm")
+    protected RDoubleVector colSumsScalarNaRmFalse(byte x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+
+        na.enable(x);
+        if (!na.check(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(RRuntime.DOUBLE_NA);
+        }
+    }
+
+    @Specialization(guards = "naRm")
+    protected RDoubleVector colSumsScalarNaRmTrue(byte x, int rowNum, int colNum, @SuppressWarnings("unused") boolean naRm) {
+        if (vectorLengthProfile.profile(rowNum * colNum > 1)) {
+            throw RError.error(RError.NO_CALLER, RError.Message.TOO_SHORT, "X");
+        }
+
+        na.enable(x);
+        if (!na.check(x)) {
+            return RDataFactory.createDoubleVectorFromScalar(x);
+        } else {
+            return RDataFactory.createDoubleVectorFromScalar(Double.NaN);
+        }
     }
 
     @Specialization
-    protected RDoubleVector colSums(RDoubleVector x, int rowNum, int colNum, boolean rnaParam) {
+    protected RDoubleVector colSums(RAbstractDoubleVector x, int rowNum, int colNum, boolean rnaParam) {
+        checkVectorLength(x, rowNum, colNum);
+
         double[] result = new double[colNum];
         boolean isComplete = true;
         na.enable(x);
         final boolean rna = removeNA.profile(rnaParam);
-        double[] data = x.getDataWithoutCopying();
+        final RAbstractDoubleVector profiledX = concreteVectorProfile.profile(x);
+
         int pos = 0;
         nextCol: for (int c = 0; c < colNum; c++) {
             double sum = 0;
             for (int i = 0; i < rowNum; i++) {
-                double el = data[pos++];
+                final double el = profiledX.getDataAt(pos++);
                 if (rna) {
                     if (!na.check(el) && !Double.isNaN(el)) {
                         sum = add.op(sum, el);
@@ -97,16 +200,18 @@ public abstract class ColSums extends RBuiltinNode {
     }
 
     @Specialization
-    protected RDoubleVector colSums(RLogicalVector x, int rowNum, int colNum, boolean rna) {
+    protected RDoubleVector colSums(RAbstractLogicalVector x, int rowNum, int colNum, boolean rna) {
+        checkVectorLength(x, rowNum, colNum);
+
         double[] result = new double[colNum];
         boolean isComplete = true;
         na.enable(x);
-        byte[] data = x.getDataWithoutCopying();
+        final RAbstractLogicalVector profiledX = concreteVectorProfile.profile(x);
         int pos = 0;
         nextCol: for (int c = 0; c < colNum; c++) {
             double sum = 0;
             for (int i = 0; i < rowNum; i++) {
-                byte el = data[pos++];
+                final byte el = profiledX.getDataAt(pos++);
                 if (rna) {
                     if (!na.check(el)) {
                         sum = add.op(sum, el);
@@ -127,16 +232,18 @@ public abstract class ColSums extends RBuiltinNode {
     }
 
     @Specialization
-    protected RDoubleVector colSums(RIntVector x, int rowNum, int colNum, boolean rna) {
+    protected RDoubleVector colSums(RAbstractIntVector x, int rowNum, int colNum, boolean rna) {
+        checkVectorLength(x, rowNum, colNum);
+
         double[] result = new double[colNum];
         boolean isComplete = true;
         na.enable(x);
-        int[] data = x.getDataWithoutCopying();
+        final RAbstractIntVector profiledX = concreteVectorProfile.profile(x);
         int pos = 0;
         nextCol: for (int c = 0; c < colNum; c++) {
             double sum = 0;
             for (int i = 0; i < rowNum; i++) {
-                int el = data[pos++];
+                final int el = profiledX.getDataAt(pos++);
                 if (rna) {
                     if (!na.check(el)) {
                         sum = add.op(sum, el);
@@ -155,5 +262,4 @@ public abstract class ColSums extends RBuiltinNode {
         }
         return RDataFactory.createDoubleVector(result, isComplete);
     }
-
 }
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 afd74a557766e6a17bee79c8c0abaa18fa33ac88..aa454c0002d8a29000c90055b468fb1ee87c8f77 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
@@ -31,7 +31,9 @@ import static com.oracle.truffle.r.nodes.unary.PrecedenceNode.LOGICAL_PRECEDENCE
 import static com.oracle.truffle.r.nodes.unary.PrecedenceNode.NO_PRECEDENCE;
 import static com.oracle.truffle.r.nodes.unary.PrecedenceNode.RAW_PRECEDENCE;
 import static com.oracle.truffle.r.nodes.unary.PrecedenceNode.STRING_PRECEDENCE;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
@@ -57,10 +59,9 @@ 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;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RAttributes;
@@ -74,7 +75,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "c", kind = PRIMITIVE, parameterNames = {"..."}, dispatch = RDispatch.INTERNAL_GENERIC)
+@RBuiltin(name = "c", kind = PRIMITIVE, parameterNames = {"..."}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class Combine extends RBuiltinNode {
 
     public static Combine create() {
@@ -361,7 +362,7 @@ public abstract class Combine extends RBuiltinNode {
             case LOGICAL_PRECEDENCE:
                 return CastLogicalNodeGen.create(true, false, false);
             case STRING_PRECEDENCE:
-                return CastStringNodeGen.create(false, true, false, false);
+                return CastStringNodeGen.create(true, false, false);
             case RAW_PRECEDENCE:
                 return CastRawNodeGen.create(true, false, false);
             case EXPRESSION_PRECEDENCE:
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java
index c491925fb9f6dd92424951659659d279745f9400..f2ad7c85cf048e88215dbd78575d5baebe2ab7b6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CommandArgs.java
@@ -22,17 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 
-@RBuiltin(name = "commandArgs", kind = INTERNAL, parameterNames = {})
+@RBuiltin(name = "commandArgs", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
 public abstract class CommandArgs extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CompilePKGS.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CompilePKGS.java
index 84f02a6229be8f0fe2075d2f2f5a040c140ef5e0..6850faf77ca56ca4492c741c59b86d14138e03fd 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CompilePKGS.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CompilePKGS.java
@@ -22,15 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
 /**
  * FastR does not byte-compile, obviously, but this keeps the package installation code happy.
  */
-@RBuiltin(name = "compilePKGS", kind = RBuiltinKind.INTERNAL, parameterNames = "enable")
+@RBuiltin(name = "compilePKGS", kind = INTERNAL, parameterNames = "enable", behavior = COMPLEX)
 public abstract class CompilePKGS extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Complex.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Complex.java
index ce364a8e0471b9ab7be948c354bace2715bd96b7..761897f79d0795cfbe701b0f57891553f3de442f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Complex.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Complex.java
@@ -22,62 +22,56 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.defaultValue;
+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.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "complex", kind = INTERNAL, parameterNames = {"length.out", "real", "imaginary"})
+@RBuiltin(name = "complex", kind = INTERNAL, parameterNames = {"length.out", "real", "imaginary"}, behavior = PURE)
 public abstract class Complex extends RBuiltinNode {
 
-    private static final RDoubleVector ZERO = RDataFactory.createDoubleVectorFromScalar(0.0);
-
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.toInteger(0);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization(guards = "resultEmpty(lengthOut, realAbsVec, imaginaryAbsVec)")
-    protected RComplexVector complexEmpty(int lengthOut, RAbstractDoubleVector realAbsVec, RAbstractDoubleVector imaginaryAbsVec) {
-        return RDataFactory.createEmptyComplexVector();
+        casts.arg("length.out").asIntegerVector().findFirst(Message.INVALID_LENGTH);
+        casts.arg("real").map(defaultValue(RDataFactory.createEmptyDoubleVector())).asDoubleVector();
+        casts.arg("imaginary").map(defaultValue(RDataFactory.createEmptyDoubleVector())).asDoubleVector();
     }
 
-    @Specialization(guards = "!resultEmpty(lengthOut, realAbsVec, imaginaryAbsVec)")
-    protected RComplexVector complex(int lengthOut, RAbstractDoubleVector realAbsVec, RAbstractDoubleVector imaginaryAbsVec) {
-        RDoubleVector real = checkLength(realAbsVec);
-        RDoubleVector imaginary = checkLength(imaginaryAbsVec);
-        int realLength = real.getLength();
-        int imaginaryLength = imaginary.getLength();
-        int length = Math.max(Math.max(realLength, imaginaryLength), lengthOut);
-        boolean complete = RDataFactory.COMPLETE_VECTOR;
+    @Specialization
+    protected RComplexVector complex(int lengthOut, RAbstractDoubleVector real, RAbstractDoubleVector imaginary,
+                    @Cached("create()") NACheck realNA,
+                    @Cached("create()") NACheck imaginaryNA,
+                    @Cached("create()") VectorLengthProfile realLengthProfile,
+                    @Cached("create()") VectorLengthProfile imaginaryLengthProfile,
+                    @Cached("create()") VectorLengthProfile lengthProfile,
+                    @Cached("createCountingProfile()") LoopConditionProfile loopProfile) {
+        int realLength = realLengthProfile.profile(real.getLength());
+        int imaginaryLength = imaginaryLengthProfile.profile(imaginary.getLength());
+        int length = lengthProfile.profile(Math.max(Math.max(lengthOut, realLength), imaginaryLength));
         double[] data = new double[length << 1];
-        for (int i = 0; i < data.length; i += 2) {
-            data[i] = real.getDataAt((i >> 1) % realLength);
-            data[i + 1] = imaginary.getDataAt((i >> 1) % imaginaryLength);
-            if (RRuntime.isNA(data[i]) || RRuntime.isNA(data[i + 1])) {
-                complete = RDataFactory.INCOMPLETE_VECTOR;
-            }
-        }
-        return RDataFactory.createComplexVector(data, complete);
-    }
-
-    private static RDoubleVector checkLength(RAbstractDoubleVector v) {
-        if (v.getLength() == 0) {
-            return ZERO;
-        } else {
-            return v.materialize();
+        realNA.enable(real);
+        imaginaryNA.enable(imaginary);
+        loopProfile.profileCounted(length);
+        for (int i = 0; loopProfile.inject(i < data.length); i += 2) {
+            double realValue = realLength == 0 ? 0 : real.getDataAt((i >> 1) % realLength);
+            double imaginaryValue = imaginaryLength == 0 ? 0 : imaginary.getDataAt((i >> 1) % imaginaryLength);
+            data[i] = realValue;
+            data[i + 1] = imaginaryValue;
+            realNA.check(realValue);
+            imaginaryNA.check(imaginaryValue);
         }
-    }
-
-    protected static boolean resultEmpty(int lengthOut, RAbstractDoubleVector realAbsVec, RAbstractDoubleVector imaginaryAbsVec) {
-        return lengthOut == 0 && realAbsVec.getLength() == 0 && imaginaryAbsVec.getLength() == 0;
+        return RDataFactory.createComplexVector(data, realNA.neverSeenNA() && imaginaryNA.neverSeenNA());
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java
index 81374f68b8ee5f258af190030caccc92b57015f9..b4b560584bdae56422dd18f0ed0ea4f4e13a089e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConditionFunctions.java
@@ -12,6 +12,9 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RErrorHandling.getHandlerStack;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -20,12 +23,10 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -49,7 +50,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = ".addCondHands", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"classes", "handlers", "parentenv", "target", "calling"})
+    @RBuiltin(name = ".addCondHands", visibility = OFF, kind = INTERNAL, parameterNames = {"classes", "handlers", "parentenv", "target", "calling"}, behavior = COMPLEX)
     public abstract static class AddCondHands extends RBuiltinNode {
 
         @SuppressWarnings("unused")
@@ -74,7 +75,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = ".resetCondHands", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"stack"})
+    @RBuiltin(name = ".resetCondHands", visibility = OFF, kind = INTERNAL, parameterNames = {"stack"}, behavior = COMPLEX)
     public abstract static class ResetCondHands extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -95,7 +96,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = ".addRestart", kind = RBuiltinKind.INTERNAL, parameterNames = "restart")
+    @RBuiltin(name = ".addRestart", kind = INTERNAL, parameterNames = "restart", behavior = COMPLEX)
     public abstract static class AddRestart extends RestartAdapter {
         @Specialization
         protected Object addRestart(RList restart) {
@@ -109,7 +110,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = ".getRestart", kind = RBuiltinKind.INTERNAL, parameterNames = "restart")
+    @RBuiltin(name = ".getRestart", kind = INTERNAL, parameterNames = "restart", behavior = COMPLEX)
     public abstract static class GetRestart extends RBuiltinNode {
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -123,7 +124,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = ".invokeRestart", kind = RBuiltinKind.INTERNAL, parameterNames = {"restart", "args"})
+    @RBuiltin(name = ".invokeRestart", kind = INTERNAL, parameterNames = {"restart", "args"}, behavior = COMPLEX)
     public abstract static class InvokeRestart extends RestartAdapter {
         @Specialization(guards = "lengthok(restart)")
         protected RNull invokeRestart(RList restart, Object args) {
@@ -141,7 +142,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = ".signalCondition", kind = RBuiltinKind.INTERNAL, parameterNames = {"condition", "msg", "call"})
+    @RBuiltin(name = ".signalCondition", kind = INTERNAL, parameterNames = {"condition", "msg", "call"}, behavior = COMPLEX)
     public abstract static class SignalCondition extends RBuiltinNode {
         @Specialization
         protected RNull signalCondition(RList condition, RAbstractStringVector msg, Object call) {
@@ -150,7 +151,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = "geterrmessage", kind = RBuiltinKind.INTERNAL, parameterNames = {})
+    @RBuiltin(name = "geterrmessage", kind = INTERNAL, parameterNames = {}, behavior = COMPLEX)
     public abstract static class Geterrmessage extends RBuiltinNode {
         @Specialization
         protected String geterrmessage() {
@@ -158,7 +159,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = "seterrmessage", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = "msg")
+    @RBuiltin(name = "seterrmessage", visibility = OFF, kind = INTERNAL, parameterNames = "msg", behavior = COMPLEX)
     public abstract static class Seterrmessage extends RBuiltinNode {
         @Specialization
         protected RNull seterrmessage(RAbstractStringVector msg) {
@@ -168,7 +169,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = ".dfltWarn", kind = RBuiltinKind.INTERNAL, parameterNames = {"message", "call"})
+    @RBuiltin(name = ".dfltWarn", kind = INTERNAL, parameterNames = {"message", "call"}, behavior = COMPLEX)
     public abstract static class DfltWarn extends RBuiltinNode {
         @Specialization
         protected RNull dfltWarn(RAbstractStringVector msg, Object call) {
@@ -177,7 +178,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = ".dfltStop", kind = RBuiltinKind.INTERNAL, parameterNames = {"message", "call"})
+    @RBuiltin(name = ".dfltStop", kind = INTERNAL, parameterNames = {"message", "call"}, behavior = COMPLEX)
     public abstract static class DfltStop extends RBuiltinNode {
         @Specialization
         protected Object dfltStop(RAbstractStringVector message, Object call) {
@@ -186,7 +187,7 @@ public class ConditionFunctions {
         }
     }
 
-    @RBuiltin(name = "printDeferredWarnings", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {})
+    @RBuiltin(name = "printDeferredWarnings", visibility = OFF, kind = INTERNAL, parameterNames = {}, behavior = COMPLEX)
     public abstract static class PrintDeferredWarnings extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
index 8484e04d1a613c859cb341ad93f16ef49f5783c2..4a350001e6212c0f0f9d45643a09a097ad44fc13 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ConnectionFunctions.java
@@ -26,7 +26,11 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElemen
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.trueValue;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 import static com.oracle.truffle.r.runtime.conn.ConnectionSupport.getBaseConnection;
 import static com.oracle.truffle.r.runtime.conn.ConnectionSupport.removeFileURLPrefix;
 import static com.oracle.truffle.r.runtime.conn.StdConnections.getStderr;
@@ -50,11 +54,10 @@ import com.oracle.truffle.api.dsl.TypeSystemReference;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.ConnectionFunctionsFactory.WriteDataNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
 import com.oracle.truffle.r.runtime.conn.FileConnections.FileRConnection;
 import com.oracle.truffle.r.runtime.conn.GZIPConnections.GZIPRConnection;
@@ -95,7 +98,7 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
  *
  */
 public abstract class ConnectionFunctions {
-    @RBuiltin(name = "stdin", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "stdin", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class Stdin extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -104,7 +107,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "stdout", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "stdout", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class Stdout extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -113,7 +116,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "stderr", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "stderr", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class Stderr extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -122,7 +125,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "file", kind = INTERNAL, parameterNames = {"description", "open", "blocking", "encoding", "raw"})
+    @RBuiltin(name = "file", kind = INTERNAL, parameterNames = {"description", "open", "blocking", "encoding", "raw"}, behavior = IO)
     public abstract static class File extends RBuiltinNode {
 
         @Override
@@ -176,7 +179,7 @@ public abstract class ConnectionFunctions {
      * compressed by {@code bzip2, xz, lzma}. Currently we only support {@code gzip} and
      * uncompressed.
      */
-    @RBuiltin(name = "gzfile", kind = INTERNAL, parameterNames = {"description", "open", "encoding", "compression"})
+    @RBuiltin(name = "gzfile", kind = INTERNAL, parameterNames = {"description", "open", "encoding", "compression"}, behavior = IO)
     public abstract static class GZFile extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -202,7 +205,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "textConnection", kind = INTERNAL, parameterNames = {"nm", "object", "open", "env", "type"})
+    @RBuiltin(name = "textConnection", kind = INTERNAL, parameterNames = {"nm", "object", "open", "env", "type"}, behavior = IO)
     public abstract static class TextConnection extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -225,7 +228,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "textConnectionValue", kind = INTERNAL, parameterNames = {"con"})
+    @RBuiltin(name = "textConnectionValue", kind = INTERNAL, parameterNames = {"con"}, behavior = IO)
     public abstract static class TextConnectionValue extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -238,7 +241,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "socketConnection", kind = INTERNAL, parameterNames = {"host", "port", "server", "blocking", "open", "encoding", "timeout"})
+    @RBuiltin(name = "socketConnection", kind = INTERNAL, parameterNames = {"host", "port", "server", "blocking", "open", "encoding", "timeout"}, behavior = IO)
     public abstract static class SocketConnection extends RBuiltinNode {
 
         @Override
@@ -266,7 +269,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "url", kind = INTERNAL, parameterNames = {"description", "open", "blocking", "encoding"})
+    @RBuiltin(name = "url", kind = INTERNAL, parameterNames = {"description", "open", "blocking", "encoding"}, behavior = IO)
     public abstract static class URLConnection extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -291,7 +294,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "summary.connection", kind = INTERNAL, parameterNames = {"object}"})
+    @RBuiltin(name = "summary.connection", kind = INTERNAL, parameterNames = {"object}"}, behavior = IO)
     public abstract static class Summary extends CheckIsConnAdapter {
         private static final RStringVector NAMES = RDataFactory.createStringVector(new String[]{"description", "class", "mode", "text", "opened", "can read", "can write"},
                         RDataFactory.COMPLETE_VECTOR);
@@ -320,7 +323,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "open", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"con", "open", "blocking"})
+    @RBuiltin(name = "open", visibility = OFF, kind = INTERNAL, parameterNames = {"con", "open", "blocking"}, behavior = IO)
     public abstract static class Open extends CheckIsConnAdapter {
         @Specialization
         @TruffleBoundary
@@ -351,7 +354,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "isOpen", kind = INTERNAL, parameterNames = {"con", "rw"})
+    @RBuiltin(name = "isOpen", kind = INTERNAL, parameterNames = {"con", "rw"}, behavior = IO)
     public abstract static class IsOpen extends CheckIsConnAdapter {
         @Specialization
         @TruffleBoundary
@@ -381,7 +384,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "close", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"con", "type"})
+    @RBuiltin(name = "close", visibility = OFF, kind = INTERNAL, parameterNames = {"con", "type"}, behavior = IO)
     public abstract static class Close extends CheckIsConnAdapter {
         @Specialization
         @TruffleBoundary
@@ -418,7 +421,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "readLines", kind = INTERNAL, parameterNames = {"con", "n", "ok", "warn", "encoding", "skipNul"})
+    @RBuiltin(name = "readLines", kind = INTERNAL, parameterNames = {"con", "n", "ok", "warn", "encoding", "skipNul"}, behavior = IO)
     public abstract static class ReadLines extends InternalCloseHelper {
 
         @Override
@@ -456,7 +459,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "writeLines", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"text", "con", "sep", "useBytes"})
+    @RBuiltin(name = "writeLines", visibility = OFF, kind = INTERNAL, parameterNames = {"text", "con", "sep", "useBytes"}, behavior = IO)
     public abstract static class WriteLines extends InternalCloseHelper {
         @Specialization
         @TruffleBoundary
@@ -477,7 +480,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "flush", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"con"})
+    @RBuiltin(name = "flush", visibility = OFF, kind = INTERNAL, parameterNames = {"con"}, behavior = IO)
     public abstract static class Flush extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -491,7 +494,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "pushBack", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"data", "connection", "newLine", "type"})
+    @RBuiltin(name = "pushBack", visibility = OFF, kind = INTERNAL, parameterNames = {"data", "connection", "newLine", "type"}, behavior = IO)
     public abstract static class PushBack extends RBuiltinNode {
 
         @Override
@@ -535,7 +538,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "pushBackLength", kind = INTERNAL, parameterNames = {"connection"})
+    @RBuiltin(name = "pushBackLength", kind = INTERNAL, parameterNames = {"connection"}, behavior = IO)
     public abstract static class PushBackLength extends RBuiltinNode {
 
         @Specialization
@@ -549,7 +552,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "clearPushBack", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"connection"})
+    @RBuiltin(name = "clearPushBack", visibility = OFF, kind = INTERNAL, parameterNames = {"connection"}, behavior = IO)
     public abstract static class PushBackClear extends RBuiltinNode {
 
         @Specialization
@@ -564,7 +567,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "readChar", kind = INTERNAL, parameterNames = {"con", "nchars", "useBytes"})
+    @RBuiltin(name = "readChar", kind = INTERNAL, parameterNames = {"con", "nchars", "useBytes"}, behavior = IO)
     public abstract static class ReadChar extends InternalCloseHelper {
 
         @SuppressWarnings("unused")
@@ -602,7 +605,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "writeChar", visibility = RVisibility.CUSTOM, kind = INTERNAL, parameterNames = {"object", "con", "nchars", "eos", "useBytes"})
+    @RBuiltin(name = "writeChar", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"object", "con", "nchars", "eos", "useBytes"}, behavior = IO)
     public abstract static class WriteChar extends InternalCloseHelper {
         @TruffleBoundary
         @Specialization
@@ -640,7 +643,7 @@ public abstract class ConnectionFunctions {
         return buffer.order(nb);
     }
 
-    @RBuiltin(name = "readBin", kind = INTERNAL, parameterNames = {"con", "what", "n", "size", "signed", "swap"})
+    @RBuiltin(name = "readBin", kind = INTERNAL, parameterNames = {"con", "what", "n", "size", "signed", "swap"}, behavior = IO)
     public abstract static class ReadBin extends InternalCloseHelper {
 
         @Override
@@ -912,7 +915,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "writeBin", visibility = RVisibility.CUSTOM, kind = INTERNAL, parameterNames = {"object", "con", "size", "swap", "useBytes"})
+    @RBuiltin(name = "writeBin", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"object", "con", "size", "swap", "useBytes"}, behavior = IO)
     public abstract static class WriteBin extends InternalCloseHelper {
 
         @Override
@@ -954,7 +957,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "getConnection", kind = INTERNAL, parameterNames = {"what"})
+    @RBuiltin(name = "getConnection", kind = INTERNAL, parameterNames = {"what"}, behavior = IO)
     public abstract static class GetConnection extends RBuiltinNode {
 
         @Override
@@ -974,7 +977,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "getAllConnections", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "getAllConnections", kind = INTERNAL, parameterNames = {}, behavior = IO)
     public abstract static class GetAllConnections extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -983,7 +986,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "isSeekable", kind = INTERNAL, parameterNames = "con")
+    @RBuiltin(name = "isSeekable", kind = INTERNAL, parameterNames = "con", behavior = IO)
     public abstract static class IsSeekable extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -992,7 +995,7 @@ public abstract class ConnectionFunctions {
         }
     }
 
-    @RBuiltin(name = "seek", kind = INTERNAL, parameterNames = {"con", "where", "origin", "rw"})
+    @RBuiltin(name = "seek", kind = INTERNAL, parameterNames = {"con", "where", "origin", "rw"}, behavior = IO)
     public abstract static class Seek extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Contributors.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Contributors.java
index ee5aa390fc5b3989cff35d2ba17b0b71ad14c762..36d37e55470772d8bb70407f72db85070b568ad5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Contributors.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Contributors.java
@@ -22,21 +22,22 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.SUBSTITUTE;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.SUBSTITUTE;
 
 import java.io.IOException;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.data.RNull;
 
-@RBuiltin(name = "contributors", visibility = RVisibility.OFF, kind = SUBSTITUTE, parameterNames = {})
+@RBuiltin(name = "contributors", visibility = OFF, kind = SUBSTITUTE, parameterNames = {}, behavior = IO)
 public abstract class Contributors extends RBuiltinNode {
 
     private static final String CONTRIBUTORS = Utils.getResourceAsString(Contributors.class, "CONTRIBUTORS", true);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java
index 1ddeffa9ec1e4b907206d850c73ebb316af39eed..7cf35c43f2710d39e66e4f09fedc23335a5c4670 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java
@@ -22,18 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "copyDFattr", kind = INTERNAL, parameterNames = {"", ""})
+@RBuiltin(name = "copyDFattr", kind = INTERNAL, parameterNames = {"", ""}, behavior = COMPLEX)
 public abstract class CopyDFAttr extends RBuiltinNode {
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java
index 7d68c9c23eac2366bfd3947200f6f27caa9a7a36..bd48e8980243abb1d75214c3cc5b599653657019 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java
@@ -22,18 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "crossprod", kind = INTERNAL, parameterNames = {"x", "y"})
+@RBuiltin(name = "crossprod", kind = INTERNAL, parameterNames = {"x", "y"}, behavior = PURE)
 public abstract class Crossprod extends RBuiltinNode {
 
     @Child private MatMult matMult;
@@ -51,7 +52,7 @@ public abstract class Crossprod extends RBuiltinNode {
         return matMult.executeObject(op1, op2);
     }
 
-    private Object transpose(Object value) {
+    private Object transpose(RAbstractVector value) {
         if (transpose == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             transpose = insert(TransposeNodeGen.create(null));
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMax.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMax.java
index 55cc481f9e172afa537d97723b53973a054f1daa..b7782c6d00e951bd5cae5355e3987c012a2748dc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMax.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMax.java
@@ -10,7 +10,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import java.util.Arrays;
 
@@ -22,10 +24,9 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -39,7 +40,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "cummax", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+@RBuiltin(name = "cummax", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
 public abstract class CumMax extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMin.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMin.java
index c5652b5ebf61bcd88d63c5ba7c16b227c6de236b..2750b3179beb14df90563f66649ddee6b23699f3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMin.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMin.java
@@ -10,7 +10,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import java.util.Arrays;
 
@@ -22,10 +24,9 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -39,15 +40,14 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "cummin", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+@RBuiltin(name = "cummin", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
 public abstract class CumMin extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
+    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
     @Child private CastDoubleNode castDouble;
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
     @Specialization
     protected double cummin(double arg) {
         return arg;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java
index c00effddff95f8f2f580e5bc733f570dcf79cf4d..96fc9287d2681b8242bd61eea13be9be67dea076 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java
@@ -10,15 +10,16 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import java.util.Arrays;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
@@ -33,15 +34,14 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "cumprod", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+@RBuiltin(name = "cumprod", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
 public abstract class CumProd extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
+    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
     @Child private BinaryArithmetic mul = BinaryArithmetic.MULTIPLY.create();
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
     @Specialization
     protected int cumprod(int arg) {
         return arg;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumSum.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumSum.java
index 22c6d7aeae888c272c0a2d9e4badd1dc80b3764d..2195f2c9dd28e04248e240ab0598c90f01edabc6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumSum.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumSum.java
@@ -22,15 +22,16 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import java.util.Arrays;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
@@ -46,15 +47,14 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "cumsum", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+@RBuiltin(name = "cumsum", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
 public abstract class CumSum extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
+    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
     @Child private BinaryArithmetic add = BinaryArithmetic.ADD.create();
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
     @Specialization
     protected double cumsum(double arg) {
         return arg;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java
index cac2f3f6fa31e385d92bfa7b990fc8d13dad7226..0f9af5573a7f20ff75e07878e87cdb3d6a7339c4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DPut.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.IOException;
 
@@ -30,16 +32,15 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 
 /**
  * The {@code dput .Internal}.
  */
-@RBuiltin(name = "dput", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"x", "file", "opts"})
+@RBuiltin(name = "dput", visibility = OFF, kind = INTERNAL, parameterNames = {"x", "file", "opts"}, behavior = IO)
 public abstract class DPut extends RBuiltinNode {
 
     @Override
@@ -50,7 +51,6 @@ public abstract class DPut extends RBuiltinNode {
     @Specialization
     @TruffleBoundary
     protected Object dput(Object x, RConnection file, int opts) {
-
         String string = RDeparse.deparse(x, RDeparse.DEFAULT_Cutoff, true, opts, -1);
         try (RConnection openConn = file.forceOpen("wt")) {
             openConn.writeString(string, true);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Date.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Date.java
index 1047c48744808d71be323c5feb89b015de72ac33..2fc9b6f40b7db921f916333bc7eccc64a093de02 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Date.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Date.java
@@ -10,17 +10,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
-@RBuiltin(name = "date", kind = RBuiltinKind.INTERNAL, parameterNames = {})
+@RBuiltin(name = "date", kind = INTERNAL, parameterNames = {}, behavior = IO)
 public abstract class Date extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
index 953e8f256a94982780ee9e117f9f50595e09367d..dbf4ae312339bcad43447c707c1947a08746efff 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
@@ -11,6 +11,14 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.defaultValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notEmpty;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import java.text.ParsePosition;
 import java.time.DateTimeException;
 import java.time.Instant;
@@ -31,10 +39,9 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -44,7 +51,7 @@ import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 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.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
@@ -55,8 +62,8 @@ public class DatePOSIXFunctions {
     private static final class POSIXltBuilder {
 
         private static final String[] LT_NAMES = new String[]{"sec", "min", "hour", "mday", "mon", "year", "wday", "yday", "isdst"};
-        private static final RStringVector LT_NAMES_VEC = RDataFactory.createStringVector(LT_NAMES, RDataFactory.COMPLETE_VECTOR);
-        private static final RStringVector CLASS_ATTR = RDataFactory.createStringVector(new String[]{"POSIXlt", "POSIXt"}, RDataFactory.COMPLETE_VECTOR);
+        private static final RStringVector LT_NAMES_VEC = (RStringVector) RDataFactory.createStringVector(LT_NAMES, RDataFactory.COMPLETE_VECTOR).makeSharedPermanent();
+        private static final RStringVector CLASS_ATTR = (RStringVector) RDataFactory.createStringVector(new String[]{"POSIXlt", "POSIXt"}, RDataFactory.COMPLETE_VECTOR).makeSharedPermanent();
 
         public final double[] sec;
         public final int[] min;
@@ -126,14 +133,14 @@ public class DatePOSIXFunctions {
         }
     }
 
-    @RBuiltin(name = "Date2POSIXlt", kind = RBuiltinKind.INTERNAL, parameterNames = "x")
+    @RBuiltin(name = "Date2POSIXlt", kind = INTERNAL, parameterNames = "x", behavior = PURE)
     public abstract static class Date2POSIXlt extends RBuiltinNode {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.toVector(0).toDouble(0, true, false, false);
+            casts.arg("x").map(defaultValue(RDataFactory.createEmptyDoubleVector())).asDoubleVector();
         }
 
         @Specialization
@@ -162,25 +169,25 @@ public class DatePOSIXFunctions {
         }
     }
 
-    @RBuiltin(name = "as.POSIXlt", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "tz"})
+    @RBuiltin(name = "as.POSIXlt", kind = INTERNAL, parameterNames = {"x", "tz"}, behavior = READS_STATE)
     public abstract static class AsPOSIXlt extends RBuiltinNode {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.toVector(0).toDouble(0, true, false, false);
+            casts.arg("x").map(defaultValue(RDataFactory.createEmptyDoubleVector())).asDoubleVector(true, false, false);
+            casts.arg("tz").asStringVector().findFirst("");
         }
 
         @Specialization
         @TruffleBoundary
-        protected RList asPOSIXlt(RAbstractDoubleVector x, RAbstractStringVector tz) {
+        protected RList asPOSIXlt(RAbstractDoubleVector x, String tz) {
             TimeZone zone;
-            String tzone = RRuntime.asString(tz);
-            if (tzone.isEmpty()) {
+            if (tz.isEmpty()) {
                 zone = RContext.getInstance().getSystemTimeZone();
             } else {
-                zone = TimeZone.getTimeZone(tzone);
+                zone = TimeZone.getTimeZone(tz);
             }
             int xLen = x.getLength();
             POSIXltBuilder builder = new POSIXltBuilder(xLen, zone.getDisplayName(false, TimeZone.SHORT));
@@ -204,12 +211,18 @@ public class DatePOSIXFunctions {
         }
     }
 
-    @RBuiltin(name = "as.POSIXct", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "tz"})
+    @RBuiltin(name = "as.POSIXct", kind = INTERNAL, parameterNames = {"x", "tz"}, behavior = READS_STATE)
     public abstract static class AsPOSIXct extends RBuiltinNode {
 
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("x").mustBe(RAbstractListVector.class);
+            casts.arg("tz").asStringVector().findFirst("");
+        }
+
         @Specialization
         @TruffleBoundary
-        protected RDoubleVector asPOSIXct(RList x, RAbstractStringVector tz) {
+        protected RDoubleVector asPOSIXct(RAbstractListVector x, String tz) {
             RAbstractVector secVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(0));
             RAbstractVector minVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(1));
             RAbstractVector hourVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(2));
@@ -217,11 +230,10 @@ public class DatePOSIXFunctions {
             RAbstractVector monVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(4));
             RAbstractVector yearVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(5));
             TimeZone zone;
-            String tzone = RRuntime.asString(tz);
-            if (tzone.isEmpty()) {
+            if (tz.isEmpty()) {
                 zone = RContext.getInstance().getSystemTimeZone();
             } else {
-                zone = TimeZone.getTimeZone(tzone);
+                zone = TimeZone.getTimeZone(tz);
             }
 
             ZoneId zoneId = zone.toZoneId();
@@ -257,13 +269,18 @@ public class DatePOSIXFunctions {
         }
     }
 
-    @RBuiltin(name = "POSIXlt2Date", kind = RBuiltinKind.INTERNAL, parameterNames = {"x"})
+    @RBuiltin(name = "POSIXlt2Date", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
     public abstract static class POSIXlt2Date extends RBuiltinNode {
-        private static final RStringVector CLASS_ATTR = RDataFactory.createStringVector(new String[]{"Date"}, RDataFactory.COMPLETE_VECTOR);
+        private static final RStringVector CLASS_ATTR = (RStringVector) RDataFactory.createStringVectorFromScalar("Date").makeSharedPermanent();
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("x").mustBe(RAbstractListVector.class);
+        }
 
         @Specialization
         @TruffleBoundary
-        protected RDoubleVector posix2date(RList x) {
+        protected RDoubleVector posix2date(RAbstractListVector x) {
             RAbstractVector secVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(0));
             RAbstractVector minVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(1));
             RAbstractVector hourVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(2));
@@ -302,7 +319,7 @@ public class DatePOSIXFunctions {
         }
     }
 
-    @RBuiltin(name = "format.POSIXlt", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "format", "usetz"})
+    @RBuiltin(name = "format.POSIXlt", kind = INTERNAL, parameterNames = {"x", "format", "usetz"}, behavior = READS_STATE)
     public abstract static class FormatPOSIXlt extends RBuiltinNode {
 
         private static final HashMap<String, String> TIME_ZONE_MAPPING = new HashMap<>();
@@ -313,9 +330,16 @@ public class DatePOSIXFunctions {
             // TODO: find a proper source for this mapping
         }
 
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("x").mustBe(RAbstractListVector.class);
+            casts.arg("format").mustBe(nullValue().not()).asStringVector().mustBe(notEmpty());
+            casts.arg("usetz").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).map(toBoolean());
+        }
+
         @Specialization
         @TruffleBoundary
-        protected RStringVector format(RList x, RAbstractStringVector format, RAbstractLogicalVector usetz) {
+        protected RStringVector format(RAbstractListVector x, RAbstractStringVector format, boolean usetz) {
             RAbstractDoubleVector secVector = (RAbstractDoubleVector) RRuntime.asAbstractVector(x.getDataAt(0));
             RAbstractIntVector minVector = (RAbstractIntVector) RRuntime.asAbstractVector(x.getDataAt(1));
             RAbstractIntVector hourVector = (RAbstractIntVector) RRuntime.asAbstractVector(x.getDataAt(2));
@@ -323,16 +347,21 @@ public class DatePOSIXFunctions {
             RAbstractIntVector monVector = (RAbstractIntVector) RRuntime.asAbstractVector(x.getDataAt(4));
             RAbstractIntVector yearVector = (RAbstractIntVector) RRuntime.asAbstractVector(x.getDataAt(5));
             ZoneId zone;
-            DateTimeFormatterBuilder builder = createFormatter(format.getDataAt(0), false);
+            DateTimeFormatterBuilder[] builders = createFormatters(format, false);
             String tzone = getTimeZomeFromAttribute(x);
-            if (usetz.getDataAt(0) == RRuntime.LOGICAL_TRUE && !tzone.isEmpty()) {
+            if (usetz && !tzone.isEmpty()) {
                 zone = ZoneId.of(tzone, TIME_ZONE_MAPPING);
-                builder.appendLiteral(' ').appendZoneText(TextStyle.SHORT);
+                for (DateTimeFormatterBuilder builder : builders) {
+                    builder.appendLiteral(' ').appendZoneText(TextStyle.SHORT);
+                }
             } else {
                 zone = RContext.getInstance().getSystemTimeZone().toZoneId();
             }
 
-            DateTimeFormatter formatter = builder.toFormatter();
+            DateTimeFormatter[] formatters = new DateTimeFormatter[builders.length];
+            for (int i = 0; i < builders.length; i++) {
+                formatters[i] = builders[i].toFormatter();
+            }
             int length = secVector.getLength();
             String[] data = new String[length];
             boolean complete = true;
@@ -346,7 +375,7 @@ public class DatePOSIXFunctions {
                     int year = yearVector.getDataAt(i) + 1900;
                     LocalDateTime time = LocalDateTime.of(year, mon, mday, hour, min, (int) sec, (int) ((sec - Math.floor(sec)) * 1000000000L));
                     ZonedDateTime zoned = time.atZone(zone);
-                    data[i] = formatter.format(zoned);
+                    data[i] = formatters[i % formatters.length].format(zoned);
                 } else {
                     data[i] = RRuntime.STRING_NA;
                     complete = false;
@@ -356,9 +385,16 @@ public class DatePOSIXFunctions {
         }
     }
 
-    @RBuiltin(name = "strptime", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "format", "tz"})
+    @RBuiltin(name = "strptime", kind = INTERNAL, parameterNames = {"x", "format", "tz"}, behavior = PURE)
     public abstract static class StrPTime extends RBuiltinNode {
 
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("x").map(defaultValue(RDataFactory.createEmptyStringVector())).asStringVector();
+            casts.arg("format").map(defaultValue(RDataFactory.createEmptyStringVector())).asStringVector();
+            casts.arg("tz").map(defaultValue(RDataFactory.createEmptyStringVector())).asStringVector();
+        }
+
         @Specialization
         @TruffleBoundary
         protected RList strptime(RAbstractStringVector x, RAbstractStringVector format, RAbstractStringVector tz) {
@@ -371,13 +407,17 @@ public class DatePOSIXFunctions {
             }
             int length = x.getLength();
             POSIXltBuilder builder = new POSIXltBuilder(length, zone.getDisplayName(false, TimeZone.SHORT));
-            DateTimeFormatter formatter = createFormatter(format.getDataAt(0), true).toFormatter();
+            DateTimeFormatterBuilder[] builders = createFormatters(format, true);
+            DateTimeFormatter[] formatters = new DateTimeFormatter[builders.length];
+            for (int i = 0; i < builders.length; i++) {
+                formatters[i] = builders[i].toFormatter();
+            }
 
             for (int i = 0; i < length; i++) {
                 String str = x.getDataAt(i);
                 TemporalAccessor parse;
                 try {
-                    parse = formatter.parse(str, new ParsePosition(0));
+                    parse = formatters[i % formatters.length].parse(str, new ParsePosition(0));
                 } catch (DateTimeParseException e) {
                     builder.setIncompleteEntry(i);
                     continue;
@@ -401,6 +441,14 @@ public class DatePOSIXFunctions {
         }
     }
 
+    private static DateTimeFormatterBuilder[] createFormatters(RAbstractStringVector formats, boolean forInput) {
+        DateTimeFormatterBuilder[] result = new DateTimeFormatterBuilder[formats.getLength()];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = createFormatter(formats.getDataAt(i), forInput);
+        }
+        return result;
+    }
+
     private static DateTimeFormatterBuilder createFormatter(String format, boolean forInput) {
         DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
         boolean escaped = false;
@@ -679,7 +727,7 @@ public class DatePOSIXFunctions {
         return result;
     }
 
-    private static String getTimeZomeFromAttribute(RList x) {
+    private static String getTimeZomeFromAttribute(RAbstractListVector x) {
         Object attr = x.getAttributes().get("tzone");
         RAbstractVector vector = (RAbstractVector) RRuntime.asAbstractVector(attr);
         if (vector.getLength() == 0) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java
index 306d58f0e1a6e0b312e129a47f5ab7faf76033ad..b3c1e4e12ecbfda586c8a7223cb96e15c3b07c3a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java
@@ -22,16 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.helpers.DebugHandling;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -54,7 +57,7 @@ public class DebugFunctions {
         }
     }
 
-    @RBuiltin(name = "debug", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"fun", "text", "condition"})
+    @RBuiltin(name = "debug", visibility = OFF, kind = INTERNAL, parameterNames = {"fun", "text", "condition"}, behavior = COMPLEX)
     public abstract static class Debug extends ErrorAdapter {
 
         @SuppressWarnings("unused")
@@ -72,7 +75,7 @@ public class DebugFunctions {
         }
     }
 
-    @RBuiltin(name = "debugonce", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"fun", "text", "condition"})
+    @RBuiltin(name = "debugonce", visibility = OFF, kind = INTERNAL, parameterNames = {"fun", "text", "condition"}, behavior = COMPLEX)
     public abstract static class DebugOnce extends ErrorAdapter {
 
         @SuppressWarnings("unused")
@@ -91,7 +94,7 @@ public class DebugFunctions {
         }
     }
 
-    @RBuiltin(name = "undebug", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"fun"})
+    @RBuiltin(name = "undebug", visibility = OFF, kind = INTERNAL, parameterNames = {"fun"}, behavior = COMPLEX)
     public abstract static class UnDebug extends ErrorAdapter {
 
         @Fallback
@@ -110,7 +113,7 @@ public class DebugFunctions {
         }
     }
 
-    @RBuiltin(name = "isdebugged", kind = RBuiltinKind.INTERNAL, parameterNames = {"fun"})
+    @RBuiltin(name = "isdebugged", kind = INTERNAL, parameterNames = {"fun"}, behavior = PURE)
     public abstract static class IsDebugged extends ErrorAdapter {
 
         @Fallback
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DelayedAssign.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DelayedAssign.java
index ed941f30006044e10b5737da476f353934dcbe45..f168a9091a6ca09a23607df8c4191f6e0b291c4b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DelayedAssign.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DelayedAssign.java
@@ -22,15 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise.Closure;
@@ -39,7 +41,7 @@ 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;
 
-@RBuiltin(name = "delayedAssign", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "value", "eval.env", "assign.env"})
+@RBuiltin(name = "delayedAssign", visibility = OFF, kind = INTERNAL, parameterNames = {"x", "value", "eval.env", "assign.env"}, behavior = COMPLEX)
 public abstract class DelayedAssign extends RBuiltinNode {
 
     private final BranchProfile errorProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Deparse.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Deparse.java
index 700fa1183918a2f73e94592c332f24c50930934f..df49796ab0444b3f8b72c4c0655f816a371107b0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Deparse.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Deparse.java
@@ -11,22 +11,24 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 
 // Part of this transcribed from GnuR src/main/deparse.c
 
-@RBuiltin(name = "deparse", kind = RBuiltinKind.INTERNAL, parameterNames = {"expr", "width.cutoff", "backtick", "control", "nlines"})
+@RBuiltin(name = "deparse", kind = INTERNAL, parameterNames = {"expr", "width.cutoff", "backtick", "control", "nlines"}, behavior = PURE)
 public abstract class Deparse extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Diag.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Diag.java
index 1552d075c7b7b87c2715025c88d422942e07300f..cef023b4ca96e6de59d5812421ae3ea33c665dab 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Diag.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Diag.java
@@ -12,8 +12,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+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;
@@ -21,9 +22,9 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -31,11 +32,13 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "diag", kind = INTERNAL, parameterNames = {"x", "nrow", "ncol"})
+@RBuiltin(name = "diag", kind = INTERNAL, parameterNames = {"x", "nrow", "ncol"}, behavior = PURE)
 public abstract class Diag extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
+        casts.arg("x").asVector();
+
         casts.arg("nrow").asIntegerVector().findFirst().mustBe(notIntNA(), Message.INVALID_LARGE_NA_VALUE, "nrow").mustBe(gte0(), Message.INVALID_NEGATIVE_VALUE, "nrow");
 
         casts.arg("ncol").asIntegerVector().findFirst().mustBe(notIntNA(), Message.INVALID_LARGE_NA_VALUE, "ncol").mustBe(gte0(), Message.INVALID_NEGATIVE_VALUE, "ncol");
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java
index 81ec3bcbf69a0fe404f25a2b2981bdb582bf9bf6..be4d0db5ec9a83264b3daeaf3390b780184ef2eb 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java
@@ -22,19 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
 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;
 
 import com.oracle.truffle.api.dsl.Cached;
 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.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 
-@RBuiltin(name = "dim", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC)
+@RBuiltin(name = "dim", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class Dim extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
index fa8fb47069bc489d581d885def8606198fb56acc..ad8f86e42f42b15c0712c43bb854fca58d8ec9aa 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
@@ -22,19 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
 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;
 
 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.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 
-@RBuiltin(name = "dimnames", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC)
+@RBuiltin(name = "dimnames", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class DimNames extends RBuiltinNode {
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
index d2d7cef6d2fb015270b3d92fa6305fad6b8f044e..48de05402f1c175814fce6bba12c085977559302 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -39,10 +41,9 @@ import com.oracle.truffle.r.nodes.function.GetCallerFrameNode;
 import com.oracle.truffle.r.nodes.function.RCallBaseNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -60,7 +61,7 @@ import com.oracle.truffle.r.runtime.nodes.InternalRSyntaxNodeChildren;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 // TODO Implement properly, this is a simple implementation that works when the environment doesn't matter
-@RBuiltin(name = "do.call", visibility = RVisibility.CUSTOM, kind = INTERNAL, parameterNames = {"what", "args", "envir"})
+@RBuiltin(name = "do.call", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"what", "args", "envir"}, behavior = COMPLEX)
 public abstract class DoCall extends RBuiltinNode implements InternalRSyntaxNodeChildren {
 
     @Child private GetFunctions.Get getNode;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Drop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Drop.java
index 3d97e4e3e396a81ea335bdbc3cbd261aec3ba925..92d1f7bb6c31f98d334945bba1395dc8e1fdf847 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Drop.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Drop.java
@@ -22,11 +22,13 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -34,7 +36,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "drop", kind = RBuiltinKind.INTERNAL, parameterNames = {"x"})
+@RBuiltin(name = "drop", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
 public abstract class Drop extends RBuiltinNode {
 
     private final ConditionProfile nullDimensions = ConditionProfile.createBinaryProfile();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java
index 9b247cd866c94cce6ad7e291e3d4260a76b2cc0f..8d3e7cd3cda9aadd72f58434d70fd1a647a92876 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DuplicatedFunctions.java
@@ -11,6 +11,9 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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;
@@ -20,10 +23,9 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -54,7 +56,7 @@ public class DuplicatedFunctions {
         }
     }
 
-    @RBuiltin(name = "duplicated", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "imcomparables", "fromLast", "nmax"})
+    @RBuiltin(name = "duplicated", kind = INTERNAL, parameterNames = {"x", "imcomparables", "fromLast", "nmax"}, behavior = PURE)
     public abstract static class Duplicated extends Adapter {
 
         @Override
@@ -101,7 +103,7 @@ public class DuplicatedFunctions {
         }
     }
 
-    @RBuiltin(name = "anyDuplicated", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "imcomparables", "fromLast"})
+    @RBuiltin(name = "anyDuplicated", kind = INTERNAL, parameterNames = {"x", "imcomparables", "fromLast"}, behavior = PURE)
     public abstract static class AnyDuplicated extends Adapter {
 
         @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
index 721b06ae46bc41107aa2229acc8adfc5c7ef9cdb..06b412f7ff1036479ec6735517bce5f188b53a8a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
@@ -22,20 +22,23 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -52,8 +55,15 @@ public class DynLoadFunctions {
 
     private static final String DLLINFOLIST_CLASS = "DLLInfoList";
 
-    @RBuiltin(name = "dyn.load", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"lib", "local", "now", "unused"})
+    @RBuiltin(name = "dyn.load", visibility = OFF, kind = INTERNAL, parameterNames = {"lib", "local", "now", "unused"}, behavior = COMPLEX)
     public abstract static class DynLoad extends RBuiltinNode {
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            // TODO: not sure if the behavior is 100% compliant
+            casts.arg("now").asLogicalVector().findFirst();
+        }
+
         @Specialization
         @TruffleBoundary
         protected RList doDynLoad(RAbstractStringVector libVec, RAbstractLogicalVector localVec, byte now, @SuppressWarnings("unused") String unused) {
@@ -80,7 +90,7 @@ public class DynLoadFunctions {
         }
     }
 
-    @RBuiltin(name = "dyn.unload", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"lib"})
+    @RBuiltin(name = "dyn.unload", visibility = OFF, kind = INTERNAL, parameterNames = {"lib"}, behavior = COMPLEX)
     public abstract static class DynUnload extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -94,7 +104,7 @@ public class DynLoadFunctions {
         }
     }
 
-    @RBuiltin(name = "getLoadedDLLs", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "getLoadedDLLs", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class GetLoadedDLLs extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -114,7 +124,7 @@ public class DynLoadFunctions {
         }
     }
 
-    @RBuiltin(name = "is.loaded", kind = INTERNAL, parameterNames = {"symbol", "package", "type"})
+    @RBuiltin(name = "is.loaded", kind = INTERNAL, parameterNames = {"symbol", "package", "type"}, behavior = READS_STATE)
     public abstract static class IsLoaded extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -142,9 +152,15 @@ public class DynLoadFunctions {
         }
     }
 
-    @RBuiltin(name = "getSymbolInfo", kind = INTERNAL, parameterNames = {"symbol", "package", "withReg"})
+    @RBuiltin(name = "getSymbolInfo", kind = INTERNAL, parameterNames = {"symbol", "package", "withReg"}, behavior = READS_STATE)
     public abstract static class GetSymbolInfo extends RBuiltinNode {
 
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            // TODO: not sure if the behavior is 100% compliant
+            casts.arg("withReg").asLogicalVector().findFirst();
+        }
+
         @Specialization
         @TruffleBoundary
         protected Object getSymbolInfo(RAbstractStringVector symbolVec, String packageName, byte withReg) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EncodeString.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EncodeString.java
index 7021e7ccaa67b1695bc5a0f74d9fd5a10d684e05..9ab436ca6dc76559ac8ab5ae4107d78c00565c48 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EncodeString.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EncodeString.java
@@ -10,24 +10,32 @@
  */
 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.gte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte0;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.intNA;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notLogicalNA;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "encodeString", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "width", "quote", "justify", "na.encode"})
+@RBuiltin(name = "encodeString", kind = INTERNAL, parameterNames = {"x", "width", "quote", "justify", "na.encode"}, behavior = READS_STATE)
 public abstract class EncodeString extends RBuiltinNode {
 
     private enum JUSTIFY {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EncodingFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EncodingFunctions.java
index f4a2677ae568257b281111937b1fc91ebb19f8d9..8957ee7919af8751dc8f0171a5a59c40b210f280 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EncodingFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EncodingFunctions.java
@@ -22,19 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 public class EncodingFunctions {
 
-    @RBuiltin(name = "Encoding", kind = RBuiltinKind.INTERNAL, parameterNames = "x")
+    @RBuiltin(name = "Encoding", kind = INTERNAL, parameterNames = "x", behavior = PURE)
     public abstract static class Encoding extends RBuiltinNode {
         @Specialization
         protected RStringVector encoding(@SuppressWarnings("unused") RAbstractStringVector x) {
@@ -49,7 +51,7 @@ public class EncodingFunctions {
         }
     }
 
-    @RBuiltin(name = "setEncoding", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "value"})
+    @RBuiltin(name = "setEncoding", kind = INTERNAL, parameterNames = {"x", "value"}, behavior = PURE)
     public abstract static class SetEncoding extends RBuiltinNode {
         @Specialization
         protected Object setEncoding(RAbstractStringVector x) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
index bc5e926c65cdcc4c46866e0449a6ebadceb97c6d..3218540666337c629535b77554f78f81e03d9f81 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
@@ -22,9 +22,13 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
 import static com.oracle.truffle.r.runtime.RDispatch.INTERNAL_GENERIC;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_FRAME;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -48,13 +52,12 @@ import com.oracle.truffle.r.nodes.function.GetCallerFrameNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseDeoptimizeFrameNode;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.VirtualEvalFrame;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -87,7 +90,7 @@ public class EnvFunctions {
         protected final BranchProfile errorProfile = BranchProfile.create();
     }
 
-    @RBuiltin(name = "as.environment", kind = PRIMITIVE, parameterNames = {"fun"}, dispatch = INTERNAL_GENERIC)
+    @RBuiltin(name = "as.environment", kind = PRIMITIVE, parameterNames = {"fun"}, dispatch = INTERNAL_GENERIC, behavior = COMPLEX)
     public abstract static class AsEnvironment extends Adapter {
 
         @Specialization
@@ -171,7 +174,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "emptyenv", kind = PRIMITIVE, parameterNames = {})
+    @RBuiltin(name = "emptyenv", kind = PRIMITIVE, parameterNames = {}, behavior = PURE)
     public abstract static class EmptyEnv extends RBuiltinNode {
 
         @Specialization
@@ -180,7 +183,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "globalenv", kind = PRIMITIVE, parameterNames = {})
+    @RBuiltin(name = "globalenv", kind = PRIMITIVE, parameterNames = {}, behavior = PURE)
     public abstract static class GlobalEnv extends RBuiltinNode {
 
         @Specialization
@@ -192,7 +195,7 @@ public class EnvFunctions {
     /**
      * Returns the "package:base" environment.
      */
-    @RBuiltin(name = "baseenv", kind = PRIMITIVE, parameterNames = {})
+    @RBuiltin(name = "baseenv", kind = PRIMITIVE, parameterNames = {}, behavior = PURE)
     public abstract static class BaseEnv extends RBuiltinNode {
 
         @Specialization
@@ -201,7 +204,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "topenv", kind = INTERNAL, parameterNames = {"envir", "matchThisEnv"})
+    @RBuiltin(name = "topenv", kind = INTERNAL, parameterNames = {"envir", "matchThisEnv"}, behavior = COMPLEX)
     public abstract static class TopEnv extends Adapter {
 
         @Child private FrameFunctions.ParentFrame parentFrameNode;
@@ -250,7 +253,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "parent.env", kind = INTERNAL, parameterNames = {"env"})
+    @RBuiltin(name = "parent.env", kind = INTERNAL, parameterNames = {"env"}, behavior = READS_FRAME)
     public abstract static class ParentEnv extends Adapter {
 
         @Specialization
@@ -263,7 +266,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "parent.env<-", kind = INTERNAL, parameterNames = {"env", "value"})
+    @RBuiltin(name = "parent.env<-", kind = INTERNAL, parameterNames = {"env", "value"}, behavior = COMPLEX)
     public abstract static class SetParentEnv extends Adapter {
 
         @Specialization
@@ -277,7 +280,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "is.environment", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.environment", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsEnvironment extends RBuiltinNode {
 
         @Specialization
@@ -286,7 +289,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "environment", kind = INTERNAL, parameterNames = {"fun"})
+    @RBuiltin(name = "environment", kind = INTERNAL, parameterNames = {"fun"}, behavior = COMPLEX)
     public abstract static class Environment extends RBuiltinNode {
 
         @Specialization
@@ -332,7 +335,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "environment<-", kind = PRIMITIVE, parameterNames = {"env", "value"})
+    @RBuiltin(name = "environment<-", kind = PRIMITIVE, parameterNames = {"env", "value"}, behavior = COMPLEX)
     public abstract static class UpdateEnvironment extends RBuiltinNode {
 
         private static RAttributeProfiles attributeProfile = RAttributeProfiles.create();
@@ -388,7 +391,7 @@ public class EnvFunctions {
 
     }
 
-    @RBuiltin(name = "environmentName", kind = INTERNAL, parameterNames = {"fun"})
+    @RBuiltin(name = "environmentName", kind = INTERNAL, parameterNames = {"fun"}, behavior = PURE)
     public abstract static class EnvironmentName extends RBuiltinNode {
 
         @Specialization
@@ -403,7 +406,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "new.env", kind = INTERNAL, parameterNames = {"hash", "parent", "size"})
+    @RBuiltin(name = "new.env", kind = INTERNAL, parameterNames = {"hash", "parent", "size"}, behavior = COMPLEX)
     public abstract static class NewEnv extends RBuiltinNode {
 
         @Override
@@ -420,7 +423,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "search", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "search", kind = INTERNAL, parameterNames = {}, behavior = COMPLEX)
     public abstract static class Search extends RBuiltinNode {
         @Specialization
         protected RStringVector search() {
@@ -428,7 +431,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "lockEnvironment", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"env", "bindings"})
+    @RBuiltin(name = "lockEnvironment", visibility = OFF, kind = INTERNAL, parameterNames = {"env", "bindings"}, behavior = COMPLEX)
     public abstract static class LockEnvironment extends RBuiltinNode {
 
         @Specialization
@@ -438,7 +441,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "environmentIsLocked", kind = INTERNAL, parameterNames = {"env"})
+    @RBuiltin(name = "environmentIsLocked", kind = INTERNAL, parameterNames = {"env"}, behavior = PURE)
     public abstract static class EnvironmentIsLocked extends RBuiltinNode {
         @Specialization
         protected Object lockEnvironment(REnvironment env) {
@@ -455,7 +458,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "lockBinding", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"sym", "env"})
+    @RBuiltin(name = "lockBinding", visibility = OFF, kind = INTERNAL, parameterNames = {"sym", "env"}, behavior = COMPLEX)
     public abstract static class LockBinding extends RBuiltinNode {
         @Specialization
         protected Object lockBinding(RSymbol sym, REnvironment env) {
@@ -469,7 +472,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "unlockBinding", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"sym", "env"})
+    @RBuiltin(name = "unlockBinding", visibility = OFF, kind = INTERNAL, parameterNames = {"sym", "env"}, behavior = COMPLEX)
     public abstract static class UnlockBinding extends RBuiltinNode {
         @Specialization
         protected RNull unlockBinding(RSymbol sym, REnvironment env) {
@@ -483,7 +486,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "bindingIsLocked", kind = INTERNAL, parameterNames = {"sym", "env"})
+    @RBuiltin(name = "bindingIsLocked", kind = INTERNAL, parameterNames = {"sym", "env"}, behavior = PURE)
     public abstract static class BindingIsLocked extends RBuiltinNode {
         @Specialization
         protected Object bindingIsLocked(RSymbol sym, REnvironment env) {
@@ -496,7 +499,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "makeActiveBinding", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"sym", "fun", "env"})
+    @RBuiltin(name = "makeActiveBinding", visibility = OFF, kind = INTERNAL, parameterNames = {"sym", "fun", "env"}, behavior = COMPLEX)
     public abstract static class MakeActiveBinding extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -506,7 +509,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "bindingIsActive", kind = INTERNAL, parameterNames = {"sym", "env"})
+    @RBuiltin(name = "bindingIsActive", kind = INTERNAL, parameterNames = {"sym", "env"}, behavior = PURE)
     public abstract static class BindingIsActive extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -516,7 +519,7 @@ public class EnvFunctions {
         }
     }
 
-    @RBuiltin(name = "env2list", kind = INTERNAL, parameterNames = {"x", "all.names", "sorted"})
+    @RBuiltin(name = "env2list", kind = INTERNAL, parameterNames = {"x", "all.names", "sorted"}, behavior = PURE)
     public abstract static class EnvToList extends RBuiltinNode {
 
         @Child private CopyNode copy;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
index 8846a5f2942c79b28ad2af8d2746045db4734117..2175b5841baeaa0313282dd7634809a4bafb0ff8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EvalFunctions.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
@@ -32,10 +34,9 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.EvalFunctionsFactory.EvalEnvCastNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RExpression;
@@ -115,7 +116,7 @@ public class EvalFunctions {
         }
     }
 
-    @RBuiltin(name = "eval", visibility = RVisibility.CUSTOM, kind = INTERNAL, parameterNames = {"expr", "envir", "enclos"})
+    @RBuiltin(name = "eval", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"expr", "envir", "enclos"}, behavior = COMPLEX)
     public abstract static class Eval extends RBuiltinNode {
 
         @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java
index 7bf781d8d08be41b09ce15791a4b29dd12b77a04..de672880e0c78411ec1a3d70de0cc9e6e7bc69f7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Exists.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -30,14 +31,14 @@ import com.oracle.truffle.r.nodes.attributes.TypeFromModeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "exists", kind = INTERNAL, parameterNames = {"x", "envir", "mode", "inherits"})
+@RBuiltin(name = "exists", kind = INTERNAL, parameterNames = {"x", "envir", "mode", "inherits"}, behavior = PURE)
 public abstract class Exists extends RBuiltinNode {
 
     @Child private TypeFromModeNode typeFromMode = TypeFromModeNodeGen.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Expression.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Expression.java
index 54c58bb0beb44ebd56de19aeacb4dd25ebb440ca..a9a681eb660cc2c2786b9d3dae63802b95cc869a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Expression.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Expression.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
@@ -30,13 +31,13 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RPromise;
 
-@RBuiltin(name = "expression", kind = PRIMITIVE, parameterNames = {"..."}, nonEvalArgs = 0)
+@RBuiltin(name = "expression", kind = PRIMITIVE, parameterNames = {"..."}, nonEvalArgs = 0, behavior = PURE)
 public abstract class Expression extends RBuiltinNode {
     /*
      * Owing to the nonEvalArgs, all arguments are RPromise, but an expression may contain
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
index f4c96efec81f155ade653fb641ecd98f54e21b9c..33546b1db6d04b2303b66e91a9fca9b7f9e497f7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
@@ -11,7 +11,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -49,12 +51,11 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.Utils;
+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;
@@ -69,7 +70,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public class FileFunctions {
 
-    @RBuiltin(name = "file.access", kind = INTERNAL, parameterNames = {"names", "mode"})
+    @RBuiltin(name = "file.access", kind = INTERNAL, parameterNames = {"names", "mode"}, behavior = IO)
     public abstract static class FileAccess extends RBuiltinNode {
         private static final int EXECUTE = 1;
         private static final int WRITE = 2;
@@ -107,7 +108,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "file.append", kind = INTERNAL, parameterNames = {"file1", "file2"})
+    @RBuiltin(name = "file.append", kind = INTERNAL, parameterNames = {"file1", "file2"}, behavior = IO)
     public abstract static class FileAppend extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -197,7 +198,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "file.create", kind = INTERNAL, parameterNames = {"vec", "showWarnings"})
+    @RBuiltin(name = "file.create", kind = INTERNAL, parameterNames = {"vec", "showWarnings"}, behavior = IO)
     public abstract static class FileCreate extends RBuiltinNode {
 
         @Specialization
@@ -231,7 +232,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "file.info", kind = INTERNAL, parameterNames = {"fn", "extra_cols"})
+    @RBuiltin(name = "file.info", kind = INTERNAL, parameterNames = {"fn", "extra_cols"}, behavior = IO)
     public abstract static class FileInfo extends RBuiltinNode {
         // @formatter:off
         private  enum Column {
@@ -431,7 +432,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "file.link", kind = INTERNAL, parameterNames = {"from", "to"})
+    @RBuiltin(name = "file.link", kind = INTERNAL, parameterNames = {"from", "to"}, behavior = IO)
     public abstract static class FileLink extends FileLinkAdaptor {
         @Specialization
         @TruffleBoundary
@@ -446,7 +447,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "file.symlink", kind = INTERNAL, parameterNames = {"from", "to"})
+    @RBuiltin(name = "file.symlink", kind = INTERNAL, parameterNames = {"from", "to"}, behavior = IO)
     public abstract static class FileSymLink extends FileLinkAdaptor {
         @Specialization
         @TruffleBoundary
@@ -461,7 +462,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "file.remove", kind = INTERNAL, parameterNames = {"vec"})
+    @RBuiltin(name = "file.remove", kind = INTERNAL, parameterNames = {"vec"}, behavior = IO)
     public abstract static class FileRemove extends RBuiltinNode {
 
         @Specialization
@@ -491,7 +492,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "file.rename", kind = INTERNAL, parameterNames = {"from", "to"})
+    @RBuiltin(name = "file.rename", kind = INTERNAL, parameterNames = {"from", "to"}, behavior = IO)
     public abstract static class FileRename extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -526,7 +527,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "file.exists", kind = INTERNAL, parameterNames = {"vec"})
+    @RBuiltin(name = "file.exists", kind = INTERNAL, parameterNames = {"vec"}, behavior = IO)
     public abstract static class FileExists extends RBuiltinNode {
 
         @Specialization
@@ -553,7 +554,7 @@ public class FileFunctions {
     }
 
     // TODO Implement all the options
-    @RBuiltin(name = "list.files", kind = INTERNAL, parameterNames = {"path", "pattern", "all.files", "full.names", "recursive", "ignore.case", "include.dirs", "no.."})
+    @RBuiltin(name = "list.files", kind = INTERNAL, parameterNames = {"path", "pattern", "all.files", "full.names", "recursive", "ignore.case", "include.dirs", "no.."}, behavior = IO)
     public abstract static class ListFiles extends RBuiltinNode {
         private static final String DOT = ".";
         private static final String DOTDOT = "..";
@@ -682,7 +683,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "list.dirs", kind = INTERNAL, parameterNames = {"path", "full.names", "recursive"})
+    @RBuiltin(name = "list.dirs", kind = INTERNAL, parameterNames = {"path", "full.names", "recursive"}, behavior = IO)
     public abstract static class ListDirs extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -732,7 +733,7 @@ public class FileFunctions {
     }
 
     // TODO handle the general case, which is similar to paste
-    @RBuiltin(name = "file.path", kind = INTERNAL, parameterNames = {"paths", "fsep"})
+    @RBuiltin(name = "file.path", kind = INTERNAL, parameterNames = {"paths", "fsep"}, behavior = IO)
     public abstract static class FilePath extends RBuiltinNode {
 
         @Child private CastStringNode castStringNode;
@@ -740,7 +741,7 @@ public class FileFunctions {
         private CastStringNode initCastStringNode() {
             if (castStringNode == null) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
-                castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
+                castStringNode = insert(CastStringNodeGen.create(false, false, false));
             }
             return castStringNode;
         }
@@ -820,7 +821,7 @@ public class FileFunctions {
     /**
      * {@code file.copy} builtin. This is only called when the target is a directory.
      */
-    @RBuiltin(name = "file.copy", kind = INTERNAL, parameterNames = {"from", "to", "overwrite", "recursive", "copy.mode", "copy.date"})
+    @RBuiltin(name = "file.copy", kind = INTERNAL, parameterNames = {"from", "to", "overwrite", "recursive", "copy.mode", "copy.date"}, behavior = IO)
     public abstract static class FileCopy extends RBuiltinNode {
 
         @Override
@@ -982,7 +983,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "dirname", kind = INTERNAL, parameterNames = {"path"})
+    @RBuiltin(name = "dirname", kind = INTERNAL, parameterNames = {"path"}, behavior = IO)
     public abstract static class DirName extends XyzNameAdapter {
         @Specialization
         @TruffleBoundary
@@ -995,7 +996,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "basename", kind = INTERNAL, parameterNames = {"path"})
+    @RBuiltin(name = "basename", kind = INTERNAL, parameterNames = {"path"}, behavior = IO)
     public abstract static class BaseName extends XyzNameAdapter {
         @Specialization
         @TruffleBoundary
@@ -1008,7 +1009,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "unlink", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"x", "recursive", "force"})
+    @RBuiltin(name = "unlink", visibility = OFF, kind = INTERNAL, parameterNames = {"x", "recursive", "force"}, behavior = IO)
     public abstract static class Unlink extends RBuiltinNode {
 
         @Override
@@ -1097,7 +1098,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "dir.create", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"path", "showWarnings", "recursive", "mode"})
+    @RBuiltin(name = "dir.create", visibility = OFF, kind = INTERNAL, parameterNames = {"path", "showWarnings", "recursive", "mode"}, behavior = IO)
     public abstract static class DirCreate extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -1143,7 +1144,7 @@ public class FileFunctions {
         }
     }
 
-    @RBuiltin(name = "dir.exists", kind = INTERNAL, parameterNames = "paths")
+    @RBuiltin(name = "dir.exists", kind = INTERNAL, parameterNames = "paths", behavior = IO)
     public abstract static class DirExists extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Floor.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Floor.java
index 67287ea29e6e2746791ffad310238f7a8a53ae3d..b4fe2e8b935e1c936ffbe19ae0074144856615fa 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Floor.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Floor.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNode;
@@ -30,13 +32,12 @@ import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.ops.UnaryArithmeticFactory;
 
-@RBuiltin(name = "floor", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+@RBuiltin(name = "floor", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
 public abstract class Floor extends RBuiltinNode {
 
     public static final UnaryArithmeticFactory FLOOR = FloorArithmetic::new;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java
index 92b45c81feb9aa1aa04fa1606b58e092eec5c195..94b46d38ee991e17127d9781521312069da90276 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForceAndCall.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Cached;
@@ -37,13 +38,13 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallBaseNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RPromise;
 
-@RBuiltin(name = "forceAndCall", kind = PRIMITIVE, parameterNames = {"n", "FUN", "..."}, nonEvalArgs = 2)
+@RBuiltin(name = "forceAndCall", kind = PRIMITIVE, parameterNames = {"n", "FUN", "..."}, nonEvalArgs = 2, behavior = COMPLEX)
 public abstract class ForceAndCall extends RBuiltinNode {
 
     private final Object argsIdentifier = new Object();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Formals.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Formals.java
index 6b8eacc956b66b65e8b464af21ae2b556a985457..4c19c57467c5d72fba300c0e7de12c60c089e866 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Formals.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Formals.java
@@ -22,6 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -29,14 +32,13 @@ import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 
-@RBuiltin(name = "formals", kind = RBuiltinKind.INTERNAL, parameterNames = {"fun"})
+@RBuiltin(name = "formals", kind = INTERNAL, parameterNames = {"fun"}, behavior = PURE)
 public abstract class Formals extends RBuiltinNode {
 
     @SuppressWarnings("unused")
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
index 086d37787874dca938a0708edace87eccf94702a..7bd299642def105a42a8605b47f345a551624993 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Format.java
@@ -11,7 +11,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.Specialization;
@@ -23,9 +24,9 @@ import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinterNode;
 import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinterNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDouble;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -40,7 +41,7 @@ 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;
 
-@RBuiltin(name = "format", kind = INTERNAL, parameterNames = {"x", "trim", "digits", "nsmall", "width", "justify", "na.encode", "scientific", "decimal.mark"})
+@RBuiltin(name = "format", kind = INTERNAL, parameterNames = {"x", "trim", "digits", "nsmall", "width", "justify", "na.encode", "scientific", "decimal.mark"}, behavior = PURE)
 public abstract class Format extends RBuiltinNode {
 
     @Child private CastIntegerNode castInteger;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
index 4fbaf2aa168d84e531fa2e6be169f64b0806db83..fa762a0a2f9e0fa7a67d30cc5a43595aaac4c592 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
@@ -11,7 +11,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.Specialization;
@@ -19,14 +20,14 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 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;
 
-@RBuiltin(name = "formatC", kind = INTERNAL, parameterNames = {"x", "mode", "width", "digits", "format", "flat", "i.strlen"})
+@RBuiltin(name = "formatC", kind = INTERNAL, parameterNames = {"x", "mode", "width", "digits", "format", "flat", "i.strlen"}, behavior = PURE)
 public abstract class FormatC extends RBuiltinNode {
 
     @Child private CastStringNode castStringNode;
@@ -34,7 +35,7 @@ public abstract class FormatC extends RBuiltinNode {
     private RStringVector castStringVector(Object o) {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(true, true, true, false));
+            castStringNode = insert(CastStringNodeGen.create(true, true, true));
         }
         return (RStringVector) ((RStringVector) castStringNode.executeString(o)).copyDropAttributes();
     }
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 3b17aa2d14ded603332a137dfb3a337dadd5baf4..b13c52d299ca16817a7bb321921900c47b79d7e5 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
@@ -22,8 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.SUBSTITUTE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.SUBSTITUTE;
 
 import java.util.ArrayList;
 import java.util.function.Function;
@@ -56,13 +57,13 @@ import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.HasSignature;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RCaller;
 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.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -153,7 +154,7 @@ public class FrameFunctions {
         }
     }
 
-    @RBuiltin(name = "sys.call", kind = INTERNAL, parameterNames = {"which"})
+    @RBuiltin(name = "sys.call", kind = INTERNAL, parameterNames = {"which"}, behavior = COMPLEX)
     public abstract static class SysCall extends FrameHelper {
 
         @Override
@@ -200,7 +201,7 @@ public class FrameFunctions {
      * In summary, although the simple cases are indeed simple, there are many possible variants
      * using "..." that make the code a lot more complex that it seems it ought to be.
      */
-    @RBuiltin(name = "match.call", kind = INTERNAL, parameterNames = {"definition", "call", "expand.dots", "envir"})
+    @RBuiltin(name = "match.call", kind = INTERNAL, parameterNames = {"definition", "call", "expand.dots", "envir"}, behavior = COMPLEX)
     public abstract static class MatchCall extends FrameHelper {
 
         @Override
@@ -386,7 +387,7 @@ public class FrameFunctions {
         }
     }
 
-    @RBuiltin(name = "sys.nframe", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "sys.nframe", kind = INTERNAL, parameterNames = {}, behavior = COMPLEX)
     public abstract static class SysNFrame extends RBuiltinNode {
 
         private final BranchProfile isPromiseCurrentProfile = BranchProfile.create();
@@ -413,7 +414,7 @@ public class FrameFunctions {
 
     }
 
-    @RBuiltin(name = "sys.frame", kind = INTERNAL, parameterNames = {"which"})
+    @RBuiltin(name = "sys.frame", kind = INTERNAL, parameterNames = {"which"}, behavior = COMPLEX)
     public abstract static class SysFrame extends DeoptHelper {
 
         private final ConditionProfile zeroProfile = ConditionProfile.createBinaryProfile();
@@ -445,7 +446,7 @@ public class FrameFunctions {
         }
     }
 
-    @RBuiltin(name = "sys.frames", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "sys.frames", kind = INTERNAL, parameterNames = {}, behavior = COMPLEX)
     public abstract static class SysFrames extends DeoptHelper {
         @Override
         protected final FrameAccess frameAccess() {
@@ -477,7 +478,7 @@ public class FrameFunctions {
         }
     }
 
-    @RBuiltin(name = "sys.calls", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "sys.calls", kind = INTERNAL, parameterNames = {}, behavior = COMPLEX)
     public abstract static class SysCalls extends FrameHelper {
 
         @Override
@@ -518,7 +519,7 @@ public class FrameFunctions {
         }
     }
 
-    @RBuiltin(name = "sys.parent", kind = INTERNAL, parameterNames = {"n"})
+    @RBuiltin(name = "sys.parent", kind = INTERNAL, parameterNames = {"n"}, behavior = COMPLEX)
     public abstract static class SysParent extends RBuiltinNode {
 
         private final BranchProfile nullCallerProfile = BranchProfile.create();
@@ -549,7 +550,7 @@ public class FrameFunctions {
         }
     }
 
-    @RBuiltin(name = "sys.function", kind = INTERNAL, parameterNames = {"which"}, splitCaller = true, alwaysSplit = true)
+    @RBuiltin(name = "sys.function", kind = INTERNAL, parameterNames = {"which"}, splitCaller = true, alwaysSplit = true, behavior = COMPLEX)
     public abstract static class SysFunction extends FrameHelper {
 
         public abstract Object executeObject(VirtualFrame frame, int which);
@@ -578,7 +579,7 @@ public class FrameFunctions {
         }
     }
 
-    @RBuiltin(name = "sys.parents", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "sys.parents", kind = INTERNAL, parameterNames = {}, behavior = COMPLEX)
     public abstract static class SysParents extends FrameHelper {
 
         @Override
@@ -622,7 +623,7 @@ public class FrameFunctions {
     /**
      * The environment of the caller of the function that called parent.frame.
      */
-    @RBuiltin(name = "parent.frame", kind = SUBSTITUTE, parameterNames = {"n"})
+    @RBuiltin(name = "parent.frame", kind = SUBSTITUTE, parameterNames = {"n"}, behavior = COMPLEX)
     public abstract static class ParentFrame extends FrameHelper {
 
         private final BranchProfile nullCallerProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Gc.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Gc.java
index c2f7807c20c3beed3310ea433b4780382dab178e..977dadd9b36be16b65c0ca63acd22ac2c6dfd601 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Gc.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Gc.java
@@ -22,19 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Arrays;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 
-@RBuiltin(name = "gc", kind = INTERNAL, parameterNames = {"verbose", "reset"})
+@RBuiltin(name = "gc", kind = INTERNAL, parameterNames = {"verbose", "reset"}, behavior = COMPLEX)
 public abstract class Gc extends RBuiltinNode {
 
     @SuppressWarnings("unused")
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java
index 268d2d8dc84325b3b4b8f13807c016d1dfa6ce80..cc9cc4610d5b5904b84b2414e6b8555aee3ca8e1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetClass.java
@@ -10,16 +10,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "class", kind = PRIMITIVE, parameterNames = {"x"})
+@RBuiltin(name = "class", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
 public abstract class GetClass extends RBuiltinNode {
 
     @Child private ClassHierarchyNode classHierarchy = ClassHierarchyNodeGen.create(true, false);
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 a38a35545bf887fbe6722da4d34d6bb4e98e99a3..19d0acc4b7f43f8d331e226fccf24349f207d157 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
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -37,6 +38,7 @@ import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.CallInlineCacheNode;
 import com.oracle.truffle.r.nodes.CallInlineCacheNodeGen;
 import com.oracle.truffle.r.nodes.RRootNode;
+import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNodeGen;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
@@ -48,12 +50,12 @@ import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
 import com.oracle.truffle.r.nodes.objects.GetS4DataSlot;
 import com.oracle.truffle.r.nodes.objects.GetS4DataSlotNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RCaller;
 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.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -63,6 +65,7 @@ import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RS4Object;
 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.Function;
 
 /**
  * assert: not expected to be fast even when called as, e.g., {@code get("x")}.
@@ -86,8 +89,12 @@ public class GetFunctions {
             }
         }
 
-        protected Object checkPromise(VirtualFrame frame, Object r) {
+        protected Object checkPromise(VirtualFrame frame, Object r, String identifier, boolean evaluateOnSlowPath) {
             if (r instanceof RPromise) {
+                if (evaluateOnSlowPath) {
+                    CompilerDirectives.transferToInterpreter();
+                    return ReadVariableNode.evalPromiseSlowPathWithName(identifier, frame, (RPromise) r);
+                }
                 return promiseHelper.evaluate(frame, (RPromise) r);
             } else {
                 return r;
@@ -100,7 +107,7 @@ public class GetFunctions {
 
         protected Object getAndCheck(VirtualFrame frame, RAbstractStringVector xv, REnvironment env, RType modeType, boolean fail) throws RError {
             String x = xv.getDataAt(0);
-            Object obj = checkPromise(frame, env.get(x));
+            Object obj = checkPromise(frame, env.get(x), x, !(env instanceof Function));
             if (obj != null && RRuntime.checkType(obj, modeType)) {
                 return obj;
             } else {
@@ -121,7 +128,7 @@ public class GetFunctions {
                 while (env != REnvironment.emptyEnv()) {
                     env = env.getParent();
                     if (env != REnvironment.emptyEnv()) {
-                        r = checkPromise(frame, env.get(x));
+                        r = checkPromise(frame, env.get(x), x, !(env instanceof Function));
                         if (r != null && RRuntime.checkType(r, modeType)) {
                             break;
                         }
@@ -150,7 +157,7 @@ public class GetFunctions {
         }
     }
 
-    @RBuiltin(name = "get", kind = INTERNAL, parameterNames = {"x", "envir", "mode", "inherits"})
+    @RBuiltin(name = "get", kind = INTERNAL, parameterNames = {"x", "envir", "mode", "inherits"}, behavior = COMPLEX)
     public abstract static class Get extends Adapter {
 
         private final ConditionProfile inheritsProfile = ConditionProfile.createBinaryProfile();
@@ -177,7 +184,7 @@ public class GetFunctions {
         }
     }
 
-    @RBuiltin(name = "get0", kind = INTERNAL, parameterNames = {"x", "envir", "mode", "inherits", "ifnotfound"})
+    @RBuiltin(name = "get0", kind = INTERNAL, parameterNames = {"x", "envir", "mode", "inherits", "ifnotfound"}, behavior = COMPLEX)
     public abstract static class Get0 extends Adapter {
 
         private final ConditionProfile inheritsProfile = ConditionProfile.createBinaryProfile();
@@ -209,7 +216,7 @@ public class GetFunctions {
         }
     }
 
-    @RBuiltin(name = "mget", kind = INTERNAL, parameterNames = {"x", "envir", "mode", "ifnotfound", "inherits"})
+    @RBuiltin(name = "mget", kind = INTERNAL, parameterNames = {"x", "envir", "mode", "ifnotfound", "inherits"}, behavior = COMPLEX)
     public abstract static class MGet extends Adapter {
 
         private final BranchProfile wrongLengthErrorProfile = BranchProfile.create();
@@ -275,7 +282,7 @@ public class GetFunctions {
                 String x = state.checkNA(xv.getDataAt(i));
                 state.names[i] = x;
                 RType modeType = typeFromMode.execute(mode.getDataAt(state.modeLength == 1 ? 0 : i));
-                Object r = checkPromise(frame, env.get(x));
+                Object r = checkPromise(frame, env.get(x), x, !(env instanceof Function));
                 if (r != null && RRuntime.checkType(r, modeType)) {
                     state.data[i] = r;
                 } else {
@@ -299,7 +306,7 @@ public class GetFunctions {
                     while (env != REnvironment.emptyEnv()) {
                         env = env.getParent();
                         if (env != REnvironment.emptyEnv()) {
-                            r = checkPromise(frame, env.get(x));
+                            r = checkPromise(frame, env.get(x), x, !(env instanceof Function));
                             if (r != null && RRuntime.checkType(r, modeType)) {
                                 break;
                             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetOldClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetOldClass.java
index 439ae310b47a32e064a6cffb104a104bb1982cb2..947f79ac432bb838bd8752d3e99a8b45c199cff2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetOldClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetOldClass.java
@@ -22,20 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "oldClass", kind = PRIMITIVE, parameterNames = {"x"})
+@RBuiltin(name = "oldClass", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
 public abstract class GetOldClass extends RBuiltinNode {
 
     private final ConditionProfile isObjectProfile = ConditionProfile.createBinaryProfile();
@@ -59,6 +58,4 @@ public abstract class GetOldClass extends RBuiltinNode {
     protected Object getOldClass(@SuppressWarnings("unused") RNull arg) {
         return RNull.instance;
     }
-
-    public abstract Object execute(VirtualFrame frame, RAbstractVector o);
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetText.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetText.java
index 6c760541d6afbb78f8937eca0b1c86c31b78ec53..6af408b6bee5e99998275a8270665da738a59c83 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetText.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GetText.java
@@ -22,16 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "gettext", kind = INTERNAL, parameterNames = {"domain", "args"})
+@RBuiltin(name = "gettext", kind = INTERNAL, parameterNames = {"domain", "args"}, behavior = PURE)
 public abstract class GetText extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Getwd.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Getwd.java
index 511962dfb6ba4248a9c4dd31c06ee5ae9ae0b8f2..d0833557089e978f985b61f9d8a5eab136c11894 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Getwd.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Getwd.java
@@ -22,16 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
-@RBuiltin(name = "getwd", kind = INTERNAL, parameterNames = {})
+@RBuiltin(name = "getwd", kind = INTERNAL, parameterNames = {}, behavior = IO)
 public abstract class Getwd extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java
index 80f8c14c4dce9dea033f22e7a5a348821fbb437d..f36dcf3633f70c228f60ac3fcd56f68dd2341ae4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java
@@ -11,7 +11,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -24,11 +25,11 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RegExp;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -266,7 +267,7 @@ public class GrepFunctions {
         }
     }
 
-    @RBuiltin(name = "grep", kind = INTERNAL, parameterNames = {"pattern", "x", "ignore.case", "perl", "value", "fixed", "useBytes", "invert"})
+    @RBuiltin(name = "grep", kind = INTERNAL, parameterNames = {"pattern", "x", "ignore.case", "perl", "value", "fixed", "useBytes", "invert"}, behavior = PURE)
     public abstract static class Grep extends GrepAdapter {
 
         @Specialization
@@ -277,7 +278,7 @@ public class GrepFunctions {
         }
     }
 
-    @RBuiltin(name = "grepl", kind = INTERNAL, parameterNames = {"pattern", "x", "ignore.case", "value", "perl", "fixed", "useBytes", "invert"})
+    @RBuiltin(name = "grepl", kind = INTERNAL, parameterNames = {"pattern", "x", "ignore.case", "value", "perl", "fixed", "useBytes", "invert"}, behavior = PURE)
     public abstract static class GrepL extends GrepAdapter {
 
         @Specialization
@@ -539,7 +540,7 @@ public class GrepFunctions {
         }
     }
 
-    @RBuiltin(name = "sub", kind = INTERNAL, parameterNames = {"pattern", "replacement", "x", "ignore.case", "perl", "fixed", "useBytes"})
+    @RBuiltin(name = "sub", kind = INTERNAL, parameterNames = {"pattern", "replacement", "x", "ignore.case", "perl", "fixed", "useBytes"}, behavior = PURE)
     public abstract static class Sub extends SubAdapter {
 
         @Specialization
@@ -550,7 +551,7 @@ public class GrepFunctions {
         }
     }
 
-    @RBuiltin(name = "gsub", kind = INTERNAL, parameterNames = {"pattern", "replacement", "x", "ignore.case", "perl", "fixed", "useBytes"})
+    @RBuiltin(name = "gsub", kind = INTERNAL, parameterNames = {"pattern", "replacement", "x", "ignore.case", "perl", "fixed", "useBytes"}, behavior = PURE)
     public abstract static class GSub extends SubAdapter {
 
         @Specialization
@@ -561,7 +562,7 @@ public class GrepFunctions {
         }
     }
 
-    @RBuiltin(name = "regexpr", kind = INTERNAL, parameterNames = {"pattern", "text", "ignore.case", "perl", "fixed", "useBytes"})
+    @RBuiltin(name = "regexpr", kind = INTERNAL, parameterNames = {"pattern", "text", "ignore.case", "perl", "fixed", "useBytes"}, behavior = PURE)
     public abstract static class Regexp extends CommonCodeAdapter {
 
         @Specialization
@@ -608,7 +609,7 @@ public class GrepFunctions {
         }
     }
 
-    @RBuiltin(name = "gregexpr", kind = INTERNAL, parameterNames = {"pattern", "text", "ignore.case", "perl", "fixed", "useBytes"})
+    @RBuiltin(name = "gregexpr", kind = INTERNAL, parameterNames = {"pattern", "text", "ignore.case", "perl", "fixed", "useBytes"}, behavior = PURE)
     public abstract static class Gregexpr extends Regexp {
 
         @Specialization
@@ -637,7 +638,7 @@ public class GrepFunctions {
         }
     }
 
-    @RBuiltin(name = "agrep", kind = INTERNAL, parameterNames = {"pattern", "x", "max.distance", "costs", "ignore.case", "value", "fixed", "useBytes"})
+    @RBuiltin(name = "agrep", kind = INTERNAL, parameterNames = {"pattern", "x", "max.distance", "costs", "ignore.case", "value", "fixed", "useBytes"}, behavior = PURE)
     public abstract static class AGrep extends CommonCodeAdapter {
 
         @SuppressWarnings("unused")
@@ -741,7 +742,7 @@ public class GrepFunctions {
         }
     }
 
-    @RBuiltin(name = "agrepl", kind = INTERNAL, parameterNames = {"pattern", "x", "max.distance", "costs", "ignore.case", "fixed", "useBytes"})
+    @RBuiltin(name = "agrepl", kind = INTERNAL, parameterNames = {"pattern", "x", "max.distance", "costs", "ignore.case", "fixed", "useBytes"}, behavior = PURE)
     public abstract static class AGrepL extends CommonCodeAdapter {
 
         @SuppressWarnings("unused")
@@ -759,7 +760,7 @@ public class GrepFunctions {
         }
     }
 
-    @RBuiltin(name = "strsplit", kind = INTERNAL, parameterNames = {"x", "split", "fixed", "perl", "useBytes"})
+    @RBuiltin(name = "strsplit", kind = INTERNAL, parameterNames = {"x", "split", "fixed", "perl", "useBytes"}, behavior = PURE)
     public abstract static class Strsplit extends CommonCodeAdapter {
 
         private final NACheck na = NACheck.create();
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 f92b2e82cd3074ba7514d61bec07f3a451bfbd0a..899f34632cfbc9570f8ca59d055e8179bf9bf4cc 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
@@ -11,8 +11,11 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -40,8 +43,6 @@ import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RCompression;
 import com.oracle.truffle.r.runtime.RDeparse;
@@ -50,8 +51,8 @@ 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.RSerialize;
-import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.SubstituteVirtualFrame;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -80,7 +81,7 @@ public class HiddenInternalFunctions {
     /**
      * Transcribed from GnuR {@code do_makeLazy} in src/main/builtin.c.
      */
-    @RBuiltin(name = "makeLazy", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"names", "values", "expr", "eenv", "aenv"})
+    @RBuiltin(name = "makeLazy", visibility = OFF, kind = INTERNAL, parameterNames = {"names", "values", "expr", "eenv", "aenv"}, behavior = COMPLEX)
     public abstract static class MakeLazy extends RBuiltinNode {
         @Child private Eval eval;
 
@@ -137,7 +138,7 @@ public class HiddenInternalFunctions {
      * This function copies values of variables from one environment to another environment,
      * possibly with different names. Promises are not forced and active bindings are preserved.
      */
-    @RBuiltin(name = "importIntoEnv", kind = INTERNAL, parameterNames = {"impEnv", "impNames", "expEnv", "expNames"})
+    @RBuiltin(name = "importIntoEnv", kind = INTERNAL, parameterNames = {"impEnv", "impNames", "expEnv", "expNames"}, behavior = COMPLEX)
     public abstract static class ImportIntoEnv extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -171,7 +172,7 @@ public class HiddenInternalFunctions {
     /**
      * Transcribed from {@code lazyLoaadDBFetch} in src/serialize.c.
      */
-    @RBuiltin(name = "lazyLoadDBfetch", kind = PRIMITIVE, parameterNames = {"key", "datafile", "compressed", "envhook"})
+    @RBuiltin(name = "lazyLoadDBfetch", kind = PRIMITIVE, parameterNames = {"key", "datafile", "compressed", "envhook"}, behavior = PURE)
     public abstract static class LazyLoadDBFetch extends RBuiltinNode {
 
         @Child private CallInlineCacheNode callCache = CallInlineCacheNodeGen.create();
@@ -276,7 +277,7 @@ public class HiddenInternalFunctions {
         }
     }
 
-    @RBuiltin(name = "getRegisteredRoutines", kind = INTERNAL, parameterNames = "info")
+    @RBuiltin(name = "getRegisteredRoutines", kind = INTERNAL, parameterNames = "info", behavior = COMPLEX)
     public abstract static class GetRegisteredRoutines extends RBuiltinNode {
         private static final RStringVector NAMES = RDataFactory.createStringVector(new String[]{".C", ".Call", ".Fortran", ".External"}, RDataFactory.COMPLETE_VECTOR);
         private static final RStringVector NATIVE_ROUTINE_LIST = RDataFactory.createStringVectorFromScalar("NativeRoutineList");
@@ -321,7 +322,7 @@ public class HiddenInternalFunctions {
         }
     }
 
-    @RBuiltin(name = "getVarsFromFrame", kind = INTERNAL, parameterNames = {"vars", "e", "force"})
+    @RBuiltin(name = "getVarsFromFrame", kind = INTERNAL, parameterNames = {"vars", "e", "force"}, behavior = COMPLEX)
     public abstract static class GetVarsFromFrame extends RBuiltinNode {
         @Child private PromiseHelperNode promiseHelper;
 
@@ -354,14 +355,16 @@ public class HiddenInternalFunctions {
         }
     }
 
-    @RBuiltin(name = "lazyLoadDBinsertValue", kind = INTERNAL, parameterNames = {"value", "file", "ascii", "compsxp", "hook"})
+    @RBuiltin(name = "lazyLoadDBinsertValue", kind = INTERNAL, parameterNames = {"value", "file", "ascii", "compsxp", "hook"}, behavior = COMPLEX)
     public abstract static class LazyLoadDBinsertValue extends RBuiltinNode {
 
         @Child private CallInlineCacheNode callCache = CallInlineCacheNodeGen.create();
 
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.toInteger(2).toInteger(3);
+            // TODO: not sure if the behavior is 100% compliant
+            casts.arg("ascii").asIntegerVector().findFirst();
+            casts.arg("compsxp").asIntegerVector().findFirst();
         }
 
         @Specialization
@@ -458,7 +461,7 @@ public class HiddenInternalFunctions {
         }
     }
 
-    @RBuiltin(name = "lazyLoadDBflush", kind = INTERNAL, parameterNames = "path")
+    @RBuiltin(name = "lazyLoadDBflush", kind = INTERNAL, parameterNames = "path", behavior = COMPLEX)
     public abstract static class LazyLoadDBFlush extends RBuiltinNode {
         @Specialization
         protected RNull doLazyLoadDBFlush(RAbstractStringVector dbPath) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java
index 0d054322e12f79d49920dfdd2416a87f9777d47c..9ce81965a3c80242179ecc8c36d2b38cebcf7001 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IConv.java
@@ -22,15 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "iconv", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "from", "to", "sub", "mark", "toRaw"})
+@RBuiltin(name = "iconv", kind = INTERNAL, parameterNames = {"x", "from", "to", "sub", "mark", "toRaw"}, behavior = PURE)
 public abstract class IConv extends RBuiltinNode {
     @SuppressWarnings("unused")
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
index 1bd3fcd7ec5a4dd89809b87fd59f4b4e697ee3a1..6497ebe1d2c003569da706c433475b68cfa19ae9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Identical.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Iterator;
 
@@ -33,9 +34,9 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributes;
@@ -66,7 +67,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  * needs to be fast! The five defaulted logical arguments are supposed to be cast to logical and
  * checked for NA (regardless of whether they are used).
  */
-@RBuiltin(name = "identical", kind = INTERNAL, parameterNames = {"x", "y", "num.eq", "single.NA", "attrib.as.set", "ignore.bytecode", "ignore.environment"})
+@RBuiltin(name = "identical", kind = INTERNAL, parameterNames = {"x", "y", "num.eq", "single.NA", "attrib.as.set", "ignore.bytecode", "ignore.environment"}, behavior = PURE)
 public abstract class Identical extends RBuiltinNode {
 
     protected abstract byte executeByte(Object x, Object y, boolean numEq, boolean singleNA, boolean attribAsSet, boolean ignoreBytecode, boolean ignoreEnvironment);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixFunctions.java
index c4cc37bb6087f1fcd07393095a7c10f86afc37e5..a22697f278e1978f7bb11848042eddd7c400fd0f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixFunctions.java
@@ -23,6 +23,12 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RDispatch.INTERNAL_GENERIC;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_FRAME;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import java.util.Arrays;
 
@@ -44,14 +50,14 @@ import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.unary.CastListNode;
 import com.oracle.truffle.r.nodes.unary.CastListNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBehavior;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLanguage;
@@ -139,7 +145,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "[", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "...", "drop"}, dispatch = INTERNAL_GENERIC)
+    @RBuiltin(name = "[", kind = PRIMITIVE, parameterNames = {"x", "...", "drop"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
     public abstract static class AccessArraySubsetBuiltin extends AccessArrayBuiltin {
 
         @Override
@@ -171,12 +177,12 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = ".subset", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", "...", "drop"})
+    @RBuiltin(name = ".subset", kind = PRIMITIVE, parameterNames = {"", "...", "drop"}, behavior = PURE)
     public abstract static class AccessArraySubsetDefaultBuiltin {
 
     }
 
-    @RBuiltin(name = "[[", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", "...", "exact", "drop"}, dispatch = INTERNAL_GENERIC)
+    @RBuiltin(name = "[[", kind = PRIMITIVE, parameterNames = {"", "...", "exact", "drop"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
     public abstract static class AccessArraySubscriptBuiltin extends AccessArrayBuiltin {
 
         @Override
@@ -225,7 +231,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = ".subset2", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "...", "exact", "drop"})
+    @RBuiltin(name = ".subset2", kind = PRIMITIVE, parameterNames = {"x", "...", "exact", "drop"}, behavior = PURE)
     public abstract static class AccessArraySubscriptDefaultBuiltin {
     }
 
@@ -260,7 +266,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "[<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", "..."}, dispatch = INTERNAL_GENERIC)
+    @RBuiltin(name = "[<-", kind = PRIMITIVE, parameterNames = {"", "..."}, dispatch = INTERNAL_GENERIC, behavior = PURE)
     public abstract static class UpdateArraySubsetBuiltin extends UpdateArrayBuiltin {
 
         private static final boolean IS_SUBSET = true;
@@ -272,7 +278,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "[[<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", "..."}, dispatch = INTERNAL_GENERIC)
+    @RBuiltin(name = "[[<-", kind = PRIMITIVE, parameterNames = {"", "..."}, dispatch = INTERNAL_GENERIC, behavior = PURE)
     public abstract static class UpdateArrayNodeSubscriptBuiltin extends UpdateArrayBuiltin {
 
         private static final boolean IS_SUBSET = false;
@@ -284,7 +290,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "<-", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
+    @RBuiltin(name = "<-", visibility = RVisibility.OFF, kind = PRIMITIVE, parameterNames = {"x", "i"}, behavior = RBehavior.MODIFIES_FRAME)
     public abstract static class AssignBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -293,7 +299,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "=", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
+    @RBuiltin(name = "=", visibility = RVisibility.OFF, kind = PRIMITIVE, parameterNames = {"x", "i"}, behavior = RBehavior.MODIFIES_FRAME)
     public abstract static class AssignBuiltinEq extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -302,7 +308,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "<<-", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "i"})
+    @RBuiltin(name = "<<-", visibility = RVisibility.OFF, kind = PRIMITIVE, parameterNames = {"x", "i"}, behavior = COMPLEX)
     public abstract static class AssignOuterBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -311,7 +317,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "$", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, dispatch = INTERNAL_GENERIC)
+    @RBuiltin(name = "$", kind = PRIMITIVE, parameterNames = {"", ""}, dispatch = INTERNAL_GENERIC, behavior = PURE)
     public abstract static class AccessFieldBuiltin extends RBuiltinNode {
 
         @Child private ExtractVectorNode extract = ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true);
@@ -336,7 +342,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "$<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", "", "value"}, dispatch = INTERNAL_GENERIC)
+    @RBuiltin(name = "$<-", kind = PRIMITIVE, parameterNames = {"", "", "value"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
     public abstract static class UpdateFieldBuiltin extends RBuiltinNode {
 
         private final BranchProfile coerceList = BranchProfile.create();
@@ -374,7 +380,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "{", visibility = RVisibility.CUSTOM, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "{", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class BraceBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -383,7 +389,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "(", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "(", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class ParenBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -398,7 +404,7 @@ public class InfixFunctions {
      * handled by an evaluated argument of type {@link RMissing}, although it appears as if the
      * "model" argument is missing, i.e. {@code ~ x} result in {@code `~`(x)}.
      */
-    @RBuiltin(name = "~", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "y"}, nonEvalArgs = {0, 1})
+    @RBuiltin(name = "~", kind = PRIMITIVE, parameterNames = {"x", "y"}, nonEvalArgs = {0, 1}, behavior = READS_FRAME)
     public abstract static class TildeBuiltin extends RBuiltinNode {
         private static final RStringVector FORMULA_CLASS = RDataFactory.createStringVectorFromScalar(RRuntime.FORMULA_CLASS);
 
@@ -427,7 +433,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "if", visibility = RVisibility.CUSTOM, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "if", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IfBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -436,7 +442,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "while", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "while", visibility = OFF, kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class WhileBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -445,7 +451,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "repeat", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "repeat", visibility = OFF, kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class RepeatBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -454,7 +460,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "for", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "for", visibility = OFF, kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class ForBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -463,7 +469,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "break", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "break", kind = PRIMITIVE, parameterNames = {"x"}, behavior = COMPLEX)
     public abstract static class BreakBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -472,7 +478,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "next", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "next", kind = PRIMITIVE, parameterNames = {"x"}, behavior = COMPLEX)
     public abstract static class NextBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -481,7 +487,7 @@ public class InfixFunctions {
         }
     }
 
-    @RBuiltin(name = "function", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "function", kind = PRIMITIVE, parameterNames = {"x"}, behavior = READS_FRAME)
     public abstract static class FunctionBuiltin extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java
index c4a20c0a1819fdf61111dec62e3953d02fd9fd1b..b55bcba0c9a18371cc85157f85b6f1867f8f2ae2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Inherits.java
@@ -11,13 +11,14 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.InheritsNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
@@ -29,7 +30,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "inherits", kind = INTERNAL, parameterNames = {"x", "what", "which"})
+@RBuiltin(name = "inherits", kind = INTERNAL, parameterNames = {"x", "what", "which"}, behavior = PURE)
 // TODO inherits is applicable to every type of object, if only because of "try-error".
 public abstract class Inherits extends RBuiltinNode {
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IntToBits.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IntToBits.java
index 9364b83fa93ecafb4b86a0c1306bb49e327d8747..716c189e58f9d9785db54cc5469e3eed7a7d332f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IntToBits.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IntToBits.java
@@ -22,17 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
 
-@RBuiltin(name = "intToBits", kind = RBuiltinKind.INTERNAL, parameterNames = {"x"})
+@RBuiltin(name = "intToBits", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
 public abstract class IntToBits extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java
index 9a70126cbe34810af72f0c8fe0d00055e429e917..55bf8f6b8c1b8d87a22533e736818476d4c1a7e1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Interactive.java
@@ -22,14 +22,16 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 
-@RBuiltin(name = "interactive", kind = RBuiltinKind.PRIMITIVE, parameterNames = {})
+@RBuiltin(name = "interactive", kind = PRIMITIVE, parameterNames = {}, behavior = READS_STATE)
 public abstract class Interactive extends RBuiltinNode {
     @Specialization
     protected byte interactive() {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java
index 9efe984d50488761b94d6c9406ab34ecd9521531..70c4b5fc8a83da31212e59bb0a3142756f4f6a41 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Internal.java
@@ -22,23 +22,24 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.api.nodes.NodeCost.NONE;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -59,8 +60,8 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  * parent of the the {@code .Internal}, which will be an {@code RNodeWrapper}, will remain so any
  * instrumentation at that level will remain in place.
  */
-@NodeInfo(cost = NodeCost.NONE)
-@RBuiltin(name = ".Internal", visibility = RVisibility.CUSTOM, kind = PRIMITIVE, parameterNames = {"call"}, nonEvalArgs = 0)
+@NodeInfo(cost = NONE)
+@RBuiltin(name = ".Internal", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {"call"}, nonEvalArgs = 0, behavior = COMPLEX)
 public abstract class Internal extends RBuiltinNode {
 
     private final BranchProfile errorProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Invisible.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Invisible.java
index f4aa641168437be6b05e9c1fdb37a3b628641de2..c30e2666760295444d105e320e58ff5963cf58b0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Invisible.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Invisible.java
@@ -22,15 +22,16 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RNull;
 
-@RBuiltin(name = "invisible", visibility = RVisibility.OFF, kind = PRIMITIVE, parameterNames = {"x"})
+@RBuiltin(name = "invisible", visibility = OFF, kind = PRIMITIVE, parameterNames = {"x"}, behavior = COMPLEX)
 public abstract class Invisible extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsATTY.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsATTY.java
index 6e0ce9cde5df4d71f73ddd760e43da096e27875b..5d1c2807f96c59cd41141200ce604371605d4f3b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsATTY.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsATTY.java
@@ -22,17 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.StdConnections.StdConnection;
 
-@SuppressWarnings("unused")
-@RBuiltin(name = "isatty", kind = PRIMITIVE, parameterNames = {"con"})
+@RBuiltin(name = "isatty", kind = PRIMITIVE, parameterNames = {"con"}, behavior = PURE)
 public abstract class IsATTY extends RBuiltinNode {
 
     @Specialization
@@ -41,7 +41,7 @@ public abstract class IsATTY extends RBuiltinNode {
     }
 
     @Specialization(guards = "!isRConnection(con)")
-    protected byte isATTYNonConnection(Object con) {
+    protected byte isATTYNonConnection(@SuppressWarnings("unused") Object con) {
         return RRuntime.LOGICAL_FALSE;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsFiniteFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsFiniteFunctions.java
index d16c24e13b006520bf01d934c0a45e7dcc0661e2..600bd243f8c58edbf75aea4ee69c0de38f84427d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsFiniteFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsFiniteFunctions.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import java.util.Arrays;
 import java.util.function.DoublePredicate;
@@ -33,9 +34,9 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
@@ -126,7 +127,7 @@ public class IsFiniteFunctions {
         }
     }
 
-    @RBuiltin(name = "is.finite", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.finite", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsFinite extends Adapter {
 
         @Specialization
@@ -160,7 +161,7 @@ public class IsFiniteFunctions {
         }
     }
 
-    @RBuiltin(name = "is.infinite", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.infinite", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsInfinite extends Adapter {
 
         @Specialization
@@ -184,7 +185,7 @@ public class IsFiniteFunctions {
         }
     }
 
-    @RBuiltin(name = "is.nan", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.nan", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsNaN extends Adapter {
 
         private static boolean isNaN(double value) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsListFactor.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsListFactor.java
index b2d028a3e8e389f51e2d34b6aa826fbc9234fbd0..5d105cad8443d6c8cbcd9eb94e212c96e0526df7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsListFactor.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsListFactor.java
@@ -10,6 +10,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.Fallback;
@@ -19,14 +22,13 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.IsListFactorNodeGen.IsListFactorInternalNodeGen;
 import com.oracle.truffle.r.nodes.unary.IsFactorNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 
 // from apply.c
 
-@RBuiltin(name = "islistfactor", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "recursive"})
+@RBuiltin(name = "islistfactor", kind = INTERNAL, parameterNames = {"x", "recursive"}, behavior = PURE)
 public abstract class IsListFactor extends RBuiltinNode {
 
     protected abstract static class IsListFactorInternal extends Node {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsMethodsDispatchOn.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsMethodsDispatchOn.java
index 10a33b1bb474b3f3a8e2e638abc5d447f0810aa7..22f316102e814c4c4fc05860d39973dc5e37faec 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsMethodsDispatchOn.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsMethodsDispatchOn.java
@@ -22,14 +22,16 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 
-@RBuiltin(name = ".isMethodsDispatchOn", kind = RBuiltinKind.PRIMITIVE, parameterNames = {})
+@RBuiltin(name = ".isMethodsDispatchOn", kind = PRIMITIVE, parameterNames = {}, behavior = READS_STATE)
 public abstract class IsMethodsDispatchOn extends RBuiltinNode {
 
     public abstract byte execute();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
index 42461ac11012557d372a73036a6e3c8dd1f56c06..c1800d0e0d80ad7f2a92b45960cb682f4cedceac 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
@@ -22,16 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
@@ -47,7 +48,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
-@RBuiltin(name = "is.na", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.INTERNAL_GENERIC)
+@RBuiltin(name = "is.na", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class IsNA extends RBuiltinNode {
 
     @Child private IsNA recursiveIsNA;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsS4.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsS4.java
index cd2bde71e1d3a1ff66ac5774a2039c16a2faa7f5..f4b26fcf8a8cb22d73345c7efde39196de4dd194 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsS4.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsS4.java
@@ -22,24 +22,24 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 
-@SuppressWarnings("unused")
-@RBuiltin(name = "isS4", kind = PRIMITIVE, parameterNames = {"object"})
+@RBuiltin(name = "isS4", kind = PRIMITIVE, parameterNames = {"object"}, behavior = PURE)
 public abstract class IsS4 extends RBuiltinNode {
 
     public abstract byte execute(Object value);
 
     @Specialization
-    protected byte isS4(RNull object) {
+    protected byte isS4(@SuppressWarnings("unused") RNull object) {
         return RRuntime.asLogical(RContext.getInstance().isNullS4Object());
     }
 
@@ -49,7 +49,7 @@ public abstract class IsS4 extends RBuiltinNode {
     }
 
     @Specialization(guards = "!isTypedValue(object)")
-    protected byte isS4(Object object) {
+    protected byte isS4(@SuppressWarnings("unused") Object object) {
         return RRuntime.LOGICAL_FALSE;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java
index 96e8752dacb9cf509caa265b5ba7e3865c68ab2c..2d10974c2f2cd5be2a516cc84e5e1df3201ff6d1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java
@@ -22,8 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
@@ -33,11 +34,10 @@ 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.helpers.InheritsCheckNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
@@ -83,7 +83,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.array", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.array", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsArray extends MissingAdapter {
 
         public abstract byte execute(Object value);
@@ -99,7 +99,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.recursive", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.recursive", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsRecursive extends MissingAdapter {
 
         @Specialization
@@ -127,7 +127,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.atomic", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.atomic", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsAtomic extends MissingAdapter {
 
         @Child private InheritsCheckNode inheritsFactorCheck = new InheritsCheckNode(RRuntime.CLASS_FACTOR);
@@ -157,7 +157,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.call", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.call", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsCall extends MissingAdapter {
 
         @Specialization
@@ -171,7 +171,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.character", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.character", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsCharacter extends MissingAdapter {
 
         @Specialization
@@ -189,7 +189,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.complex", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.complex", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsComplex extends MissingAdapter {
 
         @Specialization
@@ -207,7 +207,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.double", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.double", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsDouble extends MissingAdapter {
 
         @Specialization
@@ -225,7 +225,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.expression", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.expression", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsExpression extends MissingAdapter {
 
         @Specialization
@@ -239,7 +239,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.function", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.function", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsFunction extends MissingAdapter {
 
         @Specialization
@@ -253,7 +253,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.integer", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.integer", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsInteger extends MissingAdapter {
 
         @Specialization
@@ -271,7 +271,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.language", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.language", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsLanguage extends MissingAdapter {
         @Specialization
         protected byte isType(RSymbol value) {
@@ -294,7 +294,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.list", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.list", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsList extends MissingAdapter {
 
         private final ConditionProfile isListProfile = ConditionProfile.createBinaryProfile();
@@ -317,7 +317,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.logical", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.logical", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsLogical extends MissingAdapter {
 
         @Specialization
@@ -335,7 +335,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.matrix", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.matrix", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsMatrix extends MissingAdapter {
 
         private final ConditionProfile isMatrixProfile = ConditionProfile.createBinaryProfile();
@@ -351,7 +351,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.name", aliases = {"is.symbol"}, kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.name", aliases = {"is.symbol"}, kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsName extends MissingAdapter {
 
         @Specialization
@@ -365,7 +365,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.numeric", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.numeric", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsNumeric extends MissingAdapter {
 
         @Specialization(guards = "!isFactor(value)")
@@ -399,7 +399,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.null", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.null", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsNull extends MissingAdapter {
 
         @Specialization
@@ -420,7 +420,7 @@ public class IsTypeFunctions {
      * been added explicitly to the object. If the attribute is removed, it should return
      * {@code FALSE}.
      */
-    @RBuiltin(name = "is.object", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.object", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsObject extends MissingAdapter {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
@@ -439,7 +439,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.pairlist", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.pairlist", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsPairList extends MissingAdapter {
         @Specialization
         protected byte isType(RNull value) {
@@ -457,7 +457,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.raw", kind = PRIMITIVE, parameterNames = {"x"})
+    @RBuiltin(name = "is.raw", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
     public abstract static class IsRaw extends MissingAdapter {
 
         @Specialization
@@ -475,7 +475,7 @@ public class IsTypeFunctions {
         }
     }
 
-    @RBuiltin(name = "is.vector", kind = INTERNAL, parameterNames = {"x", "mode"})
+    @RBuiltin(name = "is.vector", kind = INTERNAL, parameterNames = {"x", "mode"}, behavior = PURE)
     public abstract static class IsVector extends ErrorAdapter {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsUnsorted.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsUnsorted.java
index ca91a45af5024f2e888dd7948f012f626ed5dbfc..827a48a9edae5c1acb6085e7a02e1a243b5ce150 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsUnsorted.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsUnsorted.java
@@ -22,19 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.r.nodes.binary.BinaryMapBooleanFunctionNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.BinaryCompare;
 
-@RBuiltin(name = "is.unsorted", kind = INTERNAL, parameterNames = {"x", "strictly"})
+@RBuiltin(name = "is.unsorted", kind = INTERNAL, parameterNames = {"x", "strictly"}, behavior = PURE)
 // TODO support strictly
 // TODO support lists
 public abstract class IsUnsorted extends RBuiltinNode {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
index ee9c29326391620d178d7a2784afe2ef22b54305..fbca3bc8b730b5ae713741b793487dd8408090cd 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
@@ -11,7 +11,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -24,9 +26,9 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNodeGen;
 import com.oracle.truffle.r.runtime.RAccuracyInfo;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -49,7 +51,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
  */
 public class LaFunctions {
 
-    @RBuiltin(name = "La_version", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "La_version", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class Version extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -60,7 +62,7 @@ public class LaFunctions {
         }
     }
 
-    @RBuiltin(name = "La_rg", kind = INTERNAL, parameterNames = {"matrix", "onlyValues"})
+    @RBuiltin(name = "La_rg", kind = INTERNAL, parameterNames = {"matrix", "onlyValues"}, behavior = PURE)
     public abstract static class Rg extends RBuiltinNode {
 
         @CompilationFinal private static final String[] NAMES = new String[]{"values", "vectors"};
@@ -145,7 +147,7 @@ public class LaFunctions {
         }
     }
 
-    @RBuiltin(name = "La_qr", kind = INTERNAL, parameterNames = {"in"})
+    @RBuiltin(name = "La_qr", kind = INTERNAL, parameterNames = {"in"}, behavior = PURE)
     public abstract static class Qr extends RBuiltinNode {
 
         @CompilationFinal private static final String[] NAMES = new String[]{"qr", "rank", "qraux", "pivot"};
@@ -197,7 +199,7 @@ public class LaFunctions {
         }
     }
 
-    @RBuiltin(name = "qr_coef_real", kind = INTERNAL, parameterNames = {"q", "b"})
+    @RBuiltin(name = "qr_coef_real", kind = INTERNAL, parameterNames = {"q", "b"}, behavior = PURE)
     public abstract static class QrCoefReal extends RBuiltinNode {
 
         private final BranchProfile errorProfile = BranchProfile.create();
@@ -261,7 +263,7 @@ public class LaFunctions {
         }
     }
 
-    @RBuiltin(name = "det_ge_real", kind = INTERNAL, parameterNames = {"a", "uselog"})
+    @RBuiltin(name = "det_ge_real", kind = INTERNAL, parameterNames = {"a", "uselog"}, behavior = PURE)
     public abstract static class DetGeReal extends RBuiltinNode {
 
         private static final RStringVector NAMES_VECTOR = RDataFactory.createStringVector(new String[]{"modulus", "sign"}, RDataFactory.COMPLETE_VECTOR);
@@ -331,7 +333,7 @@ public class LaFunctions {
         }
     }
 
-    @RBuiltin(name = "La_chol", kind = INTERNAL, parameterNames = {"a", "pivot", "tol"})
+    @RBuiltin(name = "La_chol", kind = INTERNAL, parameterNames = {"a", "pivot", "tol"}, behavior = PURE)
     public abstract static class LaChol extends RBuiltinNode {
 
         private final BranchProfile errorProfile = BranchProfile.create();
@@ -393,7 +395,7 @@ public class LaFunctions {
         }
     }
 
-    @RBuiltin(name = "La_solve", kind = INTERNAL, parameterNames = {"a", "bin", "tolin"})
+    @RBuiltin(name = "La_solve", kind = INTERNAL, parameterNames = {"a", "bin", "tolin"}, behavior = PURE)
     public abstract static class LaSolve extends RBuiltinNode {
         protected final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
         @Child private CastDoubleNode castDouble = CastDoubleNodeGen.create(false, false, false);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
index 3b2c912a82e22479d61ab6889f53a7c2a038f729..7d63116162572f6333674e70744eb839b4186d8b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
@@ -11,7 +11,8 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
@@ -36,12 +37,12 @@ import com.oracle.truffle.r.nodes.builtin.base.LapplyNodeGen.LapplyInternalNodeG
 import com.oracle.truffle.r.nodes.control.RLengthNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize.State;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -61,7 +62,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  *
  * See the comment in {@link VApply} regarding "...".
  */
-@RBuiltin(name = "lapply", kind = INTERNAL, parameterNames = {"X", "FUN"}, splitCaller = true)
+@RBuiltin(name = "lapply", kind = INTERNAL, parameterNames = {"X", "FUN"}, splitCaller = true, behavior = COMPLEX)
 public abstract class Lapply extends RBuiltinNode {
 
     private static final Source CALL_SOURCE = RSource.fromTextInternal("FUN(X[[i]], ...)", RSource.Internal.LAPPLY);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Length.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Length.java
index cee6a6cf726eb1116beaeed2a8eddefdd40c239b..b5f2db37ae933b1b11a6d144b2feece6258a474a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Length.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Length.java
@@ -22,22 +22,24 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
-@RBuiltin(name = "length", kind = PRIMITIVE, parameterNames = {"x"})
+@RBuiltin(name = "length", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
 public abstract class Length extends RBuiltinNode {
 
     public abstract int executeInt(VirtualFrame frame, Object vector);
 
     @Specialization
-    protected int getLength(VirtualFrame frame, Object vector, @Cached("create()") RLengthNode lengthNode) {
+    protected int getLength(VirtualFrame frame, Object vector,
+                    @Cached("create()") RLengthNode lengthNode) {
         return lengthNode.executeInteger(frame, vector);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lengths.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lengths.java
index c0dce6d3104e83d85c3a8b62982770bc719e9697..c8210163ab4edb2e607186ccfc2685099f23396f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lengths.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lengths.java
@@ -22,6 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import java.util.Arrays;
 
 import com.oracle.truffle.api.CompilerDirectives;
@@ -32,11 +35,11 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.IsTypeFunctions.IsAtomic;
 import com.oracle.truffle.r.nodes.builtin.base.IsTypeFunctionsFactory.IsAtomicNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.nodes.control.RLengthNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -44,11 +47,11 @@ import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "lengths", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "use.names"})
+@RBuiltin(name = "lengths", kind = INTERNAL, parameterNames = {"x", "use.names"}, behavior = PURE)
 public abstract class Lengths extends RBuiltinNode {
 
     @Child private IsAtomic isAtomicNode;
-    @Child private Length lengthNode;
+    @Child private RLengthNode lengthNode;
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
@@ -60,7 +63,7 @@ public abstract class Lengths extends RBuiltinNode {
     private void initLengthNode() {
         if (lengthNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            lengthNode = insert(LengthNodeGen.create(null));
+            lengthNode = insert(RLengthNode.create());
         }
     }
 
@@ -70,7 +73,7 @@ public abstract class Lengths extends RBuiltinNode {
         int[] data = new int[list.getLength()];
         for (int i = 0; i < data.length; i++) {
             Object elem = list.getDataAt(i);
-            data[i] = lengthNode.executeInt(frame, elem);
+            data[i] = lengthNode.executeInteger(frame, elem);
         }
         return createResult(list, data, useNames);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/License.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/License.java
index 20f706f38a0985d033813194f5a9c2d6ad340294..dc2757951add8e95c945012a65148b0b2cc3ec3e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/License.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/License.java
@@ -22,21 +22,22 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.SUBSTITUTE;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.SUBSTITUTE;
 
 import java.io.IOException;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.data.RNull;
 
-@RBuiltin(name = "license", visibility = RVisibility.OFF, aliases = {"licence"}, kind = SUBSTITUTE, parameterNames = {})
+@RBuiltin(name = "license", visibility = OFF, aliases = {"licence"}, kind = SUBSTITUTE, parameterNames = {}, behavior = IO)
 public abstract class License extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/List2Env.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/List2Env.java
index eeee1b8d903e14e03460542652e63971bc78befe..bae6feead23e36ad0a1c3a7ba166c502db08338c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/List2Env.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/List2Env.java
@@ -22,17 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "list2env", kind = INTERNAL, parameterNames = {"x", "envir"})
+@RBuiltin(name = "list2env", kind = INTERNAL, parameterNames = {"x", "envir"}, behavior = PURE)
 public abstract class List2Env extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ListBuiltin.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ListBuiltin.java
index d432025c7e423de5415b102c65764845fa7da009..0fbc233c6a0b1b27d3a60aa6eec89c1f897b2df1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ListBuiltin.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ListBuiltin.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -34,8 +35,8 @@ 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.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -43,7 +44,7 @@ import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RShareable;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 
-@RBuiltin(name = "list", kind = PRIMITIVE, parameterNames = {"..."})
+@RBuiltin(name = "list", kind = PRIMITIVE, parameterNames = {"..."}, behavior = PURE)
 public abstract class ListBuiltin extends RBuiltinNode {
 
     protected static final int CACHE_LIMIT = 2;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java
index a5aa7a8858b55c67903b05b04c52c0d507c9d880..e61ffdd09d51f9b01e722c6b7b567781c44514dd 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LoadSaveFunctions.java
@@ -12,7 +12,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.BufferedInputStream;
 import java.io.FileInputStream;
@@ -26,13 +28,11 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.SerializeFunctions.Adapter;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -48,7 +48,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 public class LoadSaveFunctions {
 
-    @RBuiltin(name = "loadFromConn2", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"con", "envir", "verbose"})
+    @RBuiltin(name = "loadFromConn2", visibility = OFF, kind = INTERNAL, parameterNames = {"con", "envir", "verbose"}, behavior = IO)
     public abstract static class LoadFromConn2 extends RBuiltinNode {
 
         private final NACheck naCheck = NACheck.create();
@@ -98,7 +98,7 @@ public class LoadSaveFunctions {
         }
     }
 
-    @RBuiltin(name = "load", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"con", "envir"})
+    @RBuiltin(name = "load", visibility = OFF, kind = INTERNAL, parameterNames = {"con", "envir"}, behavior = IO)
     public abstract static class Load extends RBuiltinNode {
         // now deprecated but still used by some packages
 
@@ -169,7 +169,7 @@ public class LoadSaveFunctions {
         }
     }
 
-    @RBuiltin(name = "saveToConn", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"list", "conn", "ascii", "version", "envir", "eval.promises"})
+    @RBuiltin(name = "saveToConn", visibility = OFF, kind = INTERNAL, parameterNames = {"list", "conn", "ascii", "version", "envir", "eval.promises"}, behavior = IO)
     public abstract static class SaveToConn extends Adapter {
         private static final String ASCII_HEADER = "RDA2\n";
         private static final String XDR_HEADER = "RDX2\n";
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LocaleFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LocaleFunctions.java
index 49ecf5d8f31d6aacae4b0e932ff784306bb875aa..c55810944e2010f2a4f0612444b9a2d566b64aef 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LocaleFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LocaleFunctions.java
@@ -22,16 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.MODIFIES_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import java.nio.charset.Charset;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 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.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -41,7 +45,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 public class LocaleFunctions {
 
-    @RBuiltin(name = "Sys.getlocale", kind = RBuiltinKind.INTERNAL, parameterNames = {"category"})
+    @RBuiltin(name = "Sys.getlocale", kind = INTERNAL, parameterNames = {"category"}, behavior = READS_STATE)
     public abstract static class GetLocale extends RBuiltinNode {
 
         @Specialization
@@ -75,7 +79,7 @@ public class LocaleFunctions {
         }
     }
 
-    @RBuiltin(name = "Sys.setlocale", kind = RBuiltinKind.INTERNAL, parameterNames = {"category", "locale"})
+    @RBuiltin(name = "Sys.setlocale", kind = INTERNAL, parameterNames = {"category", "locale"}, behavior = MODIFIES_STATE)
     public abstract static class SetLocale extends RBuiltinNode {
 
         @Specialization
@@ -93,7 +97,7 @@ public class LocaleFunctions {
         }
     }
 
-    @RBuiltin(name = "Sys.localeconv", kind = RBuiltinKind.INTERNAL, parameterNames = {})
+    @RBuiltin(name = "Sys.localeconv", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class LocaleConv extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -103,7 +107,7 @@ public class LocaleFunctions {
         }
     }
 
-    @RBuiltin(name = "l10n_info", kind = RBuiltinKind.INTERNAL, parameterNames = {})
+    @RBuiltin(name = "l10n_info", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class L10nInfo extends RBuiltinNode {
         private static final RStringVector NAMES = RDataFactory.createStringVector(new String[]{"MBCS", "UTF-8", "LATIN-1"}, RDataFactory.COMPLETE_VECTOR);
 
@@ -118,7 +122,7 @@ public class LocaleFunctions {
         }
     }
 
-    @RBuiltin(name = "enc2native", kind = RBuiltinKind.PRIMITIVE, parameterNames = "x")
+    @RBuiltin(name = "enc2native", kind = PRIMITIVE, parameterNames = "x", behavior = READS_STATE)
     public abstract static class Enc2Native extends RBuiltinNode {
         @Specialization
         protected Object enc2Native(RAbstractStringVector x) {
@@ -127,7 +131,7 @@ public class LocaleFunctions {
         }
     }
 
-    @RBuiltin(name = "enc2utf8", kind = RBuiltinKind.PRIMITIVE, parameterNames = "x")
+    @RBuiltin(name = "enc2utf8", kind = PRIMITIVE, parameterNames = "x", behavior = READS_STATE)
     public abstract static class Enc2Utf8 extends RBuiltinNode {
         @Specialization
         protected Object enc2Native(RAbstractStringVector x) {
@@ -136,7 +140,7 @@ public class LocaleFunctions {
         }
     }
 
-    @RBuiltin(name = "bindtextdomain", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"domain", "dirname"})
+    @RBuiltin(name = "bindtextdomain", kind = PRIMITIVE, parameterNames = {"domain", "dirname"}, behavior = READS_STATE)
     public abstract static class BindTextDomain extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
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 29cb07a12ba959ffba96a4171d620ae89c97e2cb..bff61b25db30e3bb4a4ccaa3e64619360e00391c 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,17 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -41,7 +42,7 @@ import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 
 public class LogFunctions {
-    @RBuiltin(name = "log", kind = PRIMITIVE, parameterNames = {"x", "base"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "log", kind = PRIMITIVE, parameterNames = {"x", "base"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Log extends RBuiltinNode {
 
         @Override
@@ -104,7 +105,7 @@ public class LogFunctions {
         }
     }
 
-    @RBuiltin(name = "log10", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "log10", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Log10 extends UnaryArithmeticBuiltinNode {
 
         public Log10() {
@@ -126,7 +127,7 @@ public class LogFunctions {
         }
     }
 
-    @RBuiltin(name = "log2", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "log2", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Log2 extends UnaryArithmeticBuiltinNode {
 
         public Log2() {
@@ -148,7 +149,7 @@ public class LogFunctions {
         }
     }
 
-    @RBuiltin(name = "log1p", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "log1p", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Log1p extends UnaryArithmeticBuiltinNode {
 
         public Log1p() {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Ls.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Ls.java
index c4af62535bc0d71875771a6a5730fe49c66a6733..3f958d27efd911a5ff2df9fc5fe401f197de7a03 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Ls.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Ls.java
@@ -22,17 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "ls", aliases = {"objects"}, kind = INTERNAL, parameterNames = {"envir", "all.names", "sorted"})
+@RBuiltin(name = "ls", aliases = {"objects"}, kind = INTERNAL, parameterNames = {"envir", "all.names", "sorted"}, behavior = PURE)
 public abstract class Ls extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MakeNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MakeNames.java
index 05c980834d999e249553b55bb83945de9a4f213b..bba53b2726983cd0c892f65ec4695d5d0e8eaf92 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MakeNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MakeNames.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -34,17 +35,17 @@ import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "make.names", kind = INTERNAL, parameterNames = {"names", "allow_"})
+@RBuiltin(name = "make.names", kind = INTERNAL, parameterNames = {"names", "allow_"}, behavior = PURE)
 public abstract class MakeNames extends RBuiltinNode {
 
     private final ConditionProfile namesLengthZero = ConditionProfile.createBinaryProfile();
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 38e577403ba6d3cb49025f28a9aec877616e01ef..8a4065d95e6c666f53f372e324f74dc8066960e7 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
@@ -22,21 +22,22 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.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.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RString;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "make.unique", kind = INTERNAL, parameterNames = {"names", "sep"})
+@RBuiltin(name = "make.unique", kind = INTERNAL, parameterNames = {"names", "sep"}, behavior = PURE)
 public abstract class MakeUnique extends RBuiltinNode {
 
     private final ConditionProfile namesProfile = ConditionProfile.createBinaryProfile();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java
index 20610124fb45bf35afc7c0a651d1103f8eb4f367..d3deebf3f1f8eb22b6e59411c21b9c465da2bbbe 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java
@@ -23,7 +23,8 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -41,8 +42,8 @@ import com.oracle.truffle.r.nodes.control.RLengthNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.AnonymousFrameVariable;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -59,7 +60,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
  * {@code fun(dots[0][X], dots[1][X], , dots[N][X], MoreArgs)} for {@code X=1..M} where {@code M} is
  * the longest vector, with the usual recycling rule.
  */
-@RBuiltin(name = "mapply", kind = INTERNAL, parameterNames = {"FUN", "dots", "MoreArgs"}, splitCaller = true)
+@RBuiltin(name = "mapply", kind = INTERNAL, parameterNames = {"FUN", "dots", "MoreArgs"}, splitCaller = true, behavior = COMPLEX)
 public abstract class Mapply extends RBuiltinNode {
 
     protected static final class ElementNode extends Node {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatMult.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatMult.java
index 2b2e0dd9bbe3c78c1080d84b47c4a67cb76ddb0e..a24242b07bc1142361a4162b9efc6dc6d2746814 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatMult.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatMult.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -32,9 +33,9 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.binary.BinaryMapArithmeticFunctionNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
@@ -54,7 +55,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "%*%", kind = PRIMITIVE, parameterNames = {"", ""})
+@RBuiltin(name = "%*%", kind = PRIMITIVE, parameterNames = {"", ""}, behavior = PURE)
 public abstract class MatMult extends RBuiltinNode {
 
     private static final int BLOCK_SIZE = 64;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java
index ebe53afc3cb2e4e6a6b4a86289f9adfe99b2dd5f..bbb1f2bc9bffa1cc9b8065a6ac566e44bb48c950 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Match.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Arrays;
 
@@ -36,9 +37,9 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -56,7 +57,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
-@RBuiltin(name = "match", kind = INTERNAL, parameterNames = {"x", "table", "nomatch", "incomparables"})
+@RBuiltin(name = "match", kind = INTERNAL, parameterNames = {"x", "table", "nomatch", "incomparables"}, behavior = PURE)
 public abstract class Match extends RBuiltinNode {
 
     private static final int TABLE_SIZE_FACTOR = 10;
@@ -79,7 +80,7 @@ public abstract class Match extends RBuiltinNode {
     private RAbstractStringVector castString(RAbstractVector operand) {
         if (castString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castString = insert(CastStringNodeGen.create(false, false, false, false));
+            castString = insert(CastStringNodeGen.create(false, false, false));
         }
         return (RAbstractStringVector) castString.execute(operand);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java
index a90acdd3f05996d26b537da99104d3c44df84fc9..c857e315e262208120ed3bc35fcc060748174c39 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatchFun.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.SUBSTITUTE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.SUBSTITUTE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -41,10 +42,10 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.MatchFunNodeGen.MatchFunInternalNodeGen;
 import com.oracle.truffle.r.nodes.function.GetCallerFrameNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
@@ -56,7 +57,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-@RBuiltin(name = "match.fun", kind = SUBSTITUTE, parameterNames = {"fun", "descend"}, nonEvalArgs = 0)
+@RBuiltin(name = "match.fun", kind = SUBSTITUTE, parameterNames = {"fun", "descend"}, nonEvalArgs = 0, behavior = COMPLEX)
 public abstract class MatchFun extends RBuiltinNode {
 
     @CompilationFinal private String lastFun;
@@ -152,7 +153,7 @@ public abstract class MatchFun extends RBuiltinNode {
 
         @TruffleBoundary
         private static Object slowPathLookup(String name, MaterializedFrame frame, boolean descend) {
-            Object result = descend ? ReadVariableNode.lookupFunction(name, frame, false) : ReadVariableNode.lookupAny(name, frame, false);
+            Object result = descend ? ReadVariableNode.lookupFunction(name, frame) : ReadVariableNode.lookupAny(name, frame, false);
             if (result == null) {
                 throw RError.error(RError.SHOW_CALLER, descend ? RError.Message.UNKNOWN_FUNCTION : RError.Message.UNKNOWN_OBJECT, name);
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Matrix.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Matrix.java
index 8c9731867332ee7d94e5651b3106fa15fb491f3c..0e5d9463a59853cfc98f67f2b6d0c00fb334644c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Matrix.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Matrix.java
@@ -22,23 +22,24 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "matrix", kind = INTERNAL, parameterNames = {"data", "nrow", "ncol", "isTrue(byrow)", "dimnames", "missingNrow", "missingNcol"})
+@RBuiltin(name = "matrix", kind = INTERNAL, parameterNames = {"data", "nrow", "ncol", "isTrue(byrow)", "dimnames", "missingNrow", "missingNcol"}, behavior = PURE)
 public abstract class Matrix extends RBuiltinNode {
 
     @Child private Transpose transpose;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Max.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Max.java
index 7c468fb2f3bef6b2997b3d98eb4420e5808606b6..d6eeb7853e25c838c76cc6aa65bbce15db863bc9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Max.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Max.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RDispatch.SUMMARY_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -31,14 +33,13 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNode.ReduceSemantics;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 
-@RBuiltin(name = "max", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = RDispatch.SUMMARY_GROUP_GENERIC)
+@RBuiltin(name = "max", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = SUMMARY_GROUP_GENERIC, behavior = PURE)
 public abstract class Max extends RBuiltinNode {
 
     private static final ReduceSemantics semantics = new ReduceSemantics(RRuntime.INT_MIN_VALUE, Double.NEGATIVE_INFINITY, false, RError.Message.NO_NONMISSING_MAX,
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mean.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mean.java
index 3314ec6f9f14647fdb2558b2db09f977cb25c86f..a81455a0e79c328667f393b230b1ef93f833dd77 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mean.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mean.java
@@ -22,13 +22,14 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
@@ -37,7 +38,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 
-@RBuiltin(name = "mean", kind = INTERNAL, parameterNames = {"x"}, dispatch = RDispatch.INTERNAL_GENERIC)
+@RBuiltin(name = "mean", kind = INTERNAL, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class Mean extends RBuiltinNode {
 
     private final BranchProfile emptyProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Merge.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Merge.java
index 906f5808c5a2125ea27f4b020a30fb0acacbfa31..b8b26c8d0f9fa8c7b49909c99894cce1cb30ab7a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Merge.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Merge.java
@@ -12,22 +12,23 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.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.model.RAbstractIntVector;
 
-@RBuiltin(name = "merge", kind = INTERNAL, parameterNames = {"xinds", "xinds", "all.x", "all.y"})
+@RBuiltin(name = "merge", kind = INTERNAL, parameterNames = {"xinds", "xinds", "all.x", "all.y"}, behavior = PURE)
 public abstract class Merge extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Min.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Min.java
index a0c8fb69a0925c98e3acd1733ba9a1395414d34c..0762b506438c0c6fadfb2a8b5ff8df88533c95de 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Min.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Min.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RDispatch.SUMMARY_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -31,14 +33,13 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNode.ReduceSemantics;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 
-@RBuiltin(name = "min", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = RDispatch.SUMMARY_GROUP_GENERIC)
+@RBuiltin(name = "min", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = SUMMARY_GROUP_GENERIC, behavior = PURE)
 public abstract class Min extends RBuiltinNode {
 
     private static final ReduceSemantics semantics = new ReduceSemantics(RRuntime.INT_MAX_VALUE, Double.POSITIVE_INFINITY, false, RError.Message.NO_NONMISSING_MIN,
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Missing.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Missing.java
index 090e65da4d4b4edbd853276fc22a9ff285591305..4bb9e38c5e451f4c803729a3eafbb5ef264c16c3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Missing.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Missing.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RDispatch.SPECIAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -38,12 +40,11 @@ import com.oracle.truffle.r.nodes.builtin.base.MissingFactory.MissingCheckCacheN
 import com.oracle.truffle.r.nodes.function.GetMissingValueNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.nodes.function.RMissingHelper;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RPromise.PromiseState;
 import com.oracle.truffle.r.runtime.nodes.RNode;
@@ -51,7 +52,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-@RBuiltin(name = "missing", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.SPECIAL)
+@RBuiltin(name = "missing", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = SPECIAL, behavior = COMPLEX)
 public final class Missing extends RBuiltinNode {
 
     private final String symbol;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NArgs.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NArgs.java
index 22a692f7569978e0072cf42e137aa127f8b463a3..6a166f0a124262961f583bac969c032b8af4936a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NArgs.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NArgs.java
@@ -22,21 +22,22 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBehavior;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
 
 // TODO Figure out how to distinguish f(,,a) from f(a) - RMissing is used in both contexts
-@RBuiltin(name = "nargs", kind = PRIMITIVE, parameterNames = {})
+@RBuiltin(name = "nargs", kind = PRIMITIVE, parameterNames = {}, behavior = RBehavior.READS_FRAME)
 public abstract class NArgs extends RBuiltinNode {
 
     private final BranchProfile isPromiseProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
index cbf5f639747597685c6fce213ad7f1661bcb94ac..6f78bad2766e5866b55b01e2188e8cd499db806f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
@@ -22,91 +22,54 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asIntegerVector;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asStringVector;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+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.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.nodes.unary.ConversionFailedException;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 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;
 
 // TODO interpret "type" and "allowNA" arguments
-@RBuiltin(name = "nchar", kind = INTERNAL, parameterNames = {"x", "type", "allowNA", "keepNA"})
+@RBuiltin(name = "nchar", kind = INTERNAL, parameterNames = {"x", "type", "allowNA", "keepNA"}, behavior = PURE)
 public abstract class NChar extends RBuiltinNode {
 
-    @Child private CastStringNode convertString;
-    @Child private InheritsCheckNode factorInheritsCheck;
-
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
-    public abstract Object execute(Object value, Object type, Object allowNA, Object keepNA);
-
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.arg("x").mapIf(stringValue(), asStringVector(), asIntegerVector());
-        casts.toLogical(2).toLogical(3);
-    }
-
-    private String coerceContent(Object content) {
-        if (convertString == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            convertString = insert(CastStringNodeGen.create(false, false, false, false));
-        }
-        try {
-            return (String) convertString.executeString(content);
-        } catch (ConversionFailedException e) {
-            throw RError.error(this, RError.Message.TYPE_EXPECTED, RType.Character.getName());
-        }
+        casts.arg("x").mapIf(Predef.integerValue(), asIntegerVector(), asStringVector(true, false, false));
+        casts.arg("type").asStringVector().findFirst();
+        casts.arg("allowNA").asLogicalVector().findFirst(RRuntime.LOGICAL_TRUE).map(toBoolean());
+        casts.arg("keepNA").asLogicalVector().findFirst(RRuntime.LOGICAL_FALSE).map(toBoolean());
     }
 
     @SuppressWarnings("unused")
     @Specialization
-    protected RIntVector nchar(RNull value, String type, byte allowNA, byte keepNA) {
+    protected RIntVector nchar(RNull value, String type, boolean allowNA, boolean keepNA) {
         return RDataFactory.createEmptyIntVector();
     }
 
     @SuppressWarnings("unused")
     @Specialization
-    protected int nchar(int value, String type, byte allowNA, byte keepNA) {
-        return coerceContent(value).length();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    protected int nchar(double value, String type, byte allowNA, byte keepNA) {
-        return coerceContent(value).length();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    protected int nchar(byte value, String type, byte allowNA, byte keepNA) {
-        return coerceContent(value).length();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization
-    protected RIntVector ncharInt(RAbstractIntVector vector, String type, byte allowNA, byte keepNA) {
+    protected RIntVector ncharInt(RAbstractIntVector vector, String type, boolean allowNA, boolean keepNA,
+                    @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
+                    @Cached("create()") RAttributeProfiles attrProfiles) {
         int len = vector.getLength();
         int[] result = new int[len];
-        for (int i = 0; i < len; i++) {
+        loopProfile.profileCounted(len);
+        for (int i = 0; loopProfile.inject(i < len); i++) {
             int x = vector.getDataAt(i);
             if (x == RRuntime.INT_NA) {
                 result[i] = 2;
@@ -114,68 +77,20 @@ public abstract class NChar extends RBuiltinNode {
                 result[i] = (int) (Math.log10(x) + 1); // not the fastest one
             }
         }
-
-        return RDataFactory.createIntVector(result, false);
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization(guards = "vector.getLength() == 0")
-    protected RIntVector ncharL0(RAbstractStringVector vector, String type, byte allowNA, byte keepNA) {
-        return RDataFactory.createEmptyIntVector();
-    }
-
-    @SuppressWarnings("unused")
-    @Specialization(guards = "vector.getLength() == 1")
-    protected int ncharL1(RAbstractStringVector vector, String type, byte allowNA, byte keepNA) {
-        return vector.getDataAt(0).length();
+        return RDataFactory.createIntVector(result, true, vector.getNames(attrProfiles));
     }
 
     @SuppressWarnings("unused")
-    @Specialization(guards = "vector.getLength() > 1")
-    protected RIntVector nchar(RAbstractStringVector vector, String type, byte allowNA, byte keepNA) {
+    @Specialization
+    protected RIntVector nchar(RAbstractStringVector vector, String type, boolean allowNA, boolean keepNA,
+                    @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
+                    @Cached("create()") RAttributeProfiles attrProfiles) {
         int len = vector.getLength();
         int[] result = new int[len];
-        for (int i = 0; i < len; i++) {
+        loopProfile.profileCounted(len);
+        for (int i = 0; loopProfile.inject(i < len); i++) {
             result[i] = vector.getDataAt(i).length();
         }
-        return RDataFactory.createIntVector(result, vector.isComplete(), vector.getNames(attrProfiles));
-    }
-
-    protected static NChar createRecursive() {
-        return NCharNodeGen.create(null);
-    }
-
-    /*
-     * this builtin is sometimes used with only 3 arguments - keepNA defaults to FALSE.
-     */
-    @Specialization
-    protected Object ncharNoKeepNA(Object obj, Object type, Object allowNA, @SuppressWarnings("unused") RMissing keepNA, //
-                    @Cached("createRecursive()") NChar rec) {
-        return rec.execute(obj, type, allowNA, RRuntime.LOGICAL_FALSE);
-    }
-
-    @SuppressWarnings("unused")
-    @Fallback
-    protected RIntVector nchar(Object obj, Object type, Object allowNA, Object keepNA) {
-        if (factorInheritsCheck == null) {
-            CompilerDirectives.transferToInterpreter();
-            factorInheritsCheck = insert(new InheritsCheckNode(RRuntime.CLASS_FACTOR));
-        }
-
-        if (factorInheritsCheck.execute(obj)) {
-            throw RError.error(this, RError.Message.REQUIRES_CHAR_VECTOR, "nchar");
-        }
-
-        if (obj instanceof RAbstractVector) {
-            RAbstractVector vector = (RAbstractVector) obj;
-            int len = vector.getLength();
-            int[] result = new int[len];
-            for (int i = 0; i < len; i++) {
-                result[i] = coerceContent(vector.getDataAtAsObject(i)).length();
-            }
-            return RDataFactory.createIntVector(result, vector.isComplete(), vector.getNames(attrProfiles));
-        } else {
-            throw RError.error(this, RError.Message.CANNOT_COERCE, RRuntime.classToString(obj.getClass()), "character");
-        }
+        return RDataFactory.createIntVector(result, true, vector.getNames(attrProfiles));
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NGetText.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NGetText.java
index 487871b98461e505a1016f0a0ad4d96c67f87306..7ae86c1133e33980d7aaf413aa92d7bec032a94e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NGetText.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NGetText.java
@@ -22,22 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte0;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RString;
-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.builtins.RBuiltin;
 
 @SuppressWarnings("unused")
-@RBuiltin(name = "ngettext", kind = INTERNAL, parameterNames = {"n", "msg1", "msg2", "domain"})
+@RBuiltin(name = "ngettext", kind = INTERNAL, parameterNames = {"n", "msg1", "msg2", "domain"}, behavior = COMPLEX)
 public abstract class NGetText extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NZChar.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NZChar.java
index 69a5311f651d88637f52e3a560c49693531a713f..2400bc8fe76c7070f0f6fd940073e6772e22f7af 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NZChar.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NZChar.java
@@ -22,38 +22,32 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.nodes.unary.ConversionFailedException;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "nzchar", kind = PRIMITIVE, parameterNames = {"x"})
+@RBuiltin(name = "nzchar", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
 public abstract class NZChar extends RBuiltinNode {
     @Child private CastStringNode convertString;
 
     private String coerceContent(Object content) {
         if (convertString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            convertString = insert(CastStringNodeGen.create(false, true, false, false));
-        }
-        try {
-            return (String) convertString.execute(content);
-        } catch (ConversionFailedException e) {
-            throw RError.error(this, RError.Message.TYPE_EXPECTED, RType.Character.getName());
+            convertString = insert(CastStringNodeGen.create(false, false, false));
         }
+        return (String) convertString.execute(content);
     }
 
     private static byte isNonZeroLength(String s) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Names.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Names.java
index d36648306e7275efa571fd320231e89434b08083..91e6f763bcd36279b045cded3fa328d0a1cdf242 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Names.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Names.java
@@ -22,21 +22,22 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
 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;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 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.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "names", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC)
+@RBuiltin(name = "names", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class Names extends RBuiltinNode {
 
     private final ConditionProfile hasNames = ConditionProfile.createBinaryProfile();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java
index 53ee4515e29b14ac08934d438ab84d57114f1735..9c70a9bdcc45b919cfb1bdf2a8e00c786eeaba86 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NamespaceFunctions.java
@@ -22,13 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.MODIFIES_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RNull;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -36,7 +40,7 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 
 public class NamespaceFunctions {
 
-    @RBuiltin(name = "getRegisteredNamespace", kind = INTERNAL, parameterNames = {"name"})
+    @RBuiltin(name = "getRegisteredNamespace", kind = INTERNAL, parameterNames = {"name"}, behavior = READS_STATE)
     public abstract static class GetRegisteredNamespace extends RBuiltinNode {
         @Specialization
         protected Object doGetRegisteredNamespace(RAbstractStringVector name) {
@@ -59,7 +63,7 @@ public class NamespaceFunctions {
         }
     }
 
-    @RBuiltin(name = "isRegisteredNamespace", kind = INTERNAL, parameterNames = {"name"})
+    @RBuiltin(name = "isRegisteredNamespace", kind = INTERNAL, parameterNames = {"name"}, behavior = READS_STATE)
     public abstract static class IsRegisteredNamespace extends RBuiltinNode {
         @Specialization
         protected byte doIsRegisteredNamespace(RAbstractStringVector name) {
@@ -82,7 +86,7 @@ public class NamespaceFunctions {
         }
     }
 
-    @RBuiltin(name = "isNamespaceEnv", kind = INTERNAL, parameterNames = {"env"})
+    @RBuiltin(name = "isNamespaceEnv", kind = INTERNAL, parameterNames = {"env"}, behavior = PURE)
     public abstract static class IsNamespaceEnv extends RBuiltinNode {
         @Specialization
         protected byte doIsNamespaceEnv(REnvironment env) {
@@ -95,7 +99,7 @@ public class NamespaceFunctions {
         }
     }
 
-    @RBuiltin(name = "getNamespaceRegistry", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "getNamespaceRegistry", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class GetNamespaceRegistry extends RBuiltinNode {
         @Specialization
         protected REnvironment doGetNamespaceRegistry() {
@@ -103,8 +107,13 @@ public class NamespaceFunctions {
         }
     }
 
-    @RBuiltin(name = "registerNamespace", kind = INTERNAL, parameterNames = {"name", "env"})
+    @RBuiltin(name = "registerNamespace", kind = INTERNAL, parameterNames = {"name", "env"}, behavior = MODIFIES_STATE)
     public abstract static class RegisterNamespace extends RBuiltinNode {
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("name").asStringVector().findFirst();
+        }
+
         @Specialization
         protected RNull registerNamespace(String name, REnvironment env) {
             if (REnvironment.registerNamespace(name, env) == null) {
@@ -114,7 +123,7 @@ public class NamespaceFunctions {
         }
     }
 
-    @RBuiltin(name = "unregisterNamespace", kind = INTERNAL, parameterNames = {"name"})
+    @RBuiltin(name = "unregisterNamespace", kind = INTERNAL, parameterNames = {"name"}, behavior = MODIFIES_STATE)
     public abstract static class UnregisterNamespace extends RBuiltinNode {
         @Specialization
         protected RNull unregisterNamespace(RAbstractStringVector name) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NormalizePath.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NormalizePath.java
index f179481dc1b7a427ebea5a7bcee8a30b2c5f8761..2d0e01271104e3abec2bbb8e8cddf2b318ab955a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NormalizePath.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NormalizePath.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.IOException;
 import java.nio.file.FileSystem;
@@ -33,16 +34,16 @@ 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.runtime.RBuiltin;
 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.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "normalizePath", kind = INTERNAL, parameterNames = {"path", "winslash", "mustwork"})
+@RBuiltin(name = "normalizePath", kind = INTERNAL, parameterNames = {"path", "winslash", "mustwork"}, behavior = IO)
 public abstract class NormalizePath extends RBuiltinNode {
 
     private final ConditionProfile doesNotNeedToWork = ConditionProfile.createBinaryProfile();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NumericalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NumericalFunctions.java
index dd0529fbdc6f07e2966d236d0b58c3351a8c62b9..9e3a9909e216cdc0abf4516cae1233641d6be7bb 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NumericalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NumericalFunctions.java
@@ -22,17 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RDispatch.COMPLEX_GROUP_GENERIC;
+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;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 
 public class NumericalFunctions {
@@ -52,7 +53,7 @@ public class NumericalFunctions {
 
     }
 
-    @RBuiltin(name = "abs", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "abs", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Abs extends UnaryArithmeticBuiltinNode {
 
         public Abs() {
@@ -90,7 +91,7 @@ public class NumericalFunctions {
         }
     }
 
-    @RBuiltin(name = "Re", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = RDispatch.COMPLEX_GROUP_GENERIC)
+    @RBuiltin(name = "Re", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = COMPLEX_GROUP_GENERIC, behavior = PURE)
     public abstract static class Re extends UnaryArithmeticBuiltinNode {
 
         public Re() {
@@ -128,7 +129,7 @@ public class NumericalFunctions {
         }
     }
 
-    @RBuiltin(name = "Im", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = RDispatch.COMPLEX_GROUP_GENERIC)
+    @RBuiltin(name = "Im", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = COMPLEX_GROUP_GENERIC, behavior = PURE)
     public abstract static class Im extends UnaryArithmeticBuiltinNode {
 
         public Im() {
@@ -166,7 +167,7 @@ public class NumericalFunctions {
         }
     }
 
-    @RBuiltin(name = "Conj", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = RDispatch.COMPLEX_GROUP_GENERIC)
+    @RBuiltin(name = "Conj", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = COMPLEX_GROUP_GENERIC, behavior = PURE)
     public abstract static class Conj extends UnaryArithmeticBuiltinNode {
 
         public Conj() {
@@ -194,7 +195,7 @@ public class NumericalFunctions {
         }
     }
 
-    @RBuiltin(name = "Mod", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = RDispatch.COMPLEX_GROUP_GENERIC)
+    @RBuiltin(name = "Mod", kind = PRIMITIVE, parameterNames = {"z"}, dispatch = COMPLEX_GROUP_GENERIC, behavior = PURE)
     public abstract static class Mod extends UnaryArithmeticBuiltinNode {
 
         public Mod() {
@@ -232,7 +233,7 @@ public class NumericalFunctions {
         }
     }
 
-    @RBuiltin(name = "sign", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "sign", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Sign extends UnaryArithmeticBuiltinNode {
 
         public Sign() {
@@ -256,7 +257,7 @@ public class NumericalFunctions {
 
     }
 
-    @RBuiltin(name = "sqrt", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "sqrt", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Sqrt extends UnaryArithmeticBuiltinNode {
 
         public Sqrt() {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java
index 8ab7be5aae0c023c8ca738835368c1ff28b07898..0761d7875b918bc0585456735f27084b2550a78f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OnExit.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import java.util.ArrayList;
 
@@ -36,11 +38,10 @@ import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.FrameSlotNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
@@ -52,7 +53,7 @@ import com.oracle.truffle.r.runtime.ops.na.NAProfile;
  * evaluated, but {@code add} is. TODO arrange for the {@code expr} be stored with the currently
  * evaluating function using a new slot in {@link RArguments} and run it on function exit.
  */
-@RBuiltin(name = "on.exit", visibility = RVisibility.OFF, kind = PRIMITIVE, parameterNames = {"expr", "add"}, nonEvalArgs = 0)
+@RBuiltin(name = "on.exit", visibility = OFF, kind = PRIMITIVE, parameterNames = {"expr", "add"}, nonEvalArgs = 0, behavior = COMPLEX)
 public abstract class OnExit extends RBuiltinNode {
 
     @Child private FrameSlotNode onExitSlot = FrameSlotNode.create(RFrameSlot.OnExit, true);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java
index 7349f82ac280dd0937c5153c8c4de784dc767999..8749d957ebb229eca5851746c446f455812501af 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java
@@ -22,7 +22,10 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.MODIFIES_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Map;
 import java.util.Set;
@@ -33,13 +36,12 @@ 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.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.ROptions;
 import com.oracle.truffle.r.runtime.ROptions.OptionsException;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
@@ -55,7 +57,7 @@ public class OptionsFunctions {
      * This could be refactored using a recursive child node to handle simple cases, but it's
      * unlikely to be the fast path.
      */
-    @RBuiltin(name = "options", visibility = RVisibility.CUSTOM, kind = INTERNAL, parameterNames = {"..."})
+    @RBuiltin(name = "options", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"..."}, behavior = MODIFIES_STATE)
     public abstract static class Options extends RBuiltinNode {
 
         private final ConditionProfile argNameNull = ConditionProfile.createBinaryProfile();
@@ -164,7 +166,7 @@ public class OptionsFunctions {
         }
     }
 
-    @RBuiltin(name = "getOption", kind = INTERNAL, parameterNames = "x")
+    @RBuiltin(name = "getOption", kind = INTERNAL, parameterNames = "x", behavior = READS_STATE)
     public abstract static class GetOption extends RBuiltinNode {
 
         @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Order.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Order.java
index 3b1e353c9fea747cc19761a5cab22fc36fb00523..0e62a878031792191dbddc2c80506631455cd2c1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Order.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Order.java
@@ -12,7 +12,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.text.Collator;
 
@@ -29,9 +30,9 @@ import com.oracle.truffle.r.nodes.builtin.base.OrderNodeGen.CmpNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.OrderNodeGen.OrderVector1NodeGen;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -47,7 +48,7 @@ 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;
 
-@RBuiltin(name = "order", kind = INTERNAL, parameterNames = {"na.last", "decreasing", "..."})
+@RBuiltin(name = "order", kind = INTERNAL, parameterNames = {"na.last", "decreasing", "..."}, behavior = PURE)
 public abstract class Order extends RPrecedenceBuiltinNode {
 
     public abstract RIntVector executeRIntVector(byte naLast, byte dec, RArgsValuesAndNames args);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMatch.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMatch.java
index 571d928cf78b2b17470222e91d16b4db8f2d3d74..3bc987dd1af7768ab0d8bcaa9915e4efc7d7d21c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMatch.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMatch.java
@@ -22,19 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.model.RAbstractStringVector;
 
-@RBuiltin(name = "pmatch", kind = INTERNAL, parameterNames = {"x", "table", "nomatch", "duplicates.ok"})
+@RBuiltin(name = "pmatch", kind = INTERNAL, parameterNames = {"x", "table", "nomatch", "duplicates.ok"}, behavior = PURE)
 public abstract class PMatch extends RBuiltinNode {
 
     private final ConditionProfile nomatchNA = ConditionProfile.createBinaryProfile();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java
index 5aa8acddb102b8e5a26b6a6519a6bf86af83e8b5..78b39957ba995d6bbf24687ff2a1f96a8f0cb11f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PMinMax.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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;
@@ -42,9 +43,9 @@ 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.nodes.unary.UnaryArithmeticReduceNode.ReduceSemantics;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -117,7 +118,7 @@ public abstract class PMinMax extends RBuiltinNode {
     private CastNode getStringCastNode() {
         if (castString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castString = insert(CastStringNodeGen.create(true, true, true, false));
+            castString = insert(CastStringNodeGen.create(true, true, true));
         }
         return castString;
     }
@@ -332,7 +333,7 @@ public abstract class PMinMax extends RBuiltinNode {
         throw RError.error(this, RError.Message.INVALID_INPUT_TYPE);
     }
 
-    @RBuiltin(name = "pmax", kind = INTERNAL, parameterNames = {"na.rm", "..."})
+    @RBuiltin(name = "pmax", kind = INTERNAL, parameterNames = {"na.rm", "..."}, behavior = PURE)
     public abstract static class PMax extends PMinMax {
 
         public PMax() {
@@ -341,7 +342,7 @@ public abstract class PMinMax extends RBuiltinNode {
         }
     }
 
-    @RBuiltin(name = "pmin", kind = INTERNAL, parameterNames = {"na.rm", "..."})
+    @RBuiltin(name = "pmin", kind = INTERNAL, parameterNames = {"na.rm", "..."}, behavior = PURE)
     public abstract static class PMin extends PMinMax {
 
         public PMin() {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
index 9f714a25fdf4bafadcfd5a48db5768608929a559..10550c3ed5be05f2679fb47d7740b92f74fa5c80 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.File;
 import java.io.IOException;
@@ -39,13 +40,13 @@ import com.oracle.truffle.r.nodes.unary.CastStringNode;
 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.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 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.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
@@ -90,7 +91,7 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode;
  * <p>
  * On the R side, GnuR adds similar R attributes to the result, which is important for R tooling.
  */
-@RBuiltin(name = "parse", kind = INTERNAL, parameterNames = {"conn", "n", "text", "prompt", "srcfile", "encoding"})
+@RBuiltin(name = "parse", kind = INTERNAL, parameterNames = {"conn", "n", "text", "prompt", "srcfile", "encoding"}, behavior = IO)
 public abstract class Parse extends RBuiltinNode {
     @Child private CastIntegerNode castIntNode;
     @Child private CastStringNode castStringNode;
@@ -112,7 +113,7 @@ public abstract class Parse extends RBuiltinNode {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             castVectorNode = insert(CastToVectorNodeGen.create(false));
-            castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
+            castStringNode = insert(CastStringNodeGen.create(false, false, false));
         }
         return (RStringVector) castStringNode.executeString(castVectorNode.execute(s));
     }
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 0059367e2f98579ff1c4da546d24871da6b4f441..fef9305da7c55355d10060f099bf09024f8e2a32 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
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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;
@@ -34,17 +35,15 @@ import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 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.RSequence;
 import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "paste", kind = INTERNAL, parameterNames = {"", "sep", "collapse"})
+@RBuiltin(name = "paste", kind = INTERNAL, parameterNames = {"", "sep", "collapse"}, behavior = PURE)
 public abstract class Paste extends RBuiltinNode {
 
     private static final String[] ONE_EMPTY_STRING = new String[]{""};
@@ -58,24 +57,10 @@ public abstract class Paste extends RBuiltinNode {
     @Child private CastStringNode castCharacterNode;
 
     private final ValueProfile lengthProfile = PrimitiveValueProfile.createEqualityProfile();
-    private final ConditionProfile vectorOrSequence = ConditionProfile.createBinaryProfile();
     private final ConditionProfile reusedResultProfile = ConditionProfile.createBinaryProfile();
     private final BranchProfile nonNullElementsProfile = BranchProfile.create();
     private final BranchProfile onlyNullElementsProfile = BranchProfile.create();
 
-    private RStringVector castCharacter(Object o) {
-        if (asCharacterNode == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            asCharacterNode = insert(AsCharacterNodeGen.create(null));
-        }
-        Object ret = asCharacterNode.execute(o);
-        if (ret instanceof String) {
-            return RDataFactory.createStringVector((String) ret);
-        } else {
-            return (RStringVector) ret;
-        }
-    }
-
     /**
      * FIXME The exact semantics needs checking regarding the use of {@code as.character}. Currently
      * there are problem using it here, so we retain the previous implementation that just uses
@@ -84,13 +69,15 @@ public abstract class Paste extends RBuiltinNode {
     private RStringVector castCharacterVector(Object o) {
         if (castCharacterNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castCharacterNode = insert(CastStringNodeGen.create(false, true, false, false));
+            castCharacterNode = insert(CastStringNodeGen.create(false, false, false));
         }
         Object ret = castCharacterNode.executeString(o);
         if (ret instanceof String) {
             return RDataFactory.createStringVector((String) ret);
+        } else if (ret == RNull.instance) {
+            return RDataFactory.createEmptyStringVector();
         } else {
-            return (RStringVector) ret;
+            return (RStringVector) ((RStringVector) ret).copyDropAttributes();
         }
     }
 
@@ -141,12 +128,7 @@ public abstract class Paste extends RBuiltinNode {
         int maxLength = 1;
         for (int i = 0; i < length; i++) {
             Object element = values.getDataAt(i);
-            String[] array;
-            if (vectorOrSequence.profile(element instanceof RVector || element instanceof RSequence)) {
-                array = castCharacterVector(element).getDataWithoutCopying();
-            } else {
-                array = castCharacter(element).getDataWithoutCopying();
-            }
+            String[] array = castCharacterVector(element).getDataWithoutCopying();
             maxLength = Math.max(maxLength, array.length);
             converted[i] = array.length == 0 ? ONE_EMPTY_STRING : array;
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java
index a2a0c1200306ccd3550fe8030a4b66123948acf6..e3a56703d4d7d0d651c958482eaf9520d6a762e0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Paste0.java
@@ -22,19 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RList;
 
 /**
  * A straightforward implementation in terms of {@code paste} that doesn't attempt to be more
  * efficient.
  */
-@RBuiltin(name = "paste0", kind = INTERNAL, parameterNames = {"list", "collapse"})
+@RBuiltin(name = "paste0", kind = INTERNAL, parameterNames = {"list", "collapse"}, behavior = PURE)
 public abstract class Paste0 extends RBuiltinNode {
 
     @Child private Paste pasteNode;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PathExpand.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PathExpand.java
index 9c326079e980804cc5957a12e4390458ec9d0132..d8ce346727888f085b67a939ac392c5af4aef715 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PathExpand.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PathExpand.java
@@ -22,18 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "path.expand", kind = INTERNAL, parameterNames = "path")
+@RBuiltin(name = "path.expand", kind = INTERNAL, parameterNames = "path", behavior = IO)
 public abstract class PathExpand extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
deleted file mode 100644
index fd9176181cbeff8b626880e1ab6c876a2b9c62ca..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrettyPrinterNode.java
+++ /dev/null
@@ -1,1869 +0,0 @@
-/*
- * Copyright (c) 2013, 2016, 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.builtin.base;
-
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.dsl.NodeChild;
-import com.oracle.truffle.api.dsl.NodeChildren;
-import com.oracle.truffle.api.dsl.NodeField;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.nodes.IndirectCallNode;
-import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.r.nodes.RRootNode;
-import com.oracle.truffle.r.nodes.access.ConstantNode;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrettyPrinterSingleListElementNodeGen;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrettyPrinterSingleVectorElementNodeGen;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrintDimNodeGen;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrintVector2DimNodeGen;
-import com.oracle.truffle.r.nodes.builtin.base.PrettyPrinterNodeGen.PrintVectorMultiDimNodeGen;
-import com.oracle.truffle.r.nodes.function.FormalArguments;
-import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
-import com.oracle.truffle.r.nodes.helpers.RFactorNodes;
-import com.oracle.truffle.r.nodes.unary.CastStringNode;
-import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RArguments;
-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.conn.SocketConnections;
-import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RAttributes;
-import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
-import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDouble;
-import com.oracle.truffle.r.runtime.data.RExpression;
-import com.oracle.truffle.r.runtime.data.RExternalPtr;
-import com.oracle.truffle.r.runtime.data.RFunction;
-import com.oracle.truffle.r.runtime.data.RIntVector;
-import com.oracle.truffle.r.runtime.data.RLanguage;
-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.RPairList;
-import com.oracle.truffle.r.runtime.data.RPromise;
-import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RString;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RSymbol;
-import com.oracle.truffle.r.runtime.data.RVector;
-import com.oracle.truffle.r.runtime.data.closures.RClosures;
-import com.oracle.truffle.r.runtime.data.closures.RFactorToStringVectorClosure;
-import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-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.RAbstractRawVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
-import com.oracle.truffle.r.runtime.env.REnvironment;
-import com.oracle.truffle.r.runtime.nodes.RNode;
-import com.oracle.truffle.r.runtime.ops.na.NACheck;
-
-@SuppressWarnings("unused")
-@NodeChildren({@NodeChild(value = "operand", type = RNode.class), @NodeChild(value = "listElementName", type = RNode.class), @NodeChild(value = "quote", type = RNode.class),
-                @NodeChild(value = "right", type = RNode.class)})
-@NodeField(name = "printingAttributes", type = boolean.class)
-public abstract class PrettyPrinterNode extends RNode {
-
-    @Override
-    public abstract Object execute(VirtualFrame frame);
-
-    public abstract Object executeString(int o, Object listElementName, byte quote, byte right);
-
-    public abstract Object executeString(double o, Object listElementName, byte quote, byte right);
-
-    public abstract Object executeString(byte o, Object listElementName, byte quote, byte right);
-
-    public abstract Object executeString(Object o, Object listElementName, byte quote, byte right);
-
-    @Child private PrettyPrinterNode attributePrettyPrinter;
-    @Child private PrettyPrinterNode recursivePrettyPrinter;
-    @Child private PrettyPrinterSingleListElementNode singleListElementPrettyPrinter;
-    @Child private PrettyPrinterSingleVectorElementNode singleVectorElementPrettyPrinter;
-    @Child private PrintVectorMultiDimNode multiDimPrinter;
-
-    @Child private NumericalFunctions.Re re = NumericalFunctionsFactory.ReNodeGen.create(null);
-    @Child private NumericalFunctions.Im im = NumericalFunctionsFactory.ImNodeGen.create(null);
-
-    @Child private IndirectCallNode indirectCall = Truffle.getRuntime().createIndirectCallNode();
-
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
-    protected abstract boolean isPrintingAttributes();
-
-    private static final FrameAccess FRAME_ACCESS = FrameAccess.NONE;
-
-    private String prettyPrintAttribute(Object o) {
-        if (attributePrettyPrinter == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            attributePrettyPrinter = insert(PrettyPrinterNodeGen.create(null, null, null, null, true));
-        }
-        return (String) attributePrettyPrinter.executeString(o, null, RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE);
-    }
-
-    private String prettyPrintRecursive(Object o, Object listElementName, byte quote, byte right) {
-        if (recursivePrettyPrinter == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            recursivePrettyPrinter = insert(PrettyPrinterNodeGen.create(null, null, null, null, isPrintingAttributes()));
-        }
-        return (String) recursivePrettyPrinter.executeString(o, listElementName, quote, right);
-    }
-
-    private String prettyPrintSingleListElement(Object o, Object listElementName, byte quote, byte right) {
-        if (singleListElementPrettyPrinter == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            singleListElementPrettyPrinter = insert(PrettyPrinterSingleListElementNodeGen.create(null, null, null, null));
-        }
-        return (String) singleListElementPrettyPrinter.executeString(o, listElementName, quote, right);
-    }
-
-    private String prettyPrintSingleVectorElement(Object o, byte isQuoted) {
-        if (singleVectorElementPrettyPrinter == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            singleVectorElementPrettyPrinter = insert(PrettyPrinterSingleVectorElementNodeGen.create(null, null));
-        }
-        return (String) singleVectorElementPrettyPrinter.executeString(o, isQuoted);
-    }
-
-    private String printVectorMultiDim(RAbstractVector vector, boolean isListOrStringVector, boolean isComplexOrRawVector, byte quote) {
-        if (multiDimPrinter == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            multiDimPrinter = insert(PrintVectorMultiDimNodeGen.create(null, null, null, null));
-        }
-        StringBuilder sb = new StringBuilder();
-        sb.append((String) multiDimPrinter.executeString(vector, RRuntime.asLogical(isListOrStringVector), RRuntime.asLogical(isComplexOrRawVector), quote));
-        RAttributes attributes = vector.getAttributes();
-        if (attributes != null) {
-            sb.append(printAttributes(vector, attributes));
-        }
-        return builderToString(sb);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrint(RNull operand, Object listElementName, byte quote, byte right) {
-        return "NULL";
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintVector(byte operand, Object listElementName, byte quote, byte right) {
-        return concat("[1] ", prettyPrint(operand));
-    }
-
-    public static String prettyPrint(byte operand, int width) {
-        StringBuilder sb = new StringBuilder();
-        String valStr = RRuntime.logicalToString(operand);
-        return spaces(sb, width - valStr.length()).append(valStr).toString();
-    }
-
-    public static String prettyPrint(byte operand) {
-        return RRuntime.logicalToString(operand);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintVector(int operand, Object listElementName, byte quote, byte right) {
-        return concat("[1] ", prettyPrint(operand));
-    }
-
-    public static String prettyPrint(int operand) {
-        return RRuntime.intToString(operand);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintVector(double operand, Object listElementName, byte quote, byte right) {
-        return concat("[1] ", prettyPrint(operand));
-    }
-
-    public static String prettyPrint(double operand) {
-        return doubleToStringPrintFormat(operand, calcRoundFactor(operand, 10000000));
-    }
-
-    public static String prettyPrint(double operand, double roundFactor, int digitsBehindDot) {
-        return doubleToStringPrintFormat(operand, roundFactor, digitsBehindDot);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintVector(RComplex operand, Object listElementName, byte quote, byte right) {
-        return concat("[1] ", prettyPrint(operand));
-    }
-
-    public static String prettyPrint(RComplex operand) {
-        double rfactor = calcRoundFactor(operand.getRealPart(), 10000000);
-        double ifactor = calcRoundFactor(operand.getImaginaryPart(), 10000000);
-        return operand.toString(doubleToStringPrintFormat(operand.getRealPart(), rfactor), doubleToStringPrintFormat(operand.getImaginaryPart(), ifactor));
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintVector(String operand, Object listElementName, byte quote, byte right) {
-        if (RRuntime.fromLogical(quote)) {
-            return concat("[1] ", prettyPrint(operand));
-        }
-        return concat("[1] ", operand);
-    }
-
-    public static String prettyPrint(String operand) {
-        return RRuntime.quoteString(operand, false);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintVector(RRaw operand, Object listElementName, byte quote, byte right) {
-        return concat("[1] ", prettyPrint(operand));
-    }
-
-    public static String prettyPrint(RRaw operand) {
-        return operand.toString();
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrint(RFunction operand, Object listElementName, byte quote, byte right) {
-        return prettyPrintFunction(operand, listElementName, quote, right, true);
-    }
-
-    public String prettyPrintFunction(RFunction operand, Object listElementName, byte quote, byte right, boolean useSource) {
-        String string;
-        if (operand.isBuiltin()) {
-            RBuiltinDescriptor rBuiltin = operand.getRBuiltin();
-            RRootNode node = (RRootNode) operand.getTarget().getRootNode();
-            FormalArguments formals = node.getFormalArguments();
-            StringBuffer sb = new StringBuffer();
-            sb.append("function (");
-            ArgumentsSignature signature = formals.getSignature();
-            for (int i = 0; i < signature.getLength(); i++) {
-                RNode defaultArg = formals.getDefaultArgument(i);
-                sb.append(signature.getName(i));
-                if (defaultArg != null) {
-                    sb.append(" = ");
-                    Object value = ((ConstantNode) defaultArg).getValue();
-                    String printValue = prettyPrintRecursive(value, listElementName, quote, right);
-                    // remove the "[1] "
-                    sb.append(printValue.substring(4));
-                }
-                if (i != signature.getLength() - 1) {
-                    sb.append(", ");
-                }
-            }
-            sb.append(")  .Primitive(\"");
-            sb.append(rBuiltin.getName());
-            sb.append("\")");
-            string = sb.toString();
-        } else {
-            String source = ((RRootNode) operand.getTarget().getRootNode()).getSourceCode();
-            if (source == null || !useSource) {
-                source = RDeparse.deparse(operand);
-            }
-            REnvironment env = RArguments.getEnvironment(operand.getEnclosingFrame());
-            if (env != null && env.isNamespaceEnv()) {
-                source += "\n" + env.getPrintName();
-            }
-            string = source;
-        }
-        return printValueAndAttributes(string, operand, false);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrint(REnvironment operand, Object listElementName, byte quote, byte right) {
-        return printValueAndAttributes(operand.toString(), operand, false);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrint(RExpression expr, Object listElementName, byte quote, byte right) {
-        StringBuilder builder = new StringBuilder();
-        builder.append("expression(");
-        RList exprs = expr.getList();
-        RStringVector names = (RStringVector) expr.getAttr(attrProfiles, RRuntime.NAMES_ATTR_KEY);
-        for (int i = 0; i < exprs.getLength(); i++) {
-            if (i != 0) {
-                builder.append(", ");
-            }
-            if (names != null && names.getDataAt(i) != null) {
-                builder.append(names.getDataAt(i));
-                builder.append(" = ");
-            }
-            builder.append(prettyPrintSingleVectorElement(exprs.getDataAt(i), quote));
-        }
-        builder.append(')');
-        return builderToString(builder);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintSymbol(RSymbol operand, Object listElementName, byte quote, byte right) {
-        return printValueAndAttributes(operand.getName(), operand, false);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintExternalPtr(RExternalPtr operand, Object listElementName, byte quote, byte right) {
-        return printValueAndAttributes(String.format("<pointer: %#x>", operand.getAddr()), operand, false);
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintPromise(RPromise promise, Object listElementName, byte quote, byte right) {
-        if (promise.isEvaluated()) {
-            return prettyPrintRecursive(promise.getValue(), listElementName, quote, right);
-        } else {
-            return prettyPrintPromise(promise);
-        }
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintLanguage(RLanguage language, Object listElementName, byte quote, byte right) {
-        return printValueAndAttributes(prettyPrintLanguageInternal(language), language, true);
-    }
-
-    private static String prettyPrintLanguageInternal(RLanguage language) {
-        return RDeparse.deparse(language, 60, false, 0, -1);
-    }
-
-    private static String prettyPrintPromise(RPromise promise) {
-        RNode node = (RNode) promise.getRep();
-        SourceSection ss = node.asRSyntaxNode().getSourceSection();
-        if (ss == null) {
-            return "<no source available>";
-        } else {
-            return ss.getCode();
-        }
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintPairList(RPairList pairList, Object listElementName, byte quote, byte right) {
-        StringBuilder sb = new StringBuilder();
-        int i = 1;
-        Object plObject = pairList;
-        RPairList pl = pairList;
-        while (!RPairList.isNull(plObject)) {
-            if (!pl.isNullTag()) {
-                sb.append('$');
-                sb.append(pl.getTag());
-            } else {
-                sb.append("[[");
-                sb.append(Integer.toString(i));
-                sb.append("]]");
-            }
-            sb.append('\n');
-            if (!RPairList.isNull(pl.car())) {
-                sb.append(prettyPrintRecursive(pl.car(), listElementName, quote, right));
-                sb.append('\n');
-            }
-            plObject = pl.cdr();
-            if (!RPairList.isNull(plObject)) {
-                pl = (RPairList) plObject;
-                sb.append('\n');
-            }
-            i++;
-        }
-        return sb.toString();
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPrintMissing(RMissing missing, Object listElementName, byte quote, byte right) {
-        return "";
-    }
-
-    @TruffleBoundary
-    @Specialization
-    protected String prettyPringS4(RS4Object o, Object listElementName, byte quote, byte right) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("<S4 Type Object>");
-        for (RAttribute attr : o.getAttributes()) {
-            printAttribute(sb, attr);
-        }
-        return sb.toString();
-    }
-
-    private static String getStringFromObj(Object o, String msg) {
-        if (o instanceof String) {
-            return (String) o;
-        } else if (o instanceof RStringVector && ((RStringVector) o).getLength() == 1) {
-            return ((RStringVector) o).getDataAt(0);
-        } else {
-            throw RError.error(RError.SHOW_CALLER2, RError.Message.GENERIC, msg);
-        }
-    }
-
-    private String printAttributes(RAbstractVector vector, RAttributes attributes) {
-        StringBuilder builder = new StringBuilder();
-        for (RAttribute attr : attributes) {
-            if (attr.getName().equals(RRuntime.NAMES_ATTR_KEY) && !vector.hasDimensions()) {
-                // names attribute already printed
-                continue;
-            }
-            if (attr.getName().equals(RRuntime.DIM_ATTR_KEY) || attr.getName().equals(RRuntime.DIMNAMES_ATTR_KEY)) {
-                // dim and dimnames attributes never gets printed
-                continue;
-            }
-            printAttribute(builder, attr);
-        }
-        return builderToString(builder);
-    }
-
-    /**
-     * Encapsulates the printing of the value and attributes for {@link RAttributable} types.
-     *
-     * @param ignoreNames TODO
-     */
-    private String printValueAndAttributes(String value, RAttributable object, boolean ignoreNames) {
-        RAttributes attributes = object.getAttributes();
-        if (attributes == null) {
-            return value;
-        } else {
-            StringBuilder builder = new StringBuilder(value);
-            for (RAttribute attr : attributes) {
-                if (ignoreNames && attr.getName().equals(RRuntime.NAMES_ATTR_KEY)) {
-                    continue;
-                }
-                printAttribute(builder, attr);
-            }
-            return builderToString(builder);
-        }
-    }
-
-    private void printAttribute(StringBuilder builder, RAttribute attr) {
-        builder.append("\n");
-        builder.append(concat("attr(,\"", attr.getName(), "\")\n"));
-        builder.append(prettyPrintAttribute(attr.getValue()));
-    }
-
-    private static int getMaxPrintLength() {
-        int maxPrint = RRuntime.asInteger(RContext.getInstance().stateROptions.getValue("max.print"));
-        return RRuntime.isNA(maxPrint) ? -1 : maxPrint;
-    }
-
-    private String printVector(RAbstractVector vector, String[] values, boolean isStringVector, boolean isRawVector) {
-        assert vector.getLength() == values.length;
-        int maxPrint = getMaxPrintLength();
-        if (values.length == 0) {
-            String result = concat(RRuntime.classToString(vector.getElementClass()), "(0)");
-            if (vector.getNames(attrProfiles) != null) {
-                result = concat("named ", result);
-            }
-            return result;
-        } else {
-            boolean printNamesHeader = ((!vector.hasDimensions() || (vector.getDimensions().length == 1 && vector.getDimNames(attrProfiles) != null)) && vector.getNames(attrProfiles) != null);
-            RStringVector names = printNamesHeader ? vector.getNames(attrProfiles) : null;
-            int maxWidth = 0;
-            for (String s : values) {
-                maxWidth = Math.max(maxWidth, s.length());
-            }
-            if (printNamesHeader) {
-                for (int i = 0; i < names.getLength(); i++) {
-                    String s = names.getDataAt(i);
-                    if (RRuntime.isNA(s)) {
-                        s = RRuntime.NA_HEADER;
-                    }
-                    maxWidth = Math.max(maxWidth, s.length());
-                }
-            }
-            int columnWidth = maxWidth + 1; // There is a blank before each column.
-            int leftWidth = 0;
-            int maxPositionLength = 0;
-            if (!printNamesHeader) {
-                maxPositionLength = intString(vector.getLength()).length();
-                leftWidth = maxPositionLength + 2; // There is [] around the number.
-            }
-            int forColumns = (int) RContext.getInstance().stateROptions.getValue("width") - leftWidth;
-            int numberOfColumns = Math.max(1, forColumns / columnWidth);
-
-            int index = 0;
-            StringBuilder builder = new StringBuilder();
-            StringBuilder headerBuilder = null;
-            if (printNamesHeader) {
-                headerBuilder = new StringBuilder();
-            }
-            while (index < vector.getLength()) {
-                if (!printNamesHeader) {
-                    int position = index + 1;
-                    String positionString = intString(position);
-                    appendSpaces(builder, maxPositionLength - positionString.length());
-                    builder.append("[").append(positionString).append("]");
-                }
-                for (int j = 0; j < numberOfColumns && index < vector.getLength(); j++) {
-                    String valueString = values[index];
-                    if (!printNamesHeader) {
-                        builder.append(' ');
-                        // for some reason vectors of strings are printed differently
-                        if (isStringVector) {
-                            builder.append(valueString);
-                            appendSpaces(builder, (columnWidth - 1) - valueString.length());
-                        } else {
-                            appendSpaces(builder, (columnWidth - 1) - valueString.length());
-                            builder.append(valueString);
-                        }
-                    } else {
-                        int actualColumnWidth = columnWidth;
-                        if (j == 0) {
-                            actualColumnWidth--;
-                        }
-                        // for some reason vectors of raw values are printed differently
-                        if (!isRawVector) {
-                            appendSpaces(builder, actualColumnWidth - valueString.length());
-                        }
-                        builder.append(valueString);
-                        if (isRawVector) {
-                            builder.append(' ');
-                        }
-                        String headerString = names.getDataAt(index);
-                        if (RRuntime.isNA(headerString)) {
-                            headerString = RRuntime.NA_HEADER;
-                        }
-                        appendSpaces(headerBuilder, actualColumnWidth - headerString.length());
-                        headerBuilder.append(headerString);
-                    }
-                    index++;
-                    if (index == maxPrint) {
-                        break;
-                    }
-                }
-                builder.append('\n');
-                if (printNamesHeader) {
-                    headerBuilder.append('\n');
-                    headerBuilder.append(builderToString(builder));
-                    builder = new StringBuilder();
-                }
-                if (index == maxPrint) {
-                    break;
-                }
-            }
-            StringBuilder resultBuilder = printNamesHeader ? headerBuilder : builder;
-            resultBuilder.deleteCharAt(resultBuilder.length() - 1);
-            if (index == maxPrint) {
-                resultBuilder.append("\n [ reached getOption(\"max.print\") -- omitted ");
-                resultBuilder.append(vector.getLength() - maxPrint);
-                resultBuilder.append(" entries ]");
-            }
-            if (!(vector instanceof RFactorToStringVectorClosure)) {
-                // it's a bit of a hack, but factors are meant to be printed using the S3 function
-                // anyway - the idea is to suppress attribute printing for factors nested in lists
-                RAttributes attributes = vector.getAttributes();
-                if (attributes != null) {
-                    resultBuilder.append(printAttributes(vector, attributes));
-                }
-            }
-            return builderToString(resultBuilder);
-        }
-    }
-
-    private static void appendSpaces(StringBuilder builder, int spaces) {
-        for (int k = 0; k < spaces; k++) {
-            builder.append(' ');
-        }
-    }
-
-    protected static String padColHeader(int r, int dataColWidth, RAbstractVector vector, boolean isListOrStringVector, RAttributeProfiles attrProfiles) {
-        RList dimNames = vector.getDimNames(attrProfiles);
-        StringBuilder sb = new StringBuilder();
-        int wdiff;
-        if (dimNames == null || dimNames.getDataAt(1) == RNull.instance) {
-            String rs = intString(r);
-            wdiff = dataColWidth - (rs.length() + 3); // 3: [,]
-            if (!isListOrStringVector && wdiff > 0) {
-                spaces(sb, wdiff);
-            }
-            sb.append("[,").append(rs).append(']');
-        } else {
-            String dimId;
-            if (dimNames.getDataAt(1) instanceof String) {
-                assert r == 1;
-                dimId = (String) dimNames.getDataAt(1);
-            } else {
-                RStringVector dimNamesVector = (RStringVector) dimNames.getDataAt(1);
-                dimId = dimNamesVector.getDataAt(r - 1);
-            }
-            if (RRuntime.isNA(dimId)) {
-                dimId = RRuntime.NA_HEADER;
-            }
-            wdiff = dataColWidth - dimId.length();
-            if (!isListOrStringVector && wdiff > 0) {
-                spaces(sb, wdiff);
-            }
-            sb.append(dimId);
-        }
-        if (isListOrStringVector && wdiff > 0) {
-            spaces(sb, wdiff);
-        }
-        return builderToString(sb);
-    }
-
-    protected static boolean rowHeaderUsesIndices(RList dimNames) {
-        return dimNames == null || dimNames.getDataAt(0) == RNull.instance;
-    }
-
-    protected static String rowHeader(int c, RAbstractVector vector, RAttributeProfiles attrProfiles) {
-        RList dimNames = vector.getDimNames(attrProfiles);
-        if (rowHeaderUsesIndices(dimNames)) {
-            return concat("[", intString(c), ",]");
-        } else {
-            RAbstractStringVector dimNamesVector = (RAbstractStringVector) getDimNamesAt(dimNames, 1);
-            String dimId = dimNamesVector.getDataAt(c - 1);
-            if (RRuntime.isNA(dimId)) {
-                dimId = RRuntime.NA_HEADER;
-            }
-            return dimId;
-        }
-    }
-
-    public static StringBuilder spaces(StringBuilder sb, int s) {
-        if (s > 0) {
-            appendSpaces(sb, s);
-        }
-        return sb;
-    }
-
-    private static String getDimId(RAbstractVector vector, int dimLevel, int dimInd, RAttributeProfiles attrProfiles) {
-        String dimId;
-        RList dimNames = vector.getDimNames(attrProfiles);
-        if (dimNames == null || getDimNamesAt(dimNames, dimLevel) == RNull.instance) {
-            dimId = intString(dimInd + 1);
-        } else {
-            RAbstractStringVector dimNamesVector = (RAbstractStringVector) getDimNamesAt(dimNames, dimLevel);
-            dimId = dimNamesVector.getDataAt(dimInd);
-        }
-        return dimId;
-    }
-
-    private static Object getDimNamesAt(RList dimNames, int dimLevel) {
-        Object result = dimNames.getDataAt(dimLevel - 1);
-        if (result instanceof String) {
-            return RString.valueOf((String) result);
-        }
-        return result;
-    }
-
-    private static double calcRoundFactor(double input, long maxFactor) {
-        if (Double.isNaN(input) || Double.isInfinite(input) || input == 0.0) {
-            return maxFactor * 10;
-        }
-        double data = input;
-        double factor = 1;
-        if (Math.abs(data) > 1000000000000L) {
-            while (Math.abs(data) > 10000000L) {
-                data = data / 10;
-                factor /= 10;
-            }
-        } else if ((int) data != 0) {
-            while (Math.abs(data) < maxFactor / 10) {
-                data = data * 10;
-                factor *= 10;
-            }
-        } else {
-            long current = maxFactor / 10;
-            while (Math.abs(data) < 1 && current > 1) {
-                data = data * 10;
-                current = current * 10;
-            }
-            return current;
-        }
-        return factor;
-    }
-
-    private static String doubleToStringPrintFormat(double input, double roundFactor, int digitsBehindDot) {
-        double data = input;
-        if (digitsBehindDot == -1) {
-            // processing a single double value or a complex value; use rounding instead of
-            // digitsBehindDot (which is in this case invalid) to determine precision
-            if (!Double.isNaN(data) && !Double.isInfinite(data)) {
-                if (roundFactor < 1) {
-                    double inverse = 1 / roundFactor;
-                    data = Math.round(data / inverse) * inverse;
-                } else {
-                    data = Math.round(data * roundFactor) / roundFactor;
-                }
-            }
-        }
-        return RRuntime.doubleToString(data, digitsBehindDot);
-    }
-
-    private static String doubleToStringPrintFormat(double input, double roundFactor) {
-        return doubleToStringPrintFormat(input, roundFactor, -1);
-    }
-
-    private String prettyPrintList0(RList operand, Object listElementName, byte quote, byte right) {
-        int length = operand.getLength();
-        if (length == 0) {
-            String result = "list()";
-            if (operand.getNames(attrProfiles) != null) {
-                result = concat("named ", result);
-            }
-            return result;
-        } else {
-            StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < length; i++) {
-                if (isPrintingAttributes() && operand.elementNamePrefix != null) {
-                    sb.append(operand.elementNamePrefix);
-                }
-                Object name = operand.getNameAt(i);
-                if (listElementName != null) {
-                    name = concat(RRuntime.toString(listElementName), RRuntime.toString(name));
-                }
-                sb.append(name).append('\n');
-                Object value = operand.getDataAt(i);
-                sb.append(prettyPrintSingleListElement(value, name, quote, right)).append("\n\n");
-            }
-            sb.deleteCharAt(sb.length() - 1);
-            RAttributes attributes = operand.getAttributes();
-            if (attributes != null) {
-                sb.append(printAttributes(operand, attributes));
-            }
-            return builderToString(sb);
-        }
-    }
-
-    public static String prettyPrint(RList operand) {
-        return concat("List,", intString(operand.getLength()));
-    }
-
-    public static String prettyPrint(RAbstractVector operand) {
-        return concat(RRuntime.classToStringCap(operand.getElementClass()), ",", intString(operand.getLength()));
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "twoDimsOrMore(operand)")
-    protected String prettyPrintM(RList operand, Object listElementName, byte quote, byte right) {
-        return printVectorMultiDim(operand, true, false, quote);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "twoDimsOrMore(operand)")
-    protected String prettyPrintM(RAbstractStringVector operand, Object listElementName, byte quote, byte right) {
-        return printVectorMultiDim(operand, right == RRuntime.LOGICAL_FALSE, false, quote);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "twoDimsOrMore(operand)")
-    protected String prettyPrintM(RAbstractComplexVector operand, Object listElementName, byte quote, byte right) {
-        return printVectorMultiDim(operand, false, true, quote);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "twoDimsOrMore(operand)")
-    protected String prettyPrintM(RAbstractRawVector operand, Object listElementName, byte quote, byte right) {
-        return printVectorMultiDim(operand, false, true, quote);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "twoDimsOrMore(operand)")
-    protected String prettyPrintM(RAbstractDoubleVector operand, Object listElementName, byte quote, byte right) {
-        return printVectorMultiDim(operand, false, false, quote);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "twoDimsOrMore(operand)")
-    protected String prettyPrintM(RAbstractIntVector operand, Object listElementName, byte quote, byte right) {
-        return printVectorMultiDim(operand, false, false, quote);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "twoDimsOrMore(operand)")
-    protected String prettyPrintM(RAbstractLogicalVector operand, Object listElementName, byte quote, byte right) {
-        return printVectorMultiDim(operand, false, false, quote);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "!twoDimsOrMore(operand)")
-    protected String prettyPrint(RList operand, Object listElementName, byte quote, byte right) {
-        return prettyPrintList0(operand, listElementName, quote, right);
-    }
-
-    private static double getMaxRoundFactor(RAbstractDoubleVector operand) {
-        double maxRoundFactor = 0;
-        for (int i = 0; i < operand.getLength(); i++) {
-            double data = operand.getDataAt(i);
-            double roundFactor = calcRoundFactor(data, 10000000);
-            if (roundFactor > maxRoundFactor) {
-                maxRoundFactor = roundFactor;
-            }
-        }
-        return maxRoundFactor;
-    }
-
-    private static int getMaxDigitsBehindDot(double maxRoundFactor) {
-        int maxDigitsBehindDot = 0;
-        for (double j = 1; j < maxRoundFactor; j *= 10) {
-            maxDigitsBehindDot++;
-        }
-        return maxDigitsBehindDot;
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "!twoDimsOrMore(operand)")
-    protected String prettyPrint(RAbstractDoubleVector operand, Object listElementName, byte quote, byte right) {
-        int length = operand.getLength();
-        String[] values = new String[length];
-        double maxRoundFactor = getMaxRoundFactor(operand);
-        int maxDigitsBehindDot = getMaxDigitsBehindDot(maxRoundFactor);
-        for (int i = 0; i < length; i++) {
-            double data = operand.getDataAt(i);
-            values[i] = prettyPrint(data, maxRoundFactor, maxDigitsBehindDot);
-        }
-        padTrailingDecimalPointAndZeroesIfRequired(values);
-        return printVector(operand, values, false, false);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "!twoDimsOrMore(operand)")
-    protected String prettyPrint(RAbstractIntVector operand, Object listElementName, byte quote, byte right) {
-        int length = operand.getLength();
-        String[] values = new String[length];
-        for (int i = 0; i < length; i++) {
-            int data = operand.getDataAt(i);
-            values[i] = prettyPrint(data);
-        }
-        return printVector(operand, values, false, false);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "!twoDimsOrMore(operand)")
-    protected String prettyPrint(RAbstractStringVector operand, Object listElementName, byte quote, byte right) {
-        int length = operand.getLength();
-        String[] values = new String[length];
-        for (int i = 0; i < length; i++) {
-            String data = operand.getDataAt(i);
-            if (RRuntime.fromLogical(quote)) {
-                values[i] = prettyPrint(data);
-            } else {
-                if (RRuntime.isNA(data)) {
-                    values[i] = RRuntime.NA_HEADER;
-                } else {
-                    values[i] = data;
-                }
-            }
-        }
-        return printVector(operand, values, true, false);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "!twoDimsOrMore(operand)")
-    protected String prettyPrint(RAbstractLogicalVector operand, Object listElementName, byte quote, byte right) {
-        int length = operand.getLength();
-        String[] values = new String[length];
-        for (int i = 0; i < length; i++) {
-            byte data = operand.getDataAt(i);
-            values[i] = prettyPrint(data);
-        }
-        return printVector(operand, values, false, false);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "!twoDimsOrMore(operand)")
-    protected String prettyPrint(RAbstractRawVector operand, Object listElementName, byte quote, byte right) {
-        int length = operand.getLength();
-        String[] values = new String[length];
-        for (int i = 0; i < length; i++) {
-            RRaw data = operand.getDataAt(i);
-            values[i] = prettyPrint(data);
-        }
-        return printVector(operand, values, false, true);
-    }
-
-    @TruffleBoundary
-    @Specialization(guards = "!twoDimsOrMore(operand)")
-    protected String prettyPrint(RAbstractComplexVector operand, Object listElementName, byte quote, byte right) {
-
-        RAbstractDoubleVector realParts = (RAbstractDoubleVector) re.calculateUnboxed(operand);
-        RAbstractDoubleVector imaginaryParts = (RAbstractDoubleVector) im.calculateUnboxed(operand);
-
-        int length = operand.getLength();
-        String[] realValues = new String[length];
-        String[] imaginaryValues = new String[length];
-        for (int i = 0; i < length; i++) {
-            realValues[i] = prettyPrint(realParts.getDataAt(i));
-            imaginaryValues[i] = prettyPrint(imaginaryParts.getDataAt(i));
-        }
-        padTrailingDecimalPointAndZeroesIfRequired(realValues);
-        padTrailingDecimalPointAndZeroesIfRequired(imaginaryValues);
-        removeLeadingMinus(imaginaryValues);
-        rightJustify(imaginaryValues);
-
-        String[] values = new String[length];
-        for (int i = 0; i < length; i++) {
-            values[i] = operand.getDataAt(i).isNA() ? "NA" : concat(realValues[i], imaginaryParts.getDataAt(i) < 0.0 ? "-" : "+", imaginaryValues[i], "i");
-        }
-        return printVector(operand, values, false, false);
-    }
-
-    protected static boolean twoDimsOrMore(RAbstractContainer v) {
-        return v.hasDimensions() && v.getDimensions().length > 1;
-    }
-
-    protected static boolean isLengthOne(RAbstractIntVector v) {
-        return v.getLength() == 1;
-    }
-
-    private static String builderToString(StringBuilder sb) {
-        return sb.toString();
-    }
-
-    private static String builderToSubstring(StringBuilder sb, int start, int end) {
-        return sb.substring(start, end);
-    }
-
-    private static String intString(int x) {
-        return Integer.toString(x);
-    }
-
-    private static String stringFormat(String format, Object arg) {
-        return String.format(format, arg);
-    }
-
-    private static String concat(String... ss) {
-        StringBuilder sb = new StringBuilder();
-        for (String s : ss) {
-            sb.append(s);
-        }
-        return builderToString(sb);
-    }
-
-    private static String substring(String s, int start) {
-        return s.substring(start);
-    }
-
-    private static int requiresDecimalPointsAndTrailingZeroes(String[] values, int[] decimalPointOffsets, int[] lenAfterPoint) {
-        boolean foundWithDecimalPoint = false;
-        boolean foundWithoutDecimalPoint = false;
-        boolean inequalLenAfterPoint = false;
-        int maxLenAfterPoint = -1;
-        for (int i = 0; i < values.length; i++) {
-            String v = values[i];
-            decimalPointOffsets[i] = v.indexOf('.');
-            if (decimalPointOffsets[i] == -1) {
-                foundWithoutDecimalPoint = true;
-                lenAfterPoint[i] = 0;
-            } else {
-                foundWithDecimalPoint = true;
-                int lap = substring(v, decimalPointOffsets[i] + 1).length();
-                lenAfterPoint[i] = lap;
-                if (lap > maxLenAfterPoint) {
-                    if (maxLenAfterPoint == -1) {
-                        inequalLenAfterPoint = true;
-                    }
-                    maxLenAfterPoint = lap;
-                }
-            }
-        }
-        return (foundWithDecimalPoint && foundWithoutDecimalPoint) || inequalLenAfterPoint ? maxLenAfterPoint : -1;
-    }
-
-    public static void padTrailingDecimalPointAndZeroesIfRequired(String[] values) {
-        int[] decimalPointOffsets = new int[values.length];
-        int[] lenAfterPoint = new int[values.length];
-        int maxLenAfterPoint = requiresDecimalPointsAndTrailingZeroes(values, decimalPointOffsets, lenAfterPoint);
-        if (maxLenAfterPoint == -1) {
-            return;
-        }
-
-        for (int i = 0; i < values.length; i++) {
-            String v = values[i];
-            if (RRuntime.isNA(v) || "NaN".equals(v)) {
-                continue;
-            }
-            if (decimalPointOffsets[i] == -1) {
-                v = concat(v, ".");
-            }
-            if (lenAfterPoint[i] < maxLenAfterPoint) {
-                values[i] = concat(v, stringFormat(concat("%0", intString(maxLenAfterPoint - lenAfterPoint[i]), "d"), 0));
-            }
-        }
-    }
-
-    private static void rightJustify(String[] values) {
-        int maxLen = 0;
-        boolean inequalLengths = false;
-        int lastLen = 0;
-        for (int i = 0; i < values.length; i++) {
-            String v = values[i];
-            if (RRuntime.isNA(v)) {
-                // do not use NA for deciding alignment
-                continue;
-            }
-            int l = v.length();
-            maxLen = Math.max(maxLen, l);
-            inequalLengths = lastLen != 0 && lastLen != l;
-            lastLen = l;
-        }
-        if (!inequalLengths) {
-            return;
-        }
-        for (int i = 0; i < values.length; i++) {
-            String v = values[i];
-            int l = v.length();
-            if (l < maxLen) {
-                int d = maxLen - l;
-                if (d == 1) {
-                    values[i] = concat(" ", v);
-                } else {
-                    values[i] = concat(stringFormat(concat("%", intString(d), "s"), " "), v);
-                }
-            }
-        }
-    }
-
-    private static void removeLeadingMinus(String[] values) {
-        for (int i = 0; i < values.length; i++) {
-            String v = values[i];
-            if (v.charAt(0) == '-') {
-                values[i] = substring(v, 1);
-            }
-        }
-    }
-
-    public static String prettyPrintDefault(Object value) {
-        return (String) Truffle.getRuntime().createCallTarget(new RootNode(TruffleLanguage.class, null, null) {
-
-            @Child PrettyPrinterNode node = PrettyPrinterNodeGen.create(null, null, null, null, false);
-
-            @Override
-            public Object execute(VirtualFrame frame) {
-                return node.executeString(frame.getArguments()[0], null, RRuntime.LOGICAL_FALSE, RRuntime.LOGICAL_FALSE);
-            }
-        }).call(value);
-    }
-
-    @NodeChildren({@NodeChild(value = "operand", type = RNode.class), @NodeChild(value = "listElementName", type = RNode.class), @NodeChild(value = "quote", type = RNode.class),
-                    @NodeChild(value = "right", type = RNode.class)})
-    abstract static class PrettyPrinterSingleListElementNode extends RNode {
-
-        @Child private PrettyPrinterNode prettyPrinter;
-        @Child private CastStringNode castStringNode;
-
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
-        private final NACheck naCheck = NACheck.create();
-
-        private void initCast(Object listElementName) {
-            if (prettyPrinter == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                prettyPrinter = insert(PrettyPrinterNodeGen.create(null, null, null, null, false));
-            }
-        }
-
-        private void initCast() {
-            if (castStringNode == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
-            }
-        }
-
-        private String prettyPrintSingleElement(byte o, Object listElementName, byte quote, byte right) {
-            initCast(listElementName);
-            return (String) prettyPrinter.executeString(o, listElementName, quote, right);
-        }
-
-        private String prettyPrintSingleElement(int o, Object listElementName, byte quote, byte right) {
-            initCast(listElementName);
-            return (String) prettyPrinter.executeString(o, listElementName, quote, right);
-        }
-
-        private String prettyPrintSingleElement(double o, Object listElementName, byte quote, byte right) {
-            initCast(listElementName);
-            return (String) prettyPrinter.executeString(o, listElementName, quote, right);
-        }
-
-        private String prettyPrintSingleElement(Object o, Object listElementName, byte quote, byte right) {
-            initCast(listElementName);
-            return (String) prettyPrinter.executeString(o, listElementName, quote, right);
-        }
-
-        public abstract Object executeString(int o, Object listElementName, byte quote, byte right);
-
-        public abstract Object executeString(double o, Object listElementName, byte quote, byte right);
-
-        public abstract Object executeString(byte o, Object listElementName, byte quote, byte right);
-
-        public abstract Object executeString(Object o, Object listElementName, byte quote, byte right);
-
-        @Specialization
-        protected String prettyPrintListElement(SocketConnections.RSocketConnection operand, Object listElementName, byte quote, byte right) {
-            // TODO; fixing this properly would likely require overhaul of the whole
-            // formatting/printing infrastructure, which does not seem worth it at this point
-            return "SOCKET CONNECTION";
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(RNull operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(byte operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(int operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(double operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(RComplex operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(String operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(RRaw operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization(guards = "!isFactor(operand)")
-        protected String prettyPrintListElement(RAbstractVector operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(RSymbol operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(RLanguage operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(REnvironment operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(RFunction operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintListElement(RExternalPtr operand, Object listElementName, byte quote, byte right) {
-            return prettyPrintSingleElement(operand, listElementName, quote, right);
-        }
-
-        @Child InheritsCheckNode factorInheritsCheck = new InheritsCheckNode(RRuntime.CLASS_FACTOR);
-        @Child RFactorNodes.GetLevels getFactorLevels;
-
-        protected boolean isFactor(Object o) {
-            return this.factorInheritsCheck.execute(o);
-        }
-
-        // TODO: this should be handled by an S3 function
-        @Specialization(guards = "isFactor(factor)")
-        protected String prettyPrintListElement(RAbstractIntVector factor, Object listElementName, byte quote, byte right) {
-            if (getFactorLevels == null) {
-                CompilerDirectives.transferToInterpreter();
-                getFactorLevels = insert(new RFactorNodes.GetLevels());
-            }
-
-            RVector vec = getFactorLevels.execute(factor);
-            String[] strings;
-            if (vec == null) {
-                strings = new String[0];
-            } else {
-                initCast();
-                strings = new String[vec.getLength()];
-                for (int i = 0; i < vec.getLength(); i++) {
-                    strings[i] = (String) castStringNode.executeString(vec.getDataAtAsObject(i));
-                }
-            }
-            return formatLevelStrings(factor, listElementName, right, vec, strings);
-        }
-
-        @TruffleBoundary
-        private String formatLevelStrings(RAbstractIntVector operand, Object listElementName, byte right, RVector vec, String[] strings) {
-            StringBuilder sb = new StringBuilder(prettyPrintSingleElement(RClosures.createFactorToVector(operand, true, attrProfiles), listElementName, RRuntime.LOGICAL_FALSE, right));
-            sb.append("\nLevels:");
-            if (vec != null) {
-                for (int i = 0; i < vec.getLength(); i++) {
-                    sb.append(" ");
-                    sb.append(strings[i]);
-                }
-            }
-            return sb.toString();
-        }
-    }
-
-    @NodeChildren({@NodeChild(value = "operand", type = RNode.class), @NodeChild(value = "isQuoted", type = RNode.class)})
-    abstract static class PrettyPrinterSingleVectorElementNode extends RNode {
-
-        @Child private PrettyPrinterSingleVectorElementNode recursivePrettyPrinter;
-
-        private String prettyPrintRecursive(Object o, byte isQuoted) {
-            if (recursivePrettyPrinter == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                recursivePrettyPrinter = insert(PrettyPrinterSingleVectorElementNodeGen.create(null, null));
-            }
-            return (String) recursivePrettyPrinter.executeString(o, isQuoted);
-        }
-
-        public abstract Object executeString(Object o, byte isQuoted);
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(RNull operand, byte isQuoted) {
-            return "NULL";
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(byte operand, byte isQuoted) {
-            return prettyPrint(operand);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(int operand, byte isQuoted) {
-            return prettyPrint(operand);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(double operand, byte isQuoted) {
-            return prettyPrint(operand);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(RComplex operand, byte isQuoted) {
-            return prettyPrint(operand);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(String operand, byte isQuoted) {
-            if (RRuntime.fromLogical(isQuoted)) {
-                return prettyPrint(operand);
-            }
-            return operand;
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(RRaw operand, byte isQuoted) {
-            return prettyPrint(operand);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(RList operand, byte isQuoted) {
-            return prettyPrint(operand);
-        }
-
-        @TruffleBoundary
-        @Specialization(guards = {"operand.getLength() != 1", "!isVectorList(operand)"})
-        protected String prettyPrintVectorElement(RAbstractVector operand, byte isQuoted) {
-            return prettyPrint(operand);
-        }
-
-        @TruffleBoundary
-        @Specialization(guards = {"operand.getLength() == 1", "!isVectorList(operand)"})
-        protected String prettyPrintVectorElementLengthOne(RAbstractVector operand, byte isQuoted) {
-            return prettyPrintRecursive(operand.getDataAtAsObject(0), isQuoted);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(RLanguage operand, byte isQuoted) {
-            return prettyPrintLanguageInternal(operand);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String prettyPrintVectorElement(RSymbol operand, byte isQuoted) {
-            return operand.getName();
-        }
-
-        protected static boolean isVectorList(RAbstractVector v) {
-            return v instanceof RList;
-        }
-
-        protected static boolean isLengthOne(RAbstractVector v) {
-            return v.getLength() == 1;
-        }
-    }
-
-    @NodeChildren({@NodeChild(value = "vector", type = RNode.class), @NodeChild(value = "isListOrStringVector", type = RNode.class), @NodeChild(value = "isComplexOrRawVector", type = RNode.class),
-                    @NodeChild(value = "isQuoted", type = RNode.class)})
-    abstract static class PrintVectorMultiDimNode extends RNode {
-
-        @Child private PrintVector2DimNode vector2DimPrinter;
-        @Child private PrintDimNode dimPrinter;
-
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
-        private String printVector2Dim(RAbstractVector vector, RIntVector dimensions, int offset, byte isListOrStringVector, byte isComplexOrRawVector, byte isQuoted) {
-            if (vector2DimPrinter == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                vector2DimPrinter = insert(PrintVector2DimNodeGen.create(null, null, null, null, null, null));
-            }
-            return (String) vector2DimPrinter.executeString(vector, dimensions, offset, isListOrStringVector, isComplexOrRawVector, isQuoted);
-        }
-
-        private String printDim(RAbstractVector vector, byte isListOrStringVector, byte isComplexOrRawVector, int currentDimLevel, int arrayBase, int accDimensions, String header, byte isQuoted) {
-            if (dimPrinter == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                dimPrinter = insert(PrintDimNodeGen.create(null, null, null, null, null, null, null, null));
-            }
-            return (String) dimPrinter.executeString(vector, isListOrStringVector, isComplexOrRawVector, currentDimLevel, arrayBase, accDimensions, header, isQuoted);
-        }
-
-        public abstract Object executeString(RAbstractVector vector, byte isListOrStringVector, byte isComplexOrRawVector, byte isQuoted);
-
-        @TruffleBoundary
-        @Specialization
-        protected String printVectorMultiDim(RAbstractVector vector, byte isListOrStringVector, byte isComplexOrRawVector, byte isQuoted) {
-            int[] dimensions = vector.getDimensions();
-            RIntVector dimensionsVector = RDataFactory.createIntVector(dimensions, RDataFactory.COMPLETE_VECTOR);
-            assert dimensions != null;
-            int numDimensions = dimensions.length;
-            assert numDimensions > 1;
-            if (numDimensions == 2) {
-                return printVector2Dim(vector, dimensionsVector, 0, isListOrStringVector, isComplexOrRawVector, isQuoted);
-            } else {
-                int dimSize = dimensions[numDimensions - 1];
-                if (dimSize == 0) {
-                    return "";
-                }
-                StringBuilder sb = new StringBuilder();
-                if (numDimensions == 3) {
-                    int matrixSize = dimensions[0] * dimensions[1];
-                    for (int dimInd = 0; dimInd < dimSize; dimInd++) {
-                        // Checkstyle: stop
-                        sb.append(", , ");
-                        // Checkstyle: resume
-                        sb.append(getDimId(vector, numDimensions, dimInd, attrProfiles));
-                        sb.append("\n\n");
-                        sb.append(printVector2Dim(vector, dimensionsVector, dimInd * matrixSize, isListOrStringVector, isComplexOrRawVector, isQuoted));
-                        sb.append("\n");
-                        if (dimInd < (dimSize - 1) && vector.getLength() > 0 || vector.getLength() == 0) {
-                            sb.append("\n");
-                        }
-                    }
-                } else {
-                    int accDimensions = vector.getLength() / dimSize;
-                    for (int dimInd = 0; dimInd < dimSize; dimInd++) {
-                        int arrayBase = accDimensions * dimInd;
-                        String dimId = getDimId(vector, numDimensions, dimInd, attrProfiles);
-                        String innerDims = printDim(vector, isListOrStringVector, isComplexOrRawVector, numDimensions - 1, arrayBase, accDimensions, dimId, isQuoted);
-                        if (innerDims == null) {
-                            return "";
-                        } else {
-                            sb.append(innerDims);
-                        }
-                    }
-                }
-                if (vector.getLength() == 0) {
-                    // remove last line break
-                    return builderToSubstring(sb, 0, sb.length() - 1);
-                } else {
-                    return builderToString(sb);
-                }
-            }
-        }
-    }
-
-    @NodeChildren({@NodeChild(value = "vector", type = RNode.class), @NodeChild(value = "dimensions", type = RNode.class), @NodeChild(value = "offset", type = RNode.class),
-                    @NodeChild(value = "isListOrStringVector", type = RNode.class), @NodeChild(value = "isComplexOrRawVector", type = RNode.class), @NodeChild(value = "isQuoted", type = RNode.class)})
-    abstract static class PrintVector2DimNode extends RNode {
-
-        @Child private PrettyPrinterSingleVectorElementNode singleVectorElementPrettyPrinter;
-
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
-        private String prettyPrintSingleVectorElement(Object o, byte isQuoted) {
-            if (singleVectorElementPrettyPrinter == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                singleVectorElementPrettyPrinter = insert(PrettyPrinterSingleVectorElementNodeGen.create(null, null));
-            }
-            return (String) singleVectorElementPrettyPrinter.executeString(o, isQuoted);
-        }
-
-        public abstract Object executeString(RAbstractVector vector, RIntVector dimensions, int offset, byte isListOrStringVector, byte isComplexOrRawVector, byte isQuoted);
-
-        private static String getDimId(RList dimNames, int dimension, int ind, byte isComplexOrRawVector) {
-            StringBuilder sb = new StringBuilder();
-            if (dimNames == null || dimNames.getDataAt(dimension) == RNull.instance) {
-                String rs = intString(ind);
-                sb.append("[");
-                if (dimension == 1) {
-                    // columns
-                    sb.append(',');
-                }
-                sb.append(rs);
-                if (dimension == 0) {
-                    // rows
-                    sb.append(',');
-                }
-                sb.append(']');
-            } else {
-                RStringVector dimNamesVector = (RStringVector) dimNames.getDataAt(dimension);
-                String dimId = dimNamesVector.getDataAt(ind - 1);
-                if (dimension == 1 && isComplexOrRawVector == RRuntime.LOGICAL_TRUE && dimId.length() == 1) {
-                    sb.append(' ');
-                }
-                sb.append(dimId);
-            }
-            return builderToString(sb);
-        }
-
-        @TruffleBoundary
-        @Specialization(guards = "vector.getLength() == 0")
-        protected String printVector2DimEmpty(RAbstractVector vector, RIntVector dimensions, int offset, byte isListOrStringVector, byte isComplexOrRawVector, byte isQuoted) {
-            int nrow = dimensions.getDataAt(0);
-            int ncol = dimensions.getDataAt(1);
-
-            if (nrow == 0 && ncol == 0) {
-                if (dimensions.getLength() == 2) {
-                    return "<0 x 0 matrix>";
-                } else {
-                    return "";
-                }
-            }
-
-            StringBuilder sb = new StringBuilder();
-            RList dimNames = vector.getDimNames(attrProfiles);
-            if (ncol > 0) {
-                sb.append("     ");
-                for (int c = 1; c <= ncol; c++) {
-                    sb.append(getDimId(dimNames, 1, c, isComplexOrRawVector));
-                    if (c < ncol) {
-                        sb.append(' ');
-                    }
-                }
-            }
-            if (nrow > 0) {
-                sb.append('\n');
-                for (int r = 1; r <= nrow; r++) {
-                    sb.append(getDimId(dimNames, 0, r, isComplexOrRawVector));
-                    if (r < nrow) {
-                        sb.append('\n');
-                    }
-                }
-            }
-            return builderToString(sb);
-        }
-
-        @TruffleBoundary
-        @Specialization(guards = "vector.getLength() != 0")
-        protected String printVector2Dim(RAbstractDoubleVector vector, RIntVector dimensions, int offset, byte isListOrStringVector, byte isComplexOrRawVector, byte isQuoted) {
-            int nrow = dimensions.getDataAt(0);
-            int ncol = dimensions.getDataAt(1);
-
-            // prepare data (relevant for column widths)
-            String[] dataStrings = new String[nrow * ncol];
-            int[] dataColWidths = new int[ncol];
-            RList dimNames = vector.getDimNames(attrProfiles);
-            RStringVector columnDimNames = null;
-            if (dimNames != null && dimNames.getDataAt(1) != RNull.instance) {
-                columnDimNames = (RStringVector) dimNames.getDataAt(1);
-            }
-            double[] maxRoundFactors = new double[ncol];
-            int[] maxDigitsBehindDot = new int[ncol];
-            for (int c = 0; c < ncol; c++) {
-                maxRoundFactors[c] = 0;
-                for (int r = 0; r < nrow; r++) {
-                    int index = c * nrow + r;
-                    double data = vector.getDataAt(index + offset);
-                    double roundFactor = calcRoundFactor(data, 10000000);
-                    if (roundFactor > maxRoundFactors[c]) {
-                        maxRoundFactors[c] = roundFactor;
-                    }
-                }
-                maxDigitsBehindDot[c] = getMaxDigitsBehindDot(maxRoundFactors[c]);
-            }
-            int rowHeaderWidth = 0;
-            for (int c = 0; c < ncol; c++) {
-                for (int r = 0; r < nrow; r++) {
-                    int index = c * nrow + r;
-                    dataStrings[index] = prettyPrint(vector.getDataAt(index + offset), maxRoundFactors[c], maxDigitsBehindDot[c]);
-                    maintainColumnData(dataColWidths, columnDimNames, c, dataStrings[index]);
-                    rowHeaderWidth = Math.max(rowHeaderWidth, rowHeader(r + 1, vector, attrProfiles).length());
-                }
-            }
-
-            // probably add trailing decimal points and zeroes
-            // iterate over columns
-            for (int c = 0; c < ncol; c++) {
-                postProcessDoubleColumn(dataStrings, nrow, ncol, c);
-                // final adjustment of column width
-                boolean hasNegative = false;
-                for (int r = 0; r < nrow; r++) {
-                    // do not count minus signs
-                    String data = dataStrings[c * nrow + r];
-                    boolean isNegative = data.charAt(0) == '-';
-                    hasNegative = hasNegative || isNegative;
-                    int l = isNegative ? data.length() - 1 : data.length();
-                    if (l > dataColWidths[c]) {
-                        dataColWidths[c] = l;
-                    }
-                }
-                if (hasNegative) {
-                    dataColWidths[c] = -dataColWidths[c];
-                }
-            }
-
-            return formatResult(vector, nrow, ncol, dataStrings, dataColWidths, rowHeaderWidth, isListOrStringVector == RRuntime.LOGICAL_TRUE);
-        }
-
-        @TruffleBoundary
-        @Specialization(guards = "vector.getLength() != 0")
-        protected String printVector2Dim(RAbstractComplexVector vector, RIntVector dimensions, int offset, byte isListOrStringVector, byte isComplexOrRawVector, byte isQuoted) {
-            int nrow = dimensions.getDataAt(0);
-            int ncol = dimensions.getDataAt(1);
-
-            // prepare data (relevant for column widths)
-            String[] reStrings = new String[nrow * ncol];
-            String[] imStrings = new String[nrow * ncol];
-            String[] siStrings = new String[nrow * ncol];
-            int[] dataColWidths = new int[ncol];
-            RList dimNames = vector.getDimNames(attrProfiles);
-            RStringVector columnDimNames = null;
-            if (dimNames != null && dimNames.getDataAt(1) != RNull.instance) {
-                columnDimNames = (RStringVector) dimNames.getDataAt(1);
-            }
-            int rowHeaderWidth = 0;
-            for (int r = 0; r < nrow; r++) {
-                for (int c = 0; c < ncol; c++) {
-                    int index = c * nrow + r;
-                    reStrings[index] = prettyPrintSingleVectorElement(vector.getDataAt(index + offset).getRealPart(), isQuoted);
-                    imStrings[index] = prettyPrintSingleVectorElement(vector.getDataAt(index + offset).getImaginaryPart(), isQuoted);
-                    siStrings[index] = vector.getDataAt(index + offset).getImaginaryPart() < 0.0 ? "-" : "+";
-                    // "" because column width is computed later
-                    maintainColumnData(dataColWidths, columnDimNames, c, "");
-                }
-                rowHeaderWidth = Math.max(rowHeaderWidth, rowHeader(r + 1, vector, attrProfiles).length());
-            }
-
-            // adjust formatting
-            // iterate over columns
-            for (int c = 0; c < ncol; c++) {
-                postProcessComplexColumn(reStrings, imStrings, nrow, ncol, c);
-            }
-
-            String[] dataStrings = new String[nrow * ncol];
-            for (int i = 0; i < dataStrings.length; i++) {
-                dataStrings[i] = vector.getDataAt(i).isNA() ? "NA" : concat(reStrings[i], siStrings[i], imStrings[i], "i");
-            }
-
-            // final adjustment of column width
-            for (int c = 0; c < ncol; c++) {
-                for (int r = 0; r < nrow; r++) {
-                    // do not count minus signs
-                    String data = dataStrings[c * nrow + r];
-                    int l = data.charAt(0) == '-' ? data.length() - 1 : data.length();
-                    if (l > dataColWidths[c]) {
-                        dataColWidths[c] = l;
-                    }
-                }
-            }
-
-            return formatResult(vector, nrow, ncol, dataStrings, dataColWidths, rowHeaderWidth, isListOrStringVector == RRuntime.LOGICAL_TRUE);
-        }
-
-        @TruffleBoundary
-        @Specialization(guards = {"vector.getLength() != 0", "notDoubleOrComplex(vector)"})
-        protected String printVector2Dim(RAbstractVector vector, RIntVector dimensions, int offset, byte isListOrStringVector, byte isComplexOrRawVector, byte isQuoted) {
-            int nrow = dimensions.getDataAt(0);
-            int ncol = dimensions.getDataAt(1);
-
-            // prepare data (relevant for column widths)
-            String[] dataStrings = new String[nrow * ncol];
-            int[] dataColWidths = new int[ncol];
-            RList dimNames = vector.getDimNames(attrProfiles);
-            RStringVector columnDimNames = null;
-            if (dimNames != null && dimNames.getDataAt(1) != RNull.instance) {
-                if (dimNames.getDataAt(1) instanceof String) {
-                    columnDimNames = RDataFactory.createStringVector((String) dimNames.getDataAt(1));
-                } else {
-                    columnDimNames = (RStringVector) dimNames.getDataAt(1);
-                }
-            }
-            int rowHeaderWidth = 0;
-            for (int r = 0; r < nrow; r++) {
-                for (int c = 0; c < ncol; c++) {
-                    int index = c * nrow + r;
-                    dataStrings[index] = prettyPrintSingleVectorElement(vector.getDataAtAsObject(index + offset), isQuoted);
-                    maintainColumnData(dataColWidths, columnDimNames, c, dataStrings[index]);
-                }
-                rowHeaderWidth = Math.max(rowHeaderWidth, rowHeader(r + 1, vector, attrProfiles).length());
-            }
-
-            return formatResult(vector, nrow, ncol, dataStrings, dataColWidths, rowHeaderWidth, isListOrStringVector == RRuntime.LOGICAL_TRUE);
-        }
-
-        protected boolean notDoubleOrComplex(RAbstractVector vector) {
-            return vector.getElementClass() != RDouble.class && vector.getElementClass() != RComplex.class;
-        }
-
-        private static void postProcessDoubleColumn(String[] dataStrings, int nrow, int ncol, int col) {
-            // create and populate array with column data
-            String[] columnData = new String[nrow];
-            for (int r = 0; r < nrow; r++) {
-                columnData[r] = dataStrings[col * nrow + r];
-            }
-            padTrailingDecimalPointAndZeroesIfRequired(columnData);
-            // put possibly changed data back
-            for (int r = 0; r < nrow; r++) {
-                dataStrings[col * nrow + r] = columnData[r];
-            }
-        }
-
-        private static void postProcessComplexColumn(String[] re, String[] im, int nrow, int ncol, int col) {
-            // create and populate arrays with column data
-            String[] cre = new String[nrow];
-            String[] cim = new String[nrow];
-            for (int r = 0; r < nrow; r++) {
-                cre[r] = re[col * nrow + r];
-                cim[r] = im[col * nrow + r];
-            }
-
-            padTrailingDecimalPointAndZeroesIfRequired(cre);
-            padTrailingDecimalPointAndZeroesIfRequired(cim);
-            removeLeadingMinus(cim);
-            rightJustify(cre);
-            rightJustify(cim);
-
-            // put possibly changed data back
-            for (int r = 0; r < nrow; r++) {
-                re[col * nrow + r] = cre[r];
-                im[col * nrow + r] = cim[r];
-            }
-        }
-
-        private static void maintainColumnData(int[] dataColWidths, RStringVector columnDimNames, int c, String data) {
-            // do not count minus signs
-            int dataLength = !data.equals("") && data.charAt(0) == '-' ? data.length() - 1 : data.length();
-            if (dataLength > dataColWidths[c]) {
-                dataColWidths[c] = dataLength;
-            }
-            if (columnDimNames != null) {
-                String columnName = columnDimNames.getDataAt(c);
-                if (RRuntime.isNA(columnName)) {
-                    columnName = RRuntime.NA_HEADER;
-                }
-                if (columnName.length() > dataColWidths[c]) {
-                    dataColWidths[c] = columnName.length();
-                }
-            }
-        }
-
-        private String formatResult(RAbstractVector vector, int nrow, int ncol, String[] dataStrings, int[] dataColWidths, int rowHeaderWidth, boolean isListOrStringVector) {
-            boolean isComplexVector = vector.getElementClass() == RComplex.class;
-            boolean isDoubleVector = vector.getElementClass() == RDouble.class;
-            String rowFormat = concat("%", intString(rowHeaderWidth), "s");
-
-            StringBuilder b = new StringBuilder();
-
-            int colInd = 0;
-            while (true) {
-                int totalWidth = rowHeaderWidth + 1;
-                int startColInd = colInd;
-                for (; colInd < dataColWidths.length; colInd++) {
-                    boolean hasNegative = dataColWidths[colInd] < 0;
-                    totalWidth += Math.abs(dataColWidths[colInd]) + ((isDoubleVector || isComplexVector) && hasNegative ? 2 : 1);
-                    if (totalWidth > (int) RContext.getInstance().stateROptions.getValue("name")) {
-                        if (colInd == startColInd) {
-                            // the first column is already too wide but needs to be printed
-                            // nevertheless
-                            colInd++;
-                        }
-                        break;
-                    }
-                }
-
-                // column header
-                spaces(b, rowHeaderWidth + 1);
-                for (int c = startColInd + 1; c <= colInd; c++) {
-                    boolean hasNegative = dataColWidths[c - 1] < 0;
-                    // header of the first column needs extra padding if this column has negative
-                    // numbers
-                    int padding = Math.abs(dataColWidths[c - 1]) + ((isDoubleVector || isComplexVector) && hasNegative ? 1 : 0);
-                    b.append(padColHeader(c, padding, vector, isListOrStringVector, attrProfiles));
-                    if (c < colInd) {
-                        b.append(" ");
-                    }
-                }
-                b.append('\n');
-
-                boolean indexRowHeaders = rowHeaderUsesIndices(vector.getDimNames(attrProfiles));
-
-                // rows
-                for (int r = 1; r <= nrow; r++) {
-                    String headerString = rowHeader(r, vector, attrProfiles);
-                    if (indexRowHeaders) {
-                        spaces(b, rowHeaderWidth - headerString.length());
-                        b.append(headerString).append(' ');
-                    } else {
-                        b.append(headerString);
-                        spaces(b, rowHeaderWidth - headerString.length() + 1);
-                    }
-                    for (int c = startColInd + 1; c <= colInd; c++) {
-                        String dataString = dataStrings[(c - 1) * nrow + (r - 1)];
-                        boolean hasNegative = dataColWidths[c - 1] < 0;
-                        int padding = Math.abs(dataColWidths[c - 1]) + ((isDoubleVector || isComplexVector) && hasNegative ? 1 : 0);
-                        if (isListOrStringVector || (isComplexVector && !RRuntime.STRING_NA.equals(dataString))) {
-                            // list elements are left-justified, and so are complex matrix elements
-                            // that are not NA
-                            b.append(dataString);
-                            spaces(b, padColHeader(c, padding, vector, isListOrStringVector, attrProfiles).length() - dataString.length());
-                        } else {
-                            // vector elements are right-justified, and so are NAs in complex
-                            // matrices
-                            String cellFormat = concat("%", intString(padColHeader(c, padding, vector, isListOrStringVector, attrProfiles).length()), "s");
-                            b.append(stringFormat(cellFormat, dataString));
-                        }
-                        if (c < colInd) {
-                            b.append(' ');
-                        }
-                    }
-                    if (r < nrow) {
-                        b.append('\n');
-                    }
-                }
-                if (colInd < dataColWidths.length) {
-                    b.append('\n');
-                } else {
-                    break;
-                }
-            }
-            return builderToString(b);
-        }
-    }
-
-    @NodeChildren({@NodeChild(value = "vector", type = RNode.class), @NodeChild(value = "isListOrStringVector", type = RNode.class), @NodeChild(value = "isComplexOrRawVector", type = RNode.class),
-                    @NodeChild(value = "currentDimLevel", type = RNode.class), @NodeChild(value = "arrayBase", type = RNode.class), @NodeChild(value = "accDimensions", type = RNode.class),
-                    @NodeChild(value = "header", type = RNode.class), @NodeChild(value = "isQuoted", type = RNode.class)})
-    abstract static class PrintDimNode extends RNode {
-
-        public abstract Object executeString(RAbstractVector vector, byte isListOrStringVector, byte isComplexOrRawVector, int currentDimLevel, int arrayBase, int accDimensions,
-                        String header, byte isQuoted);
-
-        @Child private PrintVector2DimNode vector2DimPrinter;
-        @Child private PrintDimNode dimPrinter;
-
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
-        private String printVector2Dim(RAbstractVector vector, RIntVector dimensions, int offset, byte isListOrStringVector, byte isComplexOrRawVector, byte isQuoted) {
-            if (vector2DimPrinter == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                vector2DimPrinter = insert(PrintVector2DimNodeGen.create(null, null, null, null, null, null));
-            }
-            return (String) vector2DimPrinter.executeString(vector, dimensions, offset, isListOrStringVector, isComplexOrRawVector, isQuoted);
-        }
-
-        private String printDimRecursive(RAbstractVector vector, byte isListOrStringVector, byte isComplexOrRawVector, int currentDimLevel, int arrayBase, int accDimensions, String header,
-                        byte isQuoted) {
-            if (dimPrinter == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                dimPrinter = insert(PrintDimNodeGen.create(null, null, null, null, null, null, null, null));
-            }
-            return (String) dimPrinter.executeString(vector, isListOrStringVector, isComplexOrRawVector, currentDimLevel, arrayBase, accDimensions, header, isQuoted);
-        }
-
-        @TruffleBoundary
-        @Specialization
-        protected String printDim(RAbstractVector vector, byte isListOrStringVector, byte isComplexOrRawVector, int currentDimLevel, int arrayBase, int accDimensions, String header, byte isQuoted) {
-            int[] dimensions = vector.getDimensions();
-            RIntVector dimensionsVector = RDataFactory.createIntVector(dimensions, RDataFactory.COMPLETE_VECTOR);
-            StringBuilder sb = new StringBuilder();
-            int dimSize = dimensions[currentDimLevel - 1];
-            if (dimSize == 0) {
-                return null;
-            }
-            if (currentDimLevel == 3) {
-                int matrixSize = dimensions[0] * dimensions[1];
-                for (int dimInd = 0; dimInd < dimSize; dimInd++) {
-                    // Checkstyle: stop
-                    sb.append(", , ");
-                    // Checkstyle: resume
-                    sb.append(getDimId(vector, currentDimLevel, dimInd, attrProfiles));
-                    sb.append(", ");
-                    sb.append(header);
-                    sb.append("\n\n");
-                    sb.append(printVector2Dim(vector, dimensionsVector, arrayBase + (dimInd * matrixSize), isListOrStringVector, isComplexOrRawVector, isQuoted));
-                    sb.append("\n");
-                    if ((arrayBase + (dimInd * matrixSize) + matrixSize) < vector.getLength() || vector.getLength() == 0) {
-                        sb.append("\n");
-                    }
-                }
-            } else {
-                int newAccDimensions = accDimensions / dimSize;
-                for (int dimInd = 0; dimInd < dimSize; dimInd++) {
-                    int newArrayBase = arrayBase + newAccDimensions * dimInd;
-                    String dimId = getDimId(vector, currentDimLevel, dimInd, attrProfiles);
-                    String innerDims = printDimRecursive(vector, isListOrStringVector, isComplexOrRawVector, currentDimLevel - 1, newArrayBase, newAccDimensions, concat(dimId, ", ", header),
-                                    isQuoted);
-                    if (innerDims == null) {
-                        return null;
-                    } else {
-                        sb.append(innerDims);
-                    }
-                }
-                return builderToString(sb);
-            }
-            return builderToString(sb);
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java
index 0429fcc447257ca1c71a26d962e4adbd1940fe42..fdcad6132cc24b5119b071873ca097ca83765435 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Primitive.java
@@ -22,36 +22,54 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+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.PRIMITIVE;
 
-import com.oracle.truffle.api.dsl.Fallback;
+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.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
 
-@RBuiltin(name = ".Primitive", kind = PRIMITIVE, parameterNames = "name")
+@RBuiltin(name = ".Primitive", kind = PRIMITIVE, parameterNames = "name", behavior = PURE)
 public abstract class Primitive extends RBuiltinNode {
+
     private final BranchProfile errorProfile = BranchProfile.create();
 
-    @Specialization
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg("name").defaultError(Message.STRING_ARGUMENT_REQUIRED).mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+    }
+
+    @Specialization(guards = "name == cachedName")
+    protected RFunction primitiveCached(@SuppressWarnings("unused") String name,
+                    @Cached("name") @SuppressWarnings("unused") String cachedName,
+                    @Cached("lookup(name)") RFunction function) {
+        return function;
+    }
+
+    @Specialization(contains = "primitiveCached")
     protected RFunction primitive(String name) {
+        RFunction function = lookup(name);
+        return function;
+    }
+
+    @TruffleBoundary
+    protected RFunction lookup(String name) {
         RFunction function = RContext.lookupBuiltin(name);
         if (function == null || function.getRBuiltin() != null && function.getRBuiltin().getKind() != RBuiltinKind.PRIMITIVE) {
             errorProfile.enter();
             throw RError.error(this, RError.Message.NO_SUCH_PRIMITIVE, name);
         }
-
-        // .Primitive function is validated
         return function;
     }
-
-    @Fallback
-    protected RFunction primitive(@SuppressWarnings("unused") Object name) {
-        throw RError.error(this, RError.Message.STRING_ARGUMENT_REQUIRED);
-    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java
index 760c12d79b473e2988b281d135c54b5822a35f83..cfb8266c0508dbce669831f572566bec85c5a3be 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/PrintFunctions.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
@@ -34,9 +36,8 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.printer.PrintParameters;
 import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinterNode;
 import com.oracle.truffle.r.nodes.builtin.base.printer.ValuePrinterNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributable;
@@ -47,7 +48,7 @@ import com.oracle.truffle.r.runtime.data.RTypedValue;
 
 public class PrintFunctions {
 
-    @RBuiltin(name = "print.default", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"x", "digits", "quote", "na.print", "print.gap", "right", "max", "useSource", "noOpt"})
+    @RBuiltin(name = "print.default", visibility = OFF, kind = INTERNAL, parameterNames = {"x", "digits", "quote", "na.print", "print.gap", "right", "max", "useSource", "noOpt"}, behavior = IO)
     public abstract static class PrintDefault extends RBuiltinNode {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
@@ -71,7 +72,7 @@ public class PrintFunctions {
         }
 
         protected static RFunction createShowFunction(VirtualFrame frame) {
-            return ReadVariableNode.lookupFunction("show", frame, false);
+            return ReadVariableNode.lookupFunction("show", frame);
         }
 
         @SuppressWarnings("unused")
@@ -80,7 +81,7 @@ public class PrintFunctions {
                         @Cached("createShowFunction(frame)") RFunction showFunction) {
             if (noOpt) {
                 // S4 should only be called in case noOpt is true
-                RContext.getEngine().evalFunction(showFunction, null, null, o);
+                RContext.getEngine().evalFunction(showFunction, null, null, null, o);
             } else {
                 printDefault(showFunction, digits, quote, naPrint, printGap, right, max, useSource, noOpt);
             }
@@ -92,7 +93,7 @@ public class PrintFunctions {
         }
     }
 
-    @RBuiltin(name = "print.function", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"x", "useSource", "..."})
+    @RBuiltin(name = "print.function", visibility = OFF, kind = INTERNAL, parameterNames = {"x", "useSource", "..."}, behavior = IO)
     public abstract static class PrintFunction extends RBuiltinNode {
 
         @Child private ValuePrinterNode valuePrinter = ValuePrinterNodeGen.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
index 73ad80badcbe872f41040c7390caf507578e0779..be182b91b23a5b1ef9147f6ae56359df1f2d456c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
@@ -22,20 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.Engine;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 
-@RBuiltin(name = "proc.time", kind = PRIMITIVE, parameterNames = {})
+@RBuiltin(name = "proc.time", kind = PRIMITIVE, parameterNames = {}, behavior = IO)
 public abstract class ProcTime extends RBuiltinNode {
 
     private static final String[] NAMES = new String[]{"user.self", "sys.self", "elapsed", "user.child", "sys.child"};
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Prod.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Prod.java
index 26adccf1fb38342f7ed2d8704d261d5c15b9f903..f9981b384a80df6c59d7069678fef068a35044f9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Prod.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Prod.java
@@ -10,13 +10,15 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.RDispatch.SUMMARY_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
@@ -25,9 +27,11 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 
-@RBuiltin(name = "prod", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = RDispatch.SUMMARY_GROUP_GENERIC)
+@RBuiltin(name = "prod", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = SUMMARY_GROUP_GENERIC, behavior = PURE)
 public abstract class Prod extends RBuiltinNode {
 
+    // TODO: handle multiple arguments, handle na.rm
+
     @Override
     public Object[] getDefaultParameterValues() {
         return new Object[]{RArgsValuesAndNames.EMPTY, RRuntime.LOGICAL_FALSE};
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java
index fe4860d7a7b3cc5b35a79c475f1f03a8dc6e73c7..17aa680ce81c3782274c132ced48f1b299217f6d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quit.java
@@ -12,26 +12,27 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RCleanUp;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RStartParams;
 import com.oracle.truffle.r.runtime.RStartParams.SA_TYPE;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "quit", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"save", "status", "runLast"})
+@RBuiltin(name = "quit", visibility = OFF, kind = INTERNAL, parameterNames = {"save", "status", "runLast"}, behavior = COMPLEX)
 public abstract class Quit extends RBuiltinNode {
 
     @Override
@@ -80,5 +81,4 @@ public abstract class Quit extends RBuiltinNode {
         }
         throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quote.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quote.java
index 6a8e2b2c5b84458a0cb4802fec9462991aa57152..40dd919e5b331e986751e1b0e6ecf6562665bed8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quote.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Quote.java
@@ -22,27 +22,28 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.ReadVariadicComponentNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
-@RBuiltin(name = "quote", nonEvalArgs = 0, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"expr"})
+@RBuiltin(name = "quote", nonEvalArgs = 0, kind = PRIMITIVE, parameterNames = {"expr"}, behavior = PURE)
 public abstract class Quote extends RBuiltinNode {
 
-    public abstract Object execute(VirtualFrame frame, RPromise expr);
+    public abstract Object execute(RPromise expr);
 
     private final ConditionProfile rvn = ConditionProfile.createBinaryProfile();
     private final ConditionProfile rvcn = ConditionProfile.createBinaryProfile();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RNGFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RNGFunctions.java
index 56e7112de4503e70598f36e83e168abae2a11bbe..8c7c8ca2f7e10e958b0ea21955b58b896630a6ea 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RNGFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RNGFunctions.java
@@ -22,14 +22,15 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.MODIFIES_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
+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.RNull;
@@ -37,7 +38,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.rng.RRNG;
 
 public class RNGFunctions {
-    @RBuiltin(name = "set.seed", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"seed", "kind", "normal.kind"})
+    @RBuiltin(name = "set.seed", visibility = OFF, kind = INTERNAL, parameterNames = {"seed", "kind", "normal.kind"}, behavior = MODIFIES_STATE)
     public abstract static class SetSeed extends RBuiltinNode {
 
         @SuppressWarnings("unused")
@@ -73,7 +74,7 @@ public class RNGFunctions {
         }
     }
 
-    @RBuiltin(name = "RNGkind", kind = INTERNAL, parameterNames = {"kind", "normkind"})
+    @RBuiltin(name = "RNGkind", kind = INTERNAL, parameterNames = {"kind", "normkind"}, behavior = MODIFIES_STATE)
     public abstract static class RNGkind extends RBuiltinNode {
 
         @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RVersion.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RVersion.java
index 513633048964ab1949e916c98e8083ba2cc6916b..53e566ab53d8bf98d87755dc076fcfbefb984e12 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RVersion.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RVersion.java
@@ -22,16 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RVersionInfo;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 
-@RBuiltin(name = "Version", kind = INTERNAL, parameterNames = {})
+@RBuiltin(name = "Version", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
 public abstract class RVersion extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Range.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Range.java
index 0d9a78341c6ce7deeff8ed0dec6b7ddd3a812f9e..5db642574c7f1e5925a1260bc6177e5bca5c3ce6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Range.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Range.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RDispatch.SUMMARY_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -31,16 +33,15 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNode.ReduceSemantics;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 
-@RBuiltin(name = "range", kind = PRIMITIVE, parameterNames = {"...", "na.rm", "finite"}, dispatch = RDispatch.SUMMARY_GROUP_GENERIC)
+@RBuiltin(name = "range", kind = PRIMITIVE, parameterNames = {"...", "na.rm", "finite"}, dispatch = SUMMARY_GROUP_GENERIC, behavior = PURE)
 public abstract class Range extends RBuiltinNode {
 
     private static final ReduceSemantics minSemantics = new ReduceSemantics(RRuntime.INT_MAX_VALUE, Double.POSITIVE_INFINITY, false, RError.Message.NO_NONMISSING_MIN,
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rank.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rank.java
index 3c67a370cf89e23b918461218264934ca6c3b460..808450ebf3bd2d4012d71e35239fd801358bafde 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rank.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rank.java
@@ -12,16 +12,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.OrderNodeGen.CmpNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.OrderNodeGen.OrderVector1NodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RRawVector;
@@ -30,8 +31,9 @@ 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;
 
-@RBuiltin(name = "rank", kind = INTERNAL, parameterNames = {"x", "len", "ties.method"})
+@RBuiltin(name = "rank", kind = INTERNAL, parameterNames = {"x", "len", "ties.method"}, behavior = PURE)
 public abstract class Rank extends RBuiltinNode {
+
     @Child private Order.OrderVector1Node orderVector1Node;
     @Child private Order.CmpNode orderCmpNode;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RawFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RawFunctions.java
index 455636e60cf94369accbca19093b227c4cd8599a..2d3786c3cc50fb170e9896fef60f6bec5480126a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RawFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RawFunctions.java
@@ -22,6 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -29,10 +32,9 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -44,7 +46,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
  */
 public class RawFunctions {
 
-    @RBuiltin(name = "charToRaw", kind = RBuiltinKind.INTERNAL, parameterNames = "x")
+    @RBuiltin(name = "charToRaw", kind = INTERNAL, parameterNames = "x", behavior = PURE)
     public abstract static class CharToRaw extends RBuiltinNode {
         @Specialization
         protected RRawVector charToRaw(RAbstractStringVector x) {
@@ -65,7 +67,7 @@ public class RawFunctions {
         }
     }
 
-    @RBuiltin(name = "rawToChar", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "multiple"})
+    @RBuiltin(name = "rawToChar", kind = INTERNAL, parameterNames = {"x", "multiple"}, behavior = PURE)
     public abstract static class RawToChar extends RBuiltinNode {
         @Specialization
         protected RStringVector rawToChar(RAbstractRawVector x, byte multiple) {
@@ -100,7 +102,7 @@ public class RawFunctions {
         }
     }
 
-    @RBuiltin(name = "rawShift", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "n"})
+    @RBuiltin(name = "rawShift", kind = INTERNAL, parameterNames = {"x", "n"}, behavior = PURE)
     public abstract static class RawShift extends RBuiltinNode {
         @Override
         protected void createCasts(CastBuilder casts) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadDCF.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadDCF.java
index 24f1a4edfd478b6a76a3bef812f5b8bd77aec12f..3bc66597230371ffa0fbd99c3a8bf37ade867c2d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadDCF.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadDCF.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.IOException;
 import java.util.HashSet;
@@ -36,9 +37,9 @@ import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.DCF;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -46,7 +47,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "readDCF", kind = INTERNAL, parameterNames = {"conn", "fields", "keepwhite"})
+@RBuiltin(name = "readDCF", kind = INTERNAL, parameterNames = {"conn", "fields", "keepwhite"}, behavior = IO)
 public abstract class ReadDCF extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java
index 45a89d44c7bc41796ea150cc1b1edaffc567342f..91d68a96774c913b87f198d21aced47759a51838 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ReadREnviron.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -31,15 +33,14 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "readRenviron", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = "path")
+@RBuiltin(name = "readRenviron", visibility = OFF, kind = INTERNAL, parameterNames = "path", behavior = COMPLEX)
 public abstract class ReadREnviron extends RBuiltinNode {
 
     @Specialization(guards = "lengthOneCVector(vec)")
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java
index e19dc8a7cc2f139c2d6e435001f2f7bbad3b9b37..63d70486b2bf75f7fd5b293fdf39bd0454bed25d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Readline.java
@@ -22,16 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.ConsoleHandler;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "readline", kind = RBuiltinKind.INTERNAL, parameterNames = "prompt")
+@RBuiltin(name = "readline", kind = INTERNAL, parameterNames = "prompt", behavior = IO)
 public abstract class Readline extends RBuiltinNode {
     @Specialization
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java
index 52fb642eb0a0831a934c50fe46faae3bbb61a8f7..3cc3caf3c0d0dba9049a8ab6d9502655c2971130 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Recall.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.Frame;
@@ -34,17 +36,16 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.CallMatcherNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
 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.RFunction;
 
 /**
  * The {@code Recall} {@code .Internal}.
  */
-@RBuiltin(name = "Recall", visibility = RVisibility.CUSTOM, kind = INTERNAL, parameterNames = {"..."}, nonEvalArgs = {0})
+@RBuiltin(name = "Recall", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"..."}, nonEvalArgs = {0}, behavior = COMPLEX)
 public abstract class Recall extends RBuiltinNode {
 
     private final BranchProfile errorProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RegFinalizer.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RegFinalizer.java
index f60a8e52472647f8d614b1243da3daf9b3a1cc0f..03395b1cca29c625d320395c1cac8ada93948329 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RegFinalizer.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RegFinalizer.java
@@ -22,19 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RExternalPtr;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "reg.finalizer", kind = RBuiltinKind.INTERNAL, parameterNames = {"e", "f", "onexit"})
+@RBuiltin(name = "reg.finalizer", kind = INTERNAL, parameterNames = {"e", "f", "onexit"}, behavior = COMPLEX)
 public abstract class RegFinalizer extends RBuiltinNode {
     @Specialization
     protected RNull doRegFinalizer(RExternalPtr ext, RFunction fun, byte onexit) {
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 5cd532a5cb299f55ba4d29cbbd075bb8ed999e9f..12b25af55ac9e6b46c1311898a4f3e526d8bc076 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.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import java.util.Arrays;
 
@@ -31,10 +33,9 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -64,7 +65,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
  * </ul>
  * </ol>
  */
-@RBuiltin(name = "rep", kind = PRIMITIVE, parameterNames = {"x", "times", "length.out", "each"}, dispatch = RDispatch.INTERNAL_GENERIC)
+@RBuiltin(name = "rep", kind = PRIMITIVE, parameterNames = {"x", "times", "length.out", "each"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class Repeat extends RBuiltinNode {
 
     protected abstract Object execute(RAbstractVector x, RAbstractIntVector times, int lengthOut, int each);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RepeatInternal.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RepeatInternal.java
index 49a53c7f08decbacddac8d32e836b569a18bbdb1..8515c9e92f97f8d0774f9f5ba2062522ec66dce0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RepeatInternal.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RepeatInternal.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.function.IntFunction;
 
@@ -31,8 +32,8 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -47,7 +48,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "rep.int", kind = INTERNAL, parameterNames = {"x", "times"})
+@RBuiltin(name = "rep.int", kind = INTERNAL, parameterNames = {"x", "times"}, behavior = PURE)
 public abstract class RepeatInternal extends RBuiltinNode {
 
     private final ConditionProfile timesOneProfile = ConditionProfile.createBinaryProfile();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RepeatLength.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RepeatLength.java
index f53dea1c44aa08fe00be0bb651a1263c07f2c678..9c4dc2ac4c8f163dfe80726d21008aa929a3003d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RepeatLength.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RepeatLength.java
@@ -11,16 +11,17 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Arrays;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -34,7 +35,7 @@ import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
-@RBuiltin(name = "rep_len", kind = INTERNAL, parameterNames = {"x", "length.out"})
+@RBuiltin(name = "rep_len", kind = INTERNAL, parameterNames = {"x", "length.out"}, behavior = PURE)
 public abstract class RepeatLength extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Return.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Return.java
index b6d6d662448f130cdd27f8cae3df9959439f48ac..7bd403ca3263119f4d7bf6c49f089da2d93b4ca8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Return.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Return.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -31,9 +32,9 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.ReturnException;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPromise;
@@ -44,7 +45,7 @@ import com.oracle.truffle.r.runtime.data.RPromise;
  * evaluating that in the context of a PromiseEvalFrame and the frame we need to return to is that
  * given by the PromiseEvalFrame.
  */
-@RBuiltin(name = "return", kind = PRIMITIVE, parameterNames = {"value"}, nonEvalArgs = {0})
+@RBuiltin(name = "return", kind = PRIMITIVE, parameterNames = {"value"}, nonEvalArgs = {0}, behavior = COMPLEX)
 public abstract class Return extends RBuiltinNode {
 
     private final BranchProfile isPromiseEvalProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rhome.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rhome.java
index e4c97dd35115f3f3168f8dbbee87011e0346adc7..2a735cdb0a6479d47ba055c05c72fa919b364a7e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rhome.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rhome.java
@@ -22,18 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.REnvVars;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
 /**
  * R.home builtin.
  */
-@RBuiltin(name = "R.home", kind = INTERNAL, parameterNames = {})
+@RBuiltin(name = "R.home", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
 public abstract class Rhome extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rm.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rm.java
index fe2650e98bb0dd0d596f8bb54ac9be57ba335bc7..9005d96018a4e211fafd22acff0b1c4ad1985661 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rm.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Rm.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -32,9 +34,8 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -42,7 +43,7 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 
-@RBuiltin(name = "remove", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"list", "envir", "inherits"})
+@RBuiltin(name = "remove", visibility = OFF, kind = INTERNAL, parameterNames = {"list", "envir", "inherits"}, behavior = COMPLEX)
 public abstract class Rm extends RBuiltinNode {
 
     private final BranchProfile invalidateProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Round.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Round.java
index 62cdc8b9288a86af2697060c2559a20f469ed35d..710cbe3459714bfc027d32b5dd835abc0695ddd7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Round.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Round.java
@@ -22,15 +22,16 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
@@ -44,7 +45,7 @@ import com.oracle.truffle.r.runtime.ops.UnaryArithmetic;
 import com.oracle.truffle.r.runtime.ops.UnaryArithmeticFactory;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "round", kind = PRIMITIVE, parameterNames = {"x", "digits"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+@RBuiltin(name = "round", kind = PRIMITIVE, parameterNames = {"x", "digits"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
 public abstract class Round extends RBuiltinNode {
 
     public static final UnaryArithmeticFactory ROUND = RoundArithmetic::new;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Row.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Row.java
index b27734df004ec4885f341848d17acb0d6a5ec820..7f03a651e9dd5bde1f756f1c6053939b21d08997 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Row.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Row.java
@@ -12,19 +12,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+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.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
-@RBuiltin(name = "row", kind = INTERNAL, parameterNames = {"dims"})
+@RBuiltin(name = "row", kind = INTERNAL, parameterNames = {"dims"}, behavior = PURE)
 public abstract class Row extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowMeans.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowMeans.java
index b0508eff59489fefc56957f99dc8ef880b3b3296..5d97a4dac5811ed73dcbaa0fb2723ae96f6ad00a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowMeans.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowMeans.java
@@ -11,16 +11,17 @@
 package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+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.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -30,7 +31,7 @@ import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 // Implements .rowMeans
-@RBuiltin(name = "rowMeans", kind = RBuiltinKind.INTERNAL, parameterNames = {"X", "m", "n", "na.rm"})
+@RBuiltin(name = "rowMeans", kind = INTERNAL, parameterNames = {"X", "m", "n", "na.rm"}, behavior = PURE)
 public abstract class RowMeans extends RBuiltinNode {
 
     @Child private BinaryArithmetic add = BinaryArithmetic.ADD.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowSums.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowSums.java
index 948f87db7fc991ca9b48b01e288484032a1e4b14..3660bf75ee1d03e56c6beacf49d56d6b6da2f8a5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowSums.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowSums.java
@@ -10,16 +10,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
@@ -30,7 +32,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "rowSums", kind = RBuiltinKind.INTERNAL, parameterNames = {"X", "m", "n", "na.rm"})
+@RBuiltin(name = "rowSums", kind = INTERNAL, parameterNames = {"X", "m", "n", "na.rm"}, behavior = PURE)
 public abstract class RowSums extends RBuiltinNode {
 
     /*
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowsumFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowsumFunctions.java
index f4d26ee3a7562e968f577cd5fdd594e879ac1cdb..b0e65daa31828bff7c0c7e0a1889f3f6aeaf7feb 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowsumFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/RowsumFunctions.java
@@ -12,21 +12,23 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+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 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.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -42,7 +44,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 // TODO rowsum_df
 public class RowsumFunctions {
 
-    @RBuiltin(name = "rowsum_matrix", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "g", "uniqueg", "snarm", "rn"})
+    @RBuiltin(name = "rowsum_matrix", kind = INTERNAL, parameterNames = {"x", "g", "uniqueg", "snarm", "rn"}, behavior = PURE)
     public abstract static class Rowsum extends RBuiltinNode {
 
         private final ConditionProfile typeProfile = ConditionProfile.createBinaryProfile();
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 973ab1d8d24b20fc25cfc83da64e1ed292e5d173..66144d8375b776d87c9216c5fbf13d608874aecb 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
@@ -10,8 +10,10 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.SUBSTITUTE;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.SUBSTITUTE;
 
 import java.util.Arrays;
 
@@ -40,12 +42,11 @@ import com.oracle.truffle.r.nodes.function.signature.CombineSignaturesNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -87,7 +88,7 @@ public abstract class S3DispatchFunctions extends RBuiltinNode {
         return result;
     }
 
-    @RBuiltin(name = "UseMethod", visibility = RVisibility.CUSTOM, kind = PRIMITIVE, parameterNames = {"generic", "object"})
+    @RBuiltin(name = "UseMethod", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {"generic", "object"}, behavior = COMPLEX)
     public abstract static class UseMethod extends S3DispatchFunctions {
 
         /*
@@ -172,7 +173,7 @@ public abstract class S3DispatchFunctions extends RBuiltinNode {
         }
     }
 
-    @RBuiltin(name = "NextMethod", visibility = RVisibility.CUSTOM, kind = SUBSTITUTE, parameterNames = {"generic", "object", "..."})
+    @RBuiltin(name = "NextMethod", visibility = CUSTOM, kind = SUBSTITUTE, parameterNames = {"generic", "object", "..."}, behavior = COMPLEX)
     public abstract static class NextMethod extends S3DispatchFunctions {
 
         @Child private LocalReadVariableNode rvnGroup = LocalReadVariableNode.create(RRuntime.R_DOT_GROUP, false);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sample.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sample.java
index 872ed5f4b67ccad3272e7486c6a2d3f46a2cdd25..5dfe6d00ea2500af2aa60793b1e393170a48f2b8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sample.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sample.java
@@ -14,16 +14,18 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.MODIFIES_STATE;
+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.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -31,7 +33,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.rng.RRNG;
 
-@RBuiltin(name = "sample", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "size", "replace", "prob"})
+@RBuiltin(name = "sample", kind = INTERNAL, parameterNames = {"x", "size", "replace", "prob"}, behavior = MODIFIES_STATE)
 public abstract class Sample extends RBuiltinNode {
     private final ConditionProfile sampleSizeProfile = ConditionProfile.createBinaryProfile();
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
index 98860d8bb4465026ff7c6c1b6f433c815c82ca29..049375ae56f6834addf7f3827751016107526c6f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
@@ -12,8 +12,18 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.charAt0;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.length;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lengthLte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lt;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.IOException;
 import java.util.LinkedList;
@@ -26,10 +36,10 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
@@ -43,16 +53,13 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.data.RString;
 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;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
-import com.sun.org.apache.bcel.internal.generic.INSTANCEOF;
 
-@SuppressWarnings("unused")
 @RBuiltin(name = "scan", kind = INTERNAL, parameterNames = {"file", "what", "nmax", "sep", "dec", "quote", "skip", "nlines", "na.strings", "flush", "fill", "strip.white", "quiet", "blank.lines.skip",
-                "multi.line", "comment.char", "allowEscapes", "encoding", "skipNull"})
+                "multi.line", "comment.char", "allowEscapes", "encoding", "skipNull"}, behavior = IO)
 public abstract class Scan extends RBuiltinNode {
 
     private static final int SCAN_BLOCKSIZE = 1000;
@@ -72,6 +79,7 @@ public abstract class Scan extends RBuiltinNode {
         return ((RAbstractVector) castVector.execute(value)).materialize();
     }
 
+    @SuppressWarnings("unused")
     private static class LocalData {
         RAbstractStringVector naStrings = null;
         boolean quiet = false;
@@ -133,6 +141,7 @@ public abstract class Scan extends RBuiltinNode {
         casts.arg("encoding").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
 
         casts.arg("skipNull").asLogicalVector().findFirst().notNA().map(toBoolean());
+
     }
 
     @Specialization
@@ -178,7 +187,6 @@ public abstract class Scan extends RBuiltinNode {
         // TODO: quite a few more things happen in GNU R around connections
         data.con = file;
 
-        Object result = RNull.instance;
         data.save = 0;
 
         try (RConnection openConn = data.con.forceOpen("r")) {
@@ -196,14 +204,6 @@ public abstract class Scan extends RBuiltinNode {
         }
     }
 
-    private static int firstElementOrNA(RAbstractIntVector nmaxVec) {
-        return nmaxVec.getLength() == 0 ? RRuntime.INT_NA : nmaxVec.getDataAt(0);
-    }
-
-    private static byte firstElementOrNA(RAbstractLogicalVector flushVec) {
-        return flushVec.getLength() == 0 ? RRuntime.LOGICAL_NA : flushVec.getDataAt(0);
-    }
-
     private static int getFirstQuoteInd(String str, char sepChar) {
         int quoteInd = str.indexOf(sepChar);
         if (quoteInd >= 0) {
@@ -317,7 +317,8 @@ public abstract class Scan extends RBuiltinNode {
         }
     }
 
-    private RVector scanFrame(RList what, int maxRecords, int maxLines, boolean flush, boolean fill, boolean stripWhite, boolean blSkip, boolean multiLine, LocalData data) throws IOException {
+    private RVector scanFrame(RList what, int maxRecords, int maxLines, boolean flush, boolean fill, @SuppressWarnings("unused") boolean stripWhite, boolean blSkip, boolean multiLine, LocalData data)
+                    throws IOException {
 
         int nc = what.getLength();
         if (nc == 0) {
@@ -439,7 +440,8 @@ public abstract class Scan extends RBuiltinNode {
     }
 
     @TruffleBoundary
-    private RVector scanVector(RAbstractVector what, int maxItems, int maxLines, boolean flush, boolean stripWhite, boolean blSkip, LocalData data) throws IOException {
+    private RVector scanVector(RAbstractVector what, int maxItems, int maxLines, @SuppressWarnings("unused") boolean flush, @SuppressWarnings("unused") boolean stripWhite, boolean blSkip,
+                    LocalData data) throws IOException {
         int blockSize = maxItems > 0 ? maxItems : SCAN_BLOCKSIZE;
         RVector vec = what.createEmptySameType(blockSize, RDataFactory.COMPLETE_VECTOR);
         naCheck.enable(true);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Seq.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Seq.java
index 8fd03a6015da6b90b7a1570c4459c0ee464fbbf5..4d9963247acf9d1ae12a76b1155dd7cf855a65d3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Seq.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Seq.java
@@ -22,16 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.SUBSTITUTE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+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.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleSequence;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -47,7 +48,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "seq.default", aliases = {"seq.int"}, kind = SUBSTITUTE, parameterNames = {"from", "to", "by", "length.out", "along.with"})
+@RBuiltin(name = "seq.default", aliases = {"seq.int"}, kind = SUBSTITUTE, parameterNames = {"from", "to", "by", "length.out", "along.with"}, behavior = PURE)
 // Implement in R, but seq.int is PRIMITIVE (and may have to contain most, if not all, of the code
 // below)
 @SuppressWarnings("unused")
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqAlong.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqAlong.java
index 0ad14b4b81b4f73cc157034adb1ca7383f986465..9029a2b50ca6e76be1e9df4257df29fb0bb865cc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqAlong.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqAlong.java
@@ -22,18 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
 import com.oracle.truffle.r.nodes.control.RLengthNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntSequence;
 
-@RBuiltin(name = "seq_along", kind = PRIMITIVE, parameterNames = {"along.with"})
+@RBuiltin(name = "seq_along", kind = PRIMITIVE, parameterNames = {"along.with"}, behavior = PURE)
 public abstract class SeqAlong extends RBuiltinNode {
 
     @Child private RLengthNode length = RLengthNodeGen.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqLen.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqLen.java
index 2f669612d175ba831f651959dff63e96b482a92d..aa7a9ac0e0cc459b0a0c3fa3842c411a30b68d34 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqLen.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SeqLen.java
@@ -22,19 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+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.model.RAbstractIntVector;
 
-@RBuiltin(name = "seq_len", kind = PRIMITIVE, parameterNames = {"length.out"})
+@RBuiltin(name = "seq_len", kind = PRIMITIVE, parameterNames = {"length.out"}, behavior = PURE)
 public abstract class SeqLen extends RBuiltinNode {
 
     private final BranchProfile lengthProblem = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java
index c7afe2281bbd686d23309c2d9b276e1005dd5611..a4b3465b43cfc0d321fae8473efa1d4092d56249 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SerializeFunctions.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.IOException;
 
@@ -30,11 +32,10 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -81,7 +82,7 @@ public class SerializeFunctions {
         }
     }
 
-    @RBuiltin(name = "unserializeFromConn", kind = INTERNAL, parameterNames = {"conn", "refhook"})
+    @RBuiltin(name = "unserializeFromConn", kind = INTERNAL, parameterNames = {"conn", "refhook"}, behavior = IO)
     public abstract static class UnserializeFromConn extends Adapter {
         @Specialization
         protected Object doUnserializeFromConn(RConnection conn, @SuppressWarnings("unused") RNull refhook) {
@@ -95,7 +96,7 @@ public class SerializeFunctions {
         }
     }
 
-    @RBuiltin(name = "serializeToConn", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"object", "conn", "ascii", "version", "refhook"})
+    @RBuiltin(name = "serializeToConn", visibility = OFF, kind = INTERNAL, parameterNames = {"object", "conn", "ascii", "version", "refhook"}, behavior = IO)
     public abstract static class SerializeToConn extends Adapter {
         @Specialization
         protected Object doSerializeToConn(Object object, RConnection conn, byte asciiLogical, RNull version, RNull refhook) {
@@ -111,7 +112,7 @@ public class SerializeFunctions {
         }
     }
 
-    @RBuiltin(name = "unserialize", kind = INTERNAL, parameterNames = {"conn", "refhook"})
+    @RBuiltin(name = "unserialize", kind = INTERNAL, parameterNames = {"conn", "refhook"}, behavior = IO)
     public abstract static class Unserialize extends Adapter {
         @Specialization
         protected Object unSerialize(RConnection conn, @SuppressWarnings("unused") RNull refhook) {
@@ -124,7 +125,7 @@ public class SerializeFunctions {
         }
     }
 
-    @RBuiltin(name = "serialize", kind = INTERNAL, parameterNames = {"object", "conn", "type", "version", "refhook"})
+    @RBuiltin(name = "serialize", kind = INTERNAL, parameterNames = {"object", "conn", "type", "version", "refhook"}, behavior = IO)
     public abstract static class Serialize extends Adapter {
         @Specialization
         protected Object serialize(Object object, RConnection conn, int type, RNull version, RNull refhook) {
@@ -145,7 +146,7 @@ public class SerializeFunctions {
         }
     }
 
-    @RBuiltin(name = "serializeb", kind = INTERNAL, parameterNames = {"object", "conn", "xdr", "version", "refhook"})
+    @RBuiltin(name = "serializeb", kind = INTERNAL, parameterNames = {"object", "conn", "xdr", "version", "refhook"}, behavior = IO)
     public abstract static class SerializeB extends Adapter {
         @Specialization
         protected Object serializeB(Object object, RConnection conn, byte xdrLogical, RNull version, RNull refhook) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetS4Object.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetS4Object.java
index 0f920cf0d5ee55201c1479781edf38c5deda9223..552c317a2903d1f017404e1a35629a12bb6b1ea2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetS4Object.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetS4Object.java
@@ -22,23 +22,26 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.objects.AsS4;
 import com.oracle.truffle.r.nodes.objects.AsS4NodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RSequence;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 
-@RBuiltin(name = "setS4Object", kind = INTERNAL, parameterNames = {"object", "flag", "complete"})
+@RBuiltin(name = "setS4Object", kind = INTERNAL, parameterNames = {"object", "flag", "complete"}, behavior = PURE)
 public abstract class SetS4Object extends RBuiltinNode {
 
     @Child private AsS4 asS4 = AsS4NodeGen.create();
@@ -61,13 +64,14 @@ public abstract class SetS4Object extends RBuiltinNode {
     }
 
     @Specialization
+    @TruffleBoundary
     protected RNull asS4(RNull object, RAbstractLogicalVector flagVec, RAbstractIntVector completeVec) {
         boolean flag = checkArgs(flagVec, completeVec);
         if (flag) {
-            object.setS4();
+            RContext.getInstance().setNullS4Object(true);
         } else {
-            boolean wasS4 = object.isS4();
-            object.unsetS4();
+            boolean wasS4 = RContext.getInstance().isNullS4Object();
+            RContext.getInstance().setNullS4Object(false);
             if (wasS4) {
                 throw RError.error(this, RError.Message.GENERIC, "object of class \"NULL\" does not correspond to a valid S3 object");
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetTimeLimit.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetTimeLimit.java
index ccf6bb98a9b6389ab65dfc261ab3c72e309a30a1..4eb04d2dfb03d1510bf49fd5581e53c0a29c5027 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetTimeLimit.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SetTimeLimit.java
@@ -22,14 +22,15 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RNull;
 
-@RBuiltin(name = "setTimeLimit", kind = INTERNAL, parameterNames = {"cpu", "elapsed", "transient"})
+@RBuiltin(name = "setTimeLimit", kind = INTERNAL, parameterNames = {"cpu", "elapsed", "transient"}, behavior = COMPLEX)
 public abstract class SetTimeLimit extends RBuiltinNode {
 
     @SuppressWarnings("unused")
@@ -39,5 +40,4 @@ public abstract class SetTimeLimit extends RBuiltinNode {
         // do not support
         return RNull.instance;
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Setwd.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Setwd.java
index f7eecbebc24fa63dc66e7d88322d5b5cc0740c61..b1d5e64be3a77ba5cc703c5ef6375eeb816e739f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Setwd.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Setwd.java
@@ -22,20 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
-@RBuiltin(name = "setwd", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = "path")
+@RBuiltin(name = "setwd", visibility = OFF, kind = INTERNAL, parameterNames = "path", behavior = IO)
 public abstract class Setwd extends RBuiltinNode {
 
     @Specialization
@@ -57,6 +58,7 @@ public abstract class Setwd extends RBuiltinNode {
     }
 
     @Fallback
+    @TruffleBoundary
     protected Object setwd(@SuppressWarnings("unused") Object path) {
         throw RError.error(this, RError.Message.CHAR_ARGUMENT);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ShortRowNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ShortRowNames.java
index 25d30eb770a9f8ad1357577295e01b40aa67f0ca..ec1018bb70b44465fc6981f1f33a0d92d2dadd39 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ShortRowNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ShortRowNames.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
@@ -31,16 +32,16 @@ import com.oracle.truffle.api.profiles.IntValueProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "shortRowNames", kind = INTERNAL, parameterNames = {"x", "type"})
+@RBuiltin(name = "shortRowNames", kind = INTERNAL, parameterNames = {"x", "type"}, behavior = PURE)
 public abstract class ShortRowNames extends RBuiltinNode {
 
     private final BranchProfile naValueMet = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Signif.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Signif.java
index e686e2646a094fe3068c2019cb24148a0e2525e2..f2662f5345c56f7e5b900898d61c5d9f21f787d2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Signif.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Signif.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+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;
 
 import java.math.BigDecimal;
 import java.math.MathContext;
@@ -34,9 +36,8 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -45,7 +46,7 @@ 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;
 
-@RBuiltin(name = "signif", kind = PRIMITIVE, parameterNames = {"x", "digits"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+@RBuiltin(name = "signif", kind = PRIMITIVE, parameterNames = {"x", "digits"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
 public abstract class Signif extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SinkFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SinkFunctions.java
index f4aeb2320924cdfef82813048134081be6a052a2..467dccf6b4fe4ca36769573da7b07265cd6f3e7f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SinkFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SinkFunctions.java
@@ -22,24 +22,26 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import java.io.IOException;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
 public class SinkFunctions {
-    @RBuiltin(name = "sink", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"file", "closeOnExit", "isMessage", "split"})
+    @RBuiltin(name = "sink", visibility = OFF, kind = INTERNAL, parameterNames = {"file", "closeOnExit", "isMessage", "split"}, behavior = IO)
     public abstract static class Sink extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -70,7 +72,7 @@ public class SinkFunctions {
         }
     }
 
-    @RBuiltin(name = "sink.number", kind = RBuiltinKind.INTERNAL, parameterNames = {"type"})
+    @RBuiltin(name = "sink.number", kind = INTERNAL, parameterNames = {"type"}, behavior = IO)
     public abstract static class SinkNumber extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java
index 0b496aeb485667bdabea0a7bcf757867b953d48d..c916d144022f5e7e3d863cf937e31b466d3735bc 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java
@@ -13,7 +13,13 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.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.ValueProfile;
 import com.oracle.truffle.r.nodes.access.AccessSlotNode;
 import com.oracle.truffle.r.nodes.access.AccessSlotNodeGen;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
@@ -21,13 +27,12 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.WrapArgumentNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 
-@RBuiltin(name = "@", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, nonEvalArgs = 1)
+@RBuiltin(name = "@", kind = PRIMITIVE, parameterNames = {"", ""}, nonEvalArgs = 1, behavior = COMPLEX)
 public abstract class Slot extends RBuiltinNode {
 
     @Child private AccessSlotNode accessSlotNode = AccessSlotNodeGen.create(true, null, null);
@@ -55,12 +60,14 @@ public abstract class Slot extends RBuiltinNode {
                 return ((ReadVariableNode) rep).getIdentifier();
             }
         }
+        CompilerDirectives.transferToInterpreter();
         throw RError.error(this, RError.Message.GENERIC, "invalid type or length for slot name");
     }
 
     @Specialization
-    protected Object getSlot(Object object, Object nameObj) {
-        String name = getName(nameObj);
+    protected Object getSlot(Object object, Object nameObj,
+                    @Cached("createClassProfile()") ValueProfile nameObjProfile) {
+        String name = getName(nameObjProfile.profile(nameObj));
         assert name == name.intern();
         return accessSlotNode.executeAccess(object, name);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SortFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SortFunctions.java
index 7704e3a4e1d132ba52c8baba5aa34e0ac8e0b392..19b7f6dab17953060c8f36a21ae44255191a1daa 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SortFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SortFunctions.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Arrays;
 import java.util.Collections;
@@ -31,9 +32,9 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -133,7 +134,7 @@ public class SortFunctions {
      *
      * N.B. The R code strips out {@code NA} and {@code NaN} values before calling the builtin.
      */
-    @RBuiltin(name = "sort", kind = INTERNAL, parameterNames = {"x", "decreasing"})
+    @RBuiltin(name = "sort", kind = INTERNAL, parameterNames = {"x", "decreasing"}, behavior = PURE)
     public abstract static class Sort extends Adapter {
         @Specialization
         protected RDoubleVector sort(RAbstractDoubleVector vec, byte decreasing) {
@@ -162,7 +163,7 @@ public class SortFunctions {
         }
     }
 
-    @RBuiltin(name = "qsort", kind = INTERNAL, parameterNames = {"x", "decreasing"})
+    @RBuiltin(name = "qsort", kind = INTERNAL, parameterNames = {"x", "decreasing"}, behavior = PURE)
     public abstract static class QSort extends Adapter {
 
         @Specialization
@@ -176,7 +177,7 @@ public class SortFunctions {
         }
     }
 
-    @RBuiltin(name = "psort", kind = INTERNAL, parameterNames = {"x", "partial"})
+    @RBuiltin(name = "psort", kind = INTERNAL, parameterNames = {"x", "partial"}, behavior = PURE)
     public abstract static class PartialSort extends Adapter {
         @SuppressWarnings("unused")
         @Specialization
@@ -209,7 +210,7 @@ public class SortFunctions {
         }
     }
 
-    @RBuiltin(name = "radixsort", kind = INTERNAL, parameterNames = {"zz", "na.last", "decreasing"})
+    @RBuiltin(name = "radixsort", kind = INTERNAL, parameterNames = {"zz", "na.last", "decreasing"}, behavior = PURE)
     public abstract static class RadixSort extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Split.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Split.java
index f164b80c17f51d31e580903dec190c8928ef711f..2a14a762121160743388f2a4e1680e9b6e48c09b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Split.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Split.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Arrays;
 
@@ -30,8 +31,8 @@ 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.helpers.RFactorNodes;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 
@@ -42,7 +43,7 @@ import com.oracle.truffle.r.runtime.data.model.*;
  * TODO Can we find a way to efficiently write the specializations as generics? The code is
  * identical except for the argument type.
  */
-@RBuiltin(name = "split", kind = INTERNAL, parameterNames = {"x", "f"})
+@RBuiltin(name = "split", kind = INTERNAL, parameterNames = {"x", "f"}, behavior = PURE)
 public abstract class Split extends RBuiltinNode {
 
     @Child private RFactorNodes.GetLevels getLevelNode = new RFactorNodes.GetLevels();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sprintf.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sprintf.java
index 21bbbae3be0fc1570d615ac1af8044f6aab2b2e7..72aa98773eacecc93bb2b6f98fdc3bca96ad5e1c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sprintf.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sprintf.java
@@ -22,18 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.Locale;
 
 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.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -43,10 +43,10 @@ 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;
 
-@RBuiltin(name = "sprintf", kind = INTERNAL, parameterNames = {"fmt", "..."})
+@RBuiltin(name = "sprintf", kind = INTERNAL, parameterNames = {"fmt", "..."}, behavior = PURE)
 public abstract class Sprintf extends RBuiltinNode {
 
-    public abstract Object executeObject(VirtualFrame frame, String fmt, Object args);
+    public abstract Object executeObject(String fmt, Object args);
 
     @Child private Sprintf sprintfRecursive;
 
@@ -215,12 +215,12 @@ public abstract class Sprintf extends RBuiltinNode {
     }
 
     @Specialization(guards = "oneElement(args)")
-    protected Object sprintfOneElement(VirtualFrame frame, String fmt, RArgsValuesAndNames args) {
+    protected Object sprintfOneElement(String fmt, RArgsValuesAndNames args) {
         if (sprintfRecursive == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             sprintfRecursive = insert(SprintfNodeGen.create(null));
         }
-        return sprintfRecursive.executeObject(frame, fmt, args.getArgument(0));
+        return sprintfRecursive.executeObject(fmt, args.getArgument(0));
     }
 
     @Specialization(guards = {"!oneElement(args)"})
@@ -240,18 +240,18 @@ public abstract class Sprintf extends RBuiltinNode {
     }
 
     @Specialization(guards = {"oneElement(args)", "fmtLengthOne(fmt)"})
-    protected Object sprintfOneElement(VirtualFrame frame, RAbstractStringVector fmt, RArgsValuesAndNames args) {
-        return sprintfOneElement(frame, fmt.getDataAt(0), args);
+    protected Object sprintfOneElement(RAbstractStringVector fmt, RArgsValuesAndNames args) {
+        return sprintfOneElement(fmt.getDataAt(0), args);
     }
 
     @Specialization(guards = {"oneElement(args)", "!fmtLengthOne(fmt)"})
-    protected Object sprintf(VirtualFrame frame, RAbstractStringVector fmt, RArgsValuesAndNames args) {
+    protected Object sprintf2(RAbstractStringVector fmt, RArgsValuesAndNames args) {
         if (fmt.getLength() == 0) {
             return RDataFactory.createEmptyStringVector();
         } else {
             String[] data = new String[fmt.getLength()];
             for (int i = 0; i < data.length; i++) {
-                Object formattedObj = sprintfOneElement(frame, fmt.getDataAt(i), args);
+                Object formattedObj = sprintfOneElement(fmt.getDataAt(i), args);
                 if (formattedObj instanceof String) {
                     data[i] = (String) formattedObj;
                 } else {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java
index 9cd64c59070f6381542bbb808e9b9f1421f867fd..753985e79b18a00b1517e436a7c09c8ff6849cf5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/StandardGeneric.java
@@ -12,7 +12,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -24,20 +26,18 @@ import com.oracle.truffle.r.nodes.access.variables.LocalReadVariableNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.attributes.AttributeAccess;
 import com.oracle.truffle.r.nodes.attributes.AttributeAccessNodeGen;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.objects.CollectGenericArgumentsNode;
 import com.oracle.truffle.r.nodes.objects.CollectGenericArgumentsNodeGen;
 import com.oracle.truffle.r.nodes.objects.DispatchGeneric;
 import com.oracle.truffle.r.nodes.objects.DispatchGenericNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastIntegerScalarNode;
-import com.oracle.truffle.r.nodes.unary.CastStringScalarNode;
-import com.oracle.truffle.r.nodes.unary.CastStringScalarNodeGen;
+import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
@@ -51,21 +51,29 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
 // transcribed from src/main/objects.c
-@RBuiltin(name = "standardGeneric", visibility = RVisibility.CUSTOM, kind = PRIMITIVE, parameterNames = {"f", "fdef"})
+@RBuiltin(name = "standardGeneric", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {"f", "fdef"}, behavior = COMPLEX)
 public abstract class StandardGeneric extends RBuiltinNode {
 
     // TODO: for now, we always go through generic dispatch
 
     @Child private AttributeAccess genericAttrAccess;
     @Child private FrameFunctions.SysFunction sysFunction;
-    @Child private CastStringScalarNode castStringScalar = CastStringScalarNodeGen.create();
     @Child private LocalReadVariableNode readMTableFirst = LocalReadVariableNode.create(RRuntime.DOT_ALL_MTABLE, true);
     @Child private LocalReadVariableNode readSigLength = LocalReadVariableNode.create(RRuntime.DOT_SIG_LENGTH, true);
     @Child private LocalReadVariableNode readSigARgs = LocalReadVariableNode.create(RRuntime.DOT_SIG_ARGS, true);
-    @Child private CastIntegerScalarNode castIntScalar = CastIntegerScalarNode.create();
     @Child private CollectGenericArgumentsNode collectArgumentsNode;
     @Child private DispatchGeneric dispatchGeneric = DispatchGenericNodeGen.create();
 
+    @Child private CastNode castIntScalar;
+    @Child private CastNode castStringScalar;
+    {
+        CastBuilder builder = new CastBuilder();
+        builder.arg(0).asIntegerVector().findFirst(RRuntime.INT_NA);
+        builder.arg(1).asStringVector().findFirst(RRuntime.STRING_NA);
+        castIntScalar = builder.getCasts()[0];
+        castStringScalar = builder.getCasts()[1];
+    }
+
     private final BranchProfile noGenFunFound = BranchProfile.create();
     private final ConditionProfile sameNamesProfile = ConditionProfile.createBinaryProfile();
 
@@ -80,11 +88,11 @@ public abstract class StandardGeneric extends RBuiltinNode {
             // mtable can be null the first time around, but the following call will initialize it
             // and this slow path should not be executed again
             REnvironment methodsEnv = REnvironment.getRegisteredNamespace("methods");
-            RFunction currentFunction = ReadVariableNode.lookupFunction(".getMethodsTable", methodsEnv.getFrame(), true);
-            mtable = (REnvironment) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, getOriginalCall()), fdef);
+            RFunction currentFunction = ReadVariableNode.lookupFunction(".getMethodsTable", methodsEnv.getFrame(), true, true);
+            mtable = (REnvironment) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, getOriginalCall()), null, fdef);
         }
         RList sigArgs = (RList) readSigARgs.execute(null, fnFrame);
-        int sigLength = castIntScalar.executeInt(readSigLength.execute(null, fnFrame));
+        int sigLength = (int) castIntScalar.execute(readSigLength.execute(null, fnFrame));
         if (sigLength > sigArgs.getLength()) {
             throw RError.error(this, RError.Message.GENERIC, "'.SigArgs' is shorter than '.SigLength' says it should be");
         }
@@ -118,7 +126,7 @@ public abstract class StandardGeneric extends RBuiltinNode {
             noGenFunFound.enter();
             return null;
         }
-        String gen = castStringScalar.executeString(genObj);
+        String gen = (String) castStringScalar.execute(genObj);
         if (sameNamesProfile.profile(gen == fname)) {
             return stdGenericInternal(frame, fVec, fn);
         } else {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Stop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Stop.java
index 31e919e198b61c8c7f486f9edb086e9c24adb3c8..e8a0c744fb4d30711479e6942e1ad9f3c1b142e7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Stop.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Stop.java
@@ -22,16 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.model.RAbstractStringVector;
 
-@RBuiltin(name = "stop", kind = RBuiltinKind.INTERNAL, parameterNames = {"call.", "message"})
+@RBuiltin(name = "stop", kind = INTERNAL, parameterNames = {"call.", "message"}, behavior = COMPLEX)
 public abstract class Stop extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtoi.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtoi.java
index 31e15932128348e1b91fed9b4544ba17ad3011d5..cbca860f401407a3c234193721821ec4f3a043b1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtoi.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtoi.java
@@ -22,19 +22,29 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
-@RBuiltin(name = "strtoi", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "base"})
+@RBuiltin(name = "strtoi", kind = INTERNAL, parameterNames = {"x", "base"}, behavior = PURE)
 public abstract class Strtoi extends RBuiltinNode {
+
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        // TODO: not sure if the behavior is 100% compliant
+        casts.arg("base").asIntegerVector().findFirst();
+    }
+
     @Specialization
     @TruffleBoundary
     protected RIntVector doStrtoi(RAbstractStringVector vec, int baseArg) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtrim.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtrim.java
index 7c9a973bf95014d9de6a4f5ec8f8845b82bc7f50..6124f541a90c1eeefcf644560379ea0a185ed8ba 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtrim.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Strtrim.java
@@ -12,69 +12,68 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import com.oracle.truffle.api.dsl.Fallback;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
+import java.util.function.BiFunction;
+
+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.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.nodes.builtin.base.ToLowerOrUpper.StringMapNode;
 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.RAttributeProfiles;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "strtrim", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "width"})
+@RBuiltin(name = "strtrim", kind = INTERNAL, parameterNames = {"x", "width"}, behavior = PURE)
 public abstract class Strtrim extends RBuiltinNode {
 
-    private RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.toInteger(1);
+        casts.arg("x").mustBe(Predef.stringValue(), Message.REQUIRES_CHAR_VECTOR, "strtrim()").asStringVector(true, true, true);
+        casts.arg("width").asIntegerVector();
     }
 
     @Specialization
-    protected RStringVector srtrim(RAbstractStringVector x, RAbstractIntVector width) {
+    protected RStringVector srtrim(RAbstractStringVector x, RAbstractIntVector width,
+                    @Cached("create()") StringMapNode mapNode,
+                    @Cached("createBinaryProfile()") ConditionProfile fitsProfile) {
         int len = x.getLength();
         int nw = width.getLength();
         if (nw == 0 || nw < len && (len % nw != 0)) {
+            CompilerDirectives.transferToInterpreter();
             throw RError.error(this, RError.Message.INVALID_ARGUMENT, "width");
         }
         for (int i = 0; i < nw; i++) {
-            int widthi = width.getDataAt(i);
-            if (widthi == RRuntime.INT_NA || widthi < 0) {
+            assert RRuntime.INT_NA < 0; // check for NA folded into < 0
+            if (width.getDataAt(i) < 0) {
+                CompilerDirectives.transferToInterpreter();
                 throw RError.error(this, RError.Message.INVALID_ARGUMENT, "width");
             }
         }
-        String[] data = new String[len];
-        boolean complete = RDataFactory.COMPLETE_VECTOR;
-        for (int i = 0; i < len; i++) {
-            String element = x.getDataAt(i);
-            if (RRuntime.isNA(element)) {
-                data[i] = element;
-                complete = RDataFactory.INCOMPLETE_VECTOR;
-                continue;
-            }
+        BiFunction<String, Integer, String> function = (element, i) -> {
             // TODO multibyte character handling
             int w = width.getDataAt(i % nw);
-            if (w > element.length()) {
-                data[i] = element;
+            if (fitsProfile.profile(w >= element.length())) {
+                return element;
             } else {
-                data[i] = element.substring(0, w);
+                return substring(element, w);
             }
-        }
-        RStringVector result = RDataFactory.createStringVector(data, complete);
-        result.copyAttributesFrom(attrProfiles, x);
-        return result;
+        };
+        return mapNode.apply(x, function);
     }
 
-    @SuppressWarnings("unused")
-    @Fallback
-    RStringVector strtrim(Object x, Object width) {
-        throw RError.error(this, RError.Message.REQUIRES_CHAR_VECTOR, "strtrim()");
+    @TruffleBoundary
+    private static String substring(String element, int w) {
+        return element.substring(0, w);
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
index 5ae5bca081361bd2e469157dd89df08db00d4577..9fa98073c8dea0ec9ce5f7b26fc42f1c4efd0405 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substitute.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Cached;
@@ -32,9 +33,9 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.control.IfNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RSubstitute;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -43,25 +44,25 @@ import com.oracle.truffle.r.runtime.data.RPromise;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "substitute", kind = PRIMITIVE, parameterNames = {"expr", "env"}, nonEvalArgs = 0)
+@RBuiltin(name = "substitute", kind = PRIMITIVE, parameterNames = {"expr", "env"}, nonEvalArgs = 0, behavior = COMPLEX)
 public abstract class Substitute extends RBuiltinNode {
 
     @Child private Quote quote;
 
     @Specialization
     protected Object doSubstitute(VirtualFrame frame, RPromise expr, @SuppressWarnings("unused") RMissing envMissing) {
-        return doSubstituteWithEnv(frame, expr, REnvironment.frameToEnvironment(frame.materialize()));
+        return doSubstituteWithEnv(expr, REnvironment.frameToEnvironment(frame.materialize()));
     }
 
     @Specialization
-    protected Object doSubstitute(VirtualFrame frame, RPromise expr, REnvironment env) {
-        return doSubstituteWithEnv(frame, expr, env);
+    protected Object doSubstitute(RPromise expr, REnvironment env) {
+        return doSubstituteWithEnv(expr, env);
     }
 
     @Specialization
-    protected Object doSubstitute(VirtualFrame frame, RPromise expr, RList list, //
+    protected Object doSubstitute(RPromise expr, RList list,
                     @Cached("create()") RAttributeProfiles attrProfiles) {
-        return doSubstituteWithEnv(frame, expr, REnvironment.createFromList(attrProfiles, list, REnvironment.baseEnv()));
+        return doSubstituteWithEnv(expr, REnvironment.createFromList(attrProfiles, list, REnvironment.baseEnv()));
     }
 
     @Fallback
@@ -76,14 +77,13 @@ public abstract class Substitute extends RBuiltinNode {
      * language element). E.g. {@link IfNode} is a special case because it is not (currently)
      * represented as a function, as are several other nodes.
      *
-     * @param frame
      * @param expr
      * @param env {@code null} if the {@code env} argument was {@code RMissing} to avoid always
      *            materializing the current frame.
      * @return in general an {@link RLanguage} instance, but simple cases could be a constant value
      *         or {@link RSymbol}
      */
-    private Object doSubstituteWithEnv(VirtualFrame frame, RPromise expr, REnvironment env) {
+    private Object doSubstituteWithEnv(RPromise expr, REnvironment env) {
         // In the global environment, substitute behaves like quote
         // TODO It may be too early to do this check, GnuR doesn't work this way (re promises)
         if (env == REnvironment.globalEnv()) {
@@ -91,7 +91,7 @@ public abstract class Substitute extends RBuiltinNode {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
                 quote = insert(QuoteNodeGen.create(null));
             }
-            return quote.execute(frame, expr);
+            return quote.execute(expr);
         }
 
         // The "expr" promise comes from the no-evalarg aspect of the builtin,
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substr.java
index 72eed00d2c1ed5f57b957550ebfec86b47ee87d6..eec79076483a486ef103eaa62fbfc9f3f9a28651 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Substr.java
@@ -22,17 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 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.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -40,7 +41,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "substr", kind = INTERNAL, parameterNames = {"x", "start", "stop"})
+@RBuiltin(name = "substr", kind = INTERNAL, parameterNames = {"x", "start", "stop"}, behavior = PURE)
 public abstract class Substr extends RBuiltinNode {
     private final NACheck na = NACheck.create();
     private final BranchProfile everSeenIllegalRange = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sum.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sum.java
index 3fcf179531f6d21fbb24da40f4a56ea8fd657a63..c2ad959b6031e5fc4b0781633a844dd8cb96f458 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sum.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Sum.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RDispatch.SUMMARY_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -31,16 +33,15 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNode.ReduceSemantics;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticReduceNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.ops.BinaryArithmetic;
 
 /**
  * Sum has combine semantics (TBD: exactly?) and uses a reduce operation on the resulting array.
  */
-@RBuiltin(name = "sum", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = RDispatch.SUMMARY_GROUP_GENERIC)
+@RBuiltin(name = "sum", kind = PRIMITIVE, parameterNames = {"...", "na.rm"}, dispatch = SUMMARY_GROUP_GENERIC, behavior = PURE)
 public abstract class Sum extends RBuiltinNode {
 
     private static final ReduceSemantics semantics = new ReduceSemantics(0, 0.0, true, null, null, true, false);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java
index 3ea9d2baa274254db84205eaa7b026066606dde9..a787025f2617d7a6391e07ee7ebdecd662bc5ad3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Switch.java
@@ -11,7 +11,9 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -24,10 +26,9 @@ import com.oracle.truffle.r.nodes.function.RMissingHelper;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -43,7 +44,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
  * {@link PromiseCheckHelperNode}.
  *
  */
-@RBuiltin(name = "switch", visibility = RVisibility.CUSTOM, kind = PRIMITIVE, parameterNames = {"EXPR", "..."}, nonEvalArgs = 1)
+@RBuiltin(name = "switch", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {"EXPR", "..."}, nonEvalArgs = 1, behavior = COMPLEX)
 public abstract class Switch extends RBuiltinNode {
     @Child private CastIntegerNode castIntNode;
     @Child private PromiseCheckHelperNode promiseHelper = new PromiseCheckHelperNode();
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 f45ab5df1734c78024f98df6ca0c7a02ef6180b9..c9ec3cf691058f7a0c9b720677fd0a57c8dd58b9 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
@@ -22,7 +22,13 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.MODIFIES_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -38,12 +44,11 @@ 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.RBuiltin;
 import com.oracle.truffle.r.runtime.REnvVars;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -57,7 +62,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 
 public class SysFunctions {
 
-    @RBuiltin(name = "Sys.getpid", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "Sys.getpid", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class SysGetpid extends RBuiltinNode {
 
         @Specialization
@@ -68,7 +73,7 @@ public class SysFunctions {
         }
     }
 
-    @RBuiltin(name = "Sys.getenv", kind = INTERNAL, parameterNames = {"x", "unset"})
+    @RBuiltin(name = "Sys.getenv", kind = INTERNAL, parameterNames = {"x", "unset"}, behavior = READS_STATE)
     public abstract static class SysGetenv extends RBuiltinNode {
         private final ConditionProfile zeroLengthProfile = ConditionProfile.createBinaryProfile();
 
@@ -123,7 +128,7 @@ public class SysFunctions {
         private static final String LOADNAMESPACE = "loadNamespace";
 
         protected void checkNSLoad(VirtualFrame frame, RAbstractStringVector names, RAbstractStringVector values, boolean setting) {
-            if (names.getLength() == 1 && names.getDataAt(0).equals(NS_LOAD)) {
+            if (names.getLength() == 1 && NS_LOAD.equals(names.getDataAt(0))) {
                 Frame caller = Utils.getCallerFrame(frame, FrameAccess.READ_ONLY);
                 RFunction func = RArguments.getFunction(caller);
                 if (func.toString().equals(LOADNAMESPACE)) {
@@ -140,7 +145,7 @@ public class SysFunctions {
         }
     }
 
-    @RBuiltin(name = "Sys.setenv", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"nm", "values"})
+    @RBuiltin(name = "Sys.setenv", visibility = OFF, kind = INTERNAL, parameterNames = {"nm", "values"}, behavior = MODIFIES_STATE)
     public abstract static class SysSetEnv extends LoadNamespaceAdapter {
 
         @Specialization
@@ -161,7 +166,7 @@ public class SysFunctions {
         }
     }
 
-    @RBuiltin(name = "Sys.unsetenv", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"x"})
+    @RBuiltin(name = "Sys.unsetenv", visibility = OFF, kind = INTERNAL, parameterNames = {"x"}, behavior = READS_STATE)
     public abstract static class SysUnSetEnv extends LoadNamespaceAdapter {
 
         @Specialization
@@ -182,7 +187,7 @@ public class SysFunctions {
         }
     }
 
-    @RBuiltin(name = "Sys.sleep", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"time"})
+    @RBuiltin(name = "Sys.sleep", visibility = OFF, kind = INTERNAL, parameterNames = {"time"}, behavior = COMPLEX)
     public abstract static class SysSleep extends RBuiltinNode {
 
         @Specialization
@@ -242,7 +247,7 @@ public class SysFunctions {
     /**
      * TODO: Handle ~ expansion which is not handled by POSIX.
      */
-    @RBuiltin(name = "Sys.readlink", kind = INTERNAL, parameterNames = {"paths"})
+    @RBuiltin(name = "Sys.readlink", kind = INTERNAL, parameterNames = {"paths"}, behavior = IO)
     public abstract static class SysReadlink extends RBuiltinNode {
 
         @Specialization
@@ -292,7 +297,7 @@ public class SysFunctions {
     }
 
     // TODO implement
-    @RBuiltin(name = "Sys.chmod", visibility = RVisibility.OFF, kind = INTERNAL, parameterNames = {"paths", "octmode", "use_umask"})
+    @RBuiltin(name = "Sys.chmod", visibility = OFF, kind = INTERNAL, parameterNames = {"paths", "octmode", "use_umask"}, behavior = IO)
     public abstract static class SysChmod extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -311,7 +316,7 @@ public class SysFunctions {
     }
 
     // TODO implement
-    @RBuiltin(name = "Sys.umask", visibility = RVisibility.CUSTOM, kind = INTERNAL, parameterNames = {"octmode"})
+    @RBuiltin(name = "Sys.umask", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"octmode"}, behavior = COMPLEX)
     public abstract static class SysUmask extends RBuiltinNode {
         @SuppressWarnings("unused")
         @Specialization
@@ -321,7 +326,7 @@ public class SysFunctions {
         }
     }
 
-    @RBuiltin(name = "Sys.time", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "Sys.time", kind = INTERNAL, parameterNames = {}, behavior = IO)
     public abstract static class SysTime extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -330,7 +335,7 @@ public class SysFunctions {
         }
     }
 
-    @RBuiltin(name = "Sys.info", kind = INTERNAL, parameterNames = {})
+    @RBuiltin(name = "Sys.info", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class SysInfo extends RBuiltinNode {
         private static final String[] NAMES = new String[]{"sysname", "release", "version", "nodename", "machine", "login", "user", "effective_user"};
         private static final RStringVector NAMES_ATTR = RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR);
@@ -354,7 +359,7 @@ public class SysFunctions {
         }
     }
 
-    @RBuiltin(name = "Sys.glob", kind = INTERNAL, parameterNames = {"paths", "dirmask"})
+    @RBuiltin(name = "Sys.glob", kind = INTERNAL, parameterNames = {"paths", "dirmask"}, behavior = IO)
     public abstract static class SysGlob extends RBuiltinNode {
 
         @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java
index d333971ad5e9b0da68b2fd09d6e1672e656b867c..0590d2becca1a0bc2cf94f3bafb57a962f85df7c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java
@@ -22,6 +22,10 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ProcessBuilder.Redirect;
@@ -31,16 +35,14 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.ProcessOutputManager;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "system", visibility = RVisibility.CUSTOM, kind = RBuiltinKind.INTERNAL, parameterNames = {"command", "intern"})
+@RBuiltin(name = "system", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"command", "intern"}, behavior = COMPLEX)
 public abstract class SystemFunction extends RBuiltinNode {
     @Specialization
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java
index 6323b0d49048993dc1c26d24445c7596c3ecda49..764eed17c3384b043879dbbfb4e20e350ad72ecf 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Tabulate.java
@@ -10,6 +10,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -17,15 +20,14 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
-@RBuiltin(name = "tabulate", kind = RBuiltinKind.INTERNAL, parameterNames = {"bin", "nbins"})
+@RBuiltin(name = "tabulate", kind = INTERNAL, parameterNames = {"bin", "nbins"}, behavior = PURE)
 public abstract class Tabulate extends RBuiltinNode {
 
     private final BranchProfile errorProfile = BranchProfile.create();
@@ -33,7 +35,9 @@ public abstract class Tabulate extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.toInteger(1);
+        // TODO: not sure if the behavior is 100% compliant
+        casts.arg("bin").asIntegerVector();
+        casts.arg("nbins").asIntegerVector().findFirst();
     }
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TempDir.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TempDir.java
index 8267a5483b7a4a4876f8387a16fc6b357560e8c0..022c03adde708e6e668cacd60f45ead87c70599b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TempDir.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TempDir.java
@@ -22,15 +22,16 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.TempPathName;
+import com.oracle.truffle.r.runtime.builtins.RBehavior;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 
-@RBuiltin(name = "tempdir", kind = INTERNAL, parameterNames = {})
+@RBuiltin(name = "tempdir", kind = INTERNAL, parameterNames = {}, behavior = RBehavior.READS_STATE)
 public abstract class TempDir extends RBuiltinNode {
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TempFile.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TempFile.java
index cd1126d330d92ba43f80615580dea9f04e6cd88a..d3318f5e976edf3e976ab543851e46e675dbb1af 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TempFile.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TempFile.java
@@ -22,20 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.TempPathName;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "tempfile", kind = INTERNAL, parameterNames = {"pattern", "tempdir", "fileext"})
+@RBuiltin(name = "tempfile", kind = INTERNAL, parameterNames = {"pattern", "tempdir", "fileext"}, behavior = COMPLEX)
 public abstract class TempFile extends RBuiltinNode {
     @CompilationFinal private int stringVectorsAmount;
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java
index 54dc75c3a597a855c35d39f1e2ac82e1cbb7bad4..0313cad4b98aec16177b01b33fe7021ffa3fa6ee 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java
@@ -22,72 +22,121 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.function.BiFunction;
 
 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.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNodeGen;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.nodes.RNode;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
-import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
-public abstract class ToLowerOrUpper extends RBuiltinNode {
+public abstract class ToLowerOrUpper {
 
-    @RBuiltin(name = "tolower", kind = INTERNAL, parameterNames = {"x"})
-    public static final class ToLower {
-    }
+    public static final class StringMapNode extends RBaseNode {
 
-    @RBuiltin(name = "toupper", kind = INTERNAL, parameterNames = {"x"})
-    public static final class ToUpper {
-    }
+        private final VectorLengthProfile lengthProfile = VectorLengthProfile.create();
+        private final LoopConditionProfile loopProfile = LoopConditionProfile.createCountingProfile();
+        private final NACheck na = NACheck.create();
+        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
-    public static ToLowerOrUpper createToLower(RNode[] arguments) {
-        return ToLowerOrUpperNodeGen.create(true, arguments);
-    }
+        @Child private CopyOfRegAttributesNode copyAttributes = CopyOfRegAttributesNodeGen.create();
 
-    public static ToLowerOrUpper createToUpper(RNode[] arguments) {
-        return ToLowerOrUpperNodeGen.create(false, arguments);
-    }
+        private StringMapNode() {
+            // nothing to do
+        }
 
-    private final boolean lower;
+        public static StringMapNode create() {
+            return new StringMapNode();
+        }
 
-    public ToLowerOrUpper(boolean lower) {
-        this.lower = lower;
-    }
+        private String elementFunction(String value, int i, BiFunction<String, Integer, String> function) {
+            return na.check(value) ? RRuntime.STRING_NA : function.apply(value, i);
+        }
+
+        public String apply(String value, BiFunction<String, Integer, String> function) {
+            na.enable(value);
+            return elementFunction(value, 0, function);
+        }
 
-    @TruffleBoundary
-    private String processElement(String value) {
-        return lower ? value.toLowerCase() : value.toUpperCase();
+        public RStringVector apply(RAbstractStringVector vector, BiFunction<String, Integer, String> function) {
+            na.enable(vector);
+            int length = lengthProfile.profile(vector.getLength());
+            String[] stringVector = new String[length];
+            loopProfile.profileCounted(length);
+            for (int i = 0; loopProfile.inject(i < length); i++) {
+                String value = vector.getDataAt(i);
+                stringVector[i] = elementFunction(value, i, function);
+            }
+            RStringVector result = RDataFactory.createStringVector(stringVector, vector.isComplete(), vector.getDimensions(), vector.getNames(attrProfiles));
+            copyAttributes.execute(vector, result);
+            return result;
+        }
     }
 
-    @Specialization
-    protected String toLower(String value, //
-                    @Cached("create()") NAProfile na) {
-        return na.isNA(value) ? RRuntime.STRING_NA : processElement(value);
+    @RBuiltin(name = "tolower", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
+    public abstract static class ToLower extends RBuiltinNode {
+
+        @Child private StringMapNode mapNode = StringMapNode.create();
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg(0, "x").mustBe(stringValue()).asStringVector(true, true, true);
+        }
+
+        @TruffleBoundary
+        private static String processElement(String value, @SuppressWarnings("unused") int i) {
+            return value.toLowerCase();
+        }
+
+        @Specialization
+        protected String toLower(String value) {
+            return mapNode.apply(value, ToLower::processElement);
+        }
+
+        @Specialization
+        protected RStringVector toLower(RAbstractStringVector vector) {
+            return mapNode.apply(vector, ToLower::processElement);
+        }
     }
 
-    @Specialization
-    protected RStringVector toLower(RAbstractStringVector vector, //
-                    @Cached("createCountingProfile()") LoopConditionProfile loopProfile, //
-                    @Cached("create()") NACheck na, //
-                    @Cached("create()") CopyOfRegAttributesNode copyAttributes) {
-        na.enable(vector);
-        String[] stringVector = new String[vector.getLength()];
-        loopProfile.profileCounted(vector.getLength());
-        for (int i = 0; loopProfile.inject(i < vector.getLength()); i++) {
-            String value = vector.getDataAt(i);
-            stringVector[i] = na.check(value) ? RRuntime.STRING_NA : processElement(value);
+    @RBuiltin(name = "toupper", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
+    public abstract static class ToUpper extends RBuiltinNode {
+
+        @Child private StringMapNode mapNode = StringMapNode.create();
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg(0, "x").mustBe(stringValue()).asStringVector(true, true, true);
+        }
+
+        @TruffleBoundary
+        private static String processElement(String value, @SuppressWarnings("unused") int i) {
+            return value.toUpperCase();
+        }
+
+        @Specialization
+        protected String toLower(String value) {
+            return mapNode.apply(value, ToUpper::processElement);
+        }
+
+        @Specialization
+        protected RStringVector toLower(RAbstractStringVector vector) {
+            return mapNode.apply(vector, ToUpper::processElement);
         }
-        RStringVector result = RDataFactory.createStringVector(stringVector, vector.isComplete(), vector.getDimensions());
-        copyAttributes.execute(vector, result);
-        return result;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
index c724cfb934de63b4aedf83776edd6eff31a02133..13c44d60cfdd6e5a75207c2b9d794f8507b106a1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TraceFunctions.java
@@ -25,6 +25,11 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.missingValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import java.io.IOException;
 import java.util.HashSet;
@@ -41,17 +46,15 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen;
 import com.oracle.truffle.r.nodes.builtin.helpers.TraceHandling;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RCaller;
 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.RType;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.MemoryTracer;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -72,11 +75,11 @@ public class TraceFunctions {
         }
     }
 
-    @RBuiltin(name = ".primTrace", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = "what")
+    @RBuiltin(name = ".primTrace", visibility = OFF, kind = PRIMITIVE, parameterNames = "what", behavior = COMPLEX)
     public abstract static class PrimTrace extends Helper {
 
         @Specialization
-        protected RNull primUnTrace(VirtualFrame frame, RAbstractStringVector funcName) {
+        protected RNull primTrace(VirtualFrame frame, RAbstractStringVector funcName) {
             return primTrace((RFunction) getFunction(frame, funcName.getDataAt(0)));
         }
 
@@ -92,7 +95,7 @@ public class TraceFunctions {
         }
     }
 
-    @RBuiltin(name = ".primUntrace", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = "what")
+    @RBuiltin(name = ".primUntrace", visibility = OFF, kind = PRIMITIVE, parameterNames = "what", behavior = COMPLEX)
     public abstract static class PrimUnTrace extends Helper {
 
         @Specialization
@@ -110,15 +113,17 @@ public class TraceFunctions {
         }
     }
 
-    @RBuiltin(name = "traceOnOff", kind = RBuiltinKind.INTERNAL, parameterNames = "state")
+    @RBuiltin(name = "traceOnOff", kind = INTERNAL, parameterNames = "state", behavior = COMPLEX)
     public abstract static class TraceOnOff extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
         protected byte traceOnOff(byte state) {
+            /* TODO GnuR appears to accept ANY value as an argument */
             boolean prevState = RContext.getInstance().stateInstrumentation.getTracingState();
             boolean newState = RRuntime.fromLogical(state);
             if (newState != prevState) {
                 RContext.getInstance().stateInstrumentation.setTracingState(newState);
+                MemoryCopyTracer.setTracingState(newState);
             }
             return RRuntime.asLogical(prevState);
         }
@@ -132,7 +137,7 @@ public class TraceFunctions {
 
     public abstract static class TracememBase extends RBuiltinNode {
         static {
-            MemoryTracer.setListener(new TracememBase.TracememListener());
+            MemoryCopyTracer.addListener(new TracememBase.TracememListener());
         }
 
         protected static HashSet<Object> getTracedObjects() {
@@ -144,8 +149,16 @@ public class TraceFunctions {
         }
 
         protected static void startTracing(Object x) {
+            /*
+             * There is no explicit command to enable tracing, it is implicit in the call to
+             * tracemem. However, it can be disabled by tracingState(F), so we can't unilaterally
+             * turn on tracing here.
+             */
             getTracedObjects().add(x);
-            MemoryTracer.reportEvents();
+            boolean tracingState = RContext.getInstance().stateInstrumentation.getTracingState();
+            if (tracingState) {
+                MemoryCopyTracer.setTracingState(true);
+            }
         }
 
         protected static void printToStdout(String msg) {
@@ -173,7 +186,7 @@ public class TraceFunctions {
             return result.toString();
         }
 
-        private static final class TracememListener implements MemoryTracer.Listener {
+        private static final class TracememListener implements MemoryCopyTracer.Listener {
             @Override
             public void reportCopying(RAbstractVector src, RAbstractVector dest) {
                 if (getTracedObjects().contains(src)) {
@@ -188,7 +201,7 @@ public class TraceFunctions {
      * vector class. When these are manipulated as 'vectors', they are wrapped temporarily, such
      * temporary vector wrappers cannot be traced however.
      */
-    @RBuiltin(name = "tracemem", kind = RBuiltinKind.PRIMITIVE, parameterNames = "x")
+    @RBuiltin(name = "tracemem", kind = PRIMITIVE, parameterNames = "x", behavior = COMPLEX)
     public abstract static class Tracemem extends TracememBase {
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -207,7 +220,7 @@ public class TraceFunctions {
      * build with memory tracing support or x is NULL and it has additional parameter with an
      * unclear meaning.
      */
-    @RBuiltin(name = "retracemem", kind = RBuiltinKind.PRIMITIVE, visibility = RVisibility.CUSTOM, parameterNames = {"x", "previous"})
+    @RBuiltin(name = "retracemem", kind = PRIMITIVE, visibility = CUSTOM, parameterNames = {"x", "previous"}, behavior = COMPLEX)
     public abstract static class Retracemem extends TracememBase {
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -245,7 +258,7 @@ public class TraceFunctions {
         }
     }
 
-    @RBuiltin(name = "untracemem", kind = RBuiltinKind.PRIMITIVE, visibility = RVisibility.OFF, parameterNames = "x")
+    @RBuiltin(name = "untracemem", kind = PRIMITIVE, visibility = OFF, parameterNames = "x", behavior = COMPLEX)
     public abstract static class Untracemem extends TracememBase {
         @Specialization
         protected RNull execute(Object x) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Traceback.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Traceback.java
index 88e9545c0cf61495fdf80cab2cf660bd8ba9e855..7512b96c602e04cc9c48d22d6d5da86745e7cbb5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Traceback.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Traceback.java
@@ -22,16 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
-@RBuiltin(name = "traceback", kind = INTERNAL, parameterNames = {"x"})
+@RBuiltin(name = "traceback", kind = INTERNAL, parameterNames = {"x"}, behavior = COMPLEX)
 public abstract class Traceback extends RBuiltinNode {
 
     @Override
@@ -44,5 +45,4 @@ public abstract class Traceback extends RBuiltinNode {
     protected Object traceback(int x) {
         return Utils.createTraceback(x);
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java
index 8d8c040afce1aafde176e50acc39673a9ba32e3c..e9088eeb636daa0ba882e7913ceef4134f86132b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java
@@ -12,88 +12,87 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.SUBSTITUTE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import com.oracle.truffle.api.dsl.Fallback;
 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.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNodeGen;
 import com.oracle.truffle.r.nodes.attributes.InitAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.PutAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.PutAttributeNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
+import com.oracle.truffle.r.runtime.data.RComplex;
 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.RVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
 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.RNode;
 
-@RBuiltin(name = "t.default", kind = SUBSTITUTE, parameterNames = {"x"})
-// TODO INTERNAL
+@RBuiltin(name = "t.default", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
 public abstract class Transpose extends RBuiltinNode {
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final BranchProfile hasDimNamesProfile = BranchProfile.create();
     private final ConditionProfile isMatrixProfile = ConditionProfile.createBinaryProfile();
 
+    private final VectorLengthProfile lengthProfile = VectorLengthProfile.create();
+    private final LoopConditionProfile loopProfile = LoopConditionProfile.createCountingProfile();
+
     @Child private CopyOfRegAttributesNode copyRegAttributes = CopyOfRegAttributesNodeGen.create();
     @Child private InitAttributesNode initAttributes = InitAttributesNode.create();
     @Child private PutAttributeNode putDimensions = PutAttributeNodeGen.createDim();
     @Child private PutAttributeNode putDimNames = PutAttributeNodeGen.createDimNames();
 
-    public abstract Object execute(Object o);
-
-    @Specialization
-    protected RNull transpose(RNull value) {
-        return value;
-    }
-
-    @Specialization
-    protected int transpose(int value) {
-        return value;
-    }
-
-    @Specialization
-    protected double transpose(double value) {
-        return value;
-    }
-
-    @Specialization
-    protected byte transpose(byte value) {
-        return value;
-    }
-
-    @Specialization(guards = "isEmpty2D(vector)")
-    protected RAbstractVector transpose(RAbstractVector vector) {
-        int[] dim = vector.getDimensions();
-        return vector.copyWithNewDimensions(new int[]{dim[1], dim[0]});
-    }
+    public abstract Object execute(RAbstractVector o);
 
     @FunctionalInterface
-    private interface InnerLoop<T extends RAbstractVector> {
-        RVector apply(T vector, int firstDim);
+    private interface WriteArray<T extends RAbstractVector, A> {
+        void apply(A array, T vector, int i, int j);
     }
 
-    protected <T extends RAbstractVector> RVector transposeInternal(T vector, InnerLoop<T> innerLoop) {
+    protected <T extends RAbstractVector, A> RVector transposeInternal(T vector, Function<Integer, A> createArray, WriteArray<T, A> writeArray, BiFunction<A, Boolean, RVector> createResult) {
+        int length = lengthProfile.profile(vector.getLength());
         int firstDim;
         int secondDim;
         if (isMatrixProfile.profile(vector.isMatrix())) {
             firstDim = vector.getDimensions()[0];
             secondDim = vector.getDimensions()[1];
         } else {
-            firstDim = vector.getLength();
+            firstDim = length;
             secondDim = 1;
         }
-        RNode.reportWork(this, vector.getLength());
+        RNode.reportWork(this, length);
 
-        RVector r = innerLoop.apply(vector, firstDim);
+        A array = createArray.apply(length);
+        int j = 0;
+        loopProfile.profileCounted(length);
+        for (int i = 0; loopProfile.inject(i < length); i++, j += firstDim) {
+            if (j > (length - 1)) {
+                j -= (length - 1);
+            }
+            writeArray.apply(array, vector, i, j);
+        }
+        RVector r = createResult.apply(array, vector.isComplete());
         // copy attributes
         copyRegAttributes.execute(vector, r);
         // set new dimensions
@@ -112,61 +111,47 @@ public abstract class Transpose extends RBuiltinNode {
         return r;
     }
 
-    private static RVector innerLoopInt(RAbstractIntVector vector, int firstDim) {
-        int[] result = new int[vector.getLength()];
-        int j = 0;
-        for (int i = 0; i < result.length; i++, j += firstDim) {
-            if (j > (result.length - 1)) {
-                j -= (result.length - 1);
-            }
-            result[i] = vector.getDataAt(j);
-        }
-        return RDataFactory.createIntVector(result, vector.isComplete());
+    @Specialization
+    protected RVector transpose(RAbstractIntVector x) {
+        return transposeInternal(x, l -> new int[l], (a, v, i, j) -> a[i] = v.getDataAt(j), RDataFactory::createIntVector);
     }
 
-    private static RVector innerLoopDouble(RAbstractDoubleVector vector, int firstDim) {
-        double[] result = new double[vector.getLength()];
-        int j = 0;
-        for (int i = 0; i < result.length; i++, j += firstDim) {
-            if (j > (result.length - 1)) {
-                j -= (result.length - 1);
-            }
-            result[i] = vector.getDataAt(j);
-        }
-        return RDataFactory.createDoubleVector(result, vector.isComplete());
+    @Specialization
+    protected RVector transpose(RAbstractLogicalVector x) {
+        return transposeInternal(x, l -> new byte[l], (a, v, i, j) -> a[i] = v.getDataAt(j), RDataFactory::createLogicalVector);
     }
 
-    private static RVector innerLoopString(RAbstractStringVector vector, int firstDim) {
-        String[] result = new String[vector.getLength()];
-        int j = 0;
-        for (int i = 0; i < result.length; i++, j += firstDim) {
-            if (j > (result.length - 1)) {
-                j -= (result.length - 1);
-            }
-            result[i] = vector.getDataAt(j);
-        }
-        return RDataFactory.createStringVector(result, vector.isComplete());
+    @Specialization
+    protected RVector transpose(RAbstractDoubleVector x) {
+        return transposeInternal(x, l -> new double[l], (a, v, i, j) -> a[i] = v.getDataAt(j), RDataFactory::createDoubleVector);
     }
 
-    @Specialization(guards = "!isEmpty2D(vector)")
-    protected RVector transpose(RAbstractIntVector vector) {
-        return transposeInternal(vector, Transpose::innerLoopInt);
+    @Specialization
+    protected RVector transpose(RAbstractComplexVector x) {
+        return transposeInternal(x, l -> new double[l * 2], (a, v, i, j) -> {
+            RComplex d = v.getDataAt(j);
+            a[i * 2] = d.getRealPart();
+            a[i * 2 + 1] = d.getImaginaryPart();
+        }, RDataFactory::createComplexVector);
     }
 
-    @Specialization(guards = "!isEmpty2D(vector)")
-    protected RVector transpose(RAbstractDoubleVector vector) {
-        return transposeInternal(vector, Transpose::innerLoopDouble);
+    @Specialization
+    protected RVector transpose(RAbstractStringVector x) {
+        return transposeInternal(x, l -> new String[l], (a, v, i, j) -> a[i] = v.getDataAt(j), RDataFactory::createStringVector);
     }
 
-    @Specialization(guards = "!isEmpty2D(vector)")
-    protected RVector transpose(RAbstractStringVector vector) {
-        return transposeInternal(vector, Transpose::innerLoopString);
+    @Specialization
+    protected RVector transpose(RAbstractListVector x) {
+        return transposeInternal(x, l -> new Object[l], (a, v, i, j) -> a[i] = v.getDataAt(j), (a, c) -> RDataFactory.createList(a));
     }
 
-    protected static boolean isEmpty2D(RAbstractVector vector) {
-        if (!vector.hasDimensions()) {
-            return false;
-        }
-        return vector.getDimensions().length == 2 && vector.getLength() == 0;
+    @Specialization
+    protected RVector transpose(RAbstractRawVector x) {
+        return transposeInternal(x, l -> new byte[l], (a, v, i, j) -> a[i] = v.getRawDataAt(j), (a, c) -> RDataFactory.createRawVector(a));
+    }
+
+    @Fallback
+    protected RVector transpose(@SuppressWarnings("unused") Object x) {
+        throw RError.error(RError.SHOW_CALLER, Message.ARGUMENT_NOT_MATRIX);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TrigExpFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TrigExpFunctions.java
index 2a3a6db54e8394aa9133df788ce6cece0ca5f7b2..24e9c6925e0caa423d04fa1ddd24e5ffbe52ca96 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TrigExpFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/TrigExpFunctions.java
@@ -22,6 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
@@ -42,13 +47,11 @@ import com.oracle.truffle.r.nodes.builtin.base.TrigExpFunctionsFactory.CosNodeGe
 import com.oracle.truffle.r.nodes.builtin.base.TrigExpFunctionsFactory.SinNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.TrigExpFunctionsFactory.TanNodeGen;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -173,7 +176,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "exp", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "exp", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Exp extends UnaryArithmeticBuiltinNode {
 
         public Exp() {
@@ -197,7 +200,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "expm1", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "expm1", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class ExpM1 extends UnaryArithmeticBuiltinNode {
 
         public ExpM1() {
@@ -222,7 +225,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "sin", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "sin", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Sin extends UnaryArithmeticBuiltinNode {
 
         public Sin() {
@@ -242,7 +245,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "sinh", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "sinh", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Sinh extends UnaryArithmeticBuiltinNode {
 
         public Sinh() {
@@ -262,7 +265,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "sinpi", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "sinpi", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Sinpi extends UnaryArithmeticBuiltinNode {
 
         public Sinpi() {
@@ -286,7 +289,7 @@ public class TrigExpFunctions {
 
     }
 
-    @RBuiltin(name = "cos", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "cos", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Cos extends UnaryArithmeticBuiltinNode {
 
         public Cos() {
@@ -306,7 +309,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "cosh", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "cosh", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Cosh extends UnaryArithmeticBuiltinNode {
 
         public Cosh() {
@@ -326,7 +329,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "cospi", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "cospi", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Cospi extends UnaryArithmeticBuiltinNode {
 
         public Cospi() {
@@ -354,7 +357,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "tan", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "tan", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Tan extends UnaryArithmeticBuiltinNode {
 
         public Tan() {
@@ -380,7 +383,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "tanh", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "tanh", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Tanh extends UnaryArithmeticBuiltinNode {
 
         public Tanh() {
@@ -401,7 +404,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "tanpi", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "tanpi", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Tanpi extends UnaryArithmeticBuiltinNode {
 
         public Tanpi() {
@@ -426,7 +429,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "asin", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "asin", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Asin extends UnaryArithmeticBuiltinNode {
 
         @Child private CHypot chypot;
@@ -485,7 +488,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "asinh", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "asinh", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Asinh extends UnaryArithmeticBuiltinNode {
 
         @Child private Asin asinNode = AsinNodeGen.create(null);
@@ -506,7 +509,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "acos", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "acos", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Acos extends UnaryArithmeticBuiltinNode {
 
         public Acos() {
@@ -527,7 +530,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "acosh", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "acosh", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Acosh extends UnaryArithmeticBuiltinNode {
 
         public Acosh() {
@@ -548,7 +551,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "atan", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "atan", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Atan extends UnaryArithmeticBuiltinNode {
 
         public Atan() {
@@ -578,7 +581,7 @@ public class TrigExpFunctions {
         }
     }
 
-    @RBuiltin(name = "atanh", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+    @RBuiltin(name = "atanh", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
     public abstract static class Atanh extends UnaryArithmeticBuiltinNode {
 
         public Atanh() {
@@ -603,7 +606,7 @@ public class TrigExpFunctions {
      * {@code atan2} takes two args. To avoid combinatorial explosion in specializations we coerce
      * the {@code int} forms to {@code double}.
      */
-    @RBuiltin(name = "atan2", kind = RBuiltinKind.INTERNAL, parameterNames = {"y", "x"})
+    @RBuiltin(name = "atan2", kind = INTERNAL, parameterNames = {"y", "x"}, behavior = PURE)
     public abstract static class Atan2 extends RBuiltinNode {
 
         private final NACheck yNACheck = NACheck.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Trunc.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Trunc.java
index 08beabba07683442437ca45847e3028c118bc3a5..b052598fc76904972ba2d5e71940f9b38212181f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Trunc.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Trunc.java
@@ -22,20 +22,22 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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;
+
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNode;
 import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.ops.UnaryArithmeticFactory;
 
-@RBuiltin(name = "trunc", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x"}, dispatch = RDispatch.MATH_GROUP_GENERIC)
+@RBuiltin(name = "trunc", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = MATH_GROUP_GENERIC, behavior = PURE)
 public abstract class Trunc extends RBuiltinNode {
 
     private static final UnaryArithmeticFactory TRUNC = TruncArithmetic::new;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Typeof.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Typeof.java
index 186a3f06e75493211e66bcc1093b217e0a6d12dd..735a4bf0e9a60da03b744aec6d278dc7ad546c6f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Typeof.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Typeof.java
@@ -22,27 +22,22 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.dsl.Specialization;
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
-@RBuiltin(name = "typeof", kind = INTERNAL, parameterNames = {"x"})
+@RBuiltin(name = "typeof", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
 public abstract class Typeof extends RBuiltinNode {
 
-    @Child private TypeofNode typeofNode;
-
-    public abstract String execute(VirtualFrame frame, Object x);
+    @Child private TypeofNode typeofNode = TypeofNodeGen.create();
 
     @Specialization
     protected String typeof(Object obj) {
-        if (typeofNode == null) {
-            typeofNode = insert(TypeofNodeGen.create());
-        }
         return typeofNode.execute(obj).getName();
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java
index 1c47b76ab2d2cd406a2992cea27e63eb77ef6395..fd3f5c4be7fc1e5796af7c05099009b31363aa49 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UnClass.java
@@ -11,21 +11,22 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "unclass", kind = PRIMITIVE, parameterNames = {"x"})
+@RBuiltin(name = "unclass", kind = PRIMITIVE, parameterNames = {"x"}, behavior = PURE)
 public abstract class UnClass extends RBuiltinNode {
     private final BranchProfile objectProfile = BranchProfile.create();
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unique.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unique.java
index 4e060aa91abb79fd9466dbc54997a3d643b6a5a7..0e93117dc795bbe0db17269947ff4cc17bae69ce 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unique.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unique.java
@@ -22,19 +22,19 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
 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.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
@@ -55,14 +55,12 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 // Implements default S3 method
-@RBuiltin(name = "unique", kind = INTERNAL, parameterNames = {"x", "incomparables", "fromLast", "nmax", "..."})
+@RBuiltin(name = "unique", kind = INTERNAL, parameterNames = {"x", "incomparables", "fromLast", "nmax", "..."}, behavior = PURE)
 // TODO A more efficient implementation is in order; GNU R uses hash tables so perhaps we should
 // consider using one of the existing libraries that offer hash table implementations for primitive
 // types
 public abstract class Unique extends RBuiltinNode {
 
-    protected abstract Object execute(VirtualFrame frame, Object vec, byte incomparables, byte fromLast, Object nmax, RArgsValuesAndNames vararg);
-
     private static final long BIG_THRESHOLD = 100;
 
     private final ConditionProfile bigProfile = ConditionProfile.createBinaryProfile();
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 b2e966e2d0803109cb56d8d5d8d61aa12003e3a4..d440e5ec6417d32322adaff051b0f0dbaff3bbdb 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
@@ -11,6 +11,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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;
@@ -22,10 +25,9 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.UnlistNodeGen.RecursiveLengthNodeGen;
 import com.oracle.truffle.r.nodes.unary.PrecedenceNode;
 import com.oracle.truffle.r.nodes.unary.PrecedenceNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -38,7 +40,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RTypes;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "unlist", kind = RBuiltinKind.INTERNAL, parameterNames = {"x", "recursive", "use.names"})
+@RBuiltin(name = "unlist", kind = INTERNAL, parameterNames = {"x", "recursive", "use.names"}, behavior = PURE)
 public abstract class Unlist extends RBuiltinNode {
 
     // portions of the algorithm were transcribed from GNU R
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
index 5e9ec273085f2326f29af4ef0c1840b2c0b72ba8..8741f2c60c92f5df943fd3cec2bd421d6c2a34ea 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -36,9 +37,9 @@ import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastListNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
@@ -50,7 +51,7 @@ 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.RAbstractVector;
 
-@RBuiltin(name = "attr<-", kind = PRIMITIVE, parameterNames = {"x", "which", "value"})
+@RBuiltin(name = "attr<-", kind = PRIMITIVE, parameterNames = {"x", "which", "value"}, behavior = PURE)
 public abstract class UpdateAttr extends RBuiltinNode {
 
     private final BranchProfile errorProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
index 8561c8a3ae9128fc2bee70af49475caca1ab8652..6c526dfbe848ae3c3c51154e9537e514280c4c98 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -35,9 +36,9 @@ import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastListNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -48,7 +49,7 @@ 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.RAbstractVector;
 
-@RBuiltin(name = "attributes<-", kind = PRIMITIVE, parameterNames = {"obj", "value"})
+@RBuiltin(name = "attributes<-", kind = PRIMITIVE, parameterNames = {"obj", "value"}, behavior = PURE)
 public abstract class UpdateAttributes extends RBuiltinNode {
     private final ConditionProfile numAttributesProfile = ConditionProfile.createBinaryProfile();
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
@@ -62,20 +63,20 @@ public abstract class UpdateAttributes extends RBuiltinNode {
     // it's OK for the following two methods to update attributes in-place as the container has been
     // already materialized to non-shared
 
-    private void updateNames(RAbstractContainer container, Object o) {
+    private RAbstractContainer updateNames(RAbstractContainer container, Object o) {
         if (updateNames == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             updateNames = insert(UpdateNamesNodeGen.create(null));
         }
-        updateNames.executeStringVector(container, o);
+        return (RAbstractContainer) updateNames.executeStringVector(container, o);
     }
 
-    private void updateDimNames(RAbstractContainer container, Object o) {
+    private RAbstractContainer updateDimNames(RAbstractContainer container, Object o) {
         if (updateDimNames == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
             updateDimNames = insert(UpdateDimNamesNodeGen.create(null));
         }
-        updateDimNames.executeRAbstractContainer(container, o);
+        return updateDimNames.executeRAbstractContainer(container, o);
     }
 
     private RAbstractIntVector castInteger(RAbstractVector vector) {
@@ -175,9 +176,9 @@ public abstract class UpdateAttributes extends RBuiltinNode {
             if (attrName.equals(RRuntime.DIM_ATTR_KEY)) {
                 continue;
             } else if (attrName.equals(RRuntime.NAMES_ATTR_KEY)) {
-                updateNames(res, value);
+                res = updateNames(res, value);
             } else if (attrName.equals(RRuntime.DIMNAMES_ATTR_KEY)) {
-                updateDimNames(res, value);
+                res = updateDimNames(res, value);
             } else if (attrName.equals(RRuntime.CLASS_ATTR_KEY)) {
                 if (value == RNull.instance) {
                     res = (RAbstractContainer) result.setClassAttr(null);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
index 5d4e2384e0265ca7ec4c91a34687d84137bf66e4..bba597a2585760808ef85fadaae692cb3156482f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
@@ -11,7 +11,8 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -25,9 +26,9 @@ import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
 import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
@@ -42,7 +43,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@RBuiltin(name = "class<-", kind = PRIMITIVE, parameterNames = {"x", "value"})
+@RBuiltin(name = "class<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, behavior = PURE)
 public abstract class UpdateClass extends RBuiltinNode {
 
     protected static final int CACHE_LIMIT = 2;
@@ -66,7 +67,7 @@ public abstract class UpdateClass extends RBuiltinNode {
     private void initCastStringNode() {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
+            castStringNode = insert(CastStringNodeGen.create(false, false, false));
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java
index 73a903458df5bf8022897a21a3c5147503a8d751..9d255dec5bf0fe26971533f3867a15a6f6f27547 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Cached;
@@ -30,14 +31,14 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.opt.ReuseNonSharedNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RNull;
 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.RAbstractVector;
 
-@RBuiltin(name = "dim<-", kind = PRIMITIVE, parameterNames = {"x", "value"})
+@RBuiltin(name = "dim<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, behavior = PURE)
 public abstract class UpdateDim extends RBuiltinNode {
 
     @Child private ReuseNonSharedNode reuse = ReuseNonSharedNode.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java
index 8c67c523f28176fe28445d8b3b5cd24ab0d73260..9b49702aa2fcc30d926f3fde4b21f6213c0cc8ba 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Cached;
@@ -35,9 +36,9 @@ import com.oracle.truffle.r.nodes.unary.CastStringNode;
 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.runtime.RBuiltin;
 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.RAttributes;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -46,7 +47,7 @@ import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "dimnames<-", kind = PRIMITIVE, parameterNames = {"x", "value"})
+@RBuiltin(name = "dimnames<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, behavior = PURE)
 public abstract class UpdateDimNames extends RBuiltinNode {
 
     protected static final String DIMNAMES_ATTR_KEY = RRuntime.DIMNAMES_ATTR_KEY;
@@ -60,7 +61,7 @@ public abstract class UpdateDimNames extends RBuiltinNode {
     private Object castString(Object o) {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(true, true, false, false));
+            castStringNode = insert(CastStringNodeGen.create(true, true, true));
         }
         return castStringNode.execute(o);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLength.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLength.java
index ed0d2d7737033ee0bc94dfa8517b27f8f58cfe8e..5d5f36d2a5b57d57d5583cb93ddca5b7cb2bcfc7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLength.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLength.java
@@ -22,20 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
 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;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
-@RBuiltin(name = "length<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, dispatch = INTERNAL_GENERIC)
+@RBuiltin(name = "length<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class UpdateLength extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
index 32326368e24d36dd6122ae2839434f30be968757..5ffaf8da7fb1c5c4457f4a87c73b2971c52c6241 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
@@ -11,21 +11,23 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
+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;
+
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "levels<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "value"}, dispatch = RDispatch.INTERNAL_GENERIC)
+@RBuiltin(name = "levels<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class UpdateLevels extends RBuiltinNode {
 
     @Child private CastToVectorNode castVector;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java
index 317ce79c47937da8c3668727093dd086aeb21372..93587752d7411f1f797a66f85692de3cc0bf4dbf 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateNames.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -30,16 +31,16 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 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.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@RBuiltin(name = "names<-", kind = PRIMITIVE, parameterNames = {"x", "value"})
+@RBuiltin(name = "names<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, behavior = PURE)
 public abstract class UpdateNames extends RBuiltinNode {
 
     @Child private CastStringNode castStringNode;
@@ -47,7 +48,7 @@ public abstract class UpdateNames extends RBuiltinNode {
     private Object castString(Object o) {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(false, true, false, false));
+            castStringNode = insert(CastStringNodeGen.create(false, false, false));
         }
         return castStringNode.executeString(o);
     }
@@ -68,16 +69,20 @@ public abstract class UpdateNames extends RBuiltinNode {
         if (newNames instanceof String) {
             stringVector = RDataFactory.createStringVector((String) newNames);
         } else {
-            stringVector = (RStringVector) ((RAbstractVector) newNames).materialize();
+            stringVector = (RStringVector) ((RAbstractVector) newNames).materialize().copyDropAttributes();
         }
         RAbstractContainer result = (RAbstractContainer) container.getNonShared();
         if (stringVector.getLength() < result.getLength()) {
             stringVector = (RStringVector) stringVector.copyResized(result.getLength(), true);
         } else if (stringVector.getLength() > result.getLength()) {
             throw RError.error(this, Message.NAMES_LONGER, stringVector.getLength(), result.getLength());
-        } else if (stringVector == container) {
+        } else if (stringVector == names) {
             stringVector = (RStringVector) stringVector.copy();
         }
+        if (stringVector.isTemporary()) {
+            stringVector.incRefCount();
+
+        }
         result.setNames(stringVector);
         return result;
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
index f8d823a843d744572035d8aaadc2abee997e47cb..f05261a585f42113af69f41576f5a2897eddc6df 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
@@ -23,7 +23,8 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -31,7 +32,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RString;
@@ -40,7 +41,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 // oldClass<- (as opposed to class<-), simply sets the attribute (without handling "implicit" attributes)
-@RBuiltin(name = "oldClass<-", kind = PRIMITIVE, parameterNames = {"x", "value"})
+@RBuiltin(name = "oldClass<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, behavior = PURE)
 public abstract class UpdateOldClass extends RBuiltinNode {
 
     @Child private CastStringNode castStringNode;
@@ -58,7 +59,7 @@ public abstract class UpdateOldClass extends RBuiltinNode {
     private void initCastStringNode() {
         if (castStringNode == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castStringNode = insert(CastStringNodeGen.create(false, false, false, false));
+            castStringNode = insert(CastStringNodeGen.create(false, false, false));
         }
     }
 
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 34b82ecb414147552844b3dee4600d72809f2c5b..b549fede5a4879ec25b1cc2f1f3c9e40dea1a39e 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
@@ -13,6 +13,9 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.Truffle;
@@ -32,10 +35,9 @@ import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.function.WrapArgumentNode;
 import com.oracle.truffle.r.nodes.function.signature.RArgumentsNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RPromise;
@@ -43,7 +45,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
-@RBuiltin(name = "@<-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", "", "value"}, nonEvalArgs = 1)
+@RBuiltin(name = "@<-", kind = PRIMITIVE, parameterNames = {"", "", "value"}, nonEvalArgs = 1, behavior = COMPLEX)
 public abstract class UpdateSlot extends RBuiltinNode {
 
     private static final ArgumentsSignature SIGNATURE = ArgumentsSignature.get("cl", "name", "valueClass");
@@ -107,7 +109,7 @@ public abstract class UpdateSlot extends RBuiltinNode {
             checkAtAssignmentCall.call(frame, args);
         } else {
             // slow path
-            RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, getOriginalCall()), objClass, name, valClass);
+            RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, getOriginalCall()), null, objClass, name, valClass);
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java
index aa0a04050964d45caf97bf77807098d7385292f9..23a86b5adf58dbb178712156bdcd4a0954898ad6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java
@@ -11,7 +11,8 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -24,10 +25,10 @@ import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.IsFactorNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributes;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
@@ -35,7 +36,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 
-@RBuiltin(name = "storage.mode<-", kind = PRIMITIVE, parameterNames = {"x", "value"})
+@RBuiltin(name = "storage.mode<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, behavior = PURE)
 public abstract class UpdateStorageMode extends RBuiltinNode {
 
     @Child private TypeFromModeNode typeFromMode = TypeFromModeNodeGen.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSubstr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSubstr.java
index 3db77f9f3418b30417db8babd846c21cbaacd48f..c6cb92740a5b200d81b1b70d47ac3bee7a5b673a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSubstr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSubstr.java
@@ -22,17 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+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.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 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.RNull;
 import com.oracle.truffle.r.runtime.data.RString;
@@ -42,7 +43,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = "substr<-", kind = INTERNAL, parameterNames = {"x", "start", "stop", "value"})
+@RBuiltin(name = "substr<-", kind = INTERNAL, parameterNames = {"x", "start", "stop", "value"}, behavior = PURE)
 public abstract class UpdateSubstr extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
index 4b0c19acfa1957c7c6223289e284df24d963a39c..b878565580cb566b94d9b2728004d11527840110 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
@@ -22,7 +22,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -43,10 +44,10 @@ import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -72,7 +73,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
  *
  * TODO Set dimnames on result if necessary.
  */
-@RBuiltin(name = "vapply", kind = INTERNAL, parameterNames = {"X", "FUN", "FUN.VALUE", "USE.NAMES"}, splitCaller = true)
+@RBuiltin(name = "vapply", kind = INTERNAL, parameterNames = {"X", "FUN", "FUN.VALUE", "USE.NAMES"}, splitCaller = true, behavior = COMPLEX)
 public abstract class VApply extends RBuiltinNode {
 
     private final ValueProfile funValueProfile = ValueProfile.createClassProfile();
@@ -125,7 +126,7 @@ public abstract class VApply extends RBuiltinNode {
     private Object castString(Object operand, boolean preserveAllAttr) {
         if (castString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castString = insert(CastStringNodeGen.create(false, true, preserveAllAttr, preserveAllAttr));
+            castString = insert(CastStringNodeGen.create(true, preserveAllAttr, preserveAllAttr));
         }
         return castString.execute(operand);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Vector.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Vector.java
index 6d314f2c96ab4922cae488b2172ca3cfe206a09f..5736f617e756325463f3b84daf925551432de5d6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Vector.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Vector.java
@@ -22,7 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
@@ -31,12 +33,12 @@ import com.oracle.truffle.r.nodes.attributes.TypeFromModeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNodeGen;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 
-@RBuiltin(name = "vector", kind = INTERNAL, parameterNames = {"mode", "length"})
+@RBuiltin(name = "vector", kind = INTERNAL, parameterNames = {"mode", "length"}, behavior = PURE)
 public abstract class Vector extends RBuiltinNode {
 
     private static final String CACHED_MODES_LIMIT = "3";
@@ -45,7 +47,7 @@ public abstract class Vector extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
-        casts.convertToInteger(1);
+        casts.arg("length").asIntegerVector().mustBe(singleElement()).findFirst();
     }
 
     protected RType modeToType(String mode) {
@@ -56,9 +58,10 @@ public abstract class Vector extends RBuiltinNode {
         return type;
     }
 
-    @SuppressWarnings("unused")
     @Specialization(guards = {"mode == cachedMode"}, limit = CACHED_MODES_LIMIT)
-    Object vectorCached(String mode, int length, @Cached("mode") String cachedMode, @Cached("modeToType(mode)") RType type) {
+    Object vectorCached(@SuppressWarnings("unused") String mode, int length,
+                    @SuppressWarnings("unused") @Cached("mode") String cachedMode,
+                    @Cached("modeToType(mode)") RType type) {
         return createType(type, length);
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java
index f917f0cf1da40c720f4ecce01513195677273cec..6ee52996d3b615a84c3bc978430ab84f9962f23d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Warning.java
@@ -22,6 +22,10 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+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.Fallback;
@@ -30,14 +34,12 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
-@RBuiltin(name = "warning", visibility = RVisibility.OFF, kind = RBuiltinKind.INTERNAL, parameterNames = {"call", "immediate", "nobreaks", "message"})
+@RBuiltin(name = "warning", visibility = OFF, kind = INTERNAL, parameterNames = {"call", "immediate", "nobreaks", "message"}, behavior = COMPLEX)
 public abstract class Warning extends RBuiltinNode {
 
     @Child private CastStringNode castString;
@@ -45,7 +47,7 @@ public abstract class Warning extends RBuiltinNode {
     private Object castString(Object operand) {
         if (castString == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            castString = insert(CastStringNodeGen.create(false, true, false, false));
+            castString = insert(CastStringNodeGen.create(false, false, false));
         }
         return castString.execute(operand);
     }
@@ -70,6 +72,7 @@ public abstract class Warning extends RBuiltinNode {
 
     @SuppressWarnings("unused")
     @Fallback
+    @TruffleBoundary
     protected String warning(Object callL, Object immediateL, Object noBreakWarningL, Object message) {
         throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java
index 3164c5df7fa124c00b5006e498085a195cc8dd2e..17662ce966d830026fbd35be2cfbb5ba69a8f6b2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java
@@ -22,23 +22,28 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.INTERNAL;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
-import java.util.ArrayList;
-
-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.profiles.ConditionProfile;
+import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.nodes.builtin.base.WhichFunctionsFactory.WhichMinMaxNodeGen;
+import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
+import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 /**
@@ -46,97 +51,128 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
  */
 public class WhichFunctions {
 
-    @RBuiltin(name = "which", kind = INTERNAL, parameterNames = {"x"})
+    @RBuiltin(name = "which", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
     public abstract static class Which extends RBuiltinNode {
 
-        private final NACheck naCheck = NACheck.create();
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("x").mustBe(logicalValue()).asLogicalVector();
+        }
 
-        @Specialization(guards = "!hasNames(x)")
-        @TruffleBoundary
-        protected RIntVector which(RAbstractLogicalVector x) {
-            ArrayList<Integer> w = new ArrayList<>();
-            for (int i = 0; i < x.getLength(); i++) {
+        @Specialization
+        protected RIntVector which(RAbstractLogicalVector x,
+                        @Cached("create()") VectorLengthProfile lengthProfile,
+                        @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile,
+                        @Cached("create()") RAttributeProfiles attrProfiles,
+                        @Cached("create()") NACheck naCheck) {
+            int length = lengthProfile.profile(x.getLength());
+            loopProfile.profileCounted(length);
+            // determine the length of the result
+            int resultLength = 0;
+            for (int i = 0; loopProfile.inject(i < length); i++) {
                 if (x.getDataAt(i) == RRuntime.LOGICAL_TRUE) {
-                    w.add(i);
+                    resultLength++;
                 }
             }
-            int[] result = new int[w.size()];
-            for (int i = 0; i < result.length; i++) {
-                result[i] = w.get(i) + 1;
-            }
-            return RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR);
-        }
-
-        @Specialization(guards = "hasNames(x)")
-        @TruffleBoundary
-        protected RIntVector whichNames(RAbstractLogicalVector x) {
-            ArrayList<Integer> w = new ArrayList<>();
-            ArrayList<String> n = new ArrayList<>();
-            RStringVector oldNames = x.getNames(attrProfiles);
-            naCheck.enable(oldNames);
-            for (int i = 0; i < x.getLength(); i++) {
+            // collect result indexes
+            int[] result = new int[resultLength];
+            int pos = 0;
+            for (int i = 0; loopProfile.inject(i < length); i++) {
                 if (x.getDataAt(i) == RRuntime.LOGICAL_TRUE) {
-                    w.add(i);
-                    String s = oldNames.getDataAt(i);
-                    naCheck.check(s);
-                    n.add(s);
+                    result[pos++] = i + 1;
                 }
             }
-            int[] result = new int[w.size()];
-            for (int i = 0; i < result.length; i++) {
-                result[i] = w.get(i) + 1;
+            RStringVector names = x.getNames(attrProfiles);
+            if (hasNamesProfile.profile(names != null)) {
+                // collect result names
+                String[] resultNames = new String[resultLength];
+                naCheck.enable(names);
+                pos = 0;
+                for (int i = 0; i < x.getLength(); i++) {
+                    if (x.getDataAt(i) == RRuntime.LOGICAL_TRUE) {
+                        String name = names.getDataAt(i);
+                        naCheck.check(name);
+                        resultNames[pos++] = name;
+                    }
+                }
+                return RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR, RDataFactory.createStringVector(resultNames, naCheck.neverSeenNA()));
+            } else {
+                return RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR);
             }
-            String[] names = new String[n.size()];
-            return RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR, RDataFactory.createStringVector(n.toArray(names), naCheck.neverSeenNA()));
+        }
+    }
+
+    @RBuiltin(name = "which.max", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
+    public abstract static class WhichMax {
+        private WhichMax() {
+            // private
         }
 
-        protected boolean hasNames(RAbstractLogicalVector x) {
-            return x.getNames(attrProfiles) != null;
+        public static WhichMinMax create(RNode[] arguments) {
+            return WhichMinMaxNodeGen.create(true, arguments);
         }
     }
 
-    @RBuiltin(name = "which.max", kind = RBuiltinKind.INTERNAL, parameterNames = {"x"})
-    public abstract static class WhichMax extends RBuiltinNode {
-
-        @Override
-        protected void createCasts(CastBuilder casts) {
-            casts.toDouble(0);
+    @RBuiltin(name = "which.min", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
+    public abstract static class WhichMin {
+        private WhichMin() {
+            // private
         }
 
-        @Specialization
-        protected int which(RAbstractDoubleVector x) {
-            double max = x.getDataAt(0);
-            int maxIndex = 0;
-            for (int i = 0; i < x.getLength(); i++) {
-                if (x.getDataAt(i) > max) {
-                    max = x.getDataAt(i);
-                    maxIndex = i;
-                }
-            }
-            return maxIndex + 1;
+        public static WhichMinMax create(RNode[] arguments) {
+            return WhichMinMaxNodeGen.create(false, arguments);
         }
     }
 
-    @RBuiltin(name = "which.min", kind = RBuiltinKind.INTERNAL, parameterNames = {"x"})
-    public abstract static class WhichMin extends RBuiltinNode {
+    public abstract static class WhichMinMax extends RBuiltinNode {
+
+        private final boolean isMax;
+
+        protected WhichMinMax(boolean isMax) {
+            this.isMax = isMax;
+        }
 
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.toDouble(0);
+            casts.arg(0, "x").asDoubleVector(true, false, false);
         }
 
         @Specialization
-        protected int which(RAbstractDoubleVector x) {
-            double minimum = x.getDataAt(0);
-            int minIndex = 0;
-            for (int i = 0; i < x.getLength(); i++) {
-                if (x.getDataAt(i) < minimum) {
-                    minimum = x.getDataAt(i);
-                    minIndex = i;
+        protected RIntVector which(RAbstractDoubleVector x,
+                        @Cached("create()") VectorLengthProfile lengthProfile,
+                        @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile isNaNProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile,
+                        @Cached("create()") RAttributeProfiles attrProfiles) {
+            int length = lengthProfile.profile(x.getLength());
+            loopProfile.profileCounted(length);
+            double extreme = Double.NaN;
+            int extremeIndex = -1;
+            for (int i = 0; loopProfile.inject(i < length); i++) {
+                double d = x.getDataAt(i);
+                // inverted comparison to pass when extreme is NaN
+                if (!Double.isNaN(d) && (isMax ? !(d <= extreme) : !(d >= extreme))) {
+                    extreme = x.getDataAt(i);
+                    extremeIndex = i;
                 }
             }
-            return minIndex + 1;
+            if (isNaNProfile.profile(extremeIndex == -1)) {
+                return RDataFactory.createEmptyIntVector();
+            }
+            RStringVector names = x.getNames(attrProfiles);
+            if (hasNamesProfile.profile(names != null)) {
+                // collect result names
+                RStringVector resultNames = RDataFactory.createStringVectorFromScalar(names.getDataAt(extremeIndex));
+                return RDataFactory.createIntVector(new int[]{extremeIndex + 1}, true, resultNames);
+            } else {
+                return RDataFactory.createIntVectorFromScalar(extremeIndex + 1);
+            }
+        }
+
+        @Specialization
+        protected RIntVector which(@SuppressWarnings("unused") RNull x) {
+            return RDataFactory.createEmptyIntVector();
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java
index 0a8b7ac8b195ee1b61bf43ac572c3fd551927a6a..abc807d0118d0b82c3e92ef2f59850058aebbd3a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WithVisible.java
@@ -22,27 +22,31 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.FastROptions;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 
-@RBuiltin(name = "withVisible", kind = RBuiltinKind.PRIMITIVE, parameterNames = "x")
+@RBuiltin(name = "withVisible", kind = PRIMITIVE, parameterNames = "x", behavior = COMPLEX)
 public abstract class WithVisible extends RBuiltinNode {
     private static final RStringVector LISTNAMES = RDataFactory.createStringVector(new String[]{"value", "visible"}, RDataFactory.COMPLETE_VECTOR);
 
     @Specialization(guards = "!isRMissing(x)")
     protected RList withVisible(Object x) {
-        assert !FastROptions.IgnoreVisibility.getBooleanValue() : "using withVisible with IgnoreVisibility";
+        // (LS) temporarily disabled to enable parallel benchmarks
+        // if (FastROptions.IgnoreVisibility.getBooleanValue()) {
+        // RError.warning(this, RError.Message.GENERIC, "using withVisible with IgnoreVisibility");
+        // }
 
         Object[] data = new Object[]{x, RRuntime.asLogical(RContext.getInstance().isVisible())};
         // Visibility is changed by the evaluation (else this code would not work),
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java
index 3e2a6aaa8deccf201689df416f80212bce615034..f84ded8be8fd927d4c5873c518941a09afb30117 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Xtfrm.java
@@ -22,8 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
 import static com.oracle.truffle.r.runtime.RDispatch.INTERNAL_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -31,12 +32,12 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen;
 import com.oracle.truffle.r.runtime.RArguments;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RType;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RFunction;
 
-@RBuiltin(name = "xtfrm", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC)
+@RBuiltin(name = "xtfrm", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC, behavior = COMPLEX)
 public abstract class Xtfrm extends RBuiltinNode {
     @Child private GetFunctions.Get getNode;
 
@@ -52,6 +53,6 @@ public abstract class Xtfrm extends RBuiltinNode {
             getNode = insert(GetNodeGen.create(null));
         }
         RFunction func = (RFunction) getNode.execute(frame, "xtfrm.default", RArguments.getEnvironment(frame), RType.Function.getName(), true);
-        return RContext.getEngine().evalFunction(func, null, null, x);
+        return RContext.getEngine().evalFunction(func, null, null, null, x);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java
index 6781cf35c630451bf3a67d159a3f18542c638186..60f28c4e3c08f2e47e3f6efaf291fc0a47e8526f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/DotC.java
@@ -11,6 +11,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base.foreign;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import java.util.Arrays;
 
 import com.oracle.truffle.api.CompilerAsserts;
@@ -25,10 +28,9 @@ import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
@@ -50,7 +52,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
  *
  * See <a href="https://stat.ethz.ch/R-manual/R-devel/library/base/html/Foreign.html">here</a>.
  */
-@RBuiltin(name = ".C", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "NAOK", "DUP", "PACKAGE", "ENCODING"})
+@RBuiltin(name = ".C", kind = PRIMITIVE, parameterNames = {".NAME", "...", "NAOK", "DUP", "PACKAGE", "ENCODING"}, behavior = COMPLEX)
 public abstract class DotC extends RBuiltinNode {
 
     private static final int SCALAR_DOUBLE = 0;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
index b267d9521fcbcc765c10b5591a3e3bb734484085..b8dfa18b98a9656c6d2df0059a5589ebf7deda57 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
@@ -33,11 +33,11 @@ public final class Dqrcf extends RExternalBuiltinNode {
         Object[] argValues = args.getArguments();
         try {
             RAbstractDoubleVector xVec = (RAbstractDoubleVector) argValues[0];
-            int n = (int) argValues[1];
+            int n = argValues[1] instanceof Integer ? (int) argValues[1] : ((RAbstractIntVector) argValues[1]).getDataAt(0);
             RAbstractIntVector k = (RAbstractIntVector) argValues[2];
             RAbstractDoubleVector qrauxVec = (RAbstractDoubleVector) argValues[3];
             RAbstractDoubleVector yVec = (RAbstractDoubleVector) argValues[4];
-            int ny = (int) argValues[5];
+            int ny = argValues[5] instanceof Integer ? (int) argValues[5] : ((RAbstractIntVector) argValues[5]).getDataAt(0);
             RAbstractDoubleVector bVec = (RAbstractDoubleVector) argValues[6];
             RAbstractIntVector infoVec = (RAbstractIntVector) argValues[7];
             double[] x = xVec.materialize().getDataTemp();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
index 0e8a93f22444ba6bb03f37a69de841eae38e05e4..fb842d465d81c176cdc1f5aeadaab8f3d07326c0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
@@ -32,10 +32,10 @@ public final class Dqrdc2 extends RExternalBuiltinNode {
         Object[] argValues = args.getArguments();
         try {
             RAbstractDoubleVector xVec = (RAbstractDoubleVector) argValues[0];
-            int ldx = (int) argValues[1];
-            int n = (int) argValues[2];
-            int p = (int) argValues[3];
-            double tol = (double) argValues[4];
+            int ldx = argValues[1] instanceof Integer ? (int) argValues[1] : ((RAbstractIntVector) argValues[1]).getDataAt(0);
+            int n = argValues[2] instanceof Integer ? (int) argValues[2] : ((RAbstractIntVector) argValues[2]).getDataAt(0);
+            int p = argValues[3] instanceof Integer ? (int) argValues[3] : ((RAbstractIntVector) argValues[3]).getDataAt(0);
+            double tol = argValues[4] instanceof Double ? (double) argValues[4] : ((RAbstractDoubleVector) argValues[4]).getDataAt(0);
             RAbstractIntVector rankVec = (RAbstractIntVector) argValues[5];
             RAbstractDoubleVector qrauxVec = (RAbstractDoubleVector) argValues[6];
             RAbstractIntVector pivotVec = (RAbstractIntVector) argValues[7];
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
index 254f38f888f38bac9ccc5845d39b9123a8c86b9e..624b0baa010ced6e4ec76b6d3e99758a2bff94c1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java
@@ -11,6 +11,10 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base.foreign;
 
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
@@ -62,6 +66,7 @@ import com.oracle.truffle.r.library.utils.Download;
 import com.oracle.truffle.r.library.utils.MenuNodeGen;
 import com.oracle.truffle.r.library.utils.ObjectSizeNodeGen;
 import com.oracle.truffle.r.library.utils.RprofNodeGen;
+import com.oracle.truffle.r.library.utils.RprofmemNodeGen;
 import com.oracle.truffle.r.library.utils.TypeConvertNodeGen;
 import com.oracle.truffle.r.library.utils.WriteTable;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
@@ -72,13 +77,11 @@ import com.oracle.truffle.r.nodes.builtin.RInternalCodeBuiltinNode;
 import com.oracle.truffle.r.nodes.objects.GetPrimNameNodeGen;
 import com.oracle.truffle.r.nodes.objects.NewObjectNodeGen;
 import com.oracle.truffle.r.runtime.FastROptions;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalCode;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -213,7 +216,7 @@ public class ForeignFunctions {
      * Interface to .Fortran native functions. Some functions have explicit implementations in
      * FastR, otherwise the .Fortran interface uses the machinery that implements the .C interface.
      */
-    @RBuiltin(name = ".Fortran", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "NAOK", "DUP", "PACKAGE", "ENCODING"})
+    @RBuiltin(name = ".Fortran", kind = PRIMITIVE, parameterNames = {".NAME", "...", "NAOK", "DUP", "PACKAGE", "ENCODING"}, behavior = COMPLEX)
     public abstract static class Fortran extends LookupAdapter {
 
         @Override
@@ -271,7 +274,7 @@ public class ForeignFunctions {
      * Handles the generic case, but also many special case functions that are called from the
      * default packages.
      */
-    @RBuiltin(name = ".Call", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"})
+    @RBuiltin(name = ".Call", kind = PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}, behavior = COMPLEX)
     public abstract static class DotCall extends LookupAdapter {
 
         private final BranchProfile errorProfile = BranchProfile.create();
@@ -542,7 +545,7 @@ public class ForeignFunctions {
         }
     }
 
-    @RBuiltin(name = ".External", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"})
+    @RBuiltin(name = ".External", kind = PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}, behavior = COMPLEX)
     public abstract static class DotExternal extends LookupAdapter {
 
         private final BranchProfile errorProfile = BranchProfile.create();
@@ -574,8 +577,9 @@ public class ForeignFunctions {
                     return getExternalModelBuiltinNode("termsform");
                 case "Rprof":
                     return RprofNodeGen.create();
-                case "unzip":
                 case "Rprofmem":
+                    return RprofmemNodeGen.create();
+                case "unzip":
                 case "addhistory":
                 case "loadhistory":
                 case "savehistory":
@@ -628,7 +632,7 @@ public class ForeignFunctions {
         }
     }
 
-    @RBuiltin(name = ".External2", visibility = RVisibility.CUSTOM, kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"})
+    @RBuiltin(name = ".External2", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}, behavior = COMPLEX)
     public abstract static class DotExternal2 extends LookupAdapter {
         private static final Object CALL = "call";
         private static final Object OP = "op";
@@ -701,7 +705,7 @@ public class ForeignFunctions {
         }
     }
 
-    @RBuiltin(name = ".External.graphics", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"})
+    @RBuiltin(name = ".External.graphics", kind = PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}, behavior = COMPLEX)
     public abstract static class DotExternalGraphics extends LookupAdapter {
 
         private final BranchProfile errorProfile = BranchProfile.create();
@@ -757,7 +761,7 @@ public class ForeignFunctions {
         }
     }
 
-    @RBuiltin(name = ".Call.graphics", kind = RBuiltinKind.PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"})
+    @RBuiltin(name = ".Call.graphics", kind = PRIMITIVE, parameterNames = {".NAME", "...", "PACKAGE"}, behavior = COMPLEX)
     public abstract static class DotCallGraphics extends LookupAdapter {
 
         private final BranchProfile errorProfile = BranchProfile.create();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java
index 457b1c404ba589315ba895b2bb3173381b463041..b75899478f34a0c3723eae0c6498afa13336a72e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/DoubleVectorPrinter.java
@@ -157,9 +157,9 @@ final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVector> {
         /*
          * F Format: use "F" format WHENEVER we use not more space than 'E' and still satisfy
          * 'R_print.digits' {but as if nsmall==0 !}
-         * 
+         *
          * E Format has the form [S]X[.XXX]E+XX[X]
-         * 
+         *
          * This is indicated by setting *e to non-zero (usually 1) If the additional exponent digit
          * is required *e is set to 2
          */
@@ -247,7 +247,7 @@ final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVector> {
          * for a number x , determine sgn = 1_{x < 0} {0/1} kpower = Exponent of 10; nsig =
          * min(R_print.digits, #{significant digits of alpha}) roundingwidens = 1 if rounding causes
          * x to increase in width, 0 otherwise
-         * 
+         *
          * where |x| = alpha * 10^kpower and 1 <= alpha < 10
          */
         double alpha;
@@ -382,8 +382,7 @@ final class DoubleVectorPrinter extends VectorPrinter<RAbstractDoubleVector> {
                 buff = snprintf(NB, fmt, x);
             }
         } else { /* e = 0 */
-            StringBuilder sb = new StringBuilder("#.#");
-            DecimalFormat df = new DecimalFormat(sb.toString());
+            DecimalFormat df = new DecimalFormat("#.#");
             df.setRoundingMode(RoundingMode.HALF_EVEN);
             df.setDecimalSeparatorAlwaysShown(false);
             df.setMinimumFractionDigits(d);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FunctionPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FunctionPrinter.java
index e8c0bc6d2bfbe8885f05e6d944447f5b13b864f2..0123f7ed4a1678aa21448eed250b79dea1329c99 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FunctionPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/FunctionPrinter.java
@@ -31,7 +31,7 @@ import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RDeparse;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.nodes.RNode;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/S4ObjectPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/S4ObjectPrinter.java
index ad3a084a0509bca41911f3b84dbcb649a000d033..b97c2ce73a9f1d9c34be5e0bd899e93f4edfde28 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/S4ObjectPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/S4ObjectPrinter.java
@@ -60,7 +60,7 @@ final class S4ObjectPrinter implements ValuePrinter<RS4Object> {
 
     static void printS4(PrintContext printCtx, Object o) {
         Frame frame = com.oracle.truffle.r.runtime.Utils.getActualCurrentFrame();
-        RContext.getEngine().evalFunction(createShowFunction(frame), null, null, o);
+        RContext.getEngine().evalFunction(createShowFunction(frame), null, null, null, o);
         // The show function prints an additional new line character. The following attribute
         // instructs the ValuePrinter.println method not to print the new line since it was
         // already printed.
@@ -68,6 +68,6 @@ final class S4ObjectPrinter implements ValuePrinter<RS4Object> {
     }
 
     private static RFunction createShowFunction(Frame frame) {
-        return ReadVariableNode.lookupFunction("show", frame, false);
+        return ReadVariableNode.lookupFunction("show", frame);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java
index 5020e439eb0315feca814e87cfa54aa8fe1ef7bc..d124cf1fcf353ea953fba02552e7f63f02f3d5d2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java
@@ -25,7 +25,11 @@ package com.oracle.truffle.r.nodes.builtin.base.printer;
 import java.io.IOException;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNode;
 import com.oracle.truffle.r.nodes.builtin.base.Inherits;
 import com.oracle.truffle.r.nodes.builtin.base.InheritsNodeGen;
@@ -130,4 +134,17 @@ public abstract class ValuePrinterNode extends RBaseNode {
     private static void prettyPrint(Object o, PrintContext printCtx) throws IOException {
         ValuePrinters.INSTANCE.println(o, printCtx);
     }
+
+    public static String prettyPrint(final Object value) {
+        return (String) Truffle.getRuntime().createCallTarget(new RootNode(TruffleLanguage.class, null, null) {
+
+            @Child ValuePrinterNode valuePrinterNode = ValuePrinterNodeGen.create();
+
+            @Override
+            public Object execute(VirtualFrame frame) {
+                return valuePrinterNode.prettyPrint(value, AnyVectorToStringVectorWriter::new);
+            }
+        }).call(value);
+    }
+
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
index 715197077b669b86298e6cb8a764943bb7521559..ae7d75ffc1eff9aaa109ff114b61611e4fff620b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
@@ -22,29 +22,25 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
-import java.io.IOException;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RChannel;
 import com.oracle.truffle.r.runtime.RCmdOptions;
 import com.oracle.truffle.r.runtime.RCmdOptions.Client;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RStartParams;
 import com.oracle.truffle.r.runtime.RSource;
-import com.oracle.truffle.r.runtime.RVisibility;
-import com.oracle.truffle.r.runtime.conn.StdConnections;
+import com.oracle.truffle.r.runtime.RStartParams;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.ContextInfo;
-import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -53,38 +49,32 @@ import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
+/**
+ * The FastR builtins that allow multiple "virtual" R sessions potentially executing in parallel.
+ */
 public class FastRContext {
 
-    @RBuiltin(name = ".fastr.context.create", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"args", "kind"})
-    public abstract static class Create extends RBuiltinNode {
+    private abstract static class CastHelper extends RBuiltinNode {
+        protected void exprs(CastBuilder casts) {
+            casts.arg("exprs").asStringVector().mustBe(nullValue().not().and(notEmpty()));
+        }
 
-        @Override
-        public Object[] getDefaultParameterValues() {
-            return new Object[]{"", "SHARE_NOTHING"};
+        protected void kind(CastBuilder casts) {
+            casts.arg("kind").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst().notNA().mustBe(
+                            equalTo(RContext.ContextKind.SHARE_NOTHING.name()).or(equalTo(RContext.ContextKind.SHARE_PARENT_RW.name()).or(equalTo(RContext.ContextKind.SHARE_PARENT_RO.name()))));
         }
 
-        @Specialization
-        @TruffleBoundary
-        protected int create(RAbstractStringVector args, RAbstractStringVector kindVec) {
-            try {
-                RContext.ContextKind kind = RContext.ContextKind.valueOf(kindVec.getDataAt(0));
-                RStartParams startParams = new RStartParams(RCmdOptions.parseArguments(Client.RSCRIPT, args.materialize().getDataCopy(), false), false);
-                return ContextInfo.createDeferred(startParams, kind, RContext.getInstance(), RContext.getInstance().getConsoleHandler());
-            } catch (IllegalArgumentException ex) {
-                throw RError.error(this, RError.Message.GENERIC, "invalid kind argument");
-            }
+        protected void args(CastBuilder casts) {
+            casts.arg("args").asStringVector().mustBe(nullValue().not().and(notEmpty()));
         }
 
-        @SuppressWarnings("unused")
-        @Fallback
-        protected int create(Object args, Object kindVec) {
-            throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
+        protected void pc(CastBuilder casts) {
+            casts.arg("pc").asIntegerVector().findFirst().notNA().mustBe(gt(0));
         }
     }
 
-    @RBuiltin(name = ".fastr.context.get", kind = RBuiltinKind.PRIMITIVE, parameterNames = {})
+    @RBuiltin(name = ".fastr.context.get", kind = PRIMITIVE, parameterNames = {}, behavior = READS_STATE)
     public abstract static class Get extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -93,58 +83,58 @@ public class FastRContext {
         }
     }
 
-    public abstract static class Print extends RExternalBuiltinNode.Arg1 {
-        @Specialization
-        @TruffleBoundary
-        protected RNull print(RAbstractIntVector ctxt) {
-            if (ctxt.getLength() != 1) {
-                throw RError.error(this, RError.Message.INVALID_ARGUMENT, "context");
-            }
-            int contextId = ctxt.getDataAt(0);
-            ContextInfo info = ContextInfo.get(contextId);
-            try {
-                if (info == null) {
-                    StdConnections.getStdout().writeString("obsolete context: " + contextId, true);
-                } else {
-                    StdConnections.getStdout().writeString("context: " + contextId, true);
-                }
-                return RNull.instance;
-            } catch (IOException ex) {
-                throw RError.error(this, RError.Message.GENERIC, ex.getMessage());
-            }
+    /**
+     * Similar to {@code .fastr.context.eval} but the invoking thread does not wait for completion,
+     * which is done by {@code .fastr.context.join}. The result is a vector that should be passed to
+     * {@code .fastr.context.join}.
+     *
+     */
+    @RBuiltin(name = ".fastr.context.spawn", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind", "args"}, behavior = COMPLEX)
+    public abstract static class Spawn extends CastHelper {
+        @Override
+        public Object[] getDefaultParameterValues() {
+            return new Object[]{RMissing.instance, 1, "SHARE_NOTHING", ""};
+        }
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            exprs(casts);
+            pc(casts);
+            kind(casts);
+            args(casts);
         }
-    }
 
-    @RBuiltin(name = ".fastr.context.spawn", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"contexts", "exprs"})
-    public abstract static class Spawn extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
-        protected RNull spawn(RAbstractIntVector contexts, RAbstractStringVector exprs) {
-            RContext.EvalThread[] threads = new RContext.EvalThread[contexts.getLength()];
-            for (int i = 0; i < threads.length; i++) {
-                ContextInfo info = checkContext(contexts.getDataAt(i), this);
-                threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % threads.length), RSource.Internal.CONTEXT_EVAL));
+        protected RIntVector spawn(RAbstractStringVector exprs, int pc, String kind, RAbstractStringVector args) {
+            RContext.ContextKind contextKind = RContext.ContextKind.valueOf(kind);
+            RContext.EvalThread[] threads = new RContext.EvalThread[pc];
+            int[] data = new int[pc];
+            for (int i = 0; i < pc; i++) {
+                ContextInfo info = createContextInfo(contextKind, args);
+                threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL));
+                data[i] = info.getId();
             }
-            for (int i = 0; i < threads.length; i++) {
+            for (int i = 0; i < pc; i++) {
                 threads[i].start();
             }
-            return RNull.instance;
+            return RDataFactory.createIntVector(data, RDataFactory.COMPLETE_VECTOR);
         }
 
-        @SuppressWarnings("unused")
-        @Fallback
-        protected RNull spawn(Object contexts, Object exprs) {
-            throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
-        }
     }
 
-    @RBuiltin(name = ".fastr.context.join", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"contexts"})
+    @RBuiltin(name = ".fastr.context.join", visibility = OFF, kind = PRIMITIVE, parameterNames = {"handle"}, behavior = COMPLEX)
     public abstract static class Join extends RBuiltinNode {
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("handle").asIntegerVector().mustBe(nullValue().not().and(notEmpty()));
+        }
+
         @Specialization
-        protected RNull eval(RAbstractIntVector contexts) {
+        protected RNull eval(RAbstractIntVector handle) {
             try {
-                for (int i = 0; i < contexts.getLength(); i++) {
-                    Thread thread = RContext.EvalThread.threads.get(contexts.getDataAt(i));
+                for (int i = 0; i < handle.getLength(); i++) {
+                    Thread thread = RContext.EvalThread.threads.get(handle.getDataAt(i));
                     if (thread == null) {
                         // already done
                         continue;
@@ -159,187 +149,184 @@ public class FastRContext {
             return RNull.instance;
         }
 
-        @SuppressWarnings("unused")
-        @Fallback
-        protected RNull join(Object contexts) {
-            throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
-        }
     }
 
     /**
-     * The result of eval is a list of lists. The top level list has the same number of entries as
-     * the number of contexts. The sublist contains the result of the evaluation with name "result".
-     * It may also have an attribute "error" if the evaluation threw an exception, in which case the
-     * result will be NA.
+     * Evaluate expressions in {@code pc} new contexts of type {@code kind}, with the expression
+     * taken from the expression in the usual R repeating mode. The invoking context (thread) waits
+     * for completion of all the sub-contexts. {@code args} provides the command line arguments to
+     * the contexts - this is the same for all.
+     *
+     * Each evaluation is run in a new {@link RContext}/{@link PolyglotEngine}. The result is a list
+     * of lists. The top level list has the same number of entries as the number of contexts. The
+     * sublist contains the result of the evaluation with name "result". It may also have an
+     * attribute "error" if the evaluation threw an exception, in which case the result will be NA.
      */
-    @RBuiltin(name = ".fastr.context.eval", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"contexts", "exprs", "par"})
-    public abstract static class Eval extends RBuiltinNode {
+    @RBuiltin(name = ".fastr.context.eval", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind", "args"}, behavior = COMPLEX)
+    public abstract static class Eval extends CastHelper {
         @Override
         public Object[] getDefaultParameterValues() {
-            return new Object[]{RMissing.instance, RMissing.instance, RRuntime.LOGICAL_FALSE};
+            return new Object[]{RMissing.instance, 1, "SHARE_NOTHING", ""};
+        }
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            exprs(casts);
+            pc(casts);
+            kind(casts);
+            args(casts);
         }
 
         @Specialization
         @TruffleBoundary
-        protected Object eval(RAbstractIntVector contexts, RAbstractStringVector exprs, byte par) {
-            Object[] results = new Object[contexts.getLength()];
-            if (RRuntime.fromLogical(par)) {
-                RContext.EvalThread[] threads = new RContext.EvalThread[contexts.getLength()];
-                for (int i = 0; i < threads.length; i++) {
-                    ContextInfo info = checkContext(contexts.getDataAt(i), this);
-                    threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % threads.length), RSource.Internal.CONTEXT_EVAL));
+        protected Object eval(RAbstractStringVector exprs, int pc, String kind, RAbstractStringVector args) {
+            RContext.ContextKind contextKind = RContext.ContextKind.valueOf(kind);
+
+            Object[] results = new Object[pc];
+            if (pc == 1) {
+                ContextInfo info = createContextInfo(contextKind, args);
+                PolyglotEngine vm = info.createVM();
+                results[0] = RContext.EvalThread.run(vm, info, RSource.fromTextInternal(exprs.getDataAt(0), RSource.Internal.CONTEXT_EVAL));
+            } else {
+                // separate threads that run in parallel; invoking thread waits for completion
+                RContext.EvalThread[] threads = new RContext.EvalThread[pc];
+                for (int i = 0; i < pc; i++) {
+                    ContextInfo info = createContextInfo(contextKind, args);
+                    threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL));
                 }
-                for (int i = 0; i < threads.length; i++) {
+                for (int i = 0; i < pc; i++) {
                     threads[i].start();
                 }
                 try {
-                    for (int i = 0; i < threads.length; i++) {
+                    for (int i = 0; i < pc; i++) {
                         threads[i].join();
                         results[i] = threads[i].getEvalResult();
                     }
                 } catch (InterruptedException ex) {
                     throw RError.error(this, RError.Message.GENERIC, "error finishing eval thread");
                 }
-            } else {
-                for (int i = 0; i < contexts.getLength(); i++) {
-                    ContextInfo info = checkContext(contexts.getDataAt(i), this);
-                    PolyglotEngine vm = info.createVM();
-                    try {
-                        Source source = RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL);
-                        PolyglotEngine.Value resultValue = vm.eval(source);
-                        results[i] = RContext.EvalThread.createEvalResult(resultValue);
-                    } catch (ParseException e) {
-                        e.report(info.getConsoleHandler());
-                        results[i] = RContext.EvalThread.createErrorResult(e.getMessage());
-                    } catch (IOException e) {
-                        // This is an unhandled exception, e.g. RInternalError
-                        Throwable cause = e.getCause();
-                        if (cause instanceof RInternalError) {
-                            info.getConsoleHandler().println("internal error: " + e.getMessage() + " (see fastr_errors.log)");
-                            RInternalError.reportError(e);
-                        }
-                        results[i] = RContext.EvalThread.createErrorResult(e.getCause().getMessage());
-                    } finally {
-                        vm.dispose();
-                    }
-                }
             }
             return RDataFactory.createList(results);
         }
 
-        @SuppressWarnings("unused")
-        @Fallback
-        protected RNull eval(Object contexts, Object exprs, Object par) {
-            throw RError.error(this, RError.Message.INVALID_OR_UNIMPLEMENTED_ARGUMENTS);
-        }
     }
 
-    private static ContextInfo checkContext(int contextId, RBaseNode invokingNode) throws RError {
-        ContextInfo info = ContextInfo.get(contextId);
-        if (info == null) {
-            throw RError.error(invokingNode, RError.Message.GENERIC, "no context: " + contextId);
-        } else {
-            return info;
-        }
+    private static ContextInfo createContextInfo(RContext.ContextKind contextKind, RAbstractStringVector args) {
+        RStartParams startParams = new RStartParams(RCmdOptions.parseArguments(Client.RSCRIPT, args.materialize().getDataCopy(), false), false);
+        ContextInfo info = ContextInfo.create(startParams, contextKind, RContext.getInstance(), RContext.getInstance().getConsoleHandler());
+        return info;
     }
 
-    private static int wrongChannelArg(RBaseNode baseNode, Object arg, String argName) {
-        if (!(arg instanceof RAbstractIntVector)) {
-            throw RError.error(baseNode, RError.Message.INVALID_ARG_TYPE);
-        } else {
-            // guard failed
-            throw RError.error(baseNode, RError.Message.WRONG_LENGTH_ARG, argName);
+    private abstract static class ChannelCastAdapter extends RBuiltinNode {
+        protected void key(CastBuilder casts) {
+            casts.arg("key").asIntegerVector().mustBe(nullValue().not().and(notEmpty())).findFirst();
+        }
+
+        protected void id(CastBuilder casts) {
+            casts.arg("id").asIntegerVector().mustBe(nullValue().not().and(notEmpty())).findFirst();
         }
     }
 
-    @RBuiltin(name = ".fastr.channel.create", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"key"})
-    public abstract static class CreateChannel extends RBuiltinNode {
-        @Specialization(guards = "key.getLength() == 1")
-        @TruffleBoundary
-        protected int createChannel(RAbstractIntVector key) {
-            return RChannel.createChannel(key.getDataAt(0));
+    @RBuiltin(name = ".fastr.channel.create", kind = PRIMITIVE, parameterNames = {"key"}, behavior = COMPLEX)
+    public abstract static class CreateChannel extends ChannelCastAdapter {
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            key(casts);
         }
 
-        @Fallback
-        protected int error(Object key) {
-            return wrongChannelArg(this, key, "key");
+        @Specialization
+        @TruffleBoundary
+        protected int createChannel(int key) {
+            return RChannel.createChannel(key);
         }
+
     }
 
-    @RBuiltin(name = ".fastr.channel.get", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"key"})
-    public abstract static class GetChannel extends RBuiltinNode {
-        @Specialization(guards = "key.getLength() == 1")
-        @TruffleBoundary
-        protected int getChannel(RAbstractIntVector key) {
-            return RChannel.getChannel(key.getDataAt(0));
+    @RBuiltin(name = ".fastr.channel.get", kind = PRIMITIVE, parameterNames = {"key"}, behavior = COMPLEX)
+    public abstract static class GetChannel extends ChannelCastAdapter {
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            key(casts);
         }
 
-        @Fallback
-        protected int error(Object key) {
-            return wrongChannelArg(this, key, "key");
+        @Specialization
+        @TruffleBoundary
+        protected int getChannel(int key) {
+            return RChannel.getChannel(key);
         }
+
     }
 
-    @RBuiltin(name = ".fastr.channel.close", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id"})
-    public abstract static class CloseChannel extends RBuiltinNode {
-        @Specialization(guards = "id.getLength() == 1")
+    @RBuiltin(name = ".fastr.channel.close", visibility = OFF, kind = PRIMITIVE, parameterNames = {"id"}, behavior = COMPLEX)
+    public abstract static class CloseChannel extends ChannelCastAdapter {
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            id(casts);
+        }
+
+        @Specialization
         @TruffleBoundary
-        protected RNull getChannel(RAbstractIntVector id) {
-            RChannel.closeChannel(id.getDataAt(0));
+        protected RNull getChannel(int id) {
+            RChannel.closeChannel(id);
             return RNull.instance;
         }
 
-        @Fallback
-        protected int error(Object id) {
-            return wrongChannelArg(this, id, "id");
-        }
     }
 
-    @RBuiltin(name = ".fastr.channel.send", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id", "data"})
-    public abstract static class ChannelSend extends RBuiltinNode {
-        @Specialization(guards = "id.getLength() == 1")
+    @RBuiltin(name = ".fastr.channel.send", visibility = OFF, kind = PRIMITIVE, parameterNames = {"id", "data"}, behavior = COMPLEX)
+    public abstract static class ChannelSend extends ChannelCastAdapter {
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            id(casts);
+        }
+
+        @Specialization
         @TruffleBoundary
-        protected RNull send(RAbstractIntVector id, Object data) {
-            RChannel.send(id.getDataAt(0), data);
+        protected RNull send(int id, Object data) {
+            RChannel.send(id, data);
             return RNull.instance;
         }
 
-        @Fallback
-        protected int error(Object id, @SuppressWarnings("unused") Object data) {
-            return wrongChannelArg(this, id, "id");
-        }
     }
 
-    @RBuiltin(name = ".fastr.channel.receive", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id"})
-    public abstract static class ChannelReceive extends RBuiltinNode {
-        @Specialization(guards = "id.getLength() == 1")
-        @TruffleBoundary
-        protected Object receive(RAbstractIntVector id) {
-            return RChannel.receive(id.getDataAt(0));
+    @RBuiltin(name = ".fastr.channel.receive", kind = PRIMITIVE, parameterNames = {"id"}, behavior = COMPLEX)
+    public abstract static class ChannelReceive extends ChannelCastAdapter {
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            id(casts);
         }
 
-        @Fallback
-        protected int error(Object id) {
-            return wrongChannelArg(this, id, "id");
+        @Specialization
+        @TruffleBoundary
+        protected Object receive(int id) {
+            return RChannel.receive(id);
         }
+
     }
 
-    @RBuiltin(name = ".fastr.channel.poll", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"id"})
-    public abstract static class ChannelPoll extends RBuiltinNode {
-        @Specialization(guards = "id.getLength() == 1")
-        @TruffleBoundary
-        protected Object poll(RAbstractIntVector id) {
-            return RChannel.poll(id.getDataAt(0));
+    @RBuiltin(name = ".fastr.channel.poll", kind = PRIMITIVE, parameterNames = {"id"}, behavior = COMPLEX)
+    public abstract static class ChannelPoll extends ChannelCastAdapter {
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            id(casts);
         }
 
-        @Fallback
-        protected int error(Object id) {
-            return wrongChannelArg(this, id, "id");
+        @Specialization
+        @TruffleBoundary
+        protected Object poll(int id) {
+            return RChannel.poll(id);
         }
+
     }
 
-    @RBuiltin(name = ".fastr.channel.select", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"ids"})
+    @RBuiltin(name = ".fastr.channel.select", kind = PRIMITIVE, parameterNames = {"ids"}, behavior = COMPLEX)
     public abstract static class ChannelSelect extends RBuiltinNode {
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("ids").mustBe(instanceOf(RList.class));
+        }
+
         @Specialization
         @TruffleBoundary
         protected RList select(RList nodes) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRDebug.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRDebug.java
index 9f2010404f651cfdd003064896f5be8304779a17..e58eaf4b62f6fd9328fd27a758c0052a067e6e2b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRDebug.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRDebug.java
@@ -22,20 +22,25 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.FastROptions;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = ".fastr.debug", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"values"})
+@RBuiltin(name = ".fastr.debug", visibility = OFF, kind = PRIMITIVE, parameterNames = {"values"}, behavior = COMPLEX)
 public abstract class FastRDebug extends RBuiltinNode {
+
     @Specialization
+    @TruffleBoundary
     protected RNull debug(RAbstractStringVector vec) {
         for (int i = 0; i < vec.getLength(); i++) {
             FastROptions.debugUpdate(vec.getDataAt(i));
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java
index 977de8fc70a5c51aefd9b21f23c0ab68316a6c81..580a10472010aacda10f4bc56192bca9b2f80c1a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRFunctionProfiler.java
@@ -22,26 +22,26 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
-
-import java.util.ArrayList;
-
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.missingValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.missingValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.instrumentation.RFunctionProfiler;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -55,7 +55,7 @@ import com.oracle.truffle.tools.Profiler;
 
 public class FastRFunctionProfiler {
 
-    @RBuiltin(name = ".fastr.profiler.create", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func", "mode"})
+    @RBuiltin(name = ".fastr.profiler.create", visibility = OFF, kind = PRIMITIVE, parameterNames = {"func", "mode"}, behavior = COMPLEX)
     public abstract static class Create extends RBuiltinNode {
         private static final int COUNTING = 1;
         private static final int TIMING = 2;
@@ -112,7 +112,7 @@ public class FastRFunctionProfiler {
 
     }
 
-    @RBuiltin(name = ".fastr.profiler.get", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func", "threshold", "scale"})
+    @RBuiltin(name = ".fastr.profiler.get", kind = PRIMITIVE, parameterNames = {"func", "threshold", "scale"}, behavior = COMPLEX)
     public abstract static class Get extends RBuiltinNode {
 
         private static final RStringVector COLNAMES = RDataFactory.createStringVector(new String[]{"Invocations", "TotalTime", "SelfTime"}, RDataFactory.COMPLETE_VECTOR);
@@ -235,7 +235,7 @@ public class FastRFunctionProfiler {
         }
     }
 
-    @RBuiltin(name = ".fastr.profiler.reset", kind = RBuiltinKind.PRIMITIVE, parameterNames = {}, visibility = RVisibility.OFF)
+    @RBuiltin(name = ".fastr.profiler.reset", visibility = OFF, kind = PRIMITIVE, parameterNames = {}, behavior = COMPLEX)
     public abstract static class Reset extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
@@ -245,7 +245,7 @@ public class FastRFunctionProfiler {
         }
     }
 
-    @RBuiltin(name = ".fastr.profiler.clear", kind = RBuiltinKind.PRIMITIVE, parameterNames = {}, visibility = RVisibility.OFF)
+    @RBuiltin(name = ".fastr.profiler.clear", visibility = OFF, kind = PRIMITIVE, parameterNames = {}, behavior = COMPLEX)
     public abstract static class Clear extends RBuiltinNode {
         @Specialization
         @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRIdentity.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRIdentity.java
index 72afb897490df85bbbd3f7e4e8e2a2047a74b4ea..cb129360b7b21c4c5a513fab08d6850f4524f15a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRIdentity.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRIdentity.java
@@ -22,15 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 
-@RBuiltin(name = ".fastr.identity", kind = PRIMITIVE, parameterNames = {""})
+@RBuiltin(name = ".fastr.identity", kind = PRIMITIVE, parameterNames = {""}, behavior = COMPLEX)
 public abstract class FastRIdentity extends RBuiltinNode {
+
     @Specialization
     @TruffleBoundary
     protected int typeof(Object x) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInspect.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInspect.java
index 236507ea80cd6842ac75d4a4b2aa167a94006940..b3ee8227c3d2db6bb72e83df13246bc4a9b42f40 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInspect.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInspect.java
@@ -22,18 +22,20 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RNull;
 
 /**
  * Just a convenient way to inspect values in the Java debugger from the R shell.
  */
-@RBuiltin(name = ".fastr.inspect", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"..."})
+@RBuiltin(name = ".fastr.inspect", visibility = OFF, kind = PRIMITIVE, parameterNames = {"..."}, behavior = COMPLEX)
 public abstract class FastRInspect extends RBuiltinNode {
     @Specialization
     public Object call(@SuppressWarnings("unused") RArgsValuesAndNames args) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
index 69681f4e9f31ac7fd222b294e53d0ca4d6e407a6..550f3efb3191bf745599764bd3fc6b9f598b0d80 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
@@ -22,6 +22,12 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import java.io.IOException;
 
 import com.oracle.truffle.api.CallTarget;
@@ -29,31 +35,31 @@ import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 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.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RSource;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 
 public class FastRInterop {
-    @RBuiltin(name = ".fastr.interop.eval", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"mimeType", "source"})
+
+    @RBuiltin(name = ".fastr.interop.eval", visibility = OFF, kind = PRIMITIVE, parameterNames = {"mimeType", "source"}, behavior = COMPLEX)
     public abstract static class Eval extends RBuiltinNode {
 
         @Override
         protected void createCasts(CastBuilder casts) {
-            casts.firstStringWithError(0, Message.INVALID_ARGUMENT, "mimeType");
-            casts.firstStringWithError(1, Message.INVALID_ARGUMENT, "source");
+            casts.arg("mimeType").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+            casts.arg("source").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
         }
 
         protected CallTarget parse(String mimeType, String source) {
@@ -74,9 +80,9 @@ public class FastRInterop {
 
         @SuppressWarnings("unused")
         @Specialization(guards = {"cachedMimeType != null", "cachedMimeType.equals(mimeType)", "cachedSource != null", "cachedSource.equals(source)"})
-        protected Object evalCached(VirtualFrame frame, String mimeType, String source, //
-                        @Cached("mimeType") String cachedMimeType, //
-                        @Cached("source") String cachedSource, //
+        protected Object evalCached(VirtualFrame frame, String mimeType, String source,
+                        @Cached("mimeType") String cachedMimeType,
+                        @Cached("source") String cachedSource,
                         @Cached("createCall(mimeType, source)") DirectCallNode call) {
             return call.call(frame, EMPTY_OBJECT_ARRAY);
         }
@@ -92,37 +98,54 @@ public class FastRInterop {
         }
     }
 
-    @RBuiltin(name = ".fastr.interop.export", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"name", "value"})
+    @RBuiltin(name = ".fastr.interop.export", visibility = OFF, kind = PRIMITIVE, parameterNames = {"name", "value"}, behavior = COMPLEX)
     public abstract static class Export extends RBuiltinNode {
 
-        @Specialization
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("name").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+            casts.boxPrimitive(1);
+        }
+
+        @Specialization(guards = "!isRMissing(value)")
         @TruffleBoundary
-        protected Object exportSymbol(Object name, RTypedValue value) {
-            String stringName = RRuntime.asString(name);
-            if (stringName == null) {
+        protected Object exportSymbol(String name, RTypedValue value) {
+            if (name == null) {
                 throw RError.error(this, RError.Message.INVALID_ARG_TYPE, "name");
             }
-            RContext.getInstance().getExportedSymbols().put(stringName, value);
+            RContext.getInstance().getExportedSymbols().put(name, value);
             return RNull.instance;
         }
+
+        @Specialization
+        @TruffleBoundary
+        protected Object exportSymbol(@SuppressWarnings("unused") String name, @SuppressWarnings("unused") RMissing value) {
+            throw RError.error(this, Message.ARGUMENT_MISSING, "value");
+        }
+
+        @Fallback
+        @TruffleBoundary
+        protected Object exportSymbol(@SuppressWarnings("unused") Object name, @SuppressWarnings("unused") Object value) {
+            throw RError.error(this, Message.GENERIC, "only R language objects can be exported");
+        }
     }
 
-    @RBuiltin(name = ".fastr.interop.import", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"name"})
+    @RBuiltin(name = ".fastr.interop.import", visibility = OFF, kind = PRIMITIVE, parameterNames = {"name"}, behavior = COMPLEX)
     public abstract static class Import extends RBuiltinNode {
 
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("name").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
+        }
+
         @Specialization
         @TruffleBoundary
-        protected Object importSymbol(Object name) {
-            String stringName = RRuntime.asString(name);
-            if (stringName == null) {
-                throw RError.error(this, RError.Message.INVALID_ARG_TYPE, "name");
-            }
-            Object object = RContext.getInstance().getEnv().importSymbol(stringName);
+        protected Object importSymbol(String name) {
+            Object object = RContext.getInstance().getEnv().importSymbol(name);
             if (object == null) {
-                throw RError.error(this, RError.Message.NO_IMPORT_OBJECT, stringName);
+                throw RError.error(this, RError.Message.NO_IMPORT_OBJECT, name);
             }
             return object;
         }
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRRefCountInfo.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRRefCountInfo.java
index ebbf9c234093fb3b997aa08ed58417a696e61447..600608775cb0b6820c71684c03804b9c3453b466 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRRefCountInfo.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRRefCountInfo.java
@@ -22,11 +22,12 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
-import static com.oracle.truffle.r.runtime.RBuiltinKind.PRIMITIVE;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RShareable;
 
 /**
@@ -35,8 +36,9 @@ import com.oracle.truffle.r.runtime.data.RShareable;
  * returns -1 for non-shareable, 0 for private, 1 for temp, 2 for shared and SHARED_PERMANENT_VAL
  * for permanent shared
  */
-@RBuiltin(name = ".fastr.refcountinfo", kind = PRIMITIVE, parameterNames = {""})
+@RBuiltin(name = ".fastr.refcountinfo", kind = PRIMITIVE, parameterNames = {""}, behavior = COMPLEX)
 public abstract class FastRRefCountInfo extends RBuiltinNode {
+
     @Specialization
     protected int refcount(Object x) {
         if (x instanceof RShareable) {
@@ -54,5 +56,4 @@ public abstract class FastRRefCountInfo extends RBuiltinNode {
             return -1;
         }
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRStackTrace.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRStackTrace.java
index 0d37d83331ae3fcccff86910ab7a6eb1c9710cbd..8c70d8e2534bfec03b3a9d25494ef3064b3b5769 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRStackTrace.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRStackTrace.java
@@ -22,19 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RNull;
 
-@RBuiltin(name = ".fastr.stacktrace", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"print.frame.contents"})
+@RBuiltin(name = ".fastr.stacktrace", visibility = OFF, kind = PRIMITIVE, parameterNames = {"print.frame.contents"}, behavior = COMPLEX)
 public abstract class FastRStackTrace extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSyntaxTree.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSyntaxTree.java
index 4155314e9ac0f5fe2b2c7fd4ddcaded2047864cf..ed3638bc5c18fe145211ffb5c76060e24d55ba65 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSyntaxTree.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSyntaxTree.java
@@ -22,6 +22,10 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import java.io.IOException;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -33,11 +37,9 @@ import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -66,7 +68,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxVisitor;
  * </ol>
  *
  */
-@RBuiltin(name = ".fastr.syntaxtree", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func", "visitMode", "printSource", "printTags"})
+@RBuiltin(name = ".fastr.syntaxtree", visibility = OFF, kind = PRIMITIVE, parameterNames = {"func", "visitMode", "printSource", "printTags"}, behavior = IO)
 public abstract class FastRSyntaxTree extends RBuiltinNode {
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRThrowIt.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRThrowIt.java
index a3b8d6ddf73aa0f092fc42ed659e1cde2ab1d9b7..8baed140fb8b258b6cce56d78c3dffed53e90fee 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRThrowIt.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRThrowIt.java
@@ -22,19 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.JumpToTopLevelException;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = ".fastr.throw", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"name"})
+@RBuiltin(name = ".fastr.throw", kind = PRIMITIVE, parameterNames = {"name"}, behavior = COMPLEX)
 public abstract class FastRThrowIt extends RBuiltinNode {
     @Specialization
     @TruffleBoundary
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTrace.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTrace.java
index 56800ede810a77b62d00bcce711dd3207ce80285..470f42fd0914c3dffa37ffd4c107dbf93857b17c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTrace.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTrace.java
@@ -22,6 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -41,12 +46,10 @@ import com.oracle.truffle.r.nodes.builtin.helpers.TraceHandling;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -106,7 +109,7 @@ public class FastRTrace {
 
     }
 
-    @RBuiltin(name = ".fastr.trace", visibility = RVisibility.CUSTOM, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"what", "tracer", "exit", "at", "print", "signature", "where"})
+    @RBuiltin(name = ".fastr.trace", visibility = CUSTOM, kind = PRIMITIVE, parameterNames = {"what", "tracer", "exit", "at", "print", "signature", "where"}, behavior = COMPLEX)
     public abstract static class Trace extends Helper {
 
         @Child private TraceFunctions.PrimTrace primTrace;
@@ -171,7 +174,7 @@ public class FastRTrace {
 
     }
 
-    @RBuiltin(name = ".fastr.untrace", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"what", "signature", "where"})
+    @RBuiltin(name = ".fastr.untrace", visibility = OFF, kind = PRIMITIVE, parameterNames = {"what", "signature", "where"}, behavior = COMPLEX)
     public abstract static class Untrace extends Helper {
 
         @Child private TraceFunctions.PrimUnTrace primUnTrace;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTree.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTree.java
index a3c1aa478d98bdcc807bee149692ba8791858e4b..be5c0add5012747bd87559163cab6ff6cfbdafc9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTree.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTree.java
@@ -22,20 +22,22 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.IO;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.NodeUtil;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
 
-@RBuiltin(name = ".fastr.tree", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"func", "verbose"})
+@RBuiltin(name = ".fastr.tree", visibility = OFF, kind = PRIMITIVE, parameterNames = {"func", "verbose"}, behavior = IO)
 public abstract class FastRTree extends RBuiltinNode {
     @Override
     public Object[] getDefaultParameterValues() {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTreeStats.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTreeStats.java
index ec91ca63467caf86fee87c2381f1d6ce1f4bf360..968cf379a3f1aea483d9fe70459b4a1dd4aa4265 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTreeStats.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTreeStats.java
@@ -22,6 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -31,8 +34,7 @@ import com.oracle.truffle.api.nodes.NodeVisitor;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -43,7 +45,7 @@ import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
-@RBuiltin(name = ".fastr.treestats", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"obj"})
+@RBuiltin(name = ".fastr.treestats", kind = PRIMITIVE, parameterNames = {"obj"}, behavior = COMPLEX)
 public abstract class FastRTreeStats extends RBuiltinNode {
 
     private static final RStringVector COLNAMES = RDataFactory.createStringVector(new String[]{"Total", "Syntax", "Non-Syntax"}, RDataFactory.COMPLETE_VECTOR);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java
index 4b630fc9109e8ae9b42871d9b56245345a5356ca..35cd80b4af5e7dd96c2554d5c5831590d52952e6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java
@@ -11,15 +11,17 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
+import static com.oracle.truffle.r.runtime.RVisibility.OFF;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import java.util.Arrays;
 
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -33,7 +35,7 @@ import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
  * FastR specific internal used in R code of Cdqrls, which is in GunR implemented in C and invokes
  * directly this Fortran routine.
  */
-@RBuiltin(name = ".fastr.dqrls", visibility = RVisibility.OFF, kind = RBuiltinKind.PRIMITIVE, parameterNames = {"x", "n", "p", "y", "ny", "tol", "coeff"})
+@RBuiltin(name = ".fastr.dqrls", visibility = OFF, kind = PRIMITIVE, parameterNames = {"x", "n", "p", "y", "ny", "tol", "coeff"}, behavior = PURE)
 public abstract class FastrDqrls extends RBuiltinNode {
 
     private static final String[] NAMES = new String[]{"qr", "coefficients", "residuals", "effects", "rank", "pivot", "qraux", "tol", "pivoted"};
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/parallel/R/forkcluster_overrides.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/parallel/R/forkcluster_overrides.R
index 570a7cc55af4eb3658fe6939ce25ae890920799e..42d5c9ce8b44849166eb70e73832f781475e471e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/parallel/R/forkcluster_overrides.R
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/parallel/R/forkcluster_overrides.R
@@ -56,8 +56,7 @@ fastr.newSHAREDnode <- function(rank, options = defaultClusterOptions)
     context_code <- paste0("commandArgs<-function() c('--args', 'PORT=", port, "'); source('", script, "')")
 	if (isTRUE(debug)) cat(sprintf("Starting context: %d with code %s\n", rank, context_code))
 
-    cx <- .fastr.context.create("SHARED_NOTHING")
-    .fastr.context.spawn(cx, context_code)
+    cx <- .fastr.context.spawn(context_code)
 
 	## Need to return a list here, in the same form as the
 	## "cluster" data structure.
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
index b2331663eecc2daf4b04c17dedafa90acb5a716f..e9494ac30eb5871ff6f542787dbd6be94370146a 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
@@ -22,25 +22,40 @@
  */
 package com.oracle.truffle.r.nodes.builtin;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asLogicalVector;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.asStringVector;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.chain;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.complexValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.defaultValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.elementAt;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.equalTo;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.findFirst;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.map;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.mustBe;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notEmpty;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notNA;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullConstant;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.scalarLogicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.scalarStringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.shouldBe;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.trueValue;
 import static com.oracle.truffle.r.nodes.casts.CastUtils.samples;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
-import java.io.StringWriter;
-import java.util.Arrays;
+import java.util.function.Function;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -50,10 +65,10 @@ import org.junit.Test;
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.r.nodes.access.AccessArgumentNode;
+import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNodeGen;
 import com.oracle.truffle.r.nodes.builtin.ArgumentFilter.ArgumentTypeFilter;
-import com.oracle.truffle.r.nodes.builtin.base.ColSums;
-import com.oracle.truffle.r.nodes.builtin.base.ColSumsNodeGen;
+import com.oracle.truffle.r.nodes.builtin.ArgumentFilter.ArgumentValueFilter;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder.InitialPhaseBuilder;
 import com.oracle.truffle.r.nodes.casts.ArgumentFilterSampler;
 import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
 import com.oracle.truffle.r.nodes.casts.PredefFiltersSamplers;
@@ -63,43 +78,39 @@ import com.oracle.truffle.r.nodes.casts.ValuePredicateArgumentFilterSampler;
 import com.oracle.truffle.r.nodes.test.TestUtilities;
 import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle;
 import com.oracle.truffle.r.nodes.unary.CastNode;
-import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
 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.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RInteger;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RLogical;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-import com.oracle.truffle.r.runtime.nodes.RNode;
 
 public class CastBuilderTest {
 
     private CastBuilder cb;
-    private StringWriter out;
 
     @Before
     public void setUp() {
         CastBuilder.Predef.setPredefFilters(new PredefFiltersSamplers());
         CastBuilder.Predef.setPredefMappers(new PredefMappersSamplers());
         cb = new CastBuilder(null);
-        out = new StringWriter();
-        cb.output(out);
     }
 
     @After
     public void tearDown() {
         cb = null;
-        out = null;
     }
 
     @Test
     public void testError() {
         cb.arg(0).mustBe(
-                        ValuePredicateArgumentFilterSampler.omLambdaWithResTypes(x -> x instanceof String, String.class),
-                        RError.Message.DLL_LOAD_ERROR, CastBuilder.ARG, "123");
+                        ValuePredicateArgumentFilterSampler.fromLambdaWithResTypes(x -> x instanceof String, String.class),
+                        RError.Message.DLL_LOAD_ERROR, Function.identity(), "123");
         testPipeline();
 
         assertEquals("A", cast("A"));
@@ -114,7 +125,7 @@ public class CastBuilderTest {
 
     @Test
     public void testErrorWithAttachedPredicate() {
-        cb.arg(0).mustBe(ValuePredicateArgumentFilterSampler.omLambdaWithResTypes(x -> x instanceof RAbstractIntVector || x instanceof Integer, Object.class), Message.SEED_NOT_VALID_INT);
+        cb.arg(0).mustBe(ValuePredicateArgumentFilterSampler.fromLambdaWithResTypes(x -> x instanceof RAbstractIntVector || x instanceof Integer, Object.class), Message.SEED_NOT_VALID_INT);
         testPipeline();
 
         RAbstractIntVector v = RDataFactory.createIntVectorFromScalar(1);
@@ -129,12 +140,12 @@ public class CastBuilderTest {
 
     @Test
     public void testWarning() {
-        cb.arg(0).shouldBe(ValuePredicateArgumentFilterSampler.omLambdaWithResTypes(x -> x instanceof String, Object.class), RError.Message.DLL_LOAD_ERROR, CastBuilder.ARG, "123");
+        cb.arg(0).shouldBe(ValuePredicateArgumentFilterSampler.fromLambdaWithResTypes(x -> x instanceof String, Object.class), RError.Message.DLL_LOAD_ERROR, Function.identity(), "123");
         testPipeline();
 
         assertEquals("A", cast("A"));
         assertEquals(Boolean.FALSE, cast(Boolean.FALSE));
-        assertEquals("unable to load shared object 'false'\n  123", out.toString());
+        // assertEquals("unable to load shared object 'false'\n 123", out.toString());
     }
 
     @Test
@@ -145,7 +156,7 @@ public class CastBuilderTest {
         RAbstractIntVector v = RDataFactory.createIntVectorFromScalar(1);
         assertEquals(v, cast(v));
         assertEquals(Boolean.FALSE, cast(Boolean.FALSE));
-        assertEquals(RError.Message.SEED_NOT_VALID_INT.message, out.toString());
+        // assertEquals(RError.Message.SEED_NOT_VALID_INT.message, out.toString());
     }
 
     @Test
@@ -271,7 +282,7 @@ public class CastBuilderTest {
         testPipeline();
 
         cast(RDataFactory.createIntVector(new int[]{1, 2}, true));
-        assertEquals(RError.Message.LENGTH_GT_1.message, out.toString());
+        // assertEquals(RError.Message.LENGTH_GT_1.message, out.toString());
     }
 
     @Test
@@ -280,7 +291,7 @@ public class CastBuilderTest {
         testPipeline();
 
         cast(RDataFactory.createIntVector(new int[]{1, 2}, true));
-        assertEquals(RError.Message.SEED_NOT_VALID_INT.message, out.toString());
+        // assertEquals(RError.Message.SEED_NOT_VALID_INT.message, out.toString());
     }
 
     @Test
@@ -352,13 +363,42 @@ public class CastBuilderTest {
         assertEquals("A", cast(RRuntime.DOUBLE_NA));
     }
 
+    public InitialPhaseBuilder<String> matchStringArg(InitialPhaseBuilder<Object> phaseBuilder, String... optValues) {
+        ArgumentValueFilter<String> opts = null;
+        for (String opt : optValues) {
+            opts = opts == null ? equalTo(opt) : opts.or(equalTo(opt));
+        }
+        return phaseBuilder.mustBe(scalarStringValue().and(opts));
+    }
+
+    @Test
+    public void testMatchArg() {
+
+        cb.arg(0, "foo").mustBe(nullValue().or(scalarStringValue().and(equalTo("a").or(equalTo("b").or(equalTo("c"))))), RError.Message.GENERIC, "Invalid option").mapIf(nullValue(), constant("a"));
+
+        cb.arg(0, "foo").alias(pb -> matchStringArg(pb, "a", "b", "c")).mapIf(equalTo("c"), constant("sss"));
+
+        assertEquals("a", cast("a"));
+        assertEquals("b", cast("b"));
+        assertEquals("sss", cast("c"));
+        assertEquals("a", cast(RNull.instance));
+
+        try {
+            cast("d");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
     @Test
     public void testSample0() {
         cb.arg(0, "x").asIntegerVector().shouldBe(singleElement()).findFirst(0);
         testPipeline();
 
         assertEquals(1, cast(RDataFactory.createIntVector(new int[]{1, 2}, true)));
-        assertEquals(String.format(RError.Message.INVALID_ARGUMENT.message, "x"), out.toString());
+        // assertEquals(String.format(RError.Message.INVALID_ARGUMENT.message, "x"),
+        // out.toString());
     }
 
     @Test
@@ -379,7 +419,7 @@ public class CastBuilderTest {
         testPipeline();
 
         cast(RDataFactory.createIntVector(new int[]{1, 2}, true));
-        assertEquals(String.format(RError.Message.INVALID_USE.message, 1), out.toString());
+        // assertEquals(String.format(RError.Message.INVALID_USE.message, 1), out.toString());
 
         try {
             cast(RDataFactory.createIntVector(0));
@@ -400,7 +440,7 @@ public class CastBuilderTest {
     @Test
     public void testSample4() {
         // the predicate is attached to the error message
-        cb.arg(0).mustBe(ValuePredicateArgumentFilterSampler.omLambdaWithResTypes(x -> x instanceof RAbstractIntVector || x instanceof Integer, Object.class),
+        cb.arg(0).mustBe(ValuePredicateArgumentFilterSampler.fromLambdaWithResTypes(x -> x instanceof RAbstractIntVector || x instanceof Integer, Object.class),
                         Message.SEED_NOT_VALID_INT).asIntegerVector();
         testPipeline();
 
@@ -418,7 +458,7 @@ public class CastBuilderTest {
         ArgumentTypeFilter<Object, Object> complexOrExpr = integerValue().or(doubleValue()).or(complexValue()).or(logicalValue());
         Assert.assertTrue(complexOrExpr instanceof ArgumentFilterSampler);
         cb.arg(0).defaultError(RError.Message.INVALID_ARGUMENT, "fill").mustBe(numericValue().or(logicalValue())).asVector().mustBe(singleElement()).findFirst().shouldBe(
-                        ValuePredicateArgumentFilterSampler.omLambdaWithResTypes(x -> x instanceof Byte || x instanceof Integer && ((Integer) x) > 0), Message.NON_POSITIVE_FILL).mapIf(
+                        ValuePredicateArgumentFilterSampler.fromLambdaWithResTypes(x -> x instanceof Byte || x instanceof Integer && ((Integer) x) > 0), Message.NON_POSITIVE_FILL).mapIf(
                                         scalarLogicalValue(), toBoolean());
         testPipeline();
 
@@ -443,7 +483,7 @@ public class CastBuilderTest {
             assertEquals(String.format(RError.Message.INVALID_ARGUMENT.message, "fill"), e.getMessage());
         }
         cast(-10); // warning
-        assertEquals(String.format(Message.NON_POSITIVE_FILL.message, "fill"), out.toString());
+        // assertEquals(String.format(Message.NON_POSITIVE_FILL.message, "fill"), out.toString());
     }
 
     @Test
@@ -486,6 +526,128 @@ public class CastBuilderTest {
 
     }
 
+    @Test
+    public void testSample8() {
+        cb.arg(0, "blocking").asLogicalVector().findFirst(RRuntime.LOGICAL_TRUE).map(toBoolean()).mustBe(trueValue(), RError.Message.NYI, "non-blocking mode not supported");
+        cast(RNull.instance);
+    }
+
+    @Test
+    public void testSample9() {
+        cb.arg(0, "arg").mapIf(instanceOf(RList.class).not(), nullConstant());
+
+        RList list = RDataFactory.createList();
+        assertEquals(list, cast(list));
+        assertEquals(RNull.instance, cast("abc"));
+    }
+
+    @Test
+    public void testSample10() {
+        cb.arg(0, "arg").mapIf(instanceOf(RList.class), nullConstant());
+
+        RList list = RDataFactory.createList();
+        assertEquals(RNull.instance, cast(list));
+        assertEquals("abc", cast("abc"));
+    }
+
+    //@formatter:off
+    @Test
+    public void testSample11() {
+        cb.arg(0, "arg").
+            mapIf(instanceOf(RList.class).not(),
+               chain(asLogicalVector()).
+                  with(findFirst().logicalElement()).
+                  with(notNA()).
+                  with(map(toBoolean())).
+                  with(mustBe(instanceOf(Boolean.class), false)).
+                  with(shouldBe(instanceOf(Object.class), false)).
+                  end());
+
+        RList list = RDataFactory.createList();
+        assertEquals(list, cast(list));
+        assertEquals(true, cast(1));
+        try {
+            cast(RRuntime.INT_NA);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    public Function<InitialPhaseBuilder<Object>, InitialPhaseBuilder<Object>> nonListToBoolean() {
+        return phaseBuilder -> phaseBuilder.
+                        mapIf(instanceOf(RList.class).not(),
+                           chain(asLogicalVector()).
+                              with(findFirst().logicalElement()).
+                              with(toBoolean()).
+                              end());
+    }
+
+    @Test
+    public void testSample12() {
+        cb.arg(0, "arg").alias(nonListToBoolean());
+
+        RList list = RDataFactory.createList();
+        assertEquals(list, cast(list));
+        assertEquals(true, cast(1));
+    }
+
+    @Test
+    public void testSample13() {
+        cb.arg(0, "arg").mapIf(numericValue(), BoxPrimitiveNodeGen.create());
+
+        Object res = cast(1);
+        Assert.assertTrue(res instanceof RInteger);
+        res = cast(RRuntime.LOGICAL_TRUE);
+        Assert.assertTrue(res instanceof RLogical);
+    }
+    //@formatter:on
+
+    @Test
+    public void testSample14() {
+        cb.arg(0, "arg").mustBe(stringValue().or(nullValue())).mapIf(stringValue(), chain(asStringVector()).with(findFirst().stringElement()).end());
+
+        assertEquals("abc", cast("abc"));
+        assertEquals("abc", cast(RDataFactory.createStringVector(new String[]{"abc", "xyz"}, true)));
+        assertEquals(RNull.instance, cast(RNull.instance));
+    }
+
+    @Test
+    public void testSample15() {
+        // cb.arg(0, "dim").asIntegerVector().mustBe(Predef.notEmpty());
+
+        cb.arg(0, "open").mustBe(instanceOf(RAbstractStringVector.class).and(singleElement()).and(elementAt(0, RRuntime.STRING_NA).not()));
+
+        cast(RDataFactory.createStringVector(new String[]{"abc"}, true));
+        try {
+            cast(RDataFactory.createStringVector(new String[]{"abc", "xyz"}, true));
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+        try {
+            cast(RRuntime.STRING_NA);
+        } catch (IllegalArgumentException e) {
+            // ok
+        }
+    }
+
+    private static String argType(Object arg) {
+        return arg.getClass().getSimpleName();
+    }
+
+    private String argMsg(Object arg) {
+        return "'data' must be of a vector type, was " + argType(arg);
+    }
+
+    @Test
+    public void testSample16() {
+        // cb.arg(0, "dim").asIntegerVector().mustBe(Predef.notEmpty());
+        Function<Object, Object> argMsg = this::argMsg;
+        cb.arg(0, "open").shouldBe(stringValue(), RError.Message.GENERIC, argMsg);
+
+        cast(RNull.instance);
+    }
+
     class RBuiltinRootNode extends RootNode {
 
         @Child private RBuiltinNode builtinNode;
@@ -501,37 +663,6 @@ public class CastBuilderTest {
         }
     }
 
-    @Test
-    public void autoTestColSums() {
-        RBuiltin annotation = ColSums.class.getAnnotation(RBuiltin.class);
-        String[] parameterNames = annotation.parameterNames();
-        parameterNames = Arrays.stream(parameterNames).map(n -> n.isEmpty() ? null : n).toArray(String[]::new);
-        ArgumentsSignature signature = ArgumentsSignature.get(parameterNames);
-
-        int total = signature.getLength();
-        RNode[] args = new RNode[total];
-        for (int i = 0; i < total; i++) {
-            args[i] = AccessArgumentNode.create(i);
-        }
-        RBuiltinNode builtinNode = ColSumsNodeGen.create(args.clone());
-
-        CastNode[] castNodes = builtinNode.getCasts();
-        for (int i = 0; i < castNodes.length; i++) {
-            CastNode castNode = builtinNode.getCasts()[i];
-            if (castNode == null) {
-                System.out.println("No Samples");
-            } else {
-                Samples<?> s = CastNodeSampler.createSampler(castNode).collectSamples();
-                System.out.println("Samples:\n" + s);
-            }
-        }
-
-        // RootCallTarget builtinNodeCallTarget = Truffle.getRuntime().createCallTarget(new
-        // RBuiltinRootNode(builtinNode));
-        // builtinNodeCallTarget.call(RArguments.createUnitialized(RDataFactory.createIntVector(new
-        // int[]{1}, true), 1, 1, 1));
-    }
-
     private Object cast(Object arg) {
         CastNode argCastNode = cb.getCasts()[0];
         NodeHandle<CastNode> argCastNodeHandle = TestUtilities.createHandle(argCastNode, (node, args) -> node.execute(args[0]));
@@ -544,7 +675,7 @@ public class CastBuilderTest {
 
     private void testPipeline(@SuppressWarnings("unused") boolean positiveMustNotBeEmpty) {
         CastNodeSampler<CastNode> sampler = CastNodeSampler.createSampler(cb.getCasts()[0]);
-        System.out.println(sampler);
+        sampler.collectSamples();
         // Samples<?> samples = sampler.collectSamples();
         //
         // if (positiveMustNotBeEmpty) {
@@ -569,7 +700,7 @@ public class CastBuilderTest {
             try {
                 cast(sample);
                 fail();
-            } catch (Exception e) {
+            } catch (IllegalArgumentException e) {
                 // ok
             }
         }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ArgumentFilterSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ArgumentFilterSampler.java
index f7f777f660bbfc41837cb04673e91c9db5ef3300..979813ce02f49487c5fb8184e851ffc1c7fa4bc7 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ArgumentFilterSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ArgumentFilterSampler.java
@@ -26,9 +26,11 @@ import com.oracle.truffle.r.nodes.builtin.ArgumentFilter;
 
 public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
 
-    Samples<R> collectSamples(Samples<? extends R> downStreamSamples);
+    Samples<R> collectSamples(TypeExpr inputType);
 
-    TypeExpr allowedTypes();
+    TypeExpr trueBranchType();
+
+    TypeExpr falseBranchType();
 
     interface NarrowingArgumentFilterSampler<T, R extends T> extends NarrowingArgumentFilter<T, R>, ArgumentFilterSampler<T, R> {
 
@@ -48,21 +50,15 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
                 }
 
                 @Override
-                public TypeExpr allowedTypes() {
-                    return NarrowingArgumentFilterSampler.this.allowedTypes().or(other.allowedTypes());
+                public TypeExpr trueBranchType() {
+                    return NarrowingArgumentFilterSampler.this.trueBranchType().or(other.trueBranchType());
                 }
 
-                @SuppressWarnings("unchecked")
                 @Override
-                public Samples<T> collectSamples(Samples<? extends T> downStreamSamples) {
-
-                    Samples<R> downStreamSamplesForThis = downStreamSamples.filter(x -> NarrowingArgumentFilterSampler.this.allowedTypes().isInstance(x)).map(x -> (R) x, x -> x);
-                    Samples<R> thisSamples = NarrowingArgumentFilterSampler.this.collectSamples(downStreamSamplesForThis);
-
-                    Samples<S> downStreamSamplesForOther = downStreamSamples.filter(x -> other.allowedTypes().isInstance(x)).map(x -> (S) x, x -> x);
-                    Samples<S> otherSamples = other.collectSamples(downStreamSamplesForOther);
-
-                    return Samples.<T> empty().or(thisSamples).or(otherSamples);
+                public Samples<T> collectSamples(TypeExpr inputType) {
+                    Samples<R> thisSamples = NarrowingArgumentFilterSampler.this.collectSamples(inputType);
+                    Samples<S> otherSamples = other.collectSamples(inputType);
+                    return Samples.<T> anything().and(thisSamples).or(otherSamples);
                 }
 
             };
@@ -71,6 +67,42 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
 
     interface ArgumentValueFilterSampler<T> extends ArgumentValueFilter<T>, NarrowingArgumentFilterSampler<T, T> {
 
+        @Override
+        default TypeExpr falseBranchType() {
+            this.or(null);
+            return trueBranchType();
+        }
+
+        @Override
+        default <S extends T> ArgumentValueFilterSampler<T> or(ArgumentValueFilter<T> o) {
+            final ArgumentValueFilterSampler<T> other = (ArgumentValueFilterSampler<T>) o;
+
+            return new ArgumentValueFilterSampler<T>() {
+
+                @Override
+                public boolean test(T arg) {
+                    if (ArgumentValueFilterSampler.this.test(arg)) {
+                        return true;
+                    } else {
+                        return other.test(arg);
+                    }
+                }
+
+                @Override
+                public TypeExpr trueBranchType() {
+                    return ArgumentValueFilterSampler.this.trueBranchType().or(other.trueBranchType());
+                }
+
+                @Override
+                public Samples<T> collectSamples(TypeExpr inputType) {
+                    Samples<T> thisSamples = ArgumentValueFilterSampler.this.collectSamples(inputType);
+                    Samples<T> otherSamples = other.collectSamples(inputType);
+                    return Samples.<T> anything().and(thisSamples).or(otherSamples);
+                }
+
+            };
+        }
+
         @Override
         default ArgumentValueFilterSampler<T> and(ArgumentValueFilter<T> o) {
             final ArgumentValueFilterSampler<T> other = (ArgumentValueFilterSampler<T>) o;
@@ -83,14 +115,14 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
                 }
 
                 @Override
-                public TypeExpr allowedTypes() {
-                    return ArgumentValueFilterSampler.this.allowedTypes().and(other.allowedTypes());
+                public TypeExpr trueBranchType() {
+                    return ArgumentValueFilterSampler.this.trueBranchType().and(other.trueBranchType());
                 }
 
                 @Override
-                public Samples<T> collectSamples(Samples<? extends T> downStreamSamples) {
-                    Samples<T> thisSamples = ArgumentValueFilterSampler.this.collectSamples(downStreamSamples);
-                    Samples<T> otherSamples = other.collectSamples(downStreamSamples);
+                public Samples<T> collectSamples(TypeExpr inputType) {
+                    Samples<T> thisSamples = ArgumentValueFilterSampler.this.collectSamples(inputType);
+                    Samples<T> otherSamples = other.collectSamples(inputType);
 
                     return thisSamples.and(otherSamples);
                 }
@@ -109,17 +141,17 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
                 }
 
                 @Override
-                public TypeExpr allowedTypes() {
-                    return ArgumentValueFilterSampler.this.allowedTypes().and(other.allowedTypes());
+                public TypeExpr trueBranchType() {
+                    return ArgumentValueFilterSampler.this.trueBranchType().and(other.trueBranchType());
                 }
 
                 @SuppressWarnings("unchecked")
                 @Override
-                public Samples<S> collectSamples(Samples<? extends S> downStreamSamples) {
-                    Samples<S> thisSamples = ArgumentValueFilterSampler.this.collectSamples(downStreamSamples).filter(x -> allowedTypes().isInstance(x)).map(x -> (S) x, x -> x);
-                    Samples<S> otherSamples = other.collectSamples(downStreamSamples);
+                public Samples<S> collectSamples(TypeExpr inputType) {
+                    Samples<T> thisSamples = ArgumentValueFilterSampler.this.collectSamples(inputType);
+                    Samples<S> otherSamples = other.collectSamples(inputType);
 
-                    return thisSamples.and(otherSamples);
+                    return (Samples<S>) thisSamples.and(otherSamples);
                 }
             };
         }
@@ -134,23 +166,27 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
                 }
 
                 @Override
-                public TypeExpr allowedTypes() {
-                    return ArgumentValueFilterSampler.this.allowedTypes();
+                public TypeExpr trueBranchType() {
+                    return ArgumentValueFilterSampler.this.trueBranchType();
                 }
 
                 @SuppressWarnings("unchecked")
                 @Override
-                public Samples<T> collectSamples(Samples<? extends T> downStreamSamples) {
-                    Samples<T> thisSamples = ArgumentValueFilterSampler.this.collectSamples(downStreamSamples);
-                    return thisSamples.swap().filter(x -> allowedTypes().isInstance(x)).map(x -> (T) x, x -> x);
+                public Samples<T> collectSamples(TypeExpr inputType) {
+                    Samples<T> thisSamples = ArgumentValueFilterSampler.this.collectSamples(inputType);
+                    return (Samples<T>) thisSamples.swap();
                 }
             };
         }
-
     }
 
     interface ArgumentTypeFilterSampler<T, R extends T> extends ArgumentTypeFilter<T, R>, NarrowingArgumentFilterSampler<T, R> {
 
+        @Override
+        default TypeExpr falseBranchType() {
+            return trueBranchType().not();
+        }
+
         @Override
         default <S extends R> ArgumentTypeFilterSampler<T, S> and(ArgumentTypeFilter<R, S> o) {
             final ArgumentTypeFilterSampler<R, S> other = (ArgumentTypeFilterSampler<R, S>) o;
@@ -164,17 +200,17 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
                 }
 
                 @Override
-                public TypeExpr allowedTypes() {
-                    return ArgumentTypeFilterSampler.this.allowedTypes().and(other.allowedTypes());
+                public TypeExpr trueBranchType() {
+                    return ArgumentTypeFilterSampler.this.trueBranchType().and(other.trueBranchType());
                 }
 
                 @SuppressWarnings("unchecked")
                 @Override
-                public Samples<S> collectSamples(Samples<? extends S> downStreamSamples) {
-                    Samples<S> thisSamples = ArgumentTypeFilterSampler.this.collectSamples(downStreamSamples).filter(x -> other.allowedTypes().isInstance(x)).map(x -> (S) x, x -> x);
-                    Samples<S> otherSamples = other.collectSamples(downStreamSamples);
+                public Samples<S> collectSamples(TypeExpr inputType) {
+                    Samples<R> thisSamples = ArgumentTypeFilterSampler.this.collectSamples(inputType);
+                    Samples<S> otherSamples = other.collectSamples(inputType);
 
-                    return thisSamples.and(otherSamples);
+                    return (Samples<S>) thisSamples.and(otherSamples);
                 }
             };
         }
@@ -192,17 +228,16 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
                 }
 
                 @Override
-                public TypeExpr allowedTypes() {
-                    return ArgumentTypeFilterSampler.this.allowedTypes().and(other.allowedTypes());
+                public TypeExpr trueBranchType() {
+                    return ArgumentTypeFilterSampler.this.trueBranchType().and(other.trueBranchType());
                 }
 
-                @SuppressWarnings("cast")
                 @Override
-                public Samples<R> collectSamples(Samples<? extends R> downStreamSamples) {
-                    Samples<R> thisSamples = ArgumentTypeFilterSampler.this.collectSamples(downStreamSamples);
-                    Samples<R> otherSamples = other.collectSamples(downStreamSamples).filter(x -> ArgumentTypeFilterSampler.this.allowedTypes().isInstance(x)).map(x -> (R) x, x -> x);
+                public Samples<R> collectSamples(TypeExpr inputType) {
+                    Samples<R> thisSamples = ArgumentTypeFilterSampler.this.collectSamples(inputType);
+                    Samples<R> otherSamples = other.collectSamples(inputType);
 
-                    return thisSamples.and(otherSamples);
+                    return otherSamples.and(thisSamples);
                 }
             };
         }
@@ -224,15 +259,18 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
         }
 
         @Override
-        public TypeExpr allowedTypes() {
-            return orig.allowedTypes().not();
+        public TypeExpr trueBranchType() {
+            return orig.trueBranchType().not();
         }
 
-        @SuppressWarnings("unchecked")
         @Override
-        public Samples<Object> collectSamples(Samples<?> downStreamSamples) {
-            Samples<R> swappedSamples = downStreamSamples.swap().filter(x -> orig.allowedTypes().isInstance(x)).map(x -> (R) x, x -> x);
-            Samples<R> thisSamples = orig.collectSamples(swappedSamples);
+        public TypeExpr falseBranchType() {
+            return orig.falseBranchType().not();
+        }
+
+        @Override
+        public Samples<Object> collectSamples(TypeExpr inputType) {
+            Samples<? extends R> thisSamples = orig.collectSamples(inputType);
             return thisSamples.swap();
         }
 
@@ -253,17 +291,17 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
                 }
 
                 @Override
-                public TypeExpr allowedTypes() {
-                    return InverseArgumentFilterSampler.this.allowedTypes().and(other.allowedTypes());
+                public TypeExpr trueBranchType() {
+                    return InverseArgumentFilterSampler.this.trueBranchType().and(other.trueBranchType());
                 }
 
                 @SuppressWarnings("unchecked")
                 @Override
-                public Samples<S> collectSamples(Samples<? extends S> downStreamSamples) {
-                    Samples<S> thisSamples = InverseArgumentFilterSampler.this.collectSamples(downStreamSamples).filter(x -> other.allowedTypes().isInstance(x)).map(x -> (S) x, x -> x);
-                    Samples<S> otherSamples = other.collectSamples(downStreamSamples);
+                public Samples<S> collectSamples(TypeExpr inputType) {
+                    Samples<Object> thisSamples = InverseArgumentFilterSampler.this.collectSamples(inputType);
+                    Samples<S> otherSamples = other.collectSamples(inputType);
 
-                    return thisSamples.and(otherSamples);
+                    return (Samples<S>) thisSamples.and(otherSamples);
                 }
             };
         }
@@ -280,17 +318,17 @@ public interface ArgumentFilterSampler<T, R> extends ArgumentFilter<T, R> {
                 }
 
                 @Override
-                public TypeExpr allowedTypes() {
-                    return InverseArgumentFilterSampler.this.allowedTypes().and(other.allowedTypes());
+                public TypeExpr trueBranchType() {
+                    return InverseArgumentFilterSampler.this.trueBranchType().and(other.trueBranchType());
                 }
 
                 @SuppressWarnings("unchecked")
                 @Override
-                public Samples<S> collectSamples(Samples<? extends S> downStreamSamples) {
-                    Samples<S> thisSamples = InverseArgumentFilterSampler.this.collectSamples(downStreamSamples).filter(x -> other.allowedTypes().isInstance(x)).map(x -> (S) x, x -> x);
-                    Samples<S> otherSamples = other.collectSamples(downStreamSamples);
+                public Samples<S> collectSamples(TypeExpr inputType) {
+                    Samples<Object> thisSamples = InverseArgumentFilterSampler.this.collectSamples(inputType);
+                    Samples<S> otherSamples = other.collectSamples(inputType);
 
-                    return thisSamples.and(otherSamples);
+                    return (Samples<S>) thisSamples.and(otherSamples);
                 }
             };
         }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ArgumentMapperSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ArgumentMapperSampler.java
index 7184ec3a28851157ae76d592bfe27585925ada98..8b1b6b0197f07abae556f88304bbb0047db38715 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ArgumentMapperSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ArgumentMapperSampler.java
@@ -26,7 +26,7 @@ import com.oracle.truffle.r.nodes.builtin.ArgumentMapper;
 
 public interface ArgumentMapperSampler<T, R> extends ArgumentMapper<T, R> {
 
-    TypeExpr resultTypes();
+    TypeExpr resultTypes(TypeExpr inputTypes);
 
     Samples<T> collectSamples(Samples<R> downStreamSamples);
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastNodeSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastNodeSampler.java
index 8ec3f0327cd46633fccf6743eb7a57b9f6bb8fb1..0e64e2c2cd775661cf03bad5498dce8dcaecad4a 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastNodeSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastNodeSampler.java
@@ -23,10 +23,6 @@
 package com.oracle.truffle.r.nodes.casts;
 
 import java.lang.reflect.Constructor;
-import java.lang.reflect.Type;
-import java.util.Collections;
-import java.util.Set;
-import java.util.stream.Collectors;
 
 import com.oracle.truffle.r.nodes.unary.CastNode;
 
@@ -47,20 +43,11 @@ public class CastNodeSampler<T extends CastNode> {
     }
 
     public TypeExpr resultTypes(TypeExpr inputType) {
-        return CastUtils.Casts.createCastNodeCasts(getClass()).narrow(inputType);
+        return CastUtils.Casts.createCastNodeCasts(castNode.getClass().getSuperclass()).narrow(inputType);
     }
 
     public final Samples<?> collectSamples() {
-        Set<Type> resTypes = resultTypes().normalize().stream().filter(cls -> cls != Object.class).collect(Collectors.toSet());
-
-        Set<?> defaultPositiveSamples;
-        if (resTypes.isEmpty()) {
-            defaultPositiveSamples = CastUtils.sampleValuesForType(Object.class);
-        } else {
-            defaultPositiveSamples = resTypes.stream().flatMap(cls -> CastUtils.sampleValuesForType(cls).stream()).collect(Collectors.toSet());
-        }
-
-        return collectSamples(TypeExpr.ANYTHING, new Samples<>(defaultPositiveSamples, Collections.emptySet()));
+        return collectSamples(TypeExpr.ANYTHING, Samples.anything());
     }
 
     @SuppressWarnings("unused")
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastUtils.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastUtils.java
index bff93069ed761ee950934e6e3c61ab2d6547e6a3..a59861970ff7893d9ee0e84082e813a27e9d793f 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastUtils.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/CastUtils.java
@@ -31,7 +31,9 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import com.oracle.truffle.api.dsl.Specialization;
@@ -39,9 +41,11 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RListBase;
+import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RTypes;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
@@ -447,13 +451,13 @@ public class CastUtils {
     }
 
     public static Type elementType(Class<?> vectorType) {
-        if (RAbstractIntVector.class.isAssignableFrom(vectorType) || Integer.class.isAssignableFrom(vectorType)) {
+        if (RAbstractIntVector.class.isAssignableFrom(vectorType) || Integer.class.isAssignableFrom(vectorType) || int.class.isAssignableFrom(vectorType)) {
             return Integer.class;
         }
-        if (RAbstractDoubleVector.class.isAssignableFrom(vectorType) || Double.class.isAssignableFrom(vectorType)) {
+        if (RAbstractDoubleVector.class.isAssignableFrom(vectorType) || Double.class.isAssignableFrom(vectorType) || double.class.isAssignableFrom(vectorType)) {
             return Double.class;
         }
-        if (RAbstractLogicalVector.class.isAssignableFrom(vectorType) || Byte.class.isAssignableFrom(vectorType)) {
+        if (RAbstractLogicalVector.class.isAssignableFrom(vectorType) || Byte.class.isAssignableFrom(vectorType) || byte.class.isAssignableFrom(vectorType)) {
             return Byte.class;
         }
         if (RAbstractStringVector.class.isAssignableFrom(vectorType) || String.class.isAssignableFrom(vectorType)) {
@@ -471,7 +475,7 @@ public class CastUtils {
         return Not.negateType(RAbstractVector.class);
     }
 
-    public static Object emptyVector(Class<?> elementType) {
+    public static RAbstractVector emptyVector(Class<?> elementType) {
         if (Integer.class.isAssignableFrom(elementType)) {
             return RDataFactory.createEmptyIntVector();
         }
@@ -493,6 +497,28 @@ public class CastUtils {
         return null;
     }
 
+    public static RAbstractVector vectorOfSize(Class<?> vectorType, int size) {
+        if (RAbstractIntVector.class.isAssignableFrom(vectorType) || Integer.class.isAssignableFrom(vectorType) || int.class.isAssignableFrom(vectorType)) {
+            return RDataFactory.createIntVector(size);
+        }
+        if (RAbstractDoubleVector.class.isAssignableFrom(vectorType) || Double.class.isAssignableFrom(vectorType) || double.class.isAssignableFrom(vectorType)) {
+            return RDataFactory.createDoubleVector(size);
+        }
+        if (RAbstractLogicalVector.class.isAssignableFrom(vectorType) || Byte.class.isAssignableFrom(vectorType) || byte.class.isAssignableFrom(vectorType)) {
+            return RDataFactory.createLogicalVector(size);
+        }
+        if (RAbstractStringVector.class.isAssignableFrom(vectorType) || String.class.isAssignableFrom(vectorType)) {
+            return RDataFactory.createStringVector(size);
+        }
+        if (RAbstractComplexVector.class.isAssignableFrom(vectorType) || RComplex.class.isAssignableFrom(vectorType)) {
+            return RDataFactory.createComplexVector(size);
+        }
+        if (RAbstractVector.class.isAssignableFrom(vectorType) || vectorType == Object.class) {
+            return RDataFactory.createIntVector(size);
+        }
+        return null;
+    }
+
     public static Object naVector(Class<?> elementType) {
         if (Integer.class.isAssignableFrom(elementType)) {
             return RDataFactory.createIntVectorFromScalar(RRuntime.INT_NA);
@@ -537,6 +563,25 @@ public class CastUtils {
         return null;
     }
 
+    public static boolean isNaValue(Object x) {
+        if (x instanceof Integer) {
+            return RRuntime.isNA((Integer) x);
+        }
+        if (x instanceof Double) {
+            return RRuntime.isNA((Double) x);
+        }
+        if (x instanceof Byte) {
+            return RRuntime.isNA((Byte) x);
+        }
+        if (x instanceof String) {
+            return RRuntime.isNA((String) x);
+        }
+        if (x instanceof RComplex) {
+            return RRuntime.isNA((RComplex) x);
+        }
+        return false;
+    }
+
     public static Object singletonVector(Object element) {
         if (element == RNull.instance) {
             return RNull.instance;
@@ -562,6 +607,69 @@ public class CastUtils {
         return null;
     }
 
+    @SuppressWarnings("unchecked")
+    public static <T> Optional<T> firstElement(Object vectorOrScalar, Object defaultValue) {
+        if (vectorOrScalar == RNull.instance) {
+            return defaultValue == null ? Optional.of((T) RNull.instance) : Optional.of((T) defaultValue);
+        }
+        if (vectorOrScalar == RMissing.instance) {
+            return defaultValue == null ? Optional.of((T) RMissing.instance) : Optional.of((T) defaultValue);
+        }
+        if (vectorOrScalar instanceof RAbstractContainer) {
+            if (((RAbstractContainer) vectorOrScalar).getLength() == 0) {
+                return defaultValue == null ? Optional.empty() : Optional.of((T) defaultValue);
+            } else {
+                if (vectorOrScalar instanceof RAbstractIntVector) {
+                    return Optional.of((T) ((RAbstractIntVector) vectorOrScalar).getDataAtAsObject(0));
+                }
+                if (vectorOrScalar instanceof RAbstractDoubleVector) {
+                    return Optional.of((T) ((RAbstractDoubleVector) vectorOrScalar).getDataAtAsObject(0));
+                }
+                if (vectorOrScalar instanceof RAbstractComplexVector) {
+                    return Optional.of((T) ((RAbstractComplexVector) vectorOrScalar).getDataAtAsObject(0));
+                }
+                if (vectorOrScalar instanceof RAbstractLogicalVector) {
+                    return Optional.of((T) ((RAbstractLogicalVector) vectorOrScalar).getDataAtAsObject(0));
+                }
+                if (vectorOrScalar instanceof RAbstractStringVector) {
+                    return Optional.of((T) ((RAbstractStringVector) vectorOrScalar).getDataAtAsObject(0));
+                }
+                return defaultValue == null ? Optional.empty() : Optional.of((T) defaultValue);
+            }
+        } else {
+            return Optional.of((T) vectorOrScalar);
+        }
+
+    }
+
+    public static Optional<Class<?>> vectorElementType(Object vector) {
+        if (vector instanceof RAbstractContainer) {
+            if (vector instanceof RAbstractIntVector) {
+                return Optional.of(Integer.class);
+            }
+            if (vector instanceof RAbstractDoubleVector) {
+                return Optional.of(Double.class);
+            }
+            if (vector instanceof RAbstractComplexVector) {
+                return Optional.of(RComplex.class);
+            }
+            if (vector instanceof RAbstractLogicalVector) {
+                return Optional.of(Byte.class);
+            }
+            if (vector instanceof RAbstractStringVector) {
+                return Optional.of(String.class);
+            }
+            return Optional.of(Object.class);
+        } else {
+            return Optional.empty();
+        }
+
+    }
+
+    public static Set<?> sampleValuesForTypeExpr(TypeExpr te) {
+        return te.normalize().stream().flatMap(t -> CastUtils.sampleValuesForType(t).stream()).collect(Collectors.toSet());
+    }
+
     public static Set<?> sampleValuesForType(Type t) {
         HashSet<Object> samples = new HashSet<>();
 
@@ -683,7 +791,11 @@ public class CastUtils {
         return sampleSet;
     }
 
+    @SuppressWarnings("unchecked")
     public static <T> Set<? extends T> samples(T s) {
+        if (s == null) {
+            return Collections.singleton((T) RNull.instance);
+        }
         return Collections.singleton(s);
     }
 
@@ -691,4 +803,34 @@ public class CastUtils {
         return Collections.emptySet();
     }
 
+    public static String getPredefStepDesc() {
+        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+        return stackTrace[3].toString();
+    }
+
+    private static int instrIndent = 0;
+
+    static <T> Predicate<T> instrument(Predicate<T> predicate, String desc) {
+        if (Boolean.getBoolean("rbdiag.instrument")) {
+            return x -> {
+                char[] indentChars = new char[instrIndent];
+                Arrays.fill(indentChars, ' ');
+                String indent = String.valueOf(indentChars);
+
+                System.out.println(indent + "{ " + desc + ": " + x);
+                instrIndent++;
+                boolean res;
+                try {
+                    res = predicate.test(x);
+                } finally {
+                    instrIndent--;
+                }
+                System.out.println(indent + "} " + desc + ": " + x + "->" + res);
+                return res;
+            };
+        } else {
+            return predicate;
+        }
+
+    }
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/PredefFiltersSamplers.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/PredefFiltersSamplers.java
index e5ac5f91bc6280e94a4a31286e60f381e62ab22e..abc1459de7be218498b49f1b4396e6f6784716f2 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/PredefFiltersSamplers.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/PredefFiltersSamplers.java
@@ -24,17 +24,22 @@ package com.oracle.truffle.r.nodes.casts;
 
 import static com.oracle.truffle.r.nodes.casts.CastUtils.samples;
 
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Objects;
 
 import com.oracle.truffle.r.nodes.builtin.CastBuilder.PredefFilters;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RComplex;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 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.RAbstractRawVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
@@ -52,231 +57,192 @@ public final class PredefFiltersSamplers implements PredefFilters {
 
     @Override
     public <T, R extends T> TypePredicateArgumentFilterSampler<T, R> nullValue() {
-        return TypePredicateArgumentFilterSampler.fromLambda(x -> x == RNull.instance || x == null, CastUtils.<R> samples(null), CastUtils.<R> samples(), RNull.class);
+        // return TypePredicateArgumentFilterSampler.fromLambda(x -> x == RNull.instance || x ==
+        // null, CastUtils.<R> samples(null), CastUtils.<R> samples(), RNull.class);
+        return new TypePredicateArgumentFilterSampler<>("nullValue()", x -> x == RNull.instance || x == null, CastUtils.<R> samples(null), CastUtils.<R> samples(), Collections.singleton(RNull.class),
+                        true);
     }
 
     @Override
     public <T extends RAbstractVector> VectorPredicateArgumentFilterSampler<T> notEmpty() {
-        return new VectorPredicateArgumentFilterSampler<>(x -> x.getLength() > 0, false);
+        return new VectorPredicateArgumentFilterSampler<>("notEmpty()", x -> x.getLength() > 0, false, 0);
     }
 
     @Override
     public <T extends RAbstractVector> VectorPredicateArgumentFilterSampler<T> singleElement() {
-        return new VectorPredicateArgumentFilterSampler<>(x -> x.getLength() == 1, false);
+        return new VectorPredicateArgumentFilterSampler<>("singleElement()", x -> {
+            return x.getLength() == 1;
+        }, false, 0, 2);
     }
 
     @Override
-    public <T extends RAbstractVector, R extends T> VectorPredicateArgumentFilterSampler<T> size(int s) {
-        return new VectorPredicateArgumentFilterSampler<>(x -> x.getLength() == s, false);
+    public VectorPredicateArgumentFilterSampler<RAbstractStringVector> elementAt(int index, String value) {
+        return new VectorPredicateArgumentFilterSampler<>("elementAt", x -> index < x.getLength() && value.equals(x.getDataAtAsObject(index)), false, 0, index);
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Boolean> trueValue() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples(x -> x, CastUtils.<Boolean> samples(), samples(Boolean.FALSE));
+    public VectorPredicateArgumentFilterSampler<RAbstractIntVector> elementAt(int index, int value) {
+        return new VectorPredicateArgumentFilterSampler<>("elementAt", x -> index < x.getLength() && value == (int) (x.getDataAtAsObject(index)), false, 0, index);
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Boolean> falseValue() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples(x -> x, CastUtils.<Boolean> samples(), samples(Boolean.FALSE));
+    public VectorPredicateArgumentFilterSampler<RAbstractDoubleVector> elementAt(int index, double value) {
+        return new VectorPredicateArgumentFilterSampler<>("elementAt", x -> index < x.getLength() && value == (double) (x.getDataAtAsObject(index)), false, 0, index);
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Byte> logicalTrue() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples(x -> RRuntime.LOGICAL_TRUE == x, CastUtils.<Byte> samples(),
-                        samples(RRuntime.LOGICAL_FALSE));
+    public VectorPredicateArgumentFilterSampler<RAbstractComplexVector> elementAt(int index, RComplex value) {
+        return new VectorPredicateArgumentFilterSampler<>("elementAt", x -> index < x.getLength() && value.equals(x.getDataAtAsObject(index)), false, 0, index);
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Byte> logicalFalse() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples(x -> RRuntime.LOGICAL_FALSE == x, CastUtils.<Byte> samples(),
-                        samples(RRuntime.LOGICAL_TRUE));
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<Integer> intNA() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer x) -> RRuntime.isNA(x), samples(RRuntime.INT_NA), samples(0));
+    public VectorPredicateArgumentFilterSampler<RAbstractLogicalVector> elementAt(int index, byte value) {
+        return new VectorPredicateArgumentFilterSampler<>("elementAt", x -> index < x.getLength() && value == (byte) (x.getDataAtAsObject(index)), false, 0, index);
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Integer> notIntNA() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer x) -> !RRuntime.isNA(x), CastUtils.<Integer> samples(),
-                        samples(RRuntime.INT_NA));
+    public <T extends RAbstractVector, R extends T> VectorPredicateArgumentFilterSampler<T> size(int s) {
+        if (s == 0) {
+            return new VectorPredicateArgumentFilterSampler<>("size(int)", x -> x.getLength() == s, false, s - 1, s + 1);
+        } else {
+            return new VectorPredicateArgumentFilterSampler<>("size(int)", x -> x.getLength() == s, false, 0, s - 1, s + 1);
+        }
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Byte> logicalNA() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Byte x) -> RRuntime.isNA(x), samples(RRuntime.LOGICAL_NA),
-                        samples(RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE));
+    public ValuePredicateArgumentFilterSampler<Boolean> trueValue() {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples(x -> x, samples(Boolean.TRUE), samples(Boolean.FALSE));
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Byte> notLogicalNA() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Byte x) -> !RRuntime.isNA(x), CastUtils.<Byte> samples(),
-                        samples(RRuntime.LOGICAL_NA));
+    public ValuePredicateArgumentFilterSampler<Boolean> falseValue() {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples(x -> x, samples(Boolean.FALSE), samples(Boolean.TRUE));
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Double> doubleNA() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double x) -> RRuntime.isNA(x), samples(RRuntime.DOUBLE_NA), samples(0.0));
+    public ValuePredicateArgumentFilterSampler<Byte> logicalTrue() {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples(x -> RRuntime.LOGICAL_TRUE == x, samples(RRuntime.LOGICAL_TRUE),
+                        samples(RRuntime.LOGICAL_FALSE, RRuntime.LOGICAL_NA));
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Double> notDoubleNA() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double x) -> !RRuntime.isNA(x), CastUtils.<Double> samples(),
-                        samples(RRuntime.DOUBLE_NA));
+    public ValuePredicateArgumentFilterSampler<Byte> logicalFalse() {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples(x -> RRuntime.LOGICAL_FALSE == x, samples(RRuntime.LOGICAL_FALSE),
+                        samples(RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_NA));
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<String> stringNA() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((String x) -> RRuntime.isNA(x), samples(RRuntime.STRING_NA), samples(""));
+    public ValuePredicateArgumentFilterSampler<Integer> intNA() {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer x) -> RRuntime.isNA(x), samples(RRuntime.INT_NA), samples(0));
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<String> notStringNA() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((String x) -> !RRuntime.isNA(x), CastUtils.<String> samples(),
-                        samples(RRuntime.STRING_NA));
+    public ValuePredicateArgumentFilterSampler<Byte> logicalNA() {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Byte x) -> RRuntime.isNA(x), samples(RRuntime.LOGICAL_NA),
+                        samples(RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE));
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Integer> eq(int x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer arg) -> arg != null && arg.intValue() == x, samples(x), samples(x + 1));
+    public ValuePredicateArgumentFilterSampler<Double> doubleNA() {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double x) -> RRuntime.isNA(x), samples(RRuntime.DOUBLE_NA), samples(0d));
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Double> eq(double x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg != null && arg.doubleValue() == x, samples(x), samples(x + 0.00001));
+    public ValuePredicateArgumentFilterSampler<String> stringNA() {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((String x) -> RRuntime.isNA(x), samples(RRuntime.STRING_NA), samples(""));
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Integer> neq(int x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer arg) -> arg == null || arg.intValue() != x, samples(x + 1), samples(x));
+    public ValuePredicateArgumentFilterSampler<Integer> eq(int x) {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer arg) -> arg != null && arg.intValue() == x, samples(x), CastUtils.<Integer> samples(x + 1));
     }
 
     @Override
-    public ValuePredicateArgumentFilterSampler<Double> neq(double x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg == null || arg.doubleValue() != x, samples(x + 0.00001), samples(x));
+    public ValuePredicateArgumentFilterSampler<Double> eq(double x) {
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg != null && arg.doubleValue() == x, samples(x), CastUtils.<Double> samples(x + 1));
     }
 
     @Override
     public ValuePredicateArgumentFilterSampler<Integer> gt(int x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer arg) -> arg > x, samples(x + 1), samples(x));
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer arg) -> arg != null && arg > x, samples(x + 1), samples(x));
     }
 
     @Override
     public ValuePredicateArgumentFilterSampler<Double> gt(double x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg > x, samples(x + 0.00001), samples(x));
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<Integer> gte(int x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer arg) -> arg >= x, samples(x), samples(x - 1));
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg != null && arg > x, CastUtils.<Double> samples(), samples(x));
     }
 
     @Override
     public ValuePredicateArgumentFilterSampler<Double> gte(double x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg >= x, samples(x), samples(x - 0.00001));
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg != null && arg >= x, samples(x), samples(x - 1));
     }
 
     @Override
     public ValuePredicateArgumentFilterSampler<Integer> lt(int x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer arg) -> arg < x, samples(x - 1), samples(x));
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer arg) -> arg != null && arg < x, samples(x - 1), samples(x));
     }
 
     @Override
     public ValuePredicateArgumentFilterSampler<Double> lt(double x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg < x, samples(x - 0.00001), samples(x));
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<Integer> lte(int x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer arg) -> arg <= x, samples(x), samples(x + 1));
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg < x, CastUtils.<Double> samples(), samples(x));
     }
 
     @Override
     public ValuePredicateArgumentFilterSampler<Double> lte(double x) {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg <= x, samples(x), samples(x + 0.00001));
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Double arg) -> arg <= x, samples(x), samples(x + 1));
     }
 
     @Override
     public ValuePredicateArgumentFilterSampler<String> length(int l) {
-        return ValuePredicateArgumentFilterSampler.omLambdaWithResTypes((String arg) -> arg != null && arg.length() == l);
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<String> isEmpty() {
-        return ValuePredicateArgumentFilterSampler.omLambdaWithResTypes((String arg) -> arg != null && arg.isEmpty());
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((String arg) -> arg != null && arg.length() == l, samples(sampleString(l)),
+                        samples(sampleString(l + 1)));
     }
 
     @Override
     public ValuePredicateArgumentFilterSampler<String> lengthGt(int l) {
-        return ValuePredicateArgumentFilterSampler.omLambdaWithResTypes((String arg) -> arg != null && arg.length() > l);
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<String> lengthGte(int l) {
-        return ValuePredicateArgumentFilterSampler.omLambdaWithResTypes((String arg) -> arg != null && arg.length() >= l);
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((String arg) -> arg != null && arg.length() > l, samples(sampleString(l + 1)),
+                        samples(sampleString(l)));
     }
 
     @Override
     public ValuePredicateArgumentFilterSampler<String> lengthLt(int l) {
-        return ValuePredicateArgumentFilterSampler.omLambdaWithResTypes((String arg) -> arg != null && arg.length() < l);
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<String> lengthLte(int l) {
-        return ValuePredicateArgumentFilterSampler.omLambdaWithResTypes((String arg) -> arg != null && arg.length() <= l);
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<Integer> gt0() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer x) -> x > 0, CastUtils.<Integer> samples(), samples(-1, 0),
-                        Integer.class);
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<Integer> gte0() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer x) -> x >= 0, CastUtils.<Integer> samples(), samples(-1),
-                        Integer.class);
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<Integer> gt1() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer x) -> x > 1, CastUtils.<Integer> samples(), samples(-1, 0, 1),
-                        Integer.class);
-    }
-
-    @Override
-    public ValuePredicateArgumentFilterSampler<Integer> gte1() {
-        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((Integer x) -> x >= 1, CastUtils.<Integer> samples(), samples(-1, 0),
-                        Integer.class);
+        return ValuePredicateArgumentFilterSampler.fromLambdaWithSamples((String arg) -> arg != null && arg.length() < l, samples(sampleString(l - 1)),
+                        samples(sampleString(l)));
     }
 
     @Override
     public <R> TypePredicateArgumentFilterSampler<Object, R> instanceOf(Class<R> cls) {
-        return TypePredicateArgumentFilterSampler.fromLambda(x -> cls.isInstance(x), cls);
+        return TypePredicateArgumentFilterSampler.fromLambda(x -> cls.isInstance(x), CastUtils.<R> samples(), samples(null), cls);
     }
 
     @Override
     public <R extends RAbstractIntVector> TypePredicateArgumentFilterSampler<Object, R> integerValue() {
-        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Integer || x instanceof RAbstractIntVector, RAbstractIntVector.class, Integer.class);
+        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Integer || x instanceof RAbstractIntVector, CastUtils.<R> samples(), CastUtils.<Object> samples(null),
+                        RAbstractIntVector.class,
+                        Integer.class);
     }
 
     @Override
     public <R extends RAbstractStringVector> TypePredicateArgumentFilterSampler<Object, R> stringValue() {
         return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof String ||
-                        x instanceof RAbstractStringVector, RAbstractStringVector.class, String.class);
+                        x instanceof RAbstractStringVector, CastUtils.<R> samples(), CastUtils.<Object> samples(null), RAbstractStringVector.class, String.class);
     }
 
     @Override
     public <R extends RAbstractDoubleVector> TypePredicateArgumentFilterSampler<Object, R> doubleValue() {
         return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Double ||
-                        x instanceof RAbstractDoubleVector, RAbstractDoubleVector.class, Double.class);
+                        x instanceof RAbstractDoubleVector, CastUtils.<R> samples(), CastUtils.<Object> samples(null), RAbstractDoubleVector.class, Double.class);
     }
 
+    @SuppressWarnings("unchecked")
     @Override
     public <R extends RAbstractLogicalVector> TypePredicateArgumentFilterSampler<Object, R> logicalValue() {
         return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Byte ||
-                        x instanceof RAbstractLogicalVector, RAbstractLogicalVector.class, Byte.class);
+                        x instanceof RAbstractLogicalVector,
+                        samples((R) RDataFactory.createLogicalVectorFromScalar(RRuntime.LOGICAL_TRUE), (R) RDataFactory.createLogicalVectorFromScalar(RRuntime.LOGICAL_FALSE),
+                                        (R) RDataFactory.createLogicalVectorFromScalar(RRuntime.LOGICAL_NA)),
+                        CastUtils.samples(null), RAbstractLogicalVector.class,
+                        Byte.class);
     }
 
     @Override
@@ -285,24 +251,31 @@ public final class PredefFiltersSamplers implements PredefFilters {
                         x instanceof RAbstractComplexVector, RAbstractComplexVector.class, RComplex.class);
     }
 
+    @Override
+    public <R extends RAbstractRawVector> TypePredicateArgumentFilterSampler<Object, R> rawValue() {
+        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof RRaw ||
+                        x instanceof RAbstractRawVector, RAbstractRawVector.class, RRaw.class);
+    }
+
     @Override
     public TypePredicateArgumentFilterSampler<Object, String> scalarStringValue() {
-        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof String, String.class);
+        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof String, CastUtils.<String> samples(), CastUtils.<Object> samples(null), String.class);
     }
 
     @Override
     public TypePredicateArgumentFilterSampler<Object, Integer> scalarIntegerValue() {
-        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Integer, Integer.class);
+        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Integer, CastUtils.<Integer> samples(), CastUtils.<Object> samples(null), Integer.class);
     }
 
     @Override
     public TypePredicateArgumentFilterSampler<Object, Double> scalarDoubleValue() {
-        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Double, Double.class);
+        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Double, CastUtils.<Double> samples(), CastUtils.<Object> samples(null), Double.class);
     }
 
     @Override
     public TypePredicateArgumentFilterSampler<Object, Byte> scalarLogicalValue() {
-        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Byte, Byte.class);
+        return TypePredicateArgumentFilterSampler.fromLambda(x -> x instanceof Byte, samples(RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE, RRuntime.LOGICAL_NA), CastUtils.<Object> samples(null),
+                        Byte.class);
     }
 
     @Override
@@ -315,4 +288,13 @@ public final class PredefFiltersSamplers implements PredefFilters {
         return TypePredicateArgumentFilterSampler.fromLambda(x -> RMissing.instance == x, RMissing.class);
     }
 
+    private static String sampleString(int len) {
+        if (len <= 0) {
+            return "";
+        } else {
+            char[] ch = new char[len];
+            Arrays.fill(ch, 'a');
+            return String.valueOf(ch);
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/PredefMappersSamplers.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/PredefMappersSamplers.java
index 7df2eebb5225dbe2e01418f7ecb85d1d489f74a4..81709b92c63611dbfa2fa8d0a77f26c63a19707c 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/PredefMappersSamplers.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/PredefMappersSamplers.java
@@ -24,7 +24,7 @@ package com.oracle.truffle.r.nodes.casts;
 
 import static com.oracle.truffle.r.nodes.casts.CastUtils.samples;
 
-import java.util.HashSet;
+import java.util.Collections;
 
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder.PredefMappers;
@@ -35,34 +35,55 @@ public final class PredefMappersSamplers implements PredefMappers {
 
     @Override
     public ValuePredicateArgumentMapperSampler<Byte, Boolean> toBoolean() {
-        return ValuePredicateArgumentMapperSampler.fromLambda(x -> RRuntime.fromLogical(x), x -> RRuntime.asLogical(x), Boolean.class);
+        return ValuePredicateArgumentMapperSampler.fromLambda(x -> RRuntime.fromLogical(x), x -> RRuntime.asLogical(x), samples(RRuntime.LOGICAL_TRUE, RRuntime.LOGICAL_FALSE, RRuntime.LOGICAL_NA),
+                        CastUtils.<Byte> samples(), Byte.class, Boolean.class);
     }
 
     @Override
     public ValuePredicateArgumentMapperSampler<String, Integer> charAt0(int defaultValue) {
-        final ConditionProfile profile = ConditionProfile.createBinaryProfile();
-        return ValuePredicateArgumentMapperSampler.fromLambda(x -> profile.profile(x == null || x.isEmpty()) ? defaultValue : (int) x.charAt(0),
-                        x -> x == null ? "" + (char) defaultValue : "" + (char) x.intValue(), Integer.class);
+        return ValuePredicateArgumentMapperSampler.fromLambda(x -> {
+            if (x == null || x.isEmpty()) {
+                return defaultValue;
+            } else {
+                if (x == RRuntime.STRING_NA) {
+                    return RRuntime.INT_NA;
+                } else {
+                    return (int) x.charAt(0);
+                }
+            }
+        }, x -> {
+            if (x == null) {
+                return defaultValue == RRuntime.INT_NA ? RRuntime.STRING_NA : "" + (char) defaultValue;
+            } else {
+                return x == RRuntime.INT_NA ? RRuntime.STRING_NA : "" + (char) x.intValue();
+            }
+        }, samples(defaultValue == RRuntime.INT_NA ? RRuntime.STRING_NA : "" + (char) defaultValue), CastUtils.<String> samples(), String.class, Integer.class);
+    }
+
+    @Override
+    public <T> ValuePredicateArgumentMapperSampler<T, RNull> nullConstant() {
+        return ValuePredicateArgumentMapperSampler.<T, RNull> fromLambda((T x) -> RNull.instance, null, null, RNull.class);
     }
 
     @Override
     public ValuePredicateArgumentMapperSampler<String, String> constant(String s) {
-        return ValuePredicateArgumentMapperSampler.<String, String> fromLambda((String x) -> s, (String x) -> null, samples(s), CastUtils.<String> samples(), String.class);
+        return ValuePredicateArgumentMapperSampler.<String, String> fromLambda((String x) -> s, (String x) -> s, CastUtils.<String> samples(), CastUtils.<String> samples(), String.class,
+                        String.class);
     }
 
     @Override
     public ValuePredicateArgumentMapperSampler<Integer, Integer> constant(int i) {
-        return ValuePredicateArgumentMapperSampler.fromLambda(x -> i, x -> null, samples(i), CastUtils.<Integer> samples(), Integer.class);
+        return ValuePredicateArgumentMapperSampler.fromLambda(x -> i, x -> i, CastUtils.<Integer> samples(), CastUtils.<Integer> samples(), Integer.class, Integer.class);
     }
 
     @Override
     public ValuePredicateArgumentMapperSampler<Double, Double> constant(double d) {
-        return ValuePredicateArgumentMapperSampler.fromLambda(x -> d, x -> null, samples(d), CastUtils.<Double> samples(), Double.class);
+        return ValuePredicateArgumentMapperSampler.fromLambda(x -> d, x -> d, CastUtils.<Double> samples(), CastUtils.<Double> samples(), Double.class, Double.class);
     }
 
     @Override
     public ValuePredicateArgumentMapperSampler<Byte, Byte> constant(byte l) {
-        return ValuePredicateArgumentMapperSampler.fromLambda(x -> l, x -> null, samples(l), CastUtils.<Byte> samples(), Byte.class);
+        return ValuePredicateArgumentMapperSampler.fromLambda(x -> l, x -> l, CastUtils.<Byte> samples(), CastUtils.<Byte> samples(), Byte.class, Byte.class);
     }
 
     @Override
@@ -70,8 +91,6 @@ public final class PredefMappersSamplers implements PredefMappers {
 
         assert (defVal != null);
 
-        final TypeExpr defType = TypeExpr.atom(defVal.getClass()).or(TypeExpr.atom(RNull.class).not());
-
         return new ArgumentMapperSampler<T, T>() {
 
             final ConditionProfile profile = ConditionProfile.createBinaryProfile();
@@ -86,15 +105,15 @@ public final class PredefMappersSamplers implements PredefMappers {
             }
 
             @Override
-            public TypeExpr resultTypes() {
-                return defType;
+            public TypeExpr resultTypes(TypeExpr inputTypes) {
+                return inputTypes.and(TypeExpr.atom(RNull.class).not());
             }
 
+            @SuppressWarnings("unchecked")
             @Override
             public Samples<T> collectSamples(Samples<T> downStreamSamples) {
-                HashSet<T> posSamples = new HashSet<>(downStreamSamples.positiveSamples());
-                posSamples.add(defVal);
-                return new Samples<>(posSamples, downStreamSamples.negativeSamples());
+                Samples<Object> nullOnly = new Samples<>("RNullOnly", Collections.singleton(RNull.instance), Collections.emptySet(), x -> x == RNull.instance);
+                return (Samples<T>) nullOnly.or(Samples.anything(defVal).and(downStreamSamples));
             }
         };
     }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/Samples.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/Samples.java
index f2fd82e6643b2d7b7e85864956ee55079d986169..e548800b848bc5c046f89e56cf59a3a0dd042cbf 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/Samples.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/Samples.java
@@ -24,6 +24,7 @@ package com.oracle.truffle.r.nodes.casts;
 
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -31,19 +32,37 @@ import java.util.stream.Collectors;
 
 public final class Samples<T> {
 
-    private static final Samples<?> EMPTY = new Samples<>(Collections.emptySet(), Collections.emptySet());
+    private static final Samples<?> ANYTHING = new Samples<>("anything", Collections.emptySet(), Collections.emptySet(), x -> true);
 
     @SuppressWarnings("unchecked")
-    public static <T> Samples<T> empty() {
-        return (Samples<T>) EMPTY;
+    public static <T> Samples<T> anything() {
+        return (Samples<T>) ANYTHING;
+    }
+
+    public static <T> Samples<T> anything(T x) {
+        return new Samples<>("anything(" + x + ")", Collections.singleton(x), Collections.emptySet(), xx -> true);
     }
 
     private final Set<? extends T> posSamples;
     private final Set<?> negSamples;
+    private final Predicate<Object> posMembership;
+    private final Predicate<Object> negMembership;
+    private final String name;
+
+    public Samples(String name, Set<? extends T> positiveSamples, Set<?> negativeSamples, Predicate<Object> posMembership) {
+        this.name = name;
+        this.posSamples = positiveSamples;
+        this.negSamples = negativeSamples;
+        this.posMembership = CastUtils.instrument(posMembership, name);
+        this.negMembership = CastUtils.instrument(this.posMembership.negate(), "neg(" + name + ")");
+    }
 
-    public Samples(Set<? extends T> positiveSamples, Set<?> negativeSamples) {
+    private Samples(String name, Set<? extends T> positiveSamples, Set<?> negativeSamples, Predicate<Object> posMembership, Predicate<Object> negMembership) {
+        this.name = name;
         this.posSamples = positiveSamples;
         this.negSamples = negativeSamples;
+        this.posMembership = CastUtils.instrument(posMembership, name);
+        this.negMembership = CastUtils.instrument(negMembership, "neg(" + name + ")");
     }
 
     public Set<? extends T> positiveSamples() {
@@ -54,47 +73,96 @@ public final class Samples<T> {
         return negSamples;
     }
 
-    public <R> Samples<R> map(Function<T, R> posMapper, Function<Object, Object> negMapper) {
+    public Set<Object> allSamples() {
+        HashSet<Object> all = new HashSet<>(posSamples);
+        all.addAll(negSamples);
+        return all;
+    }
+
+    public <R> Samples<R> map(Function<T, R> posMapper, Function<Object, Object> negMapper, Function<Object, Optional<T>> posUnmapper, Function<Object, Optional<Object>> negUnmapper) {
         Set<R> mappedPositive = positiveSamples().stream().map(posMapper).collect(Collectors.toSet());
         Set<Object> mappedNegative = negativeSamples().stream().map(negMapper).collect(Collectors.toSet());
-        return new Samples<>(mappedPositive, mappedNegative);
+        return new Samples<>(name + ".map", mappedPositive, mappedNegative, x -> {
+            Optional<T> um = posUnmapper.apply(x);
+            return um.isPresent() ? posMembership.test(um.get()) : false;
+        }, x -> {
+            Optional<Object> um = negUnmapper.apply(x);
+            return um.isPresent() ? negMembership.test(um.get()) : false;
+        });
     }
 
-    public Samples<T> filter(Predicate<T> posCondition) {
-        Set<T> newPositive = positiveSamples().stream().filter(posCondition).collect(Collectors.toSet());
-        Set<T> newNegativeFromPositive = positiveSamples().stream().filter(x -> !posCondition.test(x)).collect(Collectors.toSet());
-        Set<Object> newNegative = new HashSet<>(negativeSamples());
-        newNegative.addAll(newNegativeFromPositive);
-        return new Samples<>(newPositive, newNegative);
+    public Samples<T> filter(Predicate<Object> newPosCondition) {
+        return filter(newPosCondition, negMembership);
     }
 
+    public Samples<T> filter(Predicate<Object> newPosCondition, Predicate<Object> newNegCondition) {
+        Set<T> newPositive = positiveSamples().stream().filter(newPosCondition).collect(Collectors.toSet());
+        Set<Object> newNegative = negativeSamples().stream().filter(newNegCondition).collect(Collectors.toSet());
+        return new Samples<>(name + ".filter", newPositive, newNegative, x -> posMembership.test(x) && newPosCondition.test(x),
+                        x -> negMembership.test(x) && newNegCondition.test(x));
+    }
+
+    @SuppressWarnings("unchecked")
     public Samples<T> and(Samples<? extends T> other) {
+        String newName = "and(" + name + "," + other.name + ")";
+
         Set<Object> negativeUnion = new HashSet<>(other.negativeSamples());
+        negativeUnion.addAll(other.positiveSamples());
         negativeUnion.addAll(negativeSamples());
-        Set<T> positiveUnion = new HashSet<>(other.positiveSamples());
+        negativeUnion.addAll(positiveSamples());
+        Predicate<Object> newNegCondition = CastUtils.instrument(negMembership.or(other.negMembership), "and-neg");
+        negativeUnion.removeIf(CastUtils.instrument(newNegCondition.negate(), "pruningNegUnion:" + newName));
+
+        Set<Object> positiveUnion = new HashSet<>(other.positiveSamples());
+        positiveUnion.addAll(other.negativeSamples());
         positiveUnion.addAll(positiveSamples());
-        positiveUnion.removeAll(negativeUnion);
+        positiveUnion.addAll(negativeSamples());
+        Predicate<Object> newPosCondition = CastUtils.instrument(posMembership.and(other.posMembership), "and-pos");
+        positiveUnion.removeIf(CastUtils.instrument(newPosCondition.negate(), "pruningPosUnion:" + newName));
 
-        return new Samples<>(positiveUnion, negativeUnion);
+        return new Samples<>(newName, (Set<T>) positiveUnion, negativeUnion, newPosCondition, newNegCondition);
     }
 
+    @SuppressWarnings("unchecked")
     public Samples<T> or(Samples<? extends T> other) {
-        Set<T> positiveUnion = new HashSet<>(other.positiveSamples());
-        positiveUnion.addAll(positiveSamples());
+        String newName = "or(" + name + "," + other.name + ")";
 
         Set<Object> negativeUnion = new HashSet<>(other.negativeSamples());
+        negativeUnion.addAll(other.positiveSamples());
         negativeUnion.addAll(negativeSamples());
-        negativeUnion.removeAll(positiveUnion);
+        negativeUnion.addAll(positiveSamples());
+        Predicate<Object> newNegCondition = CastUtils.instrument(negMembership.and(other.negMembership), "or-neg");
+        negativeUnion.removeIf(CastUtils.instrument(newNegCondition.negate(), "pruningNegUnion:" + newName));
+
+        Set<Object> positiveUnion = new HashSet<>(other.positiveSamples());
+        positiveUnion.addAll(other.negativeSamples());
+        positiveUnion.addAll(positiveSamples());
+        positiveUnion.addAll(negativeSamples());
+        Predicate<Object> newPosCondition = CastUtils.instrument(posMembership.or(other.posMembership), "or-neg");
+        positiveUnion.removeIf(CastUtils.instrument(newPosCondition.negate(), "pruningPosUnion:" + newName));
 
-        return new Samples<>(positiveUnion, negativeUnion);
+        return new Samples<>(newName, (Set<T>) positiveUnion, negativeUnion, newPosCondition, newNegCondition);
     }
 
     public Samples<Object> swap() {
-        return new Samples<>(negSamples, posSamples);
+        return new Samples<>(name + ".swap", negSamples, posSamples, negMembership, posMembership);
+    }
+
+    public Samples<Object> makePositive() {
+        Set<Object> mergedSamples = new HashSet<>(positiveSamples());
+        // Add negative samples to positive samples
+        mergedSamples.addAll(negativeSamples());
+        return new Samples<>(name + ".makePositive", mergedSamples, Collections.emptySet(), posMembership.or(negMembership));
+    }
+
+    public Samples<T> positiveOnly() {
+        return new Samples<>(name + ".positiveOnly", posSamples, Collections.emptySet(), posMembership);
     }
 
     @Override
     public String toString() {
-        return posSamples.toString() + ":" + negSamples.toString();
+        // return posSamples.toString() + ":" + negSamples.toString();
+        return "Positive:" + posSamples.stream().map(s -> s != null ? s + "(" + s.getClass() + ")" : "null").collect(Collectors.toList()).toString() + "\nNegative:" +
+                        negSamples.stream().map(s -> s != null ? s + "(" + s.getClass() + ")" : "null").collect(Collectors.toList());
     }
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TestCasts.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TestCasts.java
index 64b3473d320c8b428f1b7c1fb57a63cde8188ea8..494f5e2116c721d4c45615faa7f51a370d209f15 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TestCasts.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TestCasts.java
@@ -45,9 +45,6 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.scalarLogica
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 
-import java.io.IOException;
-import java.io.OutputStream;
-
 import org.junit.Test;
 
 import com.oracle.truffle.api.CompilerAsserts;
@@ -347,10 +344,9 @@ public class TestCasts extends TestBase {
         class Root extends TestRootNode<CastNode> {
 
             protected Root(String name) {
-                super(name,
-                                new CastBuilder().output(NullStream.INSTANCE).arg(0).mustBe(numericValue()).asVector().mustBe(singleElement()).findFirst().mustBe(nullValue().not()).shouldBe(
-                                                ValuePredicateArgumentFilterSampler.omLambdaWithResTypes(x -> x instanceof Byte || x instanceof Integer && ((Integer) x) > 0, Object.class),
-                                                Message.NON_POSITIVE_FILL).mapIf(scalarLogicalValue(), asBoolean(), asInteger()).builder().getCasts()[0]);
+                super(name, new CastBuilder().arg(0).mustBe(numericValue()).asVector().mustBe(singleElement()).findFirst().mustBe(nullValue().not()).shouldBe(
+                                ValuePredicateArgumentFilterSampler.fromLambdaWithResTypes(x -> x instanceof Byte || x instanceof Integer && ((Integer) x) > 0, Object.class),
+                                Message.NON_POSITIVE_FILL).mapIf(scalarLogicalValue(), asBoolean(), asInteger()).builder().getCasts()[0]);
             }
 
             @Override
@@ -370,7 +366,7 @@ public class TestCasts extends TestBase {
         class Root extends TestRootNode<CastNode> {
 
             protected Root(String name) {
-                super(name, new CastBuilder().output(NullStream.INSTANCE).arg(0).mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst().mustBe(lengthLte(1)).map(
+                super(name, new CastBuilder().arg(0).mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst().mustBe(lengthLte(1)).map(
                                 charAt0(RRuntime.INT_NA)).notNA(100000).builder().getCasts()[0]);
             }
 
@@ -409,7 +405,7 @@ public class TestCasts extends TestBase {
         class Root extends TestRootNode<CastNode> {
 
             protected Root(String name) {
-                super(name, new CastBuilder().output(NullStream.INSTANCE).arg(0).mustBe(scalarIntegerValue()).shouldBe(gt0().and(lt(10))).builder().getCasts()[0]);
+                super(name, new CastBuilder().arg(0).mustBe(scalarIntegerValue()).shouldBe(gt0().and(lt(10))).builder().getCasts()[0]);
             }
 
             @Override
@@ -427,7 +423,7 @@ public class TestCasts extends TestBase {
         class Root extends TestRootNode<CastNode> {
 
             protected Root(String name) {
-                super(name, new CastBuilder().output(NullStream.INSTANCE).arg(0).mustBe(scalarIntegerValue()).shouldBe(gt0().and(lt(10)).not()).builder().getCasts()[0]);
+                super(name, new CastBuilder().arg(0).mustBe(scalarIntegerValue()).shouldBe(gt0().and(lt(10)).not()).builder().getCasts()[0]);
             }
 
             @Override
@@ -445,7 +441,7 @@ public class TestCasts extends TestBase {
         class Root extends TestRootNode<CastNode> {
 
             protected Root(String name) {
-                super(name, new CastBuilder().output(NullStream.INSTANCE).arg(0).mustBe(numericValue()).asVector().mustBe(singleElement()).findFirst().mustBe(nullValue().not()).shouldBe(
+                super(name, new CastBuilder().arg(0).mustBe(numericValue()).asVector().mustBe(singleElement()).findFirst().mustBe(nullValue().not()).shouldBe(
                                 instanceOf(Byte.class).or(instanceOf(Integer.class).and(gt0())), Message.NON_POSITIVE_FILL).mapIf(scalarLogicalValue(), asBoolean(),
                                                 asInteger()).builder().getCasts()[0]);
             }
@@ -460,14 +456,4 @@ public class TestCasts extends TestBase {
         testCompilation(new Object[]{RDataFactory.createIntVectorFromScalar(1)}, new Root("ComplexPipeline3SingleInt"));
     }
 
-    static class NullStream extends OutputStream {
-
-        static final NullStream INSTANCE = new NullStream();
-
-        @Override
-        public void write(int b) throws IOException {
-        }
-
-    }
-
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TypeExpr.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TypeExpr.java
index c4b4c0a79285697509a2ef9776c56dfbde03fa5b..32ae7bcc5e976b28d118869730659d86db62a44a 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TypeExpr.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TypeExpr.java
@@ -121,6 +121,10 @@ public final class TypeExpr {
         return disjNormForm.stream().map(conj -> normalize(conj)).filter(t -> !t.equals(Not.NOTHING)).collect(Collectors.toSet());
     }
 
+    public Set<Class<?>> toClasses() {
+        return normalize().stream().filter(t -> t instanceof Class).map(t -> (Class<?>) t).collect(Collectors.toSet());
+    }
+
     private static Type normalize(Set<? extends Type> conj) {
         Type[] conjArray = conj.toArray(new Type[conj.size()]);
         Set<Type> lessSpecific = new HashSet<>();
@@ -182,7 +186,7 @@ public final class TypeExpr {
         }).findAny().isPresent();
     }
 
-    public Cast.Coverage coverageFrom(Type from, boolean includeImplicits) {
+    public Cast.Coverage isConvertibleFrom(Type from, boolean includeImplicits) {
         return normalize().stream().map(t -> CastUtils.Casts.isConvertible(from, t, includeImplicits)).reduce((res, cvg) -> res.or(cvg)).orElse(Cast.Coverage.none);
     }
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TypePredicateArgumentFilterSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TypePredicateArgumentFilterSampler.java
index 993225e9788ebcd67de6332837471dc9a74d3775..61bded5236ca166dee7a2bb87f78362a3513b86b 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TypePredicateArgumentFilterSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/TypePredicateArgumentFilterSampler.java
@@ -24,7 +24,6 @@ package com.oracle.truffle.r.nodes.casts;
 
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -34,50 +33,55 @@ import com.oracle.truffle.r.nodes.casts.ArgumentFilterSampler.ArgumentTypeFilter
 
 public class TypePredicateArgumentFilterSampler<T, R extends T> extends TypePredicateArgumentFilter<T, R> implements ArgumentTypeFilterSampler<T, R> {
 
-    private final Set<? extends R> positiveSamples;
-    private final Set<?> negativeSamples;
-    private final TypeExpr allowedTypes;
+    private final TypeExpr trueBranchTypes;
+    private final Samples<R> samples;
+    private final String desc;
 
-    public TypePredicateArgumentFilterSampler(Predicate<? super T> valuePredicate, Set<? extends R> positiveSamples, Set<?> negativeSamples, Set<Class<?>> allowedTypeSet, boolean isNullable) {
+    @SuppressWarnings("unchecked")
+    public TypePredicateArgumentFilterSampler(String desc, Predicate<? super T> valuePredicate, Set<? extends R> positiveSamples, Set<?> negativeSamples, Set<Class<?>> allowedTypeSet,
+                    boolean isNullable) {
         super(valuePredicate, isNullable);
 
-        this.allowedTypes = allowedTypeSet.isEmpty() ? TypeExpr.ANYTHING : TypeExpr.union(allowedTypeSet);
-        this.positiveSamples = positiveSamples;
-        this.negativeSamples = negativeSamples;
+        this.trueBranchTypes = allowedTypeSet.isEmpty() ? TypeExpr.ANYTHING : TypeExpr.union(allowedTypeSet);
+        Predicate<Object> posMembership = x -> trueBranchTypes.isInstance(x) && test((T) x);
+        this.samples = new Samples<>(desc, positiveSamples, negativeSamples, posMembership);
 
         assert positiveSamples.stream().allMatch(x -> valuePredicate.test(x));
+
+        this.desc = desc;
     }
 
     @Override
-    public TypeExpr allowedTypes() {
-        return allowedTypes;
+    public TypeExpr trueBranchType() {
+        return trueBranchTypes;
     }
 
     @Override
-    public Samples<R> collectSamples(Samples<? extends R> downStreamSamples) {
-        Set<R> allowedPositiveValues = downStreamSamples.positiveSamples().stream().filter(x -> test(x)).collect(Collectors.toSet());
-        Set<R> forbiddenPositiveValues = downStreamSamples.positiveSamples().stream().filter(x -> !test(x)).collect(Collectors.toSet());
-
-        Set<Object> negativeAndForbiddenSamples = new HashSet<>(forbiddenPositiveValues);
-        negativeAndForbiddenSamples.addAll(downStreamSamples.negativeSamples());
+    public Samples<R> collectSamples(TypeExpr inputType) {
+        return samples;
+    }
 
-        return new Samples<>(allowedPositiveValues, negativeAndForbiddenSamples).and(new Samples<>(positiveSamples, negativeSamples));
+    @Override
+    public String toString() {
+        return desc;
     }
 
     public static <T, R extends T> TypePredicateArgumentFilterSampler<T, R> fromLambda(Predicate<? super T> predicate, Set<? extends R> positiveSamples, Set<?> negativeSamples,
-                    Class<?> resultClass) {
-        return new TypePredicateArgumentFilterSampler<>(predicate, positiveSamples, negativeSamples, Collections.singleton(resultClass), false);
+                    Class<?>... resultClass) {
+        return new TypePredicateArgumentFilterSampler<>(CastUtils.getPredefStepDesc(), predicate, positiveSamples, negativeSamples, Arrays.asList(resultClass).stream().collect(Collectors.toSet()),
+                        true);
     }
 
     public static <T, R extends T> TypePredicateArgumentFilterSampler<T, R> fromLambda(Predicate<T> predicate, Class<?>... resultClass) {
-        return new TypePredicateArgumentFilterSampler<>(predicate, Collections.emptySet(), Collections.emptySet(), Arrays.asList(resultClass).stream().collect(Collectors.toSet()), false);
+        return new TypePredicateArgumentFilterSampler<>(CastUtils.getPredefStepDesc(), predicate, Collections.emptySet(), Collections.emptySet(),
+                        Arrays.asList(resultClass).stream().collect(Collectors.toSet()), true);
     }
 
     public static <T, R extends T> TypePredicateArgumentFilterSampler<T, R> fromLambda(Predicate<T> predicate, Set<? extends R> positiveSamples, Set<?> negativeSamples) {
-        return new TypePredicateArgumentFilterSampler<>(predicate, positiveSamples, negativeSamples, Collections.emptySet(), false);
+        return new TypePredicateArgumentFilterSampler<>(CastUtils.getPredefStepDesc(), predicate, positiveSamples, negativeSamples, Collections.emptySet(), true);
     }
 
     public static <T, R extends T> TypePredicateArgumentFilterSampler<T, R> fromLambda(Predicate<T> predicate, @SuppressWarnings("unused") Class<R> commonAncestorClass, Set<Class<?>> resultClasses) {
-        return new TypePredicateArgumentFilterSampler<>(predicate, Collections.emptySet(), Collections.emptySet(), resultClasses, false);
+        return new TypePredicateArgumentFilterSampler<>(CastUtils.getPredefStepDesc(), predicate, Collections.emptySet(), Collections.emptySet(), resultClasses, true);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentFilterSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentFilterSampler.java
index a74481f45daa9bc869815283f415bafa5dfcaa69..1b29123007f6ee8a4fc2d0ddef798fc84de3efdc 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentFilterSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentFilterSampler.java
@@ -24,7 +24,6 @@ package com.oracle.truffle.r.nodes.casts;
 
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
@@ -34,53 +33,56 @@ import com.oracle.truffle.r.nodes.casts.ArgumentFilterSampler.ArgumentValueFilte
 
 public class ValuePredicateArgumentFilterSampler<T> extends ValuePredicateArgumentFilter<T> implements ArgumentValueFilterSampler<T> {
 
-    private final Set<? extends T> positiveSamples;
-    private final Set<?> negativeSamples;
-    private final TypeExpr allowedTypes;
+    private final TypeExpr trueBranchTypes;
+    private final Samples<T> samples;
+    private final String desc;
 
-    public ValuePredicateArgumentFilterSampler(Predicate<? super T> valuePredicate, Set<? extends T> positiveSamples, Set<? extends T> negativeSamples, Set<Class<?>> allowedTypeSet,
+    @SuppressWarnings("unchecked")
+    public ValuePredicateArgumentFilterSampler(String desc, Predicate<? super T> valuePredicate, Set<? extends T> positiveSamples, Set<? extends T> negativeSamples, Set<Class<?>> allowedTypeSet,
                     boolean isNullable) {
         super(valuePredicate, isNullable);
 
-        this.allowedTypes = allowedTypeSet.isEmpty() ? TypeExpr.ANYTHING : TypeExpr.union(allowedTypeSet);
-        this.positiveSamples = positiveSamples;
-        this.negativeSamples = negativeSamples;
+        this.trueBranchTypes = allowedTypeSet.isEmpty() ? TypeExpr.ANYTHING : TypeExpr.union(allowedTypeSet);
+        Predicate<Object> posMembership = x -> trueBranchTypes.isInstance(x) && test((T) x);
+        this.samples = new Samples<>(desc, positiveSamples, negativeSamples, posMembership);
 
         assert positiveSamples.stream().allMatch(x -> valuePredicate.test(x));
+
+        this.desc = desc;
     }
 
     @Override
-    public TypeExpr allowedTypes() {
-        return allowedTypes;
+    public TypeExpr trueBranchType() {
+        return trueBranchTypes;
     }
 
     @Override
-    public Samples<T> collectSamples(Samples<? extends T> downStreamSamples) {
-        Set<T> allowedPositiveValues = downStreamSamples.positiveSamples().stream().filter(x -> test(x)).collect(Collectors.toSet());
-        Set<T> forbiddenPositiveValues = downStreamSamples.positiveSamples().stream().filter(x -> !test(x)).collect(Collectors.toSet());
-
-        Set<Object> negativeAndForbiddenSamples = new HashSet<>(forbiddenPositiveValues);
-        negativeAndForbiddenSamples.addAll(downStreamSamples.negativeSamples());
+    public String toString() {
+        return desc;
+    }
 
-        return new Samples<>(allowedPositiveValues, negativeAndForbiddenSamples).and(new Samples<>(positiveSamples, negativeSamples));
+    @Override
+    public Samples<T> collectSamples(TypeExpr inputType) {
+        return samples;
     }
 
     public static <T> ValuePredicateArgumentFilterSampler<T> fromLambdaWithSamples(Predicate<? super T> predicate, Set<? extends T> positiveSamples, Set<? extends T> negativeSamples,
                     Class<?> resultClass) {
-        return new ValuePredicateArgumentFilterSampler<>(predicate, positiveSamples, negativeSamples, Collections.singleton(resultClass), false);
+        return new ValuePredicateArgumentFilterSampler<>(CastUtils.getPredefStepDesc(), predicate, positiveSamples, negativeSamples, Collections.singleton(resultClass), true);
     }
 
-    public static <T> ValuePredicateArgumentFilterSampler<T> omLambdaWithResTypes(Predicate<T> predicate, Class<?>... resultClass) {
-        return new ValuePredicateArgumentFilterSampler<>(predicate, Collections.emptySet(), Collections.emptySet(), Arrays.asList(resultClass).stream().collect(Collectors.toSet()), false);
+    public static <T> ValuePredicateArgumentFilterSampler<T> fromLambdaWithResTypes(Predicate<T> predicate, Class<?>... resultClass) {
+        return new ValuePredicateArgumentFilterSampler<>(CastUtils.getPredefStepDesc(), predicate, Collections.emptySet(), Collections.emptySet(),
+                        Arrays.asList(resultClass).stream().collect(Collectors.toSet()), true);
     }
 
     public static <T> ValuePredicateArgumentFilterSampler<T> fromLambdaWithSamples(Predicate<T> predicate, Set<? extends T> positiveSamples, Set<? extends T> negativeSamples) {
-        return new ValuePredicateArgumentFilterSampler<>(predicate, positiveSamples, negativeSamples, Collections.emptySet(), false);
+        return new ValuePredicateArgumentFilterSampler<>(CastUtils.getPredefStepDesc(), predicate, positiveSamples, negativeSamples, Collections.emptySet(), true);
     }
 
     public static <T> ValuePredicateArgumentFilterSampler<T> fromLambdaWithSamples(Predicate<T> predicate, Set<T> negativeSamples,
                     @SuppressWarnings("unused") Class<T> commonAncestorClass, Set<Class<?>> resultClasses) {
-        return new ValuePredicateArgumentFilterSampler<>(predicate, Collections.emptySet(), negativeSamples, resultClasses, false);
+        return new ValuePredicateArgumentFilterSampler<>(CastUtils.getPredefStepDesc(), predicate, Collections.emptySet(), negativeSamples, resultClasses, true);
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentMapperSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentMapperSampler.java
index 87d47cc59d904933ca06d4f42499092fd45d8d89..198567699625f24e3328b500745118ff3965dba6 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentMapperSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/ValuePredicateArgumentMapperSampler.java
@@ -23,44 +23,68 @@
 package com.oracle.truffle.r.nodes.casts;
 
 import java.util.Collections;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import com.oracle.truffle.r.nodes.builtin.ValuePredicateArgumentMapper;
 
 public class ValuePredicateArgumentMapperSampler<T, R> extends ValuePredicateArgumentMapper<T, R> implements ArgumentMapperSampler<T, R> {
 
     private final Function<R, T> unmapper;
-    private final TypeExpr allowedTypes;
-    private final Set<? extends T> positiveSamples;
-    private final Set<?> negativeSamples;
+    private final TypeExpr inputTypes;
+    private final TypeExpr resTypes;
+    private final String desc;
+    private final Samples<T> samples;
 
-    public ValuePredicateArgumentMapperSampler(Function<T, R> mapper, Function<R, T> unmapper, Set<? extends T> positiveSamples, Set<?> negativeSamples, Set<Class<?>> allowedTypeSet) {
+    public ValuePredicateArgumentMapperSampler(String desc, Function<T, R> mapper, Function<R, T> unmapper, Set<? extends T> positiveSamples, Set<?> negativeSamples, Set<Class<?>> inputTypeSet,
+                    Set<Class<?>> resultTypeSet) {
         super(mapper);
         this.unmapper = unmapper;
-        this.allowedTypes = allowedTypeSet.isEmpty() ? TypeExpr.ANYTHING : TypeExpr.union(allowedTypeSet);
-        this.positiveSamples = positiveSamples;
-        this.negativeSamples = negativeSamples;
+        this.inputTypes = inputTypeSet.isEmpty() ? TypeExpr.ANYTHING : TypeExpr.union(inputTypeSet);
+        this.resTypes = resultTypeSet.isEmpty() ? TypeExpr.ANYTHING : TypeExpr.union(resultTypeSet);
+        this.desc = desc;
+        Predicate<Object> posMembership = x -> inputTypes.isInstance(x) && !negativeSamples.contains(x);
+        this.samples = new Samples<>(desc, positiveSamples, negativeSamples, posMembership);
     }
 
     @Override
-    public TypeExpr resultTypes() {
-        return allowedTypes;
+    public TypeExpr resultTypes(TypeExpr it) {
+        return resTypes;
     }
 
-    public static <T, R> ValuePredicateArgumentMapperSampler<T, R> fromLambda(Function<T, R> mapper, Function<R, T> unmapper, Class<R> resultClass) {
-        return new ValuePredicateArgumentMapperSampler<>(mapper, unmapper, Collections.emptySet(), Collections.emptySet(), Collections.singleton(resultClass));
+    @Override
+    public String toString() {
+        return desc;
+    }
+
+    public static <T, R> ValuePredicateArgumentMapperSampler<T, R> fromLambda(Function<T, R> mapper, Function<R, T> unmapper, Class<T> inputClass, Class<R> resultClass) {
+        return new ValuePredicateArgumentMapperSampler<>(CastUtils.getPredefStepDesc(), mapper, unmapper, Collections.emptySet(), Collections.emptySet(),
+                        inputClass == null ? Collections.emptySet() : Collections.singleton(inputClass),
+                        resultClass == null ? Collections.emptySet() : Collections.singleton(resultClass));
     }
 
     public static <T, R> ValuePredicateArgumentMapperSampler<T, R> fromLambda(Function<T, R> mapper, Function<R, T> unmapper, Set<? extends T> positiveSamples, Set<? extends T> negativeSamples,
-                    Class<R> resultClass) {
-        return new ValuePredicateArgumentMapperSampler<>(mapper, unmapper, positiveSamples, negativeSamples, Collections.singleton(resultClass));
+                    Class<T> inputClass, Class<R> resultClass) {
+        return new ValuePredicateArgumentMapperSampler<>(CastUtils.getPredefStepDesc(), mapper, unmapper, positiveSamples, negativeSamples, Collections.singleton(inputClass),
+                        Collections.singleton(resultClass));
     }
 
     @Override
     public Samples<T> collectSamples(Samples<R> downStreamSamples) {
-        Samples<T> unmappedSamples = downStreamSamples.map(x -> unmapper.apply(x), x -> x);
-        return new Samples<T>(positiveSamples, negativeSamples).and(unmappedSamples);
+        if (unmapper == null) {
+            return samples;
+        } else {
+            Samples<R> filtered = downStreamSamples.filter(x -> resTypes.isInstance(x), x -> resTypes.isInstance(x));
+            @SuppressWarnings("unchecked")
+            Samples<T> unmappedSamples = filtered.map(x -> unmapper.apply(x), x -> unmapper.apply((R) x),
+                            x -> inputTypes.isInstance(x) ? Optional.of(mapper.apply((T) x)) : Optional.empty(),
+                            x -> inputTypes.isInstance(x) ? Optional.of(mapper.apply((T) x)) : Optional.empty());
+
+            Samples<T> combined = samples.and(unmappedSamples);
+            return combined;
+        }
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/VectorPredicateArgumentFilterSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/VectorPredicateArgumentFilterSampler.java
index eaaca7067130420e32d67660814c62f5e7b0f317..92f4853c4918c6be5ecaa85499177ff6292459b1 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/VectorPredicateArgumentFilterSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/casts/VectorPredicateArgumentFilterSampler.java
@@ -22,25 +22,59 @@
  */
 package com.oracle.truffle.r.nodes.casts;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 import com.oracle.truffle.r.nodes.builtin.VectorPredicateArgumentFilter;
 import com.oracle.truffle.r.nodes.casts.ArgumentFilterSampler.ArgumentValueFilterSampler;
+import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 public class VectorPredicateArgumentFilterSampler<T extends RAbstractVector> extends VectorPredicateArgumentFilter<T> implements ArgumentValueFilterSampler<T> {
 
-    public VectorPredicateArgumentFilterSampler(Predicate<T> valuePredicate, boolean isNullable) {
+    private final String desc;
+    private final List<Integer> invalidVectorSize;
+
+    public VectorPredicateArgumentFilterSampler(String desc, Predicate<T> valuePredicate, boolean isNullable, Integer... invalidVectorSize) {
         super(valuePredicate, isNullable);
+        this.desc = desc;
+        this.invalidVectorSize = invalidVectorSize == null ? Collections.emptyList() : Arrays.asList(invalidVectorSize);
+    }
+
+    @SuppressWarnings("unchecked")
+    private boolean testVector(Object x) {
+        if (x instanceof RAbstractVector) {
+            return test((T) x);
+        } else {
+            Object v = CastUtils.singletonVector(x);
+            if (v == RNull.instance) {
+                v = null;
+            }
+            return test((T) v);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return this.desc;
     }
 
     @Override
-    public Samples<T> collectSamples(Samples<? extends T> downStreamSamples) {
-        return Samples.empty();
+    public Samples<T> collectSamples(TypeExpr inputType) {
+        Set<RAbstractVector> negSamples = inputType.toClasses().stream().flatMap(vt -> invalidVectorSize.stream().map(sz -> CastUtils.vectorOfSize(vt, sz))).collect(Collectors.toSet());
+
+        Predicate<Object> posMembership = this::testVector;
+        final Samples<T> samples = new Samples<>(desc, Collections.emptySet(), negSamples, posMembership);
+
+        return samples;
     }
 
     @Override
-    public TypeExpr allowedTypes() {
+    public TypeExpr trueBranchType() {
         return TypeExpr.ANYTHING;
     }
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb9b3f49054c9a39298b4f3328df2ac7864771b5
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2013, 2016, 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.test;
+
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.stream.Collectors;
+
+import org.junit.Assert;
+
+import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.api.vm.PolyglotEngine.Value;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinFactory;
+import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
+import com.oracle.truffle.r.nodes.casts.Samples;
+import com.oracle.truffle.r.nodes.test.RBuiltinDiagnostics.DiagConfig;
+import com.oracle.truffle.r.nodes.test.RBuiltinDiagnostics.SingleBuiltinDiagnostics;
+import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.RDeparse;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RSource;
+import com.oracle.truffle.r.runtime.ResourceHandlerFactory;
+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.test.TestBase;
+import com.oracle.truffle.r.test.generate.FastRSession;
+import com.oracle.truffle.r.test.generate.GnuROneShotRSession;
+import com.oracle.truffle.r.test.generate.TestOutputManager;
+import com.oracle.truffle.r.test.generate.TestOutputManager.TestInfo;
+
+class ChimneySweeping extends SingleBuiltinDiagnostics {
+
+    private static final String TEST_PREFIX = "com.oracle.truffle.r.test.builtins.TestBuiltin_";
+    private static final String SWEEP_MODE_ARG = "--sweep";
+    private static final String SWEEP_MODE_ARG_SPEC = SWEEP_MODE_ARG + "-";
+
+    enum ChimneySweepingMode {
+        auto,
+        total,
+        lite;
+
+        static Optional<ChimneySweepingMode> fromArg(String arg) {
+            if (SWEEP_MODE_ARG.equals(arg)) {
+                return Optional.of(auto);
+            } else if (arg.startsWith(SWEEP_MODE_ARG_SPEC)) {
+                return Optional.of(valueOf(arg.substring(SWEEP_MODE_ARG_SPEC.length())));
+            } else {
+                return Optional.empty();
+            }
+        }
+    }
+
+    static class ChimneySweepingConfig extends DiagConfig {
+        ChimneySweepingMode sweepingMode;
+    }
+
+    static class ChimneySweepingSuite extends RBuiltinDiagnostics {
+
+        final ChimneySweepingConfig diagConfig;
+        final FastRSession fastRSession;
+        final GnuROneShotRSession gnuRSession;
+        final TestOutputManager outputManager;
+
+        ChimneySweepingSuite(ChimneySweepingConfig config) throws IOException {
+            super(config);
+            this.diagConfig = config;
+
+            System.out.println("Loading GnuR ...");
+            gnuRSession = new GnuROneShotRSession();
+
+            System.out.println("Loading FastR ...");
+            fastRSession = FastRSession.create();
+
+            System.out.println("Loading test outputs ...");
+            outputManager = loadTestOutputManager();
+        }
+
+        static Optional<RBuiltinDiagnostics> createChimneySweepingSuite(String[] args) throws IOException {
+            if (getSweepMode(args).isPresent()) {
+                ChimneySweepingConfig config = new ChimneySweepingConfig();
+                return Optional.of(new ChimneySweepingSuite(initChimneySweepingConfig(config, args)));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        static <C extends ChimneySweepingConfig> C initChimneySweepingConfig(C config, String[] args) {
+            config.sweepingMode = getSweepMode(args).flatMap(ChimneySweepingMode::fromArg).orElse(ChimneySweepingMode.auto);
+            return RBuiltinDiagnostics.initDiagConfig(config, args);
+        }
+
+        private static Optional<String> getSweepMode(String[] args) {
+            return Arrays.stream(args).filter(arg -> arg.startsWith(SWEEP_MODE_ARG)).findFirst();
+        }
+
+        @Override
+        public SingleBuiltinDiagnostics createBuiltinDiagnostics(RBuiltinFactory bf) {
+            return new ChimneySweeping(this, bf);
+        }
+
+        private static TestOutputManager loadTestOutputManager() throws IOException {
+            TestOutputManager om;
+
+            URL expectedTestOutputURL = ResourceHandlerFactory.getHandler().getResource(TestBase.class, TestOutputManager.TEST_EXPECTED_OUTPUT_FILE);
+            if (expectedTestOutputURL == null) {
+                throw new IOException("cannot find " + TestOutputManager.TEST_EXPECTED_OUTPUT_FILE + " resource");
+            } else {
+                om = new TestOutputManager(new File(expectedTestOutputURL.getPath()));
+                om.readTestOutputFile();
+            }
+
+            return om;
+        }
+    }
+
+    private final List<Samples<?>> argSamples;
+    private final ChimneySweepingSuite diagSuite;
+    private final Set<RList> validArgsList;
+
+    private final Set<List<String>> printedOutputPairs = new HashSet<>();
+    private final Set<String> printedErrors = new HashSet<>();
+    private int sweepCounter = 0;
+
+    ChimneySweeping(ChimneySweepingSuite diagSuite, RBuiltinFactory builtinFactory) {
+        super(diagSuite, builtinFactory);
+        this.diagSuite = diagSuite;
+        this.validArgsList = extractValidArgsForBuiltin();
+        this.argSamples = createSamples();
+    }
+
+    @Override
+    public void diagnoseBuiltin() throws Exception {
+        super.diagnoseBuiltin();
+
+        sweepChimney();
+    }
+
+    @Override
+    protected void diagnosePipeline(int i) {
+        super.diagnosePipeline(i);
+
+        System.out.println(" Samples:");
+        System.out.println(argSamples.get(i));
+
+        checkPipelines(i);
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    private List<Samples<?>> createSamples() {
+        DefaultArgsExtractor defArgExt = new DefaultArgsExtractor(diagSuite.fastRSession);
+        Map<String, Samples<?>> defaultArgs = defArgExt.extractDefaultArgs(builtinName);
+
+        List<Samples<?>> as = new ArrayList<>();
+        for (int i = 0; i < argLength; i++) {
+            CastNode cn;
+            if (i < castNodes.length) {
+                cn = castNodes[i];
+            } else {
+                cn = null;
+            }
+            Samples samples;
+            try {
+                if (cn == null) {
+                    samples = Samples.anything();
+                } else {
+                    CastNodeSampler<CastNode> sampler = CastNodeSampler.createSampler(cn);
+                    samples = sampler.collectSamples();
+                }
+            } catch (Exception e) {
+                throw new RuntimeException("Error in sample generation from argument " + i, e);
+            }
+
+            Samples defArgSamples = defaultArgs.get(parameterNames[i]);
+            samples = defArgSamples == null ? samples : samples.and(defArgSamples);
+
+            as.add(samples);
+
+        }
+
+        return as;
+    }
+
+    /**
+     * Checks whether the argument samples are correct by passing them to the argument's pipeline.
+     * The positive samples should pass without any error, while the negative ones should cause an
+     * error.
+     *
+     * @param i
+     */
+    private void checkPipelines(int i) {
+        CastNode cn;
+        if (i < castNodes.length) {
+            cn = castNodes[i];
+        } else {
+            cn = null;
+        }
+        if (cn != null) {
+            Samples<?> samples = argSamples.get(i);
+            if (samples.positiveSamples().isEmpty() && samples.negativeSamples().isEmpty()) {
+                System.out.println("No samples");
+            } else {
+                testPipeline(cn, samples);
+                System.out.println("Pipeline check OK (" + samples.positiveSamples().size() + "," + samples.negativeSamples().size() + ")");
+            }
+        }
+    }
+
+    private static void testPipeline(CastNode cn, Samples<?> samples) {
+        NodeHandle<CastNode> argCastNodeHandle = TestUtilities.createHandle(cn, (node, args) -> {
+            return node.execute(args[0]);
+        });
+
+        for (Object sample : samples.positiveSamples()) {
+            try {
+                argCastNodeHandle.call(sample);
+            } catch (UnsupportedSpecializationException e) {
+                System.out.println("Warning: No specialization to handle arg " + sample + " : " + e.getMessage());
+            } catch (Exception e) {
+                e.printStackTrace();
+                fail("Unexpectedly negative sample: " + sample);
+            }
+        }
+        for (Object sample : samples.negativeSamples()) {
+            try {
+                argCastNodeHandle.call(sample);
+                fail("Unexpectedly positive sample: " + sample);
+            } catch (IllegalArgumentException e) {
+                Assert.assertTrue(true);
+                // ok
+            } catch (RError e) {
+                Assert.assertTrue(true);
+                // ok
+            }
+        }
+    }
+
+    private Set<RList> extractValidArgsForBuiltin() {
+        final PolyglotEngine vm = diagSuite.fastRSession.createTestContext(null);
+
+        try {
+            String snippetAnchor;
+            switch (annotation.kind()) {
+                case INTERNAL:
+                    snippetAnchor = ".Internal(" + builtinName + "(";
+                    break;
+                default:
+                    snippetAnchor = builtinName + "(";
+                    break;
+            }
+
+            String builtinNameSimple = builtinName.replace(".", "");
+            Map<String, SortedMap<String, TestInfo>> snippets = diagSuite.outputManager.getTestMaps().entrySet().stream().filter(
+                            e -> e.getKey().startsWith(TEST_PREFIX + builtinName) || e.getKey().startsWith(TEST_PREFIX + builtinNameSimple)).collect(
+                                            Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
+            Set<String> flatSnippets = snippets.entrySet().stream().flatMap(
+                            e -> e.getValue().keySet().stream()).collect(Collectors.toSet());
+            Set<String> filteredSnippets = flatSnippets.stream().filter(a -> a.contains(snippetAnchor)).collect(Collectors.toSet());
+            Set<String> validArgs = filteredSnippets.stream().map(a -> cutOffInvocation(a, snippetAnchor)).filter(
+                            a -> a != null && !"".equals(a)).collect(Collectors.toSet());
+            Set<RList> args = validArgs.stream().map(a -> evalValidArgs(a, vm)).filter(a -> a != null).collect(Collectors.toSet());
+
+            if (args.isEmpty()) {
+                Object[] nullArgs = new Object[this.argLength];
+                Arrays.fill(nullArgs, RNull.instance);
+                args = Collections.singleton(RDataFactory.createList(nullArgs));
+                System.out.println("No suitable test snippets found. Using the default RNull argument list");
+            }
+
+            return args;
+        } finally {
+            vm.dispose();
+        }
+    }
+
+    private static RList evalValidArgs(String argsExpr, PolyglotEngine vm) {
+        try {
+            Value eval = vm.eval(RSource.fromTextInternal(argsExpr, RSource.Internal.UNIT_TEST));
+            RList args = (RList) eval.get();
+            return args;
+        } catch (IOException e) {
+            // throw new RuntimeException(e);
+            // todo: warning
+            return null;
+        }
+    }
+
+    private void sweepChimney() throws IOException {
+        System.out.println("++++++++++++++++++++++");
+        System.out.println("+  Chimney-sweeping  +");
+        System.out.println("++++++++++++++++++++++");
+        System.out.println();
+
+        boolean useDiagonalGen;
+
+        long totalCombinations = calculateNumOfSampleCombinations(argSamples);
+
+        switch (diagSuite.diagConfig.sweepingMode) {
+            case lite:
+                useDiagonalGen = true;
+                break;
+
+            case total:
+                useDiagonalGen = false;
+                break;
+
+            case auto:
+            default:
+                useDiagonalGen = totalCombinations > diagSuite.diagConfig.maxTotalCombinations;
+                break;
+        }
+
+        List<List<Object>> generatedCombinations = generateSampleArgCombinations(argSamples, useDiagonalGen);
+
+        System.out.println("Springboard argument lists: " + validArgsList.size());
+        System.out.println("Used sample combinations: " + generatedCombinations.size() + " (from total " + totalCombinations + ")");
+        System.out.println("Sweeps to perform: " + generatedCombinations.size() * validArgsList.size());
+
+        System.out.println();
+        System.out.println();
+        System.out.println("Press Enter to continue ...");
+        System.in.read();
+
+        evalArgsWithSampleCombinations(generatedCombinations);
+    }
+
+    private void evalBuiltin(RList validArgs, List<List<Object>> argSampleCombinations) {
+        List<List<Object>> mergedSampleAndValidArgs = mergeValidAndSampleArgs(validArgs, argSampleCombinations);
+
+        for (List<Object> evalArgs : mergedSampleAndValidArgs) {
+            evalBuiltin(evalArgs);
+        }
+
+    }
+
+    private void evalArgsWithSampleCombinations(List<List<Object>> argSampleCombinations) {
+        sweepCounter = 0;
+        validArgsList.forEach(validArgs -> evalBuiltin(validArgs, argSampleCombinations));
+    }
+
+    private void evalBuiltin(List<Object> args) {
+        StringBuilder sb = new StringBuilder();
+        try {
+            for (int i = 0; i < args.size(); i++) {
+                Object validArg = args.get(i);
+                String deparsedValidArg;
+                try {
+                    deparsedValidArg = RDeparse.deparse(validArg);
+                } catch (Throwable e) {
+                    throw new RuntimeException("ERROR: Cannot deparse " + validArg + ": " + e.getMessage(), e);
+                }
+
+                if (sb.length() != 0) {
+                    sb.append(",");
+                }
+                sb.append(deparsedValidArg);
+            }
+
+            String call;
+            switch (annotation.kind()) {
+                case INTERNAL:
+                    call = ".Internal(" + builtinName + "(" + sb + "))";
+                    break;
+                default:
+                    call = builtinName + "(" + sb + ")";
+                    break;
+            }
+
+            String output;
+            try {
+                output = diagSuite.fastRSession.eval(call, null, false);
+            } catch (Throwable t) {
+                output = "ERROR: " + t.getMessage();
+            }
+            String outputGnu = diagSuite.gnuRSession.eval(call, null, false);
+
+            List<String> outputPair = Arrays.asList(output, outputGnu);
+
+            if (outputGnu.equals(output)) {
+                System.out.print('.');
+            } else if (!printedOutputPairs.contains(outputPair)) {
+                System.out.println("\n#" + sweepCounter + "> " + call);
+                System.out.println("\n====== FastR output ======");
+                System.out.println(output);
+
+                System.out.println("====== GnuR output ======");
+                System.out.println(outputGnu);
+                System.out.println("==========================");
+
+                printedOutputPairs.add(outputPair);
+            } else {
+                System.out.print('!');
+            }
+
+        } catch (Throwable e) {
+            // throw new RuntimeException(e);
+            // e.printStackTrace();
+            if (!printedErrors.contains(e.getMessage())) {
+                String call = ".Internal(" + builtinName + "(" + sb + "))";
+                System.out.println("\n[" + sweepCounter + "]> " + call);
+                System.out.println("ERROR: " + e.getMessage());
+                printedErrors.add(e.getMessage());
+            }
+        } finally {
+            sweepCounter++;
+        }
+    }
+
+    static long calculateNumOfSampleCombinations(List<Samples<?>> argSamples) {
+        Long total = argSamples.stream().reduce(1L, (num, samples) -> num * (samples.allSamples().size() + 1), (n1, n2) -> n1 * n2);
+        return total;
+    }
+
+    static List<List<Object>> generateSampleArgCombinations(List<Samples<?>> argSamples, boolean diagonalMethod) {
+        if (argSamples.isEmpty()) {
+            return Collections.emptyList();
+        } else if (argSamples.size() == 1) {
+            Samples<?> as = argSamples.get(0);
+            List<Object> samples = new ArrayList<>(as.allSamples());
+            samples.add(0, null);
+            return samples.stream().map(a -> Collections.singletonList(a)).collect(Collectors.toList());
+        } else {
+            List<Samples<?>> subList = argSamples.subList(1, argSamples.size());
+            List<List<Object>> subArgs = generateSampleArgCombinations(subList, diagonalMethod);
+
+            Samples<?> as = argSamples.get(0);
+            List<Object> samples = new ArrayList<>(as.allSamples());
+            samples.add(0, null);
+            return samples.stream().flatMap(a -> {
+                if (a == null || !diagonalMethod) {
+                    return subArgs.stream().map(sa -> {
+                        List<Object> saExt = new ArrayList<>();
+                        saExt.add(a);
+                        saExt.addAll(sa);
+                        return saExt;
+                    });
+                } else {
+                    List<Object> saExt = new ArrayList<>();
+                    saExt.add(a);
+                    saExt.addAll(subArgs.get(0)); // the null vector
+                    return Collections.singletonList(saExt).stream();
+                }
+            }).collect(Collectors.toList());
+        }
+
+    }
+
+    static List<List<Object>> mergeValidAndSampleArgs(RList validArgs, List<List<Object>> sampleArgsList) {
+        return sampleArgsList.stream().map(sampleArgs -> {
+            List<Object> newSampleArgs = new ArrayList<>();
+            for (int i = 0; i < validArgs.getLength(); i++) {
+                if (i >= sampleArgs.size() || sampleArgs.get(i) == null) {
+                    newSampleArgs.add(validArgs.getDataAt(i));
+                } else {
+                    newSampleArgs.add(sampleArgs.get(i));
+                }
+            }
+            return newSampleArgs;
+        }).collect(Collectors.toList());
+    }
+
+    private static String cutOffInvocation(String a, String anchor) {
+        int i = a.indexOf(anchor);
+        if (i >= 0) {
+            return a.substring(0, i);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java
new file mode 100644
index 0000000000000000000000000000000000000000..d2e267b1c4a1bd206c6b8b4e1202a06fc9e7dcf2
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2013, 2016, 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.test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.api.vm.PolyglotEngine.Value;
+import com.oracle.truffle.r.nodes.casts.Samples;
+import com.oracle.truffle.r.runtime.RDeparse;
+import com.oracle.truffle.r.runtime.RSource;
+import com.oracle.truffle.r.runtime.data.RLanguage;
+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.model.RAbstractContainer;
+import com.oracle.truffle.r.test.generate.FastRSession;
+
+/**
+ * This helper class extracts default argument values from an R function.
+ */
+class DefaultArgsExtractor {
+
+    private final FastRSession fastRSession;
+
+    DefaultArgsExtractor(FastRSession fastRSession) {
+        this.fastRSession = fastRSession;
+    }
+
+    Map<String, Samples<?>> extractDefaultArgs(String functionName) {
+        final PolyglotEngine vm = fastRSession.createTestContext(null);
+
+        try {
+            HashMap<String, Samples<?>> samplesMap = new HashMap<>();
+
+            Source source = RSource.fromTextInternal("formals(" + functionName + ")",
+                            RSource.Internal.UNIT_TEST);
+            Value defArgVal = vm.eval(source);
+            if (defArgVal.get() instanceof RPairList) {
+                RPairList formals = (RPairList) defArgVal.get();
+                RStringVector names = formals.getNames(null);
+
+                for (int i = 0; i < names.getLength(); i++) {
+                    String name = names.getDataAt(i);
+                    Object defVal = formals.getDataAtAsObject(i);
+
+                    if (defVal instanceof RLanguage) {
+                        String deparsedDefVal = RDeparse.deparse(defVal);
+                        Value eval = vm.eval(RSource.fromTextInternal(deparsedDefVal,
+                                        RSource.Internal.UNIT_TEST));
+                        defVal = eval.get();
+
+                        if (defVal == null) {
+                            samplesMap.put(name, Samples.anything(RNull.instance));
+                        } else if (defVal instanceof RAbstractContainer) {
+                            // enumerated default arg values
+                            RAbstractContainer enumArgs = (RAbstractContainer) defVal;
+                            HashSet<Object> enumPosSamples = new HashSet<>();
+                            for (int j = 0; j < enumArgs.getLength(); j++) {
+                                // enumPosSamples.add(enumArgs.getDataAtAsObject(j));
+                                // It is assumed that the corresponding builtin wrapper function
+                                // calls 'match.arg'
+                                enumPosSamples.add(j);
+                            }
+                            samplesMap.put(name, new Samples<>(name, enumPosSamples, Collections.emptySet(), x -> true));
+                        } else {
+                            samplesMap.put(name, Samples.anything(defVal));
+                        }
+
+                    } else if (defVal instanceof RSymbol) {
+                        continue;
+                    } else {
+                        samplesMap.put(name, Samples.anything(defVal));
+                    }
+                }
+
+            }
+
+            return samplesMap;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            vm.dispose();
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java
index 0554e32e68f0f35feb83ca6089767a83c5b7ac28..1e6844fbbc770576d55ce2292a8649568836a02e 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java
@@ -48,44 +48,81 @@ import com.oracle.truffle.r.nodes.casts.Not;
 import com.oracle.truffle.r.nodes.casts.PredefFiltersSamplers;
 import com.oracle.truffle.r.nodes.casts.PredefMappersSamplers;
 import com.oracle.truffle.r.nodes.casts.TypeExpr;
+import com.oracle.truffle.r.nodes.test.ChimneySweeping.ChimneySweepingSuite;
 import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
-public final class RBuiltinDiagnostics {
+public class RBuiltinDiagnostics {
 
-    public static void main(String[] args) throws Exception {
-        boolean verbose = Arrays.stream(args).filter(arg -> "-v".equals(arg)).findFirst().isPresent();
-        boolean ignoreRNull = Arrays.stream(args).filter(arg -> "-n".equals(arg)).findFirst().isPresent();
-        boolean ignoreRMissing = Arrays.stream(args).filter(arg -> "-m".equals(arg)).findFirst().isPresent();
-        List<String> bNames = Arrays.stream(args).filter(arg -> !arg.startsWith("-")).collect(Collectors.toList());
+    static class DiagConfig {
+        boolean verbose;
+        boolean ignoreRNull;
+        boolean ignoreRMissing;
+        long maxTotalCombinations = 500L;
+    }
+
+    private final DiagConfig diagConfig;
+
+    RBuiltinDiagnostics(DiagConfig diagConfig) {
+        this.diagConfig = diagConfig;
+    }
+
+    static RBuiltinDiagnostics createRBuiltinDiagnostics(String[] args) {
+        return new RBuiltinDiagnostics(initDiagConfig(new DiagConfig(), args));
+    }
 
+    static <C extends DiagConfig> C initDiagConfig(C diagConfig, String[] args) {
+        diagConfig.verbose = Arrays.stream(args).filter(arg -> "-v".equals(arg)).findFirst().isPresent();
+        diagConfig.ignoreRNull = Arrays.stream(args).filter(arg -> "-n".equals(arg)).findFirst().isPresent();
+        diagConfig.ignoreRMissing = Arrays.stream(args).filter(arg -> "-m".equals(arg)).findFirst().isPresent();
+        return diagConfig;
+    }
+
+    public static void main(String[] args) throws Throwable {
         Predef.setPredefFilters(new PredefFiltersSamplers());
         Predef.setPredefMappers(new PredefMappersSamplers());
 
+        RBuiltinDiagnostics rbDiag = ChimneySweepingSuite.createChimneySweepingSuite(args).orElseGet(() -> createRBuiltinDiagnostics(args));
+
+        List<String> bNames = Arrays.stream(args).filter(arg -> !arg.startsWith("-")).collect(Collectors.toList());
         if (bNames.isEmpty()) {
-            diagnoseAllBuiltins(verbose, ignoreRNull, ignoreRMissing);
+            rbDiag.diagnoseAllBuiltins();
         } else {
             for (String bName : bNames) {
-                diagnoseSingleBuiltin(bName, verbose, ignoreRNull, ignoreRMissing);
+                rbDiag.diagnoseSingleBuiltin(bName);
             }
         }
     }
 
-    private static void diagnoseSingleBuiltin(String builtinName, boolean verbose, boolean ignoreRNull, boolean ignoreRMissing) throws Exception {
+    public SingleBuiltinDiagnostics createBuiltinDiagnostics(RBuiltinFactory bf) {
+        return new SingleBuiltinDiagnostics(this, bf);
+    }
+
+    public void diagnoseSingleBuiltin(String builtinName) throws Exception {
         BasePackage bp = new BasePackage();
         RBuiltinFactory bf = bp.lookupByName(builtinName);
-        diagnoseBuiltin(bf, verbose, ignoreRNull, ignoreRMissing);
+        if (bf == null) {
+            System.out.println("No builtin '" + builtinName + "' found");
+            return;
+        }
+
+        createBuiltinDiagnostics(bf).diagnoseBuiltin();
+
+        System.out.println("Finished");
+        System.out.println("--------");
+
+        System.exit(0);
     }
 
-    private static void diagnoseAllBuiltins(boolean verbose, boolean ignoreRNull, boolean ignoreRMissing) {
+    public void diagnoseAllBuiltins() {
         BasePackage bp = new BasePackage();
         for (RBuiltinFactory bf : bp.getBuiltins().values()) {
             try {
-                diagnoseBuiltin(bf, verbose, ignoreRNull, ignoreRMissing);
+                createBuiltinDiagnostics(bf).diagnoseBuiltin();
             } catch (Exception e) {
                 System.out.println(bf.getName() + " failed: " + e.getMessage());
             }
@@ -93,34 +130,100 @@ public final class RBuiltinDiagnostics {
 
         System.out.println("Finished");
         System.out.println("--------");
+
+        System.exit(0);
     }
 
-    public static void diagnoseBuiltin(RBuiltinFactory builtinFactory, boolean verbose, boolean ignoreRNull, boolean ignoreRMissing) throws Exception {
-        System.out.println("****************************************************************************");
-        System.out.println("Builtin: " + builtinFactory.getName() + " (" + builtinFactory.getBuiltinNodeClass().getName() + ")");
-        System.out.println("****************************************************************************");
+    static class SingleBuiltinDiagnostics {
+        private final RBuiltinDiagnostics diagSuite;
+        final RBuiltinFactory builtinFactory;
+        final String builtinName;
+        final int argLength;
+        final String[] parameterNames;
+        final CastNode[] castNodes;
+        final Class<?> builtinClass;
+        final RBuiltin annotation;
+        final List<Method> specMethods;
+        final List<TypeExpr> argResultSets;
+        final HashMap<Method, List<Set<Cast>>> convResultTypePerSpec;
+        final Set<List<Type>> nonCoveredArgsSet;
+
+        SingleBuiltinDiagnostics(RBuiltinDiagnostics diagSuite, RBuiltinFactory builtinFactory) {
+            this.diagSuite = diagSuite;
+            this.builtinFactory = builtinFactory;
+            this.builtinName = builtinFactory.getName();
+
+            this.builtinClass = builtinFactory.getBuiltinNodeClass();
+            this.annotation = builtinClass.getAnnotation(RBuiltin.class);
+            this.argLength = annotation.parameterNames().length;
+            String[] pn = annotation.parameterNames();
+            this.parameterNames = Arrays.stream(pn).map(n -> n.isEmpty() ? null : n).toArray(String[]::new);
+
+            this.castNodes = getCastNodesFromBuiltin();
+
+            List<TypeExpr> argResultSetsPreliminary = createArgResultSets();
+            argResultSets = argResultSetsPreliminary.stream().map(te -> te.filter(t -> {
+                return !((diagSuite.diagConfig.ignoreRNull && t == RNull.class) || (diagSuite.diagConfig.ignoreRMissing && t == RMissing.class));
+            })).collect(Collectors.toList());
+
+            this.specMethods = CastUtils.getAnnotatedMethods(builtinClass, Specialization.class);
+
+            this.convResultTypePerSpec = createConvResultTypePerSpecialization();
+            this.nonCoveredArgsSet = combineArguments();
+        }
 
-        Class<?> builtinClass = builtinFactory.getBuiltinNodeClass();
-        RBuiltin annotation = builtinClass.getAnnotation(RBuiltin.class);
-        int argLength = annotation.parameterNames().length;
-        String[] parameterNames = annotation.parameterNames();
-        parameterNames = Arrays.stream(parameterNames).map(n -> n.isEmpty() ? null : n).toArray(String[]::new);
+        private HashMap<Method, List<Set<Cast>>> createConvResultTypePerSpecialization() {
+            HashMap<Method, List<Set<Cast>>> convResultTypes = new HashMap<>();
 
-        CastNode[] castNodes = getCastNodesFromBuiltin(builtinFactory, parameterNames);
+            for (Method sm : specMethods) {
+                Class<?>[] parTypes = sm.getParameterTypes();
 
-        List<TypeExpr> argResultSets = createArgResultSets(argLength, parameterNames, castNodes);
-        argResultSets = argResultSets.stream().map(te -> te.filter(t -> {
-            return !((ignoreRNull && t == RNull.class) || (ignoreRMissing && t == RMissing.class));
-        })).collect(Collectors.toList());
+                List<Set<Cast>> convResultTypesForSpec = new ArrayList<>();
+                for (int i = 0; i < argLength; i++) {
+                    TypeExpr argResultSet = argResultSets.get(i);
+                    Type argType = getParamType(parTypes, i);
+                    Set<Cast> convArgResultCasts = CastUtils.Casts.findConvertibleActualType(argResultSet, argType, true);
+                    convResultTypesForSpec.add(convArgResultCasts);
+                }
+                convResultTypes.put(sm, convResultTypesForSpec);
+            }
+            return convResultTypes;
+        }
 
-        List<Method> specMethods = CastUtils.getAnnotatedMethods(builtinClass, Specialization.class);
+        private Set<List<Type>> combineArguments() {
+            Set<List<Type>> specPowerSetCombined = new HashSet<>();
+            for (Map.Entry<Method, List<Set<Cast>>> entry : convResultTypePerSpec.entrySet()) {
+                List<TypeExpr> actualArgTypeSets = entry.getValue().stream().map(argCasts -> Casts.inputsAsTypeExpr(argCasts)).collect(Collectors.toList());
+                Set<List<Type>> specPowerSet = CastUtils.argumentProductSet(actualArgTypeSets);
+                specPowerSetCombined.addAll(specPowerSet);
+            }
 
-        HashMap<Method, List<Set<Cast>>> convResultTypePerSpec = createConvResultTypePerSpecialization(argLength, argResultSets, specMethods);
+            Set<List<Type>> nonCovered = CastUtils.argumentProductSet(argResultSets);
+            nonCovered.removeAll(specPowerSetCombined);
+            return nonCovered;
+        }
 
-        Set<List<Type>> nonCoveredArgsSet = combineArguments(argResultSets, convResultTypePerSpec);
+        public void diagnoseBuiltin() throws Exception {
+            System.out.println("****************************************************************************");
+            System.out.println("Builtin: " + builtinName + " (" + builtinFactory.getBuiltinNodeClass().getName() + ")");
+            System.out.println("****************************************************************************");
 
-        System.out.println("Argument cast pipelines binding:");
-        for (int i = 0; i < argLength; i++) {
+            System.out.println("Argument cast pipelines binding:");
+            for (int i = 0; i < argLength; i++) {
+                diagnosePipeline(i);
+            }
+
+            System.out.println("\nUnhandled argument combinations: " + nonCoveredArgsSet.size());
+            System.out.println("");
+
+            if (diagSuite.diagConfig.verbose) {
+                for (List<Type> uncoveredArgs : nonCoveredArgsSet) {
+                    System.out.println(uncoveredArgs.stream().map(t -> typeName(t)).collect(Collectors.toList()));
+                }
+            }
+        }
+
+        protected void diagnosePipeline(int i) {
             TypeExpr argResultSet = argResultSets.get(i);
             System.out.println("\n Pipeline for '" + annotation.parameterNames()[i] + "' (arg[" + i + "]):");
             System.out.println("  Result types union:");
@@ -140,81 +243,43 @@ public final class RBuiltinDiagnostics {
             }
             System.out.println("  Unbound types:");
             System.out.println("   " + unboundArgTypes.stream().map(argType -> typeName(argType)).collect(Collectors.toSet()));
+
         }
 
-        System.out.println("\nUnhandled argument combinations: " + nonCoveredArgsSet.size());
-        System.out.println("");
+        private CastNode[] getCastNodesFromBuiltin() {
+            ArgumentsSignature signature = ArgumentsSignature.get(parameterNames);
 
-        if (verbose) {
-            for (List<Type> uncoveredArgs : nonCoveredArgsSet) {
-                System.out.println(uncoveredArgs.stream().map(t -> typeName(t)).collect(Collectors.toList()));
+            int total = signature.getLength();
+            RNode[] args = new RNode[total];
+            for (int i = 0; i < total; i++) {
+                args[i] = ReadVariableNode.create("dummy");
             }
-        }
-    }
+            RBuiltinNode builtinNode = builtinFactory.getConstructor().apply(args.clone());
 
-    private static Set<List<Type>> combineArguments(List<TypeExpr> argResultSets, HashMap<Method, List<Set<Cast>>> convResultTypePerSpec) {
-        Set<List<Type>> specPowerSetCombined = new HashSet<>();
-        for (Map.Entry<Method, List<Set<Cast>>> entry : convResultTypePerSpec.entrySet()) {
-            List<TypeExpr> actualArgTypeSets = entry.getValue().stream().map(argCasts -> Casts.inputsAsTypeExpr(argCasts)).collect(Collectors.toList());
-            Set<List<Type>> specPowerSet = CastUtils.argumentProductSet(actualArgTypeSets);
-            specPowerSetCombined.addAll(specPowerSet);
+            CastNode[] cn = builtinNode.getCasts();
+            return cn;
         }
 
-        Set<List<Type>> nonCoveredArgsSet = CastUtils.argumentProductSet(argResultSets);
-        nonCoveredArgsSet.removeAll(specPowerSetCombined);
-        return nonCoveredArgsSet;
-    }
-
-    private static HashMap<Method, List<Set<Cast>>> createConvResultTypePerSpecialization(int argLength, List<TypeExpr> argResultSets, List<Method> specMethods) {
-        HashMap<Method, List<Set<Cast>>> convResultTypePerSpec = new HashMap<>();
-
-        for (Method sm : specMethods) {
-            Class<?>[] parTypes = sm.getParameterTypes();
-
-            List<Set<Cast>> convResultTypesForSpec = new ArrayList<>();
+        private List<TypeExpr> createArgResultSets() {
+            List<TypeExpr> as = new ArrayList<>();
             for (int i = 0; i < argLength; i++) {
-                TypeExpr argResultSet = argResultSets.get(i);
-                Type argType = getParamType(parTypes, i);
-                Set<Cast> convArgResultCasts = CastUtils.Casts.findConvertibleActualType(argResultSet, argType, true);
-                convResultTypesForSpec.add(convArgResultCasts);
+                CastNode cn;
+                if (i < castNodes.length) {
+                    cn = castNodes[i];
+                } else {
+                    cn = null;
+                }
+                TypeExpr te;
+                try {
+                    te = cn == null ? TypeExpr.ANYTHING : CastNodeSampler.createSampler(cn).resultTypes();
+                } catch (Exception e) {
+                    throw new RuntimeException("Cannot create sampler for argument " + parameterNames[i], e);
+                }
+                as.add(te);
             }
-            convResultTypePerSpec.put(sm, convResultTypesForSpec);
+            return as;
         }
-        return convResultTypePerSpec;
-    }
 
-    private static List<TypeExpr> createArgResultSets(int argLength, String[] parameterNames, CastNode[] castNodes) {
-        List<TypeExpr> argResultSets = new ArrayList<>();
-        for (int i = 0; i < argLength; i++) {
-            CastNode cn;
-            if (i < castNodes.length) {
-                cn = castNodes[i];
-            } else {
-                cn = null;
-            }
-            TypeExpr te;
-            try {
-                te = cn == null ? TypeExpr.ANYTHING : CastNodeSampler.createSampler(cn).resultTypes();
-            } catch (Exception e) {
-                throw new RuntimeException("Cannot create sampler for argument " + parameterNames[i], e);
-            }
-            argResultSets.add(te);
-        }
-        return argResultSets;
-    }
-
-    private static CastNode[] getCastNodesFromBuiltin(RBuiltinFactory builtinFactory, String[] parameterNames) {
-        ArgumentsSignature signature = ArgumentsSignature.get(parameterNames);
-
-        int total = signature.getLength();
-        RNode[] args = new RNode[total];
-        for (int i = 0; i < total; i++) {
-            args[i] = ReadVariableNode.create("dummy");
-        }
-        RBuiltinNode builtinNode = builtinFactory.getConstructor().apply(args.clone());
-
-        CastNode[] castNodes = builtinNode.getCasts();
-        return castNodes;
     }
 
     private static int getRealParamIndex(Class<?>[] parTypes, int i) {
@@ -255,5 +320,4 @@ public final class RBuiltinDiagnostics {
         }
         return typeName(m.getReturnType()) + " " + m.getName() + "(" + sb + ")";
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNodeGenSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNodeGenSampler.java
index 9e47a83505476165160fdc41cc9f0cb86664b487..8ccde29473c2c72881f9b4546e677fba8a51dfe7 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNodeGenSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNodeGenSampler.java
@@ -33,14 +33,12 @@ public class ConditionalMapNodeGenSampler extends CastNodeSampler<ConditionalMap
     private final ArgumentFilterSampler argFilter;
     private final CastNodeSampler trueBranch;
     private final CastNodeSampler falseBranch;
-    private final TypeExpr conditionType;
 
     public ConditionalMapNodeGenSampler(ConditionalMapNodeGen castNode) {
         super(castNode);
         argFilter = (ArgumentFilterSampler) castNode.getFilter();
         trueBranch = createSampler(castNode.getTrueBranch());
         falseBranch = createSampler(castNode.getFalseBranch());
-        conditionType = argFilter.allowedTypes();
     }
 
     @Override
@@ -49,14 +47,14 @@ public class ConditionalMapNodeGenSampler extends CastNodeSampler<ConditionalMap
     }
 
     private TypeExpr trueBranchResultTypes(TypeExpr inputType) {
-        return trueBranch.resultTypes(conditionType.and(inputType));
+        return trueBranch.resultTypes(argFilter.trueBranchType().and(inputType));
     }
 
     private TypeExpr falseBranchResultTypes(TypeExpr inputType) {
         if (falseBranch != null) {
-            return falseBranch.resultTypes(inputType.and(conditionType.not()));
+            return falseBranch.resultTypes(argFilter.falseBranchType().and(inputType));
         } else {
-            return inputType.and(conditionType.not());
+            return argFilter.falseBranchType().and(inputType);
         }
     }
 
@@ -66,12 +64,18 @@ public class ConditionalMapNodeGenSampler extends CastNodeSampler<ConditionalMap
         TypeExpr falseBranchResultType = falseBranchResultTypes(inputType);
 
         // filter out the incompatible samples
-        Samples definedTrueBranchSamples = downStreamSamples.filter(x -> trueBranchResultType.isInstance(x));
-        Samples definedFalseBranchSamples = downStreamSamples.filter(x -> falseBranchResultType.isInstance(x));
+        Samples compatibleTrueBranchDownStreamSamples = downStreamSamples.filter(x -> trueBranchResultType.isInstance(x));
+        Samples compatibleFalseBranchDownStreamSamples = downStreamSamples.filter(x -> falseBranchResultType.isInstance(x));
 
-        Samples unmappedTrueBranchDefinedSamples = argFilter.collectSamples(trueBranch.collectSamples(conditionType.and(inputType), definedTrueBranchSamples));
-        Samples unmappedFalseBranchDefinedSamples = falseBranch == null ? definedFalseBranchSamples : falseBranch.collectSamples(inputType.and(conditionType.not()), definedFalseBranchSamples);
+        Samples trueBranchSamples = trueBranch.collectSamples(argFilter.trueBranchType().and(inputType), compatibleTrueBranchDownStreamSamples);
+        Samples falseBranchSamples = falseBranch == null ? compatibleFalseBranchDownStreamSamples
+                        : falseBranch.collectSamples(argFilter.falseBranchType().and(inputType), compatibleFalseBranchDownStreamSamples);
+        Samples bothBranchesSamples = trueBranchSamples.or(falseBranchSamples);
 
-        return unmappedTrueBranchDefinedSamples.or(unmappedFalseBranchDefinedSamples);
+        // Collect the "interesting" samples from the condition. Both positive and negative samples
+        // are actually interpreted as positive ones.
+        Samples origConditionSamples = argFilter.collectSamples(inputType).makePositive();
+        // Merge the samples from the branches with the condition samples
+        return origConditionSamples.or(bothBranchesSamples);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FilterNodeGenSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FilterNodeGenSampler.java
index 1d4da8ff9b9b21caebb1faf688f12b41d6763b09..bfdbdd71f798d6f2b8c4530c5911014aee10cfc5 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FilterNodeGenSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FilterNodeGenSampler.java
@@ -24,7 +24,6 @@ package com.oracle.truffle.r.nodes.unary;
 
 import com.oracle.truffle.r.nodes.casts.ArgumentFilterSampler;
 import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
-import com.oracle.truffle.r.nodes.casts.CastUtils;
 import com.oracle.truffle.r.nodes.casts.Samples;
 import com.oracle.truffle.r.nodes.casts.TypeExpr;
 
@@ -40,7 +39,7 @@ public class FilterNodeGenSampler extends CastNodeSampler<FilterNodeGen> {
         assert castNode.getFilter() instanceof ArgumentFilterSampler : "Check PredefFiltersSamplers is installed in Predef";
         this.filter = (ArgumentFilterSampler) castNode.getFilter();
         this.isWarning = castNode.isWarning();
-        this.resType = filter.allowedTypes();
+        this.resType = filter.trueBranchType();
     }
 
     @Override
@@ -55,8 +54,10 @@ public class FilterNodeGenSampler extends CastNodeSampler<FilterNodeGen> {
     @SuppressWarnings("unchecked")
     @Override
     public Samples<?> collectSamples(TypeExpr inputType, Samples<?> downStreamSamples) {
-        Samples samplesForError = filter.collectSamples(downStreamSamples);
-        return isWarning ? new Samples<>(samplesForError.positiveSamples(), CastUtils.samples()) : samplesForError;
+        Samples samples = filter.collectSamples(inputType);
+        samples = isWarning ? samples.positiveOnly() : samples;
+        Samples<?> combined = samples.and(downStreamSamples);
+        return combined;
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FindFirstNodeGenSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FindFirstNodeGenSampler.java
index 6ecdc9b3afefe017d690e07d18638223fa195c68..0b92ad8b7dcc6f93b0e971727b1b71ea6bad8ebb 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FindFirstNodeGenSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/FindFirstNodeGenSampler.java
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.unary;
 import java.lang.reflect.Type;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
@@ -51,8 +52,10 @@ public class FindFirstNodeGenSampler extends CastNodeSampler<FindFirstNodeGen> {
         Samples<Object> defaultSamples = defaultSamples();
 
         // convert scalar samples to vector ones
-        Samples<Object> vectorizedSamples = downStreamSamples.map(x -> CastUtils.singletonVector(x), x -> CastUtils.singletonVector(x));
-        return defaultSamples.and(vectorizedSamples);
+        Samples<Object> vectorizedSamples = downStreamSamples.map(x -> CastUtils.singletonVector(x), x -> CastUtils.singletonVector(x),
+                        x -> CastUtils.firstElement(x, defaultValue), x -> CastUtils.firstElement(x, defaultValue));
+        Samples<Object> combined = defaultSamples.and(vectorizedSamples);
+        return combined;
     }
 
     private Samples<Object> defaultSamples() {
@@ -65,12 +68,49 @@ public class FindFirstNodeGenSampler extends CastNodeSampler<FindFirstNodeGen> {
             if (emptyVec != null) {
                 defaultNegativeSamples.add(emptyVec);
             }
-            defaultNegativeSamples.add(RNull.instance);
+
+            Predicate<Object> posMembership = this::testVectorForNoDefaultValCase;
+
+            return new Samples<>("findFirst-noDef", defaultPositiveSamples, defaultNegativeSamples, posMembership);
         } else {
             defaultPositiveSamples.add(CastUtils.singletonVector(defaultValue));
+            defaultPositiveSamples.add(RNull.instance);
+            Object emptyVec = CastUtils.emptyVector(elementClass);
+            if (emptyVec != null) {
+                defaultPositiveSamples.add(emptyVec);
+            }
+
+            Predicate<Object> posMembership = this::testVectorForDefaultValCase;
+
+            return new Samples<>("findFirst-withDef", defaultPositiveSamples, defaultNegativeSamples, posMembership);
+        }
+
+    }
+
+    private boolean testVectorForNoDefaultValCase(Object x) {
+        if (x == RMissing.instance || x == RNull.instance || x == null) {
+            return false;
+        }
+
+        if (x instanceof RAbstractVector) {
+            Class<?> elemCls = CastUtils.vectorElementType(x).orElse(null);
+            return elemCls != null && elementClass.isAssignableFrom(elemCls) && ((RAbstractVector) x).getLength() > 0;
+        } else {
+            return elementClass.isInstance(x);
+        }
+    }
+
+    private boolean testVectorForDefaultValCase(Object x) {
+        if (x == RMissing.instance || x == RNull.instance || x == null) {
+            return true;
         }
 
-        return new Samples<>(defaultPositiveSamples, defaultNegativeSamples);
+        if (x instanceof RAbstractVector) {
+            Class<?> elemCls = CastUtils.vectorElementType(x).orElse(null);
+            return elemCls != null && elementClass.isAssignableFrom(elemCls);
+        } else {
+            return elementClass.isInstance(x);
+        }
     }
 
     @Override
@@ -83,7 +123,7 @@ public class FindFirstNodeGenSampler extends CastNodeSampler<FindFirstNodeGen> {
                 return TypeExpr.union(resTypes);
             }
         } else {
-            return TypeExpr.atom(elementClass).or(TypeExpr.atom(RNull.class)).or(TypeExpr.atom(RMissing.class));
+            return TypeExpr.atom(elementClass);
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/MapNodeGenSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/MapNodeGenSampler.java
index a4c9e5d9b7c11c2a3dd6de676e1ebd07d792af41..0c0844548b4bb2359722ee9172e4b8b0ba67be95 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/MapNodeGenSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/MapNodeGenSampler.java
@@ -26,8 +26,6 @@ import com.oracle.truffle.r.nodes.casts.ArgumentMapperSampler;
 import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
 import com.oracle.truffle.r.nodes.casts.Samples;
 import com.oracle.truffle.r.nodes.casts.TypeExpr;
-import com.oracle.truffle.r.nodes.casts.CastUtils.Cast.Coverage;
-import com.oracle.truffle.r.runtime.data.RNull;
 
 @SuppressWarnings({"rawtypes", "unchecked"})
 public class MapNodeGenSampler extends CastNodeSampler<MapNodeGen> {
@@ -41,12 +39,8 @@ public class MapNodeGenSampler extends CastNodeSampler<MapNodeGen> {
     }
 
     @Override
-    public TypeExpr resultTypes(TypeExpr inputType) {
-        if (inputType.coverageFrom(RNull.class, true) == Coverage.none) {
-            return mapFn.resultTypes().and(TypeExpr.atom(RNull.class).not());
-        } else {
-            return mapFn.resultTypes().or(TypeExpr.atom(RNull.class));
-        }
+    public TypeExpr resultTypes(TypeExpr inputTypes) {
+        return mapFn.resultTypes(inputTypes);
     }
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/NonNANodeGenSampler.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/NonNANodeGenSampler.java
index c24f7ca2e933a07e6fd2ae26ab57abce0b7a1a2e..c88b88024c8ff916d82da7081ba386c7c21afd8d 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/NonNANodeGenSampler.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/NonNANodeGenSampler.java
@@ -23,7 +23,10 @@
 package com.oracle.truffle.r.nodes.unary;
 
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.Optional;
 import java.util.Set;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
@@ -45,21 +48,39 @@ public class NonNANodeGenSampler extends CastNodeSampler<NonNANodeGen> {
         return inputType;
     }
 
+    @SuppressWarnings("unchecked")
+    private <T> Optional<T> unmap(T x) {
+        if (naReplacement == null) {
+            return CastUtils.isNaValue(x) ? Optional.empty() : Optional.of(x);
+        } else {
+            return CastUtils.isNaValue(x) ? Optional.of((T) naReplacement) : Optional.of(x);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
     @Override
     public Samples<?> collectSamples(TypeExpr inputTypes, Samples<?> downStreamSamples) {
         Set<Object> defaultPositiveSamples;
         Set<Object> defaultNegativeSamples;
 
+        Samples<Object> mappedSamples = ((Samples<Object>) downStreamSamples).map(x -> x, x -> x, this::unmap, this::unmap);
+
+        Set<Object> naSamples = inputTypes.normalize().stream().filter(t -> t instanceof Class).map(t -> CastUtils.naValue((Class<?>) t)).filter(x -> x != null).collect(Collectors.toSet());
         if (naReplacement != null) {
             defaultNegativeSamples = Collections.emptySet();
-            defaultPositiveSamples = Collections.singleton(naReplacement);
+            defaultPositiveSamples = new HashSet<>();
+            defaultPositiveSamples.add(naReplacement);
+            defaultPositiveSamples.addAll(naSamples);
         } else {
-            defaultNegativeSamples = inputTypes.normalize().stream().filter(t -> t instanceof Class).map(t -> CastUtils.naValue((Class<?>) t)).filter(x -> x != null).collect(Collectors.toSet());
+            defaultNegativeSamples = naSamples;
             defaultPositiveSamples = Collections.emptySet();
         }
 
-        Samples<Object> defaultSamples = new Samples<>(defaultPositiveSamples, defaultNegativeSamples);
-        return defaultSamples.and(downStreamSamples);
+        Predicate<Object> posMembership = x -> naReplacement != null || !CastUtils.isNaValue(x);
+        Samples<Object> defaultSamples = new Samples<>("nonNA-" + naReplacement == null ? "noDef" : "withDef", defaultPositiveSamples, defaultNegativeSamples, posMembership);
+
+        Samples<Object> combined = defaultSamples.and(mappedSamples);
+        return combined;
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
index e9a7bc72c7ff5b5e4e0a1a7ddaa39449827124ca..9fd030097191ab7bee93e4cd421bd6b6e9783efe 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java
@@ -55,7 +55,7 @@ import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.FastPathFactory;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.nodes.EvaluatedArgumentsVisitor;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
index b7bcb707765ff82927581f50da29633fd1f7cd8d..c03f9663f35a5d1dce446f0192b8e173ed3ef375 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTUtils.java
@@ -47,7 +47,6 @@ import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RInstrumentableNode;
 import com.oracle.truffle.r.runtime.nodes.RNode;
-import com.oracle.truffle.r.runtime.nodes.RSyntaxElement;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java
index f9d71ca263811fe431b3b5a4059e85b5c41abf9e..d59fc7e3ff22afcb38af5fb37c567cdae6c0e8ef 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RRootNode.java
@@ -36,8 +36,8 @@ import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.HasSignature;
 import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.FastPathFactory;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
 /**
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java
index 81fec609976bd179e5c43f6c9a39c3ed30c1ad7d..b808a98d1ff98a8fef89ca805a49527920db281d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessSlotNode.java
@@ -79,7 +79,7 @@ public abstract class AccessSlotNode extends RNode {
                 // TODO: any way to cache it or use a mechanism similar to overrides?
                 REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods");
                 RFunction dataPart = getDataPartFunction(methodsNamespace);
-                return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(Utils.getActualCurrentFrame(), RASTUtils.getOriginalCall(this)), object);
+                return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(Utils.getActualCurrentFrame(), RASTUtils.getOriginalCall(this)), null, object);
             } else if (name == RRuntime.NAMES_ATTR_KEY && object instanceof RAbstractVector) {
                 assert false; // RS4Object can never be a vector?
                 return RNull.instance;
@@ -127,8 +127,9 @@ public abstract class AccessSlotNode extends RNode {
 
     @TruffleBoundary
     private static RFunction getDataPartFunction(REnvironment methodsNamespace) {
-        Object f = methodsNamespace.findFunction("getDataPart");
-        return (RFunction) RContext.getRRuntimeASTAccess().forcePromise(f);
+        String name = "getDataPart";
+        Object f = methodsNamespace.findFunction(name);
+        return (RFunction) RContext.getRRuntimeASTAccess().forcePromise(name, f);
     }
 
     @SuppressWarnings("unused")
@@ -137,7 +138,7 @@ public abstract class AccessSlotNode extends RNode {
         // TODO: any way to cache it or use a mechanism similar to overrides?
         REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods");
         RFunction dataPart = getDataPartFunction(methodsNamespace);
-        return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(Utils.getActualCurrentFrame(), RASTUtils.getOriginalCall(this)), object);
+        return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(Utils.getActualCurrentFrame(), RASTUtils.getOriginalCall(this)), null, object);
     }
 
     // this is really a fallback specialization but @Fallback does not work here (because of the
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
index 46ab6dc79f9f12af5c770c05d4dd1f2f2a07cfe9..1441a1741586f7b8c1495521281adfb2dabccba5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/ReadVariadicComponentNode.java
@@ -102,7 +102,7 @@ public class ReadVariadicComponentNode extends RSourceSectionNode implements RSy
 
     @Override
     public String getIdentifier() {
-        return getPrintForm();
+        return getPrintForm().intern();
     }
 
     @Override
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java
index 5a751ab1ec34382631eabd8fad06bf13c27c47e3..6b0f907f45d7d1a37561e45c411096b9a27ea8e2 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java
@@ -66,9 +66,11 @@ public abstract class UpdateSlotNode extends RNode {
     protected Object updateSlotS4Data(RAttributable object, @SuppressWarnings("unused") String name, Object value) {
         // TODO: any way to cache it or use a mechanism similar to overrides?
         REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods");
-        Object f = methodsNamespace.findFunction("setDataPart");
-        RFunction dataPart = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(f);
-        return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(Utils.getActualCurrentFrame(), RASTUtils.getOriginalCall(this)), object, prepareValue(value),
+        String identifier = "setDataPart";
+        Object f = methodsNamespace.findFunction(identifier);
+        RFunction dataPart = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(identifier, f);
+        return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(Utils.getActualCurrentFrame(), RASTUtils.getOriginalCall(this)), null, object,
+                        prepareValue(value),
                         RRuntime.LOGICAL_TRUE);
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
index 300b9f7fa7a0ddce3d9d0d9e4b0edfe2ed7613ab..af81aeda0554f914891be7367e96c96aed66ef5f 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/variables/ReadVariableNode.java
@@ -540,7 +540,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         if (lookup != null) {
             try {
                 if (lookup.getValue() instanceof RPromise) {
-                    evalPromiseSlowPathWithName(frame, (RPromise) lookup.getValue());
+                    evalPromiseSlowPathWithName(identifierAsString, frame, (RPromise) lookup.getValue());
                 }
                 if (lookup != null) {
                     if (lookup.getValue() == null || checkTypeSlowPath(frame, lookup.getValue())) {
@@ -639,8 +639,12 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         return lastLevel;
     }
 
+    public static RFunction lookupFunction(String identifier, Frame variableFrame) {
+        return lookupFunction(identifier, variableFrame, false, true);
+    }
+
     @TruffleBoundary
-    public static RFunction lookupFunction(String identifier, Frame variableFrame, boolean localOnly) {
+    public static RFunction lookupFunction(String identifier, Frame variableFrame, boolean localOnly, boolean forcePromises) {
         Frame current = variableFrame;
         do {
             // see if the current frame has a value of the given name
@@ -657,8 +661,12 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
                         if (promise.isEvaluated()) {
                             value = promise.getValue();
                         } else {
-                            value = PromiseHelperNode.evaluateSlowPath(null, promise);
-                            return (RFunction) value;
+                            if (forcePromises) {
+                                value = evalPromiseSlowPathWithName(identifier, null, promise);
+                                return (RFunction) value;
+                            } else {
+                                return null;
+                            }
                         }
                     }
                     if (RRuntime.checkType(value, RType.Function)) {
@@ -840,7 +848,7 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
                     // we recover from a wrong type later
                     return true;
                 } else {
-                    obj = evalPromiseSlowPathWithName(frame, promise);
+                    obj = evalPromiseSlowPathWithName(identifierAsString, frame, promise);
                 }
             } else {
                 obj = promise.getValue();
@@ -849,9 +857,9 @@ public final class ReadVariableNode extends RSourceSectionNode implements RSynta
         return RRuntime.checkType(obj, mode);
     }
 
-    private Object evalPromiseSlowPathWithName(VirtualFrame frame, RPromise promise) {
+    public static Object evalPromiseSlowPathWithName(String identifier, VirtualFrame frame, RPromise promise) {
         Object obj;
-        slowPathEvaluationName.set(identifierAsString);
+        slowPathEvaluationName.set(identifier);
         try {
             obj = PromiseHelperNode.evaluateSlowPath(frame, promise);
         } finally {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java
index 8cac125c8bebde1fa4d8c0994e47ff8e290aa966..5ec428e5feefa3341125319e430de02631d25336 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java
@@ -26,6 +26,7 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 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.PositionCheckNodeFactory.Mat2indsubNodeGen;
 import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
@@ -49,6 +50,7 @@ abstract class PositionCheckNode extends Node {
     private final int dimensionIndex;
     protected final int numDimensions;
     private final VectorLengthProfile positionLengthProfile = VectorLengthProfile.create();
+    private final ConditionProfile nullDimensionsProfile = ConditionProfile.createBinaryProfile();
     protected final BranchProfile error = BranchProfile.create();
     protected final boolean replace;
     protected final RType containerType;
@@ -123,7 +125,7 @@ abstract class PositionCheckNode extends Node {
 
         if (mode.isSubset() && numDimensions == 1) {
             int[] vectorDim = vector.getDimensions();
-            if (vectorDim != null && vectorDim.length == 2) {
+            if (nullDimensionsProfile.profile(vectorDim != null) && vectorDim.length == 2) {
                 if (vector instanceof RAbstractVector && ((RAbstractVector) vector).isArray()) {
                     if (castPosition instanceof RAbstractVector) {
                         RAbstractVector vectorPosition = (RAbstractVector) castPosition;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java
index 6d813c4a45aa5e8d837aa4b520e374ca167f0980..245067ec8469059f34357adc436ef56abbfef8a0 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/CastTypeNode.java
@@ -70,7 +70,7 @@ public abstract class CastTypeNode extends BinaryNode {
     public static CastNode createCast(RType type, boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
         switch (type) {
             case Character:
-                return CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes, false);
+                return CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             case Complex:
                 return CastComplexNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
             case Double:
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/ColonNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/ColonNode.java
index 95a602ff912ccfd77d54021baded5f948547d7b5..ea4d74871de8b70c70c87123b73c6542a66f4122 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/ColonNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/ColonNode.java
@@ -22,6 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.binary;
 
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
@@ -29,19 +34,19 @@ import com.oracle.truffle.r.nodes.binary.ColonNodeGen.ColonCastNodeGen;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 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.RDoubleSequence;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntSequence;
-import com.oracle.truffle.r.runtime.data.RIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
-@RBuiltin(name = ":", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""})
+@RBuiltin(name = ":", kind = PRIMITIVE, parameterNames = {"", ""}, behavior = PURE)
 public abstract class ColonNode extends RBuiltinNode {
 
     private final BranchProfile naCheckErrorProfile = BranchProfile.create();
@@ -50,6 +55,8 @@ public abstract class ColonNode extends RBuiltinNode {
 
     @Override
     protected void createCasts(CastBuilder casts) {
+        // These casts should not be custom, but they are very hard to get right in a generic way.
+        // Also, the proper warnings cannot be produced at the moment.
         casts.custom(0, ColonCastNodeGen.create()).custom(1, ColonCastNodeGen.create());
     }
 
@@ -123,54 +130,50 @@ public abstract class ColonNode extends RBuiltinNode {
     abstract static class ColonCastNode extends CastNode {
 
         private final ConditionProfile lengthGreaterOne = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile lengthEqualsZero = ConditionProfile.createBinaryProfile();
 
         @Specialization(guards = "isIntValue(operand)")
         protected int doDoubleToInt(double operand) {
             return (int) operand;
         }
 
+        @Specialization
+        protected int doInt(int operand) {
+            return operand;
+        }
+
         @Specialization(guards = "!isIntValue(operand)")
         protected double doDouble(double operand) {
             return operand;
         }
 
-        @Specialization
-        protected int doSequence(RIntSequence sequence) {
-            if (lengthGreaterOne.profile(sequence.getLength() > 1)) {
-                RError.warning(this, RError.Message.ONLY_FIRST_USED, sequence.getLength());
+        private void checkLength(int length) {
+            if (lengthGreaterOne.profile(length > 1)) {
+                RError.warning(this, RError.Message.ONLY_FIRST_USED, length);
+            }
+            if (lengthEqualsZero.profile(length == 0)) {
+                throw RError.error(this, Message.ARGUMENT_LENGTH_0);
             }
-            return sequence.getStart();
         }
 
         @Specialization
-        protected int doSequence(RIntVector vector) {
-            if (lengthGreaterOne.profile(vector.getLength() > 1)) {
-                RError.warning(this, RError.Message.ONLY_FIRST_USED, vector.getLength());
-            }
+        protected int doSequence(RAbstractIntVector vector) {
+            checkLength(vector.getLength());
             return vector.getDataAt(0);
         }
 
         @Specialization(guards = "isFirstIntValue(vector)")
-        protected int doDoubleVectorFirstIntValue(RDoubleVector vector) {
-            if (lengthGreaterOne.profile(vector.getLength() > 1)) {
-                RError.warning(this, RError.Message.ONLY_FIRST_USED, vector.getLength());
-            }
+        protected int doDoubleVectorFirstIntValue(RAbstractDoubleVector vector) {
+            checkLength(vector.getLength());
             return (int) vector.getDataAt(0);
         }
 
         @Specialization(guards = "!isFirstIntValue(vector)")
-        protected double doDoubleVector(RDoubleVector vector) {
-            if (lengthGreaterOne.profile(vector.getLength() > 1)) {
-                RError.warning(this, RError.Message.ONLY_FIRST_USED, vector.getLength());
-            }
+        protected double doDoubleVector(RAbstractDoubleVector vector) {
+            checkLength(vector.getLength());
             return vector.getDataAt(0);
         }
 
-        @Specialization
-        protected int doInt(int operand) {
-            return operand;
-        }
-
         @Specialization
         protected int doBoolean(byte operand) {
             return RRuntime.logical2int(operand);
@@ -178,9 +181,7 @@ public abstract class ColonNode extends RBuiltinNode {
 
         @Specialization
         protected int doString(RAbstractStringVector vector) {
-            if (lengthGreaterOne.profile(vector.getLength() > 1)) {
-                RError.warning(this, RError.Message.ONLY_FIRST_USED, vector.getLength());
-            }
+            checkLength(vector.getLength());
             String val = vector.getDataAt(0);
             if (RRuntime.isNA(val)) {
                 throw RError.error(this, RError.Message.NA_OR_NAN);
@@ -194,12 +195,18 @@ public abstract class ColonNode extends RBuiltinNode {
             return result;
         }
 
+        @Fallback
+        @TruffleBoundary
+        protected int doOther(@SuppressWarnings("unused") Object value) {
+            throw RError.error(this, Message.ARGUMENT_LENGTH_0);
+        }
+
         protected static boolean isIntValue(double d) {
             return (((int) d)) == d;
         }
 
-        protected static boolean isFirstIntValue(RDoubleVector d) {
-            return (((int) d.getDataAt(0))) == d.getDataAt(0);
+        protected static boolean isFirstIntValue(RAbstractDoubleVector d) {
+            return d.getLength() > 0 && (((int) d.getDataAt(0))) == d.getDataAt(0);
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/AbstractPredicateArgumentFilter.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/AbstractPredicateArgumentFilter.java
index dc57aa5579bb1ca285092101c7003c1d8b07ba50..43f0fe3d1355ae55e1647c1c8dc6537b8e7ea5ea 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/AbstractPredicateArgumentFilter.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/AbstractPredicateArgumentFilter.java
@@ -45,7 +45,7 @@ public abstract class AbstractPredicateArgumentFilter<T, R extends T> implements
         if (profile.profile(!isNullable && (arg == RNull.instance || arg == null))) {
             return false;
         } else {
-            return valuePredicate.test(arg);
+            return valuePredicate.test(arg == RNull.instance ? null : arg);
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/ArgumentFilter.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/ArgumentFilter.java
index 3e951f277f8dce108fcc4112b82dced1c4a3321d..a6357445beccdf46f742fd368b10352c8fb66e5c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/ArgumentFilter.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/ArgumentFilter.java
@@ -51,6 +51,24 @@ public interface ArgumentFilter<T, R> {
 
     interface ArgumentValueFilter<T> extends NarrowingArgumentFilter<T, T> {
 
+        @SuppressWarnings("overloads")
+        default <S extends T> ArgumentValueFilter<T> or(ArgumentValueFilter<T> other) {
+            return new ArgumentValueFilter<T>() {
+
+                private final ConditionProfile profile = ConditionProfile.createBinaryProfile();
+
+                @Override
+                public boolean test(T arg) {
+                    if (profile.profile(ArgumentValueFilter.this.test(arg))) {
+                        return true;
+                    } else {
+                        return other.test(arg);
+                    }
+                }
+
+            };
+        }
+
         @SuppressWarnings("overloads")
         default ArgumentValueFilter<T> and(ArgumentValueFilter<T> other) {
             return new ArgumentValueFilter<T>() {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
index ab4d7f7aa168b40243a84301ffea87c404b6a355..564660b681f2661cfb9902918ce9f6fd945b3422 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
@@ -22,77 +22,62 @@
  */
 package com.oracle.truffle.r.nodes.builtin;
 
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.io.Writer;
 import java.util.Arrays;
 import java.util.Objects;
+import java.util.function.Function;
 
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNodeGen;
 import com.oracle.truffle.r.nodes.builtin.ArgumentFilter.ArgumentTypeFilter;
-import com.oracle.truffle.r.nodes.unary.CastDoubleBaseNode;
+import com.oracle.truffle.r.nodes.builtin.ArgumentFilter.ArgumentValueFilter;
+import com.oracle.truffle.r.nodes.unary.CastComplexNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastDoubleBaseNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastIntegerBaseNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerBaseNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastLogicalBaseNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalBaseNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastLogicalNode;
 import com.oracle.truffle.r.nodes.unary.CastLogicalNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastLogicalScalarNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
-import com.oracle.truffle.r.nodes.unary.CastStringBaseNode;
+import com.oracle.truffle.r.nodes.unary.CastRawNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastStringBaseNodeGen;
-import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastToAttributableNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
 import com.oracle.truffle.r.nodes.unary.ChainedCastNode;
 import com.oracle.truffle.r.nodes.unary.ConditionalMapNodeGen;
-import com.oracle.truffle.r.nodes.unary.ConvertIntNodeGen;
 import com.oracle.truffle.r.nodes.unary.FilterNodeGen;
 import com.oracle.truffle.r.nodes.unary.FindFirstNodeGen;
 import com.oracle.truffle.r.nodes.unary.FirstBooleanNodeGen;
 import com.oracle.truffle.r.nodes.unary.FirstIntNode;
 import com.oracle.truffle.r.nodes.unary.FirstStringNode;
-import com.oracle.truffle.r.nodes.unary.MapNode;
 import com.oracle.truffle.r.nodes.unary.MapNodeGen;
 import com.oracle.truffle.r.nodes.unary.NonNANodeGen;
 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.RAttributable;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 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.RAbstractRawVector;
 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;
 
 public final class CastBuilder {
 
     private static final CastNode[] EMPTY_CASTS_ARRAY = new CastNode[0];
 
-    /**
-     * This object is used as a placeholder for the cast argument in error/warning messages.
-     *
-     * @see CastBuilder#substituteArgPlaceholder(Object, Object...)
-     */
-    public static final Object ARG = new Object();
-
     private final RBuiltinNode builtinNode;
 
     private CastNode[] casts = EMPTY_CASTS_ARRAY;
 
-    private PrintWriter out;
-
     public CastBuilder(RBuiltinNode builtinNode) {
         this.builtinNode = builtinNode;
     }
@@ -146,7 +131,19 @@ public final class CastBuilder {
     }
 
     public CastBuilder toCharacter(int index) {
-        return insert(index, CastStringNodeGen.create(false, false, false, false));
+        return insert(index, CastStringNodeGen.create(false, false, false));
+    }
+
+    public CastBuilder toCharacter(int index, boolean preserveNames, boolean dimensionsPreservation, boolean attrPreservation) {
+        return insert(index, CastStringNodeGen.create(preserveNames, dimensionsPreservation, attrPreservation));
+    }
+
+    public CastBuilder toComplex(int index) {
+        return insert(index, CastComplexNodeGen.create(false, false, false));
+    }
+
+    public CastBuilder toRaw(int index) {
+        return insert(index, CastRawNodeGen.create(false, false, false));
     }
 
     public CastBuilder boxPrimitive(int index) {
@@ -162,10 +159,6 @@ public final class CastBuilder {
         return insert(index, FirstIntNode.createWithWarning(RError.Message.FIRST_ELEMENT_USED, name, intNa));
     }
 
-    public CastBuilder convertToInteger(int index) {
-        return insert(index, ConvertIntNodeGen.create());
-    }
-
     public CastBuilder firstIntegerWithError(int index, RError.Message error, String name) {
         insert(index, CastIntegerNodeGen.create(false, false, false));
         return insert(index, FirstIntNode.createWithError(error, name));
@@ -184,7 +177,8 @@ public final class CastBuilder {
     }
 
     public CastBuilder firstLogical(int index) {
-        return insert(index, CastLogicalScalarNode.create());
+        arg(index).asLogicalVector().findFirst(RRuntime.LOGICAL_NA);
+        return this;
     }
 
     public InitialPhaseBuilder<Object> arg(int argumentIndex, String argumentName) {
@@ -212,39 +206,17 @@ public final class CastBuilder {
         throw RInternalError.shouldNotReachHere(String.format("Argument %s not found in builtin %s", argumentName, builtinNode.getRBuiltin().name()));
     }
 
-    /**
-     * Overrides the default output for warnings. Used in tests only.
-     *
-     * @param o the overriding output writer for warnings
-     * @return this builder
-     */
-    public CastBuilder output(Writer o) {
-        out = new PrintWriter(o);
-        return this;
-    }
-
-    public CastBuilder output(OutputStream o) {
-        out = new PrintWriter(o);
-        return this;
-    }
-
+    @SuppressWarnings({"unchecked", "rawtypes"})
     public static Object[] substituteArgPlaceholder(Object arg, Object[] messageArgs) {
-        int argPlaceholderIndex = -1;
+        Object[] newMsgArgs = Arrays.copyOf(messageArgs, messageArgs.length);
+
         for (int i = 0; i < messageArgs.length; i++) {
-            if (messageArgs[i] == ARG) {
-                argPlaceholderIndex = i;
-                break;
+            final Object msgArg = messageArgs[i];
+            if (msgArg instanceof Function) {
+                newMsgArgs[i] = ((Function) msgArg).apply(arg);
             }
         }
 
-        Object[] newMsgArgs;
-        if (argPlaceholderIndex >= 0) {
-            newMsgArgs = Arrays.copyOf(messageArgs, messageArgs.length);
-            newMsgArgs[argPlaceholderIndex] = arg;
-        } else {
-            newMsgArgs = messageArgs;
-        }
-
         return newMsgArgs;
     }
 
@@ -262,6 +234,16 @@ public final class CastBuilder {
 
         <T extends RAbstractVector, R extends T> VectorPredicateArgumentFilter<T> size(int s);
 
+        VectorPredicateArgumentFilter<RAbstractStringVector> elementAt(int index, String value);
+
+        VectorPredicateArgumentFilter<RAbstractIntVector> elementAt(int index, int value);
+
+        VectorPredicateArgumentFilter<RAbstractDoubleVector> elementAt(int index, double value);
+
+        VectorPredicateArgumentFilter<RAbstractComplexVector> elementAt(int index, RComplex value);
+
+        VectorPredicateArgumentFilter<RAbstractLogicalVector> elementAt(int index, byte value);
+
         ValuePredicateArgumentFilter<Boolean> trueValue();
 
         ValuePredicateArgumentFilter<Boolean> falseValue();
@@ -272,64 +254,34 @@ public final class CastBuilder {
 
         ValuePredicateArgumentFilter<Integer> intNA();
 
-        ValuePredicateArgumentFilter<Integer> notIntNA();
-
         ValuePredicateArgumentFilter<Byte> logicalNA();
 
-        ValuePredicateArgumentFilter<Byte> notLogicalNA();
-
         ValuePredicateArgumentFilter<Double> doubleNA();
 
-        ValuePredicateArgumentFilter<Double> notDoubleNA();
-
         ValuePredicateArgumentFilter<String> stringNA();
 
-        ValuePredicateArgumentFilter<String> notStringNA();
-
         ValuePredicateArgumentFilter<Integer> eq(int x);
 
         ValuePredicateArgumentFilter<Double> eq(double x);
 
-        ValuePredicateArgumentFilter<Integer> neq(int x);
-
-        ValuePredicateArgumentFilter<Double> neq(double x);
-
         ValuePredicateArgumentFilter<Integer> gt(int x);
 
         ValuePredicateArgumentFilter<Double> gt(double x);
 
-        ValuePredicateArgumentFilter<Integer> gte(int x);
-
         ValuePredicateArgumentFilter<Double> gte(double x);
 
         ValuePredicateArgumentFilter<Integer> lt(int x);
 
         ValuePredicateArgumentFilter<Double> lt(double x);
 
-        ValuePredicateArgumentFilter<Integer> lte(int x);
-
         ValuePredicateArgumentFilter<Double> lte(double x);
 
         ValuePredicateArgumentFilter<String> length(int l);
 
-        ValuePredicateArgumentFilter<String> isEmpty();
-
         ValuePredicateArgumentFilter<String> lengthGt(int l);
 
-        ValuePredicateArgumentFilter<String> lengthGte(int l);
-
         ValuePredicateArgumentFilter<String> lengthLt(int l);
 
-        ValuePredicateArgumentFilter<String> lengthLte(int l);
-
-        ValuePredicateArgumentFilter<Integer> gt0();
-
-        ValuePredicateArgumentFilter<Integer> gte0();
-
-        ValuePredicateArgumentFilter<Integer> gt1();
-
-        ValuePredicateArgumentFilter<Integer> gte1();
-
         <R> TypePredicateArgumentFilter<Object, R> instanceOf(Class<R> cls);
 
         <R extends RAbstractIntVector> TypePredicateArgumentFilter<Object, R> integerValue();
@@ -342,6 +294,8 @@ public final class CastBuilder {
 
         <R extends RAbstractComplexVector> TypePredicateArgumentFilter<Object, R> complexValue();
 
+        <R extends RAbstractRawVector> TypePredicateArgumentFilter<Object, R> rawValue();
+
         TypePredicateArgumentFilter<Object, String> scalarStringValue();
 
         TypePredicateArgumentFilter<Object, Integer> scalarIntegerValue();
@@ -361,6 +315,8 @@ public final class CastBuilder {
 
         ValuePredicateArgumentMapper<String, Integer> charAt0(int defaultValue);
 
+        <T> ValuePredicateArgumentMapper<T, RNull> nullConstant();
+
         ValuePredicateArgumentMapper<String, String> constant(String s);
 
         ValuePredicateArgumentMapper<Integer, Integer> constant(int i);
@@ -405,6 +361,31 @@ public final class CastBuilder {
             return new VectorPredicateArgumentFilter<>(x -> x.getLength() == s, false);
         }
 
+        @Override
+        public VectorPredicateArgumentFilter<RAbstractStringVector> elementAt(int index, String value) {
+            return new VectorPredicateArgumentFilter<>(x -> index < x.getLength() && value.equals(x.getDataAtAsObject(index)), false);
+        }
+
+        @Override
+        public VectorPredicateArgumentFilter<RAbstractIntVector> elementAt(int index, int value) {
+            return new VectorPredicateArgumentFilter<>(x -> index < x.getLength() && value == (int) x.getDataAtAsObject(index), false);
+        }
+
+        @Override
+        public VectorPredicateArgumentFilter<RAbstractDoubleVector> elementAt(int index, double value) {
+            return new VectorPredicateArgumentFilter<>(x -> index < x.getLength() && value == (double) x.getDataAtAsObject(index), false);
+        }
+
+        @Override
+        public VectorPredicateArgumentFilter<RAbstractComplexVector> elementAt(int index, RComplex value) {
+            return new VectorPredicateArgumentFilter<>(x -> index < x.getLength() && value.equals(x.getDataAtAsObject(index)), false);
+        }
+
+        @Override
+        public VectorPredicateArgumentFilter<RAbstractLogicalVector> elementAt(int index, byte value) {
+            return new VectorPredicateArgumentFilter<>(x -> index < x.getLength() && value == (byte) (x.getDataAtAsObject(index)), false);
+        }
+
         @Override
         public ValuePredicateArgumentFilter<Boolean> trueValue() {
             return ValuePredicateArgumentFilter.fromLambda(x -> x);
@@ -430,41 +411,21 @@ public final class CastBuilder {
             return ValuePredicateArgumentFilter.fromLambda((Integer x) -> RRuntime.isNA(x));
         }
 
-        @Override
-        public ValuePredicateArgumentFilter<Integer> notIntNA() {
-            return ValuePredicateArgumentFilter.fromLambda((Integer x) -> !RRuntime.isNA(x));
-        }
-
         @Override
         public ValuePredicateArgumentFilter<Byte> logicalNA() {
             return ValuePredicateArgumentFilter.fromLambda((Byte x) -> RRuntime.isNA(x));
         }
 
-        @Override
-        public ValuePredicateArgumentFilter<Byte> notLogicalNA() {
-            return ValuePredicateArgumentFilter.fromLambda((Byte x) -> !RRuntime.isNA(x));
-        }
-
         @Override
         public ValuePredicateArgumentFilter<Double> doubleNA() {
             return ValuePredicateArgumentFilter.fromLambda((Double x) -> RRuntime.isNA(x));
         }
 
-        @Override
-        public ValuePredicateArgumentFilter<Double> notDoubleNA() {
-            return ValuePredicateArgumentFilter.fromLambda((Double x) -> !RRuntime.isNA(x));
-        }
-
         @Override
         public ValuePredicateArgumentFilter<String> stringNA() {
             return ValuePredicateArgumentFilter.fromLambda((String x) -> RRuntime.isNA(x));
         }
 
-        @Override
-        public ValuePredicateArgumentFilter<String> notStringNA() {
-            return ValuePredicateArgumentFilter.fromLambda((String x) -> !RRuntime.isNA(x));
-        }
-
         @Override
         public ValuePredicateArgumentFilter<Integer> eq(int x) {
             return ValuePredicateArgumentFilter.fromLambda((Integer arg) -> arg != null && arg.intValue() == x);
@@ -475,39 +436,24 @@ public final class CastBuilder {
             return ValuePredicateArgumentFilter.fromLambda((Double arg) -> arg != null && arg.doubleValue() == x);
         }
 
-        @Override
-        public ValuePredicateArgumentFilter<Integer> neq(int x) {
-            return ValuePredicateArgumentFilter.fromLambda((Integer arg) -> arg == null || arg.intValue() != x);
-        }
-
-        @Override
-        public ValuePredicateArgumentFilter<Double> neq(double x) {
-            return ValuePredicateArgumentFilter.fromLambda((Double arg) -> arg == null || arg.doubleValue() != x);
-        }
-
         @Override
         public ValuePredicateArgumentFilter<Integer> gt(int x) {
-            return ValuePredicateArgumentFilter.fromLambda((Integer arg) -> arg > x);
+            return ValuePredicateArgumentFilter.fromLambda((Integer arg) -> arg != null && arg > x);
         }
 
         @Override
         public ValuePredicateArgumentFilter<Double> gt(double x) {
-            return ValuePredicateArgumentFilter.fromLambda((Double arg) -> arg > x);
-        }
-
-        @Override
-        public ValuePredicateArgumentFilter<Integer> gte(int x) {
-            return ValuePredicateArgumentFilter.fromLambda((Integer arg) -> arg >= x);
+            return ValuePredicateArgumentFilter.fromLambda((Double arg) -> arg != null && arg > x);
         }
 
         @Override
         public ValuePredicateArgumentFilter<Double> gte(double x) {
-            return ValuePredicateArgumentFilter.fromLambda((Double arg) -> arg >= x);
+            return ValuePredicateArgumentFilter.fromLambda((Double arg) -> arg != null && arg >= x);
         }
 
         @Override
         public ValuePredicateArgumentFilter<Integer> lt(int x) {
-            return ValuePredicateArgumentFilter.fromLambda((Integer arg) -> arg < x);
+            return ValuePredicateArgumentFilter.fromLambda((Integer arg) -> arg != null && arg < x);
         }
 
         @Override
@@ -515,11 +461,6 @@ public final class CastBuilder {
             return ValuePredicateArgumentFilter.fromLambda((Double arg) -> arg < x);
         }
 
-        @Override
-        public ValuePredicateArgumentFilter<Integer> lte(int x) {
-            return ValuePredicateArgumentFilter.fromLambda((Integer arg) -> arg <= x);
-        }
-
         @Override
         public ValuePredicateArgumentFilter<Double> lte(double x) {
             return ValuePredicateArgumentFilter.fromLambda((Double arg) -> arg <= x);
@@ -530,51 +471,16 @@ public final class CastBuilder {
             return ValuePredicateArgumentFilter.fromLambda((String arg) -> arg != null && arg.length() == l);
         }
 
-        @Override
-        public ValuePredicateArgumentFilter<String> isEmpty() {
-            return ValuePredicateArgumentFilter.fromLambda((String arg) -> arg != null && arg.isEmpty());
-        }
-
         @Override
         public ValuePredicateArgumentFilter<String> lengthGt(int l) {
             return ValuePredicateArgumentFilter.fromLambda((String arg) -> arg != null && arg.length() > l);
         }
 
-        @Override
-        public ValuePredicateArgumentFilter<String> lengthGte(int l) {
-            return ValuePredicateArgumentFilter.fromLambda((String arg) -> arg != null && arg.length() >= l);
-        }
-
         @Override
         public ValuePredicateArgumentFilter<String> lengthLt(int l) {
             return ValuePredicateArgumentFilter.fromLambda((String arg) -> arg != null && arg.length() < l);
         }
 
-        @Override
-        public ValuePredicateArgumentFilter<String> lengthLte(int l) {
-            return ValuePredicateArgumentFilter.fromLambda((String arg) -> arg != null && arg.length() <= l);
-        }
-
-        @Override
-        public ValuePredicateArgumentFilter<Integer> gt0() {
-            return ValuePredicateArgumentFilter.fromLambda((Integer x) -> x > 0);
-        }
-
-        @Override
-        public ValuePredicateArgumentFilter<Integer> gte0() {
-            return ValuePredicateArgumentFilter.fromLambda((Integer x) -> x >= 0);
-        }
-
-        @Override
-        public ValuePredicateArgumentFilter<Integer> gt1() {
-            return ValuePredicateArgumentFilter.fromLambda((Integer x) -> x > 1);
-        }
-
-        @Override
-        public ValuePredicateArgumentFilter<Integer> gte1() {
-            return ValuePredicateArgumentFilter.fromLambda((Integer x) -> x >= 1);
-        }
-
         @Override
         public <R> TypePredicateArgumentFilter<Object, R> instanceOf(Class<R> cls) {
             return TypePredicateArgumentFilter.fromLambda(x -> cls.isInstance(x));
@@ -605,6 +511,11 @@ public final class CastBuilder {
             return TypePredicateArgumentFilter.fromLambda(x -> x instanceof RComplex || x instanceof RAbstractComplexVector);
         }
 
+        @Override
+        public <R extends RAbstractRawVector> TypePredicateArgumentFilter<Object, R> rawValue() {
+            return TypePredicateArgumentFilter.fromLambda(x -> x instanceof RRaw || x instanceof RAbstractRawVector);
+        }
+
         @Override
         public TypePredicateArgumentFilter<Object, String> scalarStringValue() {
             return TypePredicateArgumentFilter.fromLambda(x -> x instanceof String);
@@ -647,7 +558,23 @@ public final class CastBuilder {
         @Override
         public ValuePredicateArgumentMapper<String, Integer> charAt0(int defaultValue) {
             final ConditionProfile profile = ConditionProfile.createBinaryProfile();
-            return ValuePredicateArgumentMapper.fromLambda(x -> profile.profile(x == null || x.isEmpty()) ? defaultValue : (int) x.charAt(0));
+            final ConditionProfile profile2 = ConditionProfile.createBinaryProfile();
+            return ValuePredicateArgumentMapper.fromLambda(x -> {
+                if (profile.profile(x == null || x.isEmpty())) {
+                    return defaultValue;
+                } else {
+                    if (profile2.profile(x == RRuntime.STRING_NA)) {
+                        return RRuntime.INT_NA;
+                    } else {
+                        return (int) x.charAt(0);
+                    }
+                }
+            });
+        }
+
+        @Override
+        public <T> ValuePredicateArgumentMapper<T, RNull> nullConstant() {
+            return ValuePredicateArgumentMapper.fromLambda(x -> RNull.instance);
         }
 
         @Override
@@ -723,44 +650,131 @@ public final class CastBuilder {
             return predefMappers;
         }
 
-        public static <T, R> MapNode mapNode(ArgumentMapper<T, R> mapper) {
-            return MapNodeGen.create(mapper);
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> mustBe(ArgumentFilter<?, ?> argFilter, RBaseNode callObj, boolean boxPrimitives, RError.Message message, Object... messageArgs) {
+            return phaseBuilder -> FilterNodeGen.create(argFilter, false, callObj, message, messageArgs, boxPrimitives);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> mustBe(ArgumentFilter<?, ?> argFilter, boolean boxPrimitives) {
+            return phaseBuilder -> FilterNodeGen.create(argFilter, false, phaseBuilder.state().defaultError().callObj, phaseBuilder.state().defaultError().message,
+                            phaseBuilder.state().defaultError().args, boxPrimitives);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> shouldBe(ArgumentFilter<?, ?> argFilter, RBaseNode callObj, boolean boxPrimitives, RError.Message message, Object... messageArgs) {
+            return phaseBuilder -> FilterNodeGen.create(argFilter, true, callObj, message, messageArgs, boxPrimitives);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> shouldBe(ArgumentFilter<?, ?> argFilter, boolean boxPrimitives) {
+            return phaseBuilder -> FilterNodeGen.create(argFilter, true, phaseBuilder.state().defaultError().callObj, phaseBuilder.state().defaultError().message,
+                            phaseBuilder.state().defaultError().args, boxPrimitives);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> map(ArgumentMapper<?, ?> mapper) {
+            return phaseBuilder -> MapNodeGen.create(mapper);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> mapIf(ArgumentFilter<?, ?> filter, Function<ArgCastBuilder<T, ?>, CastNode> trueBranchFactory,
+                        Function<ArgCastBuilder<T, ?>, CastNode> falseBranchFactory) {
+            return phaseBuilder -> ConditionalMapNodeGen.create(filter, trueBranchFactory.apply(phaseBuilder), falseBranchFactory.apply(phaseBuilder));
+        }
+
+        public static <T> ChainBuilder<T> chain(CastNode firstCast) {
+            return new ChainBuilder<>(pb -> firstCast);
+        }
+
+        public static <T> ChainBuilder<T> chain(Function<ArgCastBuilder<T, ?>, CastNode> firstCastNodeFactory) {
+            return new ChainBuilder<>(firstCastNodeFactory);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asInteger() {
+            return phaseBuilder -> CastIntegerBaseNodeGen.create(false, false, false);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asIntegerVector() {
+            return phaseBuilder -> CastIntegerNodeGen.create(false, false, false);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asIntegerVector(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+            return phaseBuilder -> CastIntegerNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asDouble() {
+            return phaseBuilder -> CastDoubleBaseNodeGen.create(false, false, false);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asDoubleVector() {
+            return phaseBuilder -> CastDoubleNodeGen.create(false, false, false);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asDoubleVector(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+            return phaseBuilder -> CastDoubleNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asString() {
+            return phaseBuilder -> CastStringBaseNodeGen.create(false, false, false);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asStringVector() {
+            return phaseBuilder -> CastStringNodeGen.create(false, false, false);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asComplexVector() {
+            return phaseBuilder -> CastComplexNodeGen.create(false, false, false);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asRawVector() {
+            return phaseBuilder -> CastRawNodeGen.create(false, false, false);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asStringVector(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+            return phaseBuilder -> CastStringNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asLogical() {
+            return phaseBuilder -> CastLogicalBaseNodeGen.create(false, false, false);
         }
 
-        public static CastIntegerBaseNode asInteger() {
-            return CastIntegerBaseNodeGen.create(false, false, false);
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asLogicalVector() {
+            return phaseBuilder -> CastLogicalNodeGen.create(false, false, false);
         }
 
-        public static CastIntegerNode asIntegerVector() {
-            return CastIntegerNodeGen.create(false, false, false);
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asLogicalVector(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+            return phaseBuilder -> CastLogicalNodeGen.create(preserveNames, preserveDimensions, preserveAttributes);
         }
 
-        public static CastDoubleBaseNode asDouble() {
-            return CastDoubleBaseNodeGen.create(false, false, false);
+        public static <T> FindFirstNodeBuilder<T> findFirst(RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            return new FindFirstNodeBuilder<>(callObj, message, messageArgs);
         }
 
-        public static CastDoubleNode asDoubleVector() {
-            return CastDoubleNodeGen.create(false, false, false);
+        public static <T> FindFirstNodeBuilder<T> findFirst(RError.Message message, Object... messageArgs) {
+            return new FindFirstNodeBuilder<>(null, message, messageArgs);
         }
 
-        public static CastStringBaseNode asString() {
-            return CastStringBaseNodeGen.create(false, false, false);
+        public static <T> FindFirstNodeBuilder<T> findFirst() {
+            return new FindFirstNodeBuilder<>(null, null, null);
         }
 
-        public static CastStringNode asStringVector() {
-            return CastStringNodeGen.create(false, false, false, false);
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> notNA(RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            return phaseBuilder -> NonNANodeGen.create(callObj, message, messageArgs, null);
         }
 
-        public static CastLogicalBaseNode asLogical() {
-            return CastLogicalBaseNodeGen.create(false, false, false);
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> notNA(RError.Message message, Object... messageArgs) {
+            return phaseBuilder -> NonNANodeGen.create(null, message, messageArgs, null);
         }
 
-        public static CastLogicalNode asLogicalVector() {
-            return CastLogicalNodeGen.create(false, false, false);
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> notNA(T naReplacement, RError.Message message, Object... messageArgs) {
+            return phaseBuilder -> NonNANodeGen.create(null, message, messageArgs, naReplacement);
         }
 
-        public static MapNode asBoolean() {
-            return mapNode(toBoolean());
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> notNA(T naReplacement) {
+            return phaseBuilder -> NonNANodeGen.create(naReplacement);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> notNA() {
+            return phaseBuilder -> NonNANodeGen.create(phaseBuilder.state().defaultError().callObj, phaseBuilder.state().defaultError().message, phaseBuilder.state().defaultError().args, null);
+        }
+
+        public static <T> Function<ArgCastBuilder<T, ?>, CastNode> asBoolean() {
+            return map(toBoolean());
         }
 
         public static <T> ValuePredicateArgumentFilter<T> sameAs(T x) {
@@ -787,6 +801,26 @@ public final class CastBuilder {
             return predefFilters().size(s);
         }
 
+        public static VectorPredicateArgumentFilter<RAbstractStringVector> elementAt(int index, String value) {
+            return predefFilters().elementAt(index, value);
+        }
+
+        public static VectorPredicateArgumentFilter<RAbstractIntVector> elementAt(int index, int value) {
+            return predefFilters().elementAt(index, value);
+        }
+
+        public static VectorPredicateArgumentFilter<RAbstractDoubleVector> elementAt(int index, double value) {
+            return predefFilters().elementAt(index, value);
+        }
+
+        public static VectorPredicateArgumentFilter<RAbstractComplexVector> elementAt(int index, RComplex value) {
+            return predefFilters().elementAt(index, value);
+        }
+
+        public static VectorPredicateArgumentFilter<RAbstractLogicalVector> elementAt(int index, byte value) {
+            return predefFilters().elementAt(index, value);
+        }
+
         public static ValuePredicateArgumentFilter<Boolean> trueValue() {
             return predefFilters().trueValue();
         }
@@ -807,32 +841,32 @@ public final class CastBuilder {
             return predefFilters().intNA();
         }
 
-        public static ValuePredicateArgumentFilter<Integer> notIntNA() {
-            return predefFilters().notIntNA();
+        public static ArgumentValueFilter<Integer> notIntNA() {
+            return predefFilters().intNA().not();
         }
 
         public static ValuePredicateArgumentFilter<Byte> logicalNA() {
             return predefFilters().logicalNA();
         }
 
-        public static ValuePredicateArgumentFilter<Byte> notLogicalNA() {
-            return predefFilters().notLogicalNA();
+        public static ArgumentValueFilter<Byte> notLogicalNA() {
+            return predefFilters().logicalNA().not();
         }
 
         public static ValuePredicateArgumentFilter<Double> doubleNA() {
             return predefFilters().doubleNA();
         }
 
-        public static ValuePredicateArgumentFilter<Double> notDoubleNA() {
-            return predefFilters().notDoubleNA();
+        public static ArgumentValueFilter<Double> notDoubleNA() {
+            return predefFilters().doubleNA().not();
         }
 
         public static ValuePredicateArgumentFilter<String> stringNA() {
             return predefFilters().stringNA();
         }
 
-        public static ValuePredicateArgumentFilter<String> notStringNA() {
-            return predefFilters().notStringNA();
+        public static ArgumentValueFilter<String> notStringNA() {
+            return predefFilters().stringNA().not();
         }
 
         public static ValuePredicateArgumentFilter<Integer> eq(int x) {
@@ -843,12 +877,12 @@ public final class CastBuilder {
             return predefFilters().eq(x);
         }
 
-        public static ValuePredicateArgumentFilter<Integer> neq(int x) {
-            return predefFilters().neq(x);
+        public static ArgumentValueFilter<Integer> neq(int x) {
+            return predefFilters().eq(x).not();
         }
 
-        public static ValuePredicateArgumentFilter<Double> neq(double x) {
-            return predefFilters().neq(x);
+        public static ArgumentValueFilter<Double> neq(double x) {
+            return predefFilters().eq(x).not();
         }
 
         public static ValuePredicateArgumentFilter<Integer> gt(int x) {
@@ -860,7 +894,7 @@ public final class CastBuilder {
         }
 
         public static ValuePredicateArgumentFilter<Integer> gte(int x) {
-            return predefFilters().gte(x);
+            return predefFilters().gt(x - 1);
         }
 
         public static ValuePredicateArgumentFilter<Double> gte(double x) {
@@ -876,7 +910,7 @@ public final class CastBuilder {
         }
 
         public static ValuePredicateArgumentFilter<Integer> lte(int x) {
-            return predefFilters().lte(x);
+            return predefFilters().lt(x + 1);
         }
 
         public static ValuePredicateArgumentFilter<Double> lte(double x) {
@@ -888,7 +922,7 @@ public final class CastBuilder {
         }
 
         public static ValuePredicateArgumentFilter<String> isEmpty() {
-            return predefFilters().isEmpty();
+            return predefFilters().lengthLt(1);
         }
 
         public static ValuePredicateArgumentFilter<String> lengthGt(int l) {
@@ -896,7 +930,7 @@ public final class CastBuilder {
         }
 
         public static ValuePredicateArgumentFilter<String> lengthGte(int l) {
-            return predefFilters().lengthGte(l);
+            return predefFilters().lengthGt(l - 1);
         }
 
         public static ValuePredicateArgumentFilter<String> lengthLt(int l) {
@@ -904,23 +938,23 @@ public final class CastBuilder {
         }
 
         public static ValuePredicateArgumentFilter<String> lengthLte(int l) {
-            return predefFilters().lengthLte(l);
+            return predefFilters().lengthLt(l + 1);
         }
 
         public static ValuePredicateArgumentFilter<Integer> gt0() {
-            return predefFilters().gt0();
+            return predefFilters().gt(0);
         }
 
         public static ValuePredicateArgumentFilter<Integer> gte0() {
-            return predefFilters().gte0();
+            return predefFilters().gt(-1);
         }
 
         public static ValuePredicateArgumentFilter<Integer> gt1() {
-            return predefFilters().gt1();
+            return predefFilters().gt(1);
         }
 
         public static ValuePredicateArgumentFilter<Integer> gte1() {
-            return predefFilters().gte1();
+            return predefFilters().gt(0);
         }
 
         public static <R> TypePredicateArgumentFilter<Object, R> instanceOf(Class<R> cls) {
@@ -947,6 +981,10 @@ public final class CastBuilder {
             return predefFilters().complexValue();
         }
 
+        public static <R extends RAbstractRawVector> TypePredicateArgumentFilter<Object, R> rawValue() {
+            return predefFilters().rawValue();
+        }
+
         public static ArgumentTypeFilter<Object, Object> numericValue() {
             return integerValue().or(doubleValue()).or(logicalValue());
         }
@@ -983,6 +1021,10 @@ public final class CastBuilder {
             return predefMappers().charAt0(defaultValue);
         }
 
+        public static <T> ValuePredicateArgumentMapper<T, RNull> nullConstant() {
+            return predefMappers().nullConstant();
+        }
+
         public static ValuePredicateArgumentMapper<String, String> constant(String s) {
             return predefMappers().constant(s);
         }
@@ -1014,18 +1056,33 @@ public final class CastBuilder {
             return state().castBuilder();
         }
 
+        default THIS defaultError(RBaseNode callObj, RError.Message message, Object... args) {
+            state().setDefaultError(callObj, message, args);
+            return (THIS) this;
+        }
+
         default THIS defaultError(RError.Message message, Object... args) {
             state().setDefaultError(message, args);
             return (THIS) this;
         }
 
+        default THIS defaultWarning(RBaseNode callObj, RError.Message message, Object... args) {
+            state().setDefaultWarning(callObj, message, args);
+            return (THIS) this;
+        }
+
         default THIS defaultWarning(RError.Message message, Object... args) {
             state().setDefaultWarning(message, args);
             return (THIS) this;
         }
 
         default THIS shouldBe(ArgumentFilter<? super T, ?> argFilter, RError.Message message, Object... messageArgs) {
-            state().castBuilder().insert(state().index(), FilterNodeGen.create(argFilter, true, message, messageArgs, state().boxPrimitives, state().cb.out));
+            state().castBuilder().insert(state().index(), FilterNodeGen.create(argFilter, true, null, message, messageArgs, state().boxPrimitives));
+            return (THIS) this;
+        }
+
+        default THIS shouldBe(ArgumentFilter<? super T, ?> argFilter, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().castBuilder().insert(state().index(), FilterNodeGen.create(argFilter, true, callObj, message, messageArgs, state().boxPrimitives));
             return (THIS) this;
         }
 
@@ -1033,6 +1090,10 @@ public final class CastBuilder {
             return shouldBe(argFilter, state().defaultWarning().message, state().defaultWarning().args);
         }
 
+        default <R, THAT extends ArgCastBuilder<R, THAT>> THAT alias(Function<THIS, THAT> aliaser) {
+            return aliaser.apply((THIS) this);
+        }
+
     }
 
     interface ArgCastBuilderFactory {
@@ -1048,10 +1109,12 @@ public final class CastBuilder {
     }
 
     static class DefaultError {
+        final RBaseNode callObj;
         final RError.Message message;
         final Object[] args;
 
-        DefaultError(RError.Message message, Object... args) {
+        DefaultError(RBaseNode callObj, RError.Message message, Object... args) {
+            this.callObj = callObj;
             this.message = message;
             this.args = args;
         }
@@ -1075,7 +1138,7 @@ public final class CastBuilder {
             this.factory = fact;
             this.cb = cb;
             this.boxPrimitives = boxPrimitives;
-            this.defaultDefaultError = new DefaultError(RError.Message.INVALID_ARGUMENT, argumentName);
+            this.defaultDefaultError = new DefaultError(null, RError.Message.INVALID_ARGUMENT, argumentName);
         }
 
         ArgCastBuilderState(ArgCastBuilderState prevState, boolean boxPrimitives) {
@@ -1086,7 +1149,7 @@ public final class CastBuilder {
             this.boxPrimitives = boxPrimitives;
             this.defError = prevState.defError;
             this.defWarning = prevState.defWarning;
-            this.defaultDefaultError = new DefaultError(RError.Message.INVALID_ARGUMENT, argumentName);
+            this.defaultDefaultError = new DefaultError(null, RError.Message.INVALID_ARGUMENT, argumentName);
         }
 
         public int index() {
@@ -1109,44 +1172,60 @@ public final class CastBuilder {
             return defWarning != null;
         }
 
+        void setDefaultError(RBaseNode callObj, RError.Message message, Object... args) {
+            defError = new DefaultError(callObj, message, args);
+        }
+
         void setDefaultError(RError.Message message, Object... args) {
-            defError = new DefaultError(message, args);
+            defError = new DefaultError(null, message, args);
+        }
+
+        void setDefaultWarning(RBaseNode callObj, RError.Message message, Object... args) {
+            defWarning = new DefaultError(callObj, message, args);
         }
 
         void setDefaultWarning(RError.Message message, Object... args) {
-            defWarning = new DefaultError(message, args);
+            defWarning = new DefaultError(null, message, args);
         }
 
         DefaultError defaultError() {
             return defError == null ? defaultDefaultError : defError;
         }
 
+        DefaultError defaultError(RBaseNode callObj, RError.Message defaultDefaultMessage, Object... defaultDefaultArgs) {
+            return defError == null ? new DefaultError(callObj, defaultDefaultMessage, defaultDefaultArgs) : defError;
+        }
+
         DefaultError defaultError(RError.Message defaultDefaultMessage, Object... defaultDefaultArgs) {
-            return defError == null ? new DefaultError(defaultDefaultMessage, defaultDefaultArgs) : defError;
+            return defError == null ? new DefaultError(null, defaultDefaultMessage, defaultDefaultArgs) : defError;
         }
 
         DefaultError defaultWarning() {
             return defWarning == null ? defaultDefaultError : defWarning;
         }
 
+        DefaultError defaultWarning(RBaseNode callObj, RError.Message defaultDefaultMessage, Object... defaultDefaultArgs) {
+            return defWarning == null ? new DefaultError(callObj, defaultDefaultMessage, defaultDefaultArgs) : defWarning;
+        }
+
         DefaultError defaultWarning(RError.Message defaultDefaultMessage, Object... defaultDefaultArgs) {
-            return defWarning == null ? new DefaultError(defaultDefaultMessage, defaultDefaultArgs) : defWarning;
+            return defWarning == null ? new DefaultError(null, defaultDefaultMessage, defaultDefaultArgs) : defWarning;
         }
 
-        void mustBe(ArgumentFilter<?, ?> argFilter, RError.Message message, Object... messageArgs) {
-            castBuilder().insert(index(), FilterNodeGen.create(argFilter, false, message, messageArgs, boxPrimitives, cb.out));
+        void mustBe(ArgumentFilter<?, ?> argFilter, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            castBuilder().insert(index(), FilterNodeGen.create(argFilter, false, callObj, message, messageArgs, boxPrimitives));
         }
 
         void mustBe(ArgumentFilter<?, ?> argFilter) {
-            mustBe(argFilter, defaultError().message, defaultError().args);
+            mustBe(argFilter, defaultError().callObj, defaultError().message, defaultError().args);
         }
 
-        void shouldBe(ArgumentFilter<?, ?> argFilter, RError.Message message, Object... messageArgs) {
-            castBuilder().insert(index(), FilterNodeGen.create(argFilter, true, message, messageArgs, boxPrimitives, cb.out));
+        void shouldBe(ArgumentFilter<?, ?> argFilter, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            castBuilder().insert(index(), FilterNodeGen.create(argFilter, true, callObj, message, messageArgs, boxPrimitives));
         }
 
         void shouldBe(ArgumentFilter<?, ?> argFilter) {
-            shouldBe(argFilter, defaultWarning().message, defaultWarning().args);
+            shouldBe(argFilter, defaultWarning().callObj, defaultWarning().message, defaultWarning().args);
         }
 
     }
@@ -1167,8 +1246,13 @@ public final class CastBuilder {
 
     public interface InitialPhaseBuilder<T> extends ArgCastBuilder<T, InitialPhaseBuilder<T>> {
 
+        default <S> InitialPhaseBuilder<S> mustBe(ArgumentFilter<? super T, S> argFilter, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().mustBe(argFilter, callObj, message, messageArgs);
+            return state().factory.newInitialPhaseBuilder(this);
+        }
+
         default <S> InitialPhaseBuilder<S> mustBe(ArgumentFilter<? super T, S> argFilter, RError.Message message, Object... messageArgs) {
-            state().mustBe(argFilter, message, messageArgs);
+            state().mustBe(argFilter, null, message, messageArgs);
             return state().factory.newInitialPhaseBuilder(this);
         }
 
@@ -1176,6 +1260,11 @@ public final class CastBuilder {
             return mustBe(argFilter, state().defaultError().message, state().defaultError().args);
         }
 
+        default <S> InitialPhaseBuilder<S> mustBe(Class<S> cls, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            mustBe(Predef.instanceOf(cls), callObj, message, messageArgs);
+            return state().factory.newInitialPhaseBuilder(this);
+        }
+
         default <S> InitialPhaseBuilder<S> mustBe(Class<S> cls, RError.Message message, Object... messageArgs) {
             mustBe(Predef.instanceOf(cls), message, messageArgs);
             return state().factory.newInitialPhaseBuilder(this);
@@ -1186,24 +1275,48 @@ public final class CastBuilder {
             return state().factory.newInitialPhaseBuilder(this);
         }
 
+        default <S> InitialPhaseBuilder<S> shouldBe(Class<S> cls, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            shouldBe(Predef.instanceOf(cls), callObj, message, messageArgs);
+            return state().factory.newInitialPhaseBuilder(this);
+        }
+
+        default <S> InitialPhaseBuilder<S> shouldBe(Class<S> cls, RError.Message message, Object... messageArgs) {
+            shouldBe(Predef.instanceOf(cls), message, messageArgs);
+            return state().factory.newInitialPhaseBuilder(this);
+        }
+
+        default <S> InitialPhaseBuilder<S> shouldBe(Class<S> cls) {
+            shouldBe(Predef.instanceOf(cls));
+            return state().factory.newInitialPhaseBuilder(this);
+        }
+
         default <S> InitialPhaseBuilder<S> map(ArgumentMapper<T, S> mapFn) {
             state().castBuilder().insert(state().index(), MapNodeGen.create(mapFn));
             return state().factory.newInitialPhaseBuilder(this);
         }
 
-        default <S, R> InitialPhaseBuilder<S> mapIf(ArgumentFilter<? super T, S> argFilter, ArgumentMapper<S, R> trueBranchMapper) {
+        @SuppressWarnings("overloads")
+        default <S, R> InitialPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, ArgumentMapper<S, R> trueBranchMapper) {
             state().castBuilder().insert(state().index(), ConditionalMapNodeGen.create(argFilter, MapNodeGen.create(trueBranchMapper), null));
 
             return state().factory.newInitialPhaseBuilder(this);
         }
 
-        default <S, R> InitialPhaseBuilder<S> mapIf(ArgumentFilter<? super T, S> argFilter, CastNode trueBranchNode) {
+        default <S, R> InitialPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, CastNode trueBranchNode) {
             state().castBuilder().insert(state().index(), ConditionalMapNodeGen.create(argFilter, trueBranchNode, null));
 
             return state().factory.newInitialPhaseBuilder(this);
         }
 
-        default <S, R> InitialPhaseBuilder<T> mapIf(ArgumentFilter<? super T, S> argFilter, ArgumentMapper<S, R> trueBranchMapper, ArgumentMapper<T, T> falseBranchMapper) {
+        @SuppressWarnings("overloads")
+        default <S, R> InitialPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, Function<ArgCastBuilder<T, ?>, CastNode> trueBranchNode) {
+            state().castBuilder().insert(state().index(), ConditionalMapNodeGen.create(argFilter, trueBranchNode.apply(this), null));
+
+            return state().factory.newInitialPhaseBuilder(this);
+        }
+
+        @SuppressWarnings("overloads")
+        default <S, R> InitialPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, ArgumentMapper<S, R> trueBranchMapper, ArgumentMapper<T, T> falseBranchMapper) {
             state().castBuilder().insert(
                             state().index(),
                             ConditionalMapNodeGen.create(argFilter, MapNodeGen.create(trueBranchMapper),
@@ -1212,19 +1325,37 @@ public final class CastBuilder {
             return state().factory.newInitialPhaseBuilder(this);
         }
 
-        default <S, R> InitialPhaseBuilder<T> mapIf(ArgumentFilter<? super T, S> argFilter, CastNode trueBranchNode, CastNode falseBranchNode) {
+        default <S, R> InitialPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, CastNode trueBranchNode, CastNode falseBranchNode) {
             state().castBuilder().insert(state().index(), ConditionalMapNodeGen.create(argFilter, trueBranchNode, falseBranchNode));
 
+            return state().factory.newInitialPhaseBuilder(this);
+        }
+
+        @SuppressWarnings("overloads")
+        default <S, R> InitialPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, Function<ArgCastBuilder<T, ?>, CastNode> trueBranchNodeFactory,
+                        Function<ArgCastBuilder<T, ?>, CastNode> falseBranchNodeFactory) {
+            state().castBuilder().insert(state().index(), ConditionalMapNodeGen.create(argFilter, trueBranchNodeFactory.apply(this), falseBranchNodeFactory.apply(this)));
+
+            return state().factory.newInitialPhaseBuilder(this);
+        }
+
+        default InitialPhaseBuilder<T> notNA(RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(callObj, message, messageArgs, null));
             return this;
         }
 
         default InitialPhaseBuilder<T> notNA(RError.Message message, Object... messageArgs) {
-            state().castBuilder().insert(state().index(), NonNANodeGen.create(message, messageArgs, state().cb.out, null));
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(null, message, messageArgs, null));
+            return this;
+        }
+
+        default InitialPhaseBuilder<T> notNA(T naReplacement, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(callObj, message, messageArgs, naReplacement));
             return this;
         }
 
         default InitialPhaseBuilder<T> notNA(T naReplacement, RError.Message message, Object... messageArgs) {
-            state().castBuilder().insert(state().index(), NonNANodeGen.create(message, messageArgs, state().cb.out, naReplacement));
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(null, message, messageArgs, naReplacement));
             return this;
         }
 
@@ -1234,7 +1365,7 @@ public final class CastBuilder {
         }
 
         default InitialPhaseBuilder<T> notNA() {
-            state().castBuilder().insert(state().index(), NonNANodeGen.create(state().defaultError().message, state().defaultError().args, state().cb.out, null));
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(state().defaultError().callObj, state().defaultError().message, state().defaultError().args, null));
             return this;
         }
 
@@ -1261,11 +1392,26 @@ public final class CastBuilder {
             return state().factory.newCoercedPhaseBuilder(this, Byte.class);
         }
 
+        default CoercedPhaseBuilder<RAbstractStringVector, String> asStringVector(boolean preserveNames, boolean dimensionsPreservation, boolean attrPreservation) {
+            state().castBuilder().toCharacter(state().index(), preserveNames, dimensionsPreservation, attrPreservation);
+            return state().factory.newCoercedPhaseBuilder(this, String.class);
+        }
+
         default CoercedPhaseBuilder<RAbstractStringVector, String> asStringVector() {
             state().castBuilder().toCharacter(state().index());
             return state().factory.newCoercedPhaseBuilder(this, String.class);
         }
 
+        default CoercedPhaseBuilder<RAbstractComplexVector, RComplex> asComplexVector() {
+            state().castBuilder().toComplex(state().index());
+            return state().factory.newCoercedPhaseBuilder(this, RComplex.class);
+        }
+
+        default CoercedPhaseBuilder<RAbstractRawVector, RRaw> asRawVector() {
+            state().castBuilder().toRaw(state().index());
+            return state().factory.newCoercedPhaseBuilder(this, RRaw.class);
+        }
+
         default CoercedPhaseBuilder<RAbstractVector, Object> asVector() {
             state().castBuilder().toVector(state().index());
             return state().factory.newCoercedPhaseBuilder(this, Object.class);
@@ -1285,7 +1431,12 @@ public final class CastBuilder {
          * reports the warning message.
          */
         default HeadPhaseBuilder<S> findFirst(S defaultValue, RError.Message message, Object... messageArgs) {
-            state().castBuilder().insert(state().index(), FindFirstNodeGen.create(elementClass(), message, messageArgs, state().cb.out, defaultValue));
+            state().castBuilder().insert(state().index(), FindFirstNodeGen.create(elementClass(), null, message, messageArgs, defaultValue));
+            return state().factory.newHeadPhaseBuilder(this);
+        }
+
+        default HeadPhaseBuilder<S> findFirst(S defaultValue, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().castBuilder().insert(state().index(), FindFirstNodeGen.create(elementClass(), callObj, message, messageArgs, defaultValue));
             return state().factory.newHeadPhaseBuilder(this);
         }
 
@@ -1293,7 +1444,12 @@ public final class CastBuilder {
          * The inserted cast node raises an error if the input vector is empty.
          */
         default HeadPhaseBuilder<S> findFirst(RError.Message message, Object... messageArgs) {
-            state().castBuilder().insert(state().index(), FindFirstNodeGen.create(elementClass(), message, messageArgs, state().cb.out, null));
+            state().castBuilder().insert(state().index(), FindFirstNodeGen.create(elementClass(), null, message, messageArgs, null));
+            return state().factory.newHeadPhaseBuilder(this);
+        }
+
+        default HeadPhaseBuilder<S> findFirst(RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().castBuilder().insert(state().index(), FindFirstNodeGen.create(elementClass(), callObj, message, messageArgs, null));
             return state().factory.newHeadPhaseBuilder(this);
         }
 
@@ -1302,9 +1458,9 @@ public final class CastBuilder {
          * RError.Message.LENGTH_ZERO error if the input vector is empty.
          */
         default HeadPhaseBuilder<S> findFirst() {
-            DefaultError err = state().isDefaultErrorDefined() ? state().defaultError() : new DefaultError(RError.Message.LENGTH_ZERO);
+            DefaultError err = state().isDefaultErrorDefined() ? state().defaultError() : new DefaultError(null, RError.Message.LENGTH_ZERO);
             state().castBuilder().insert(state().index(),
-                            FindFirstNodeGen.create(elementClass(), err.message, err.args, state().cb.out, null));
+                            FindFirstNodeGen.create(elementClass(), err.callObj, err.message, err.args, null));
             return state().factory.newHeadPhaseBuilder(this);
         }
 
@@ -1319,8 +1475,13 @@ public final class CastBuilder {
 
         Class<?> elementClass();
 
+        default CoercedPhaseBuilder<T, S> mustBe(ArgumentFilter<? super T, ? extends T> argFilter, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().mustBe(argFilter, callObj, message, messageArgs);
+            return this;
+        }
+
         default CoercedPhaseBuilder<T, S> mustBe(ArgumentFilter<? super T, ? extends T> argFilter, RError.Message message, Object... messageArgs) {
-            state().mustBe(argFilter, message, messageArgs);
+            state().mustBe(argFilter, null, message, messageArgs);
             return this;
         }
 
@@ -1337,19 +1498,20 @@ public final class CastBuilder {
             return state().factory.newHeadPhaseBuilder(this);
         }
 
-        default <S, R> HeadPhaseBuilder<S> mapIf(ArgumentFilter<? super T, S> argFilter, ArgumentMapper<S, R> trueBranchMapper) {
+        default <S, R> HeadPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, ArgumentMapper<S, R> trueBranchMapper) {
             state().castBuilder().insert(state().index(), ConditionalMapNodeGen.create(argFilter, MapNodeGen.create(trueBranchMapper), null));
 
             return state().factory.newHeadPhaseBuilder(this);
         }
 
-        default <S, R> HeadPhaseBuilder<S> mapIf(ArgumentFilter<? super T, S> argFilter, CastNode trueBranchNode) {
+        default <S, R> HeadPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, CastNode trueBranchNode) {
             state().castBuilder().insert(state().index(), ConditionalMapNodeGen.create(argFilter, trueBranchNode, null));
 
             return state().factory.newHeadPhaseBuilder(this);
         }
 
-        default <S, R> HeadPhaseBuilder<T> mapIf(ArgumentFilter<? super T, S> argFilter, ArgumentMapper<S, R> trueBranchMapper, ArgumentMapper<T, T> falseBranchMapper) {
+        @SuppressWarnings("overloads")
+        default <S, R> HeadPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, ArgumentMapper<S, R> trueBranchMapper, ArgumentMapper<T, T> falseBranchMapper) {
             state().castBuilder().insert(
                             state().index(),
                             ConditionalMapNodeGen.create(argFilter, MapNodeGen.create(trueBranchMapper),
@@ -1358,14 +1520,27 @@ public final class CastBuilder {
             return state().factory.newHeadPhaseBuilder(this);
         }
 
-        default <S, R> HeadPhaseBuilder<T> mapIf(ArgumentFilter<? super T, S> argFilter, CastNode trueBranchNode, CastNode falseBranchNode) {
+        @SuppressWarnings("overloads")
+        default <S, R> HeadPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, Function<ArgCastBuilder<T, ?>, CastNode> trueBranchNodeFactory,
+                        Function<ArgCastBuilder<T, ?>, CastNode> falseBranchNodeFactory) {
+            state().castBuilder().insert(state().index(), ConditionalMapNodeGen.create(argFilter, trueBranchNodeFactory.apply(this), falseBranchNodeFactory.apply(this)));
+
+            return state().factory.newHeadPhaseBuilder(this);
+        }
+
+        default <S, R> HeadPhaseBuilder<Object> mapIf(ArgumentFilter<? super T, S> argFilter, CastNode trueBranchNode, CastNode falseBranchNode) {
             state().castBuilder().insert(state().index(), ConditionalMapNodeGen.create(argFilter, trueBranchNode, falseBranchNode));
 
             return state().factory.newHeadPhaseBuilder(this);
         }
 
+        default <S> HeadPhaseBuilder<S> mustBe(ArgumentFilter<? super T, S> argFilter, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().mustBe(argFilter, callObj, message, messageArgs);
+            return state().factory.newHeadPhaseBuilder(this);
+        }
+
         default <S> HeadPhaseBuilder<S> mustBe(ArgumentFilter<? super T, S> argFilter, RError.Message message, Object... messageArgs) {
-            state().mustBe(argFilter, message, messageArgs);
+            state().mustBe(argFilter, null, message, messageArgs);
             return state().factory.newHeadPhaseBuilder(this);
         }
 
@@ -1383,18 +1558,43 @@ public final class CastBuilder {
             return state().factory.newHeadPhaseBuilder(this);
         }
 
+        default <S> HeadPhaseBuilder<S> shouldBe(Class<S> cls, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            shouldBe(Predef.instanceOf(cls), callObj, message, messageArgs);
+            return state().factory.newHeadPhaseBuilder(this);
+        }
+
+        default <S> HeadPhaseBuilder<S> shouldBe(Class<S> cls, RError.Message message, Object... messageArgs) {
+            shouldBe(Predef.instanceOf(cls), message, messageArgs);
+            return state().factory.newHeadPhaseBuilder(this);
+        }
+
+        default <S> HeadPhaseBuilder<S> shouldBe(Class<S> cls) {
+            shouldBe(Predef.instanceOf(cls));
+            return state().factory.newHeadPhaseBuilder(this);
+        }
+
+        default HeadPhaseBuilder<T> notNA(RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(callObj, message, messageArgs, null));
+            return this;
+        }
+
         default HeadPhaseBuilder<T> notNA(RError.Message message, Object... messageArgs) {
-            state().castBuilder().insert(state().index(), NonNANodeGen.create(message, messageArgs, state().cb.out, null));
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(null, message, messageArgs, null));
+            return this;
+        }
+
+        default HeadPhaseBuilder<T> notNA(T naReplacement, RBaseNode callObj, RError.Message message, Object... messageArgs) {
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(callObj, message, messageArgs, naReplacement));
             return this;
         }
 
         default HeadPhaseBuilder<T> notNA(T naReplacement, RError.Message message, Object... messageArgs) {
-            state().castBuilder().insert(state().index(), NonNANodeGen.create(message, messageArgs, state().cb.out, naReplacement));
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(null, message, messageArgs, naReplacement));
             return this;
         }
 
         default HeadPhaseBuilder<T> notNA() {
-            state().castBuilder().insert(state().index(), NonNANodeGen.create(state().defaultError().message, state().defaultError().args, state().cb.out, null));
+            state().castBuilder().insert(state().index(), NonNANodeGen.create(state().defaultError().callObj, state().defaultError().message, state().defaultError().args, null));
             return this;
         }
 
@@ -1468,4 +1668,107 @@ public final class CastBuilder {
 
     }
 
+    public static final class ChainBuilder<T> {
+        private final Function<ArgCastBuilder<T, ?>, CastNode> firstCastNodeFactory;
+
+        private ChainBuilder(Function<ArgCastBuilder<T, ?>, CastNode> firstCastNodeFactory) {
+            this.firstCastNodeFactory = firstCastNodeFactory;
+        }
+
+        private Function<ArgCastBuilder<T, ?>, CastNode> makeChain(Function<ArgCastBuilder<T, ?>, CastNode> secondCastNodeFactory) {
+            return phaseBuilder -> {
+                CastNode firstCast = firstCastNodeFactory.apply(phaseBuilder);
+                CastNode secondCast = secondCastNodeFactory.apply(phaseBuilder);
+                return new ChainedCastNode(firstCast, secondCast);
+            };
+        }
+
+        @SuppressWarnings("overloads")
+        public ChainBuilder<T> with(Function<ArgCastBuilder<T, ?>, CastNode> secondCastNodeFactory) {
+            return new ChainBuilder<>(makeChain(secondCastNodeFactory));
+        }
+
+        @SuppressWarnings("overloads")
+        public ChainBuilder<T> with(ArgumentMapper<?, ?> mapper) {
+            return with(Predef.map(mapper));
+        }
+
+        public ChainBuilder<T> with(CastNode secondCastNode) {
+            return new ChainBuilder<>(makeChain(pb -> secondCastNode));
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> end() {
+            return firstCastNodeFactory;
+        }
+
+    }
+
+    public static final class FindFirstNodeBuilder<T> {
+        private final RBaseNode callObj;
+        private final Message message;
+        private final Object[] messageArgs;
+
+        private FindFirstNodeBuilder(RBaseNode callObj, Message message, Object[] messageArgs) {
+            this.callObj = callObj;
+            this.message = message;
+            this.messageArgs = messageArgs;
+        }
+
+        private Function<ArgCastBuilder<T, ?>, CastNode> create(Class<?> elementClass, Object defaultValue) {
+            return phaseBuilder -> {
+                Message actualMessage = message;
+                Object[] actualMessageArgs = messageArgs;
+                RBaseNode actualCallObj = callObj;
+                if (message == null) {
+                    DefaultError err = phaseBuilder.state().isDefaultErrorDefined() ? phaseBuilder.state().defaultError() : new DefaultError(null, RError.Message.LENGTH_ZERO);
+                    actualMessage = err.message;
+                    actualMessageArgs = err.args;
+                    actualCallObj = err.callObj;
+                }
+                return FindFirstNodeGen.create(elementClass, actualCallObj, actualMessage, actualMessageArgs, defaultValue);
+            };
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> logicalElement() {
+            return create(Byte.class, null);
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> logicalElement(byte defaultValue) {
+            return create(Byte.class, defaultValue);
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> doubleElement() {
+            return create(Double.class, null);
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> doubleElement(double defaultValue) {
+            return create(Double.class, defaultValue);
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> integerElement() {
+            return create(Integer.class, null);
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> integerElement(int defaultValue) {
+            return create(Integer.class, defaultValue);
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> stringElement() {
+            return create(String.class, null);
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> stringElement(String defaultValue) {
+            return create(String.class, defaultValue);
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> objectElement() {
+            return create(Object.class, null);
+        }
+
+        public Function<ArgCastBuilder<T, ?>, CastNode> objectElement(Object defaultValue) {
+            return create(Object.class, defaultValue);
+        }
+
+    }
+
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinFactory.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinFactory.java
index 350b81546d363c1aa0085a20e6c84db7db231117..2b0cce3072a90103586c8a2311cb0742f426d116 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinFactory.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinFactory.java
@@ -26,10 +26,11 @@ import java.util.Arrays;
 import java.util.function.Function;
 
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RVisibility;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBehavior;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
 public final class RBuiltinFactory extends RBuiltinDescriptor {
@@ -37,8 +38,8 @@ public final class RBuiltinFactory extends RBuiltinDescriptor {
     private final Function<RNode[], RBuiltinNode> constructor;
 
     RBuiltinFactory(String name, Class<?> builtinNodeClass, RVisibility visibility, String[] aliases, RBuiltinKind kind, ArgumentsSignature signature, int[] nonEvalArgs, boolean splitCaller,
-                    boolean alwaysSplit, RDispatch dispatch, Function<RNode[], RBuiltinNode> constructor) {
-        super(name, builtinNodeClass, visibility, aliases, kind, signature, nonEvalArgs, splitCaller, alwaysSplit, dispatch);
+                    boolean alwaysSplit, RDispatch dispatch, Function<RNode[], RBuiltinNode> constructor, RBehavior behavior) {
+        super(name, builtinNodeClass, visibility, aliases, kind, signature, nonEvalArgs, splitCaller, alwaysSplit, dispatch, behavior);
         this.constructor = constructor;
     }
 
@@ -49,6 +50,6 @@ public final class RBuiltinFactory extends RBuiltinDescriptor {
     @Override
     public String toString() {
         return "RBuiltinFactory [name=" + getName() + ", aliases=" + Arrays.toString(getAliases()) + ", kind=" + getKind() + ", siagnature=" + getSignature() + ", nonEvaledArgs=" +
-                        Arrays.toString(getNonEvalArgs()) + ", splitCaller=" + isSplitCaller() + ", dispatch=" + getDispatch() + "]";
+                        Arrays.toString(getNonEvalArgs()) + ", splitCaller=" + isSplitCaller() + ", dispatch=" + getDispatch() + ", behavior=" + getBehavior() + "]";
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java
index 243f7e191466eb625104b7aabaf1f8d961c5898e..86bf53a2747bd84ebfc7167d7c2e9dcec3c9be66 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java
@@ -36,11 +36,11 @@ import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.unary.ApplyCastNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RDispatch;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java
index 4a11884d6e44d7e4d729a6b5a3a1aaee03f16945..12b61f2bc13a85ee86ec411a2b0316c59d33656b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java
@@ -33,8 +33,8 @@ import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.FastPathFactory;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 
 public final class RBuiltinRootNode extends RRootNode {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/ValuePredicateArgumentMapper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/ValuePredicateArgumentMapper.java
index 04d7ac485c2146d35ed374a9b1ca2c49c6d003f6..c7cae7f3f0852f9c6f491d3586a81185b957787d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/ValuePredicateArgumentMapper.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/ValuePredicateArgumentMapper.java
@@ -26,7 +26,7 @@ import java.util.function.Function;
 
 public class ValuePredicateArgumentMapper<T, R> implements ArgumentMapper<T, R> {
 
-    private final Function<T, R> mapper;
+    protected final Function<T, R> mapper;
 
     public ValuePredicateArgumentMapper(Function<T, R> mapper) {
         this.mapper = mapper;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
index 7e8fb2a604d3a64d518b2cbb396b97aa48551f83..a56caa191cadd1e6ac3cde0064ab64bd3d226668 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java
@@ -42,14 +42,14 @@ import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.S3DefaultArguments;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.data.FastPathFactory;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
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 d0be229b8ae28d07c86c50eef7b72d1af1308919..a28e67e0ffcdb8844f13e333bb4defb6d1bc4c9d 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
@@ -32,11 +32,11 @@ import com.oracle.truffle.r.nodes.unary.CastNode;
 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.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -246,18 +246,18 @@ public abstract class CallMatcherNode extends RBaseNode {
                 // Note: see CallMatcherNode#specialize for details on suppliedSignature/Arguments
 
                 // this unrolls all varargs instances in suppliedArgs into a flat array of arguments
-                RArgsValuesAndNames preparedArguments = prepareSuppliedArgument(preparePermutation, suppliedArguments, suppliedSignature);
+                Object[] preparedArguments = prepareSuppliedArgument(preparePermutation, suppliedArguments);
 
                 // This is then matched to formal signature: the result is non-flat array of
                 // arguments possibly containing varargs -- something that argument matching for a
                 // direct function call would create would this be a direct function call
-                RArgsValuesAndNames matchedArgs = ArgumentMatcher.matchArgumentsEvaluated(permutation, preparedArguments.getArguments(), null, formals);
+                RArgsValuesAndNames matchedArgs = ArgumentMatcher.matchArgumentsEvaluated(permutation, preparedArguments, null, formals);
                 Object[] reorderedArgs = matchedArgs.getArguments();
                 evaluatePromises(frame, cachedFunction, reorderedArgs, formals.getSignature().getVarArgIndex());
                 if (call != null) {
                     RCaller parent = RArguments.getCall(frame).getParent();
                     String genFunctionName = functionName == null ? function.getName() : functionName;
-                    Supplier<RSyntaxNode> argsSupplier = RCallerHelper.createFromArguments(genFunctionName, preparedArguments);
+                    Supplier<RSyntaxNode> argsSupplier = RCallerHelper.createFromArguments(genFunctionName, preparePermutation, suppliedArguments, suppliedSignature);
                     RCaller caller = genFunctionName == null ? RCaller.createInvalid(frame, parent) : RCaller.create(frame, parent, argsSupplier);
                     Object[] arguments = prepareArguments(reorderedArgs, matchedArgs.getSignature(), cachedFunction, dispatchArgs, caller);
                     return call.call(frame, arguments);
@@ -311,27 +311,24 @@ public abstract class CallMatcherNode extends RBaseNode {
         }
 
         @ExplodeLoop
-        private static RArgsValuesAndNames prepareSuppliedArgument(long[] preparePermutation, Object[] arguments, ArgumentsSignature suppliedSignature) {
+        private static Object[] prepareSuppliedArgument(long[] preparePermutation, Object[] arguments) {
             Object[] values = new Object[preparePermutation.length];
-            String[] names = new String[preparePermutation.length];
             for (int i = 0; i < values.length; i++) {
                 long source = preparePermutation[i];
-                if (!ArgumentsSignature.isVarArgsIndex(source)) {
-                    values[i] = arguments[(int) source];
-                    names[i] = suppliedSignature.getName((int) source);
-                } else {
+                if (ArgumentsSignature.isVarArgsIndex(source)) {
                     int varArgsIdx = ArgumentsSignature.extractVarArgsIndex(source);
                     int argsIdx = ArgumentsSignature.extractVarArgsArgumentIndex(source);
                     RArgsValuesAndNames varargs = (RArgsValuesAndNames) arguments[varArgsIdx];
                     values[i] = varargs.getArguments()[argsIdx];
-                    names[i] = varargs.getSignature().getName(argsIdx);
+                } else {
+                    values[i] = arguments[(int) source];
                 }
             }
-            return new RArgsValuesAndNames(values, ArgumentsSignature.get(names));
+            return values;
         }
     }
 
-    private static final class CallMatcherGenericNode extends CallMatcherNode {
+    public static final class CallMatcherGenericNode extends CallMatcherNode {
 
         CallMatcherGenericNode(boolean forNextMethod, boolean argsAreEvaluated) {
             super(forNextMethod, argsAreEvaluated);
@@ -339,12 +336,9 @@ public abstract class CallMatcherNode extends RBaseNode {
 
         @Child private IndirectCallNode call = Truffle.getRuntime().createIndirectCallNode();
 
-        private final ConditionProfile hasVarArgsProfile = ConditionProfile.createBinaryProfile();
-        private final ConditionProfile hasUnmatchedProfile = ConditionProfile.createBinaryProfile();
-
         @Override
         public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, String functionName, DispatchArgs dispatchArgs) {
-            RArgsValuesAndNames reorderedArgs = reorderArguments(suppliedArguments, function, suppliedSignature);
+            RArgsValuesAndNames reorderedArgs = reorderArguments(suppliedArguments, function, suppliedSignature, forNextMethod, this);
             evaluatePromises(frame, function, reorderedArgs.getArguments(), reorderedArgs.getSignature().getVarArgIndex());
 
             RCaller parent = RArguments.getCall(frame).getParent();
@@ -368,7 +362,7 @@ public abstract class CallMatcherNode extends RBaseNode {
         }
 
         @TruffleBoundary
-        protected RArgsValuesAndNames reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature) {
+        public static RArgsValuesAndNames reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature, boolean forNextMethod, RBaseNode callingNode) {
             assert paramSignature.getLength() == args.length;
 
             int argCount = args.length;
@@ -378,10 +372,10 @@ public abstract class CallMatcherNode extends RBaseNode {
             boolean hasUnmatched = false;
             for (int fi = 0; fi < argCount; fi++) {
                 Object arg = args[fi];
-                if (hasVarArgsProfile.profile(arg instanceof RArgsValuesAndNames)) {
+                if (arg instanceof RArgsValuesAndNames) {
                     hasVarArgs = true;
                     argListSize += ((RArgsValuesAndNames) arg).getLength() - 1;
-                } else if (hasUnmatchedProfile.profile(paramSignature.isUnmatched(fi))) {
+                } else if (paramSignature.isUnmatched(fi)) {
                     hasUnmatched = true;
                     argListSize--;
                 }
@@ -429,7 +423,7 @@ public abstract class CallMatcherNode extends RBaseNode {
             RArgsValuesAndNames evaledArgs = new RArgsValuesAndNames(argValues, signature);
 
             // ...to match them against the chosen function's formal arguments
-            RArgsValuesAndNames evaluated = ArgumentMatcher.matchArgumentsEvaluated((RRootNode) function.getRootNode(), evaledArgs, null, forNextMethod, this);
+            RArgsValuesAndNames evaluated = ArgumentMatcher.matchArgumentsEvaluated((RRootNode) function.getRootNode(), evaledArgs, null, forNextMethod, callingNode);
             return evaluated;
         }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java
index a680de8e2b195b3c1980f4cb0be923b10fb9081d..7fe3e749de501ad8ed7e44ee8299225157f7a38b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ClassHierarchyNode.java
@@ -181,11 +181,12 @@ abstract class S4Class extends RBaseNode {
         RStringVector s4Extends = RContext.getInstance().getS4Extends(classAttr);
         if (s4Extends == null) {
             REnvironment methodsEnv = REnvironment.getRegisteredNamespace("methods");
-            RFunction sExtendsForS3Function = ReadVariableNode.lookupFunction(".extendsForS3", methodsEnv.getFrame(), false);
+            RFunction sExtendsForS3Function = ReadVariableNode.lookupFunction(".extendsForS3", methodsEnv.getFrame());
             // the assumption here is that the R function can only return either a String or
             // RStringVector
             s4Extends = (RStringVector) castToVector.execute(
-                            RContext.getEngine().evalFunction(sExtendsForS3Function, methodsEnv.getFrame(), RCaller.create(Utils.getActualCurrentFrame(), RASTUtils.getOriginalCall(this)), classAttr));
+                            RContext.getEngine().evalFunction(sExtendsForS3Function, methodsEnv.getFrame(), RCaller.create(Utils.getActualCurrentFrame(), RASTUtils.getOriginalCall(this)), null,
+                                            classAttr));
             RContext.getInstance().putS4Extends(classAttr, s4Extends);
         }
         return s4Extends;
@@ -193,7 +194,9 @@ abstract class S4Class extends RBaseNode {
 
     @SuppressWarnings("unused")
     @Specialization(guards = "classAttr == cachedClassAttr")
-    protected RStringVector getS4ClassCachedEqOp(String classAttr, @Cached("classAttr") String cachedClassAttr, @Cached("getS4ClassInternal(cachedClassAttr)") RStringVector s4Classes) {
+    protected RStringVector getS4ClassCachedEqOp(String classAttr,
+                    @Cached("classAttr") String cachedClassAttr,
+                    @Cached("getS4ClassInternal(cachedClassAttr)") RStringVector s4Classes) {
         return s4Classes;
     }
 
@@ -203,7 +206,9 @@ abstract class S4Class extends RBaseNode {
      */
     @SuppressWarnings("unused")
     @Specialization(contains = "getS4ClassCachedEqOp", guards = "classAttr.equals(cachedClassAttr)")
-    protected RStringVector getS4ClassCachedEqMethod(String classAttr, @Cached("classAttr") String cachedClassAttr, @Cached("getS4ClassInternal(cachedClassAttr)") RStringVector s4Classes) {
+    protected RStringVector getS4ClassCachedEqMethod(String classAttr,
+                    @Cached("classAttr") String cachedClassAttr,
+                    @Cached("getS4ClassInternal(cachedClassAttr)") RStringVector s4Classes) {
         return s4Classes;
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index 7918968e9b44a954492ae3e5f2a5c49020c397ff..260ee359b51c6c32724245f659bf10cf8ca94ada 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -49,6 +49,7 @@ import com.oracle.truffle.r.nodes.control.BreakException;
 import com.oracle.truffle.r.nodes.control.NextException;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.JumpToTopLevelException;
+import com.oracle.truffle.r.runtime.ExitException;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.DispatchArgs;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
@@ -61,8 +62,8 @@ import com.oracle.truffle.r.runtime.RSerialize;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.ReturnException;
 import com.oracle.truffle.r.runtime.Utils.DebugExitException;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
@@ -266,10 +267,11 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         } catch (RError e) {
             CompilerDirectives.transferToInterpreter();
             throw e;
-        } catch (DebugExitException | JumpToTopLevelException | ThreadDeath e) {
+        } catch (DebugExitException | JumpToTopLevelException | ExitException | ThreadDeath e) {
             /*
-             * These relate to the debugging support. exitHandlers must be suppressed and the
-             * exceptions must pass through unchanged; they are not errors
+             * These relate to debugging support and various other reasons for returning to the top
+             * level. exitHandlers must be suppressed and the exceptions must pass through
+             * unchanged; they are not errors
              */
             CompilerDirectives.transferToInterpreter();
             runOnExitHandlers = false;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionExpressionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionExpressionNode.java
index b4dd48549c46b868691e576d6a7e2864bd5f65c9..1c6ad1065678d682ac7c361b83d37ae25c33be5a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionExpressionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionExpressionNode.java
@@ -35,7 +35,7 @@ import com.oracle.truffle.r.nodes.function.opt.EagerEvalHelper;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RSerialize;
-import com.oracle.truffle.r.runtime.data.FastPathFactory;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
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 4b501f181d8dcbc2686992ef040bf030c116d155..639dc42f661459026a4129b0636bd15454e7c215 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
@@ -75,7 +75,9 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RArguments.S3Args;
 import com.oracle.truffle.r.runtime.RArguments.S3DefaultArguments;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RDispatch;
@@ -87,9 +89,7 @@ import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.SubstituteVirtualFrame;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.FastPathFactory;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java
index 13b9a157c77574f6ee21f603f8d72fa7281aebad..af7f0639df7fd2f1dc27484dbcd0017a644bce79 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java
@@ -51,7 +51,7 @@ public final class RCallerHelper {
      * S3/S4 dispatch), and that can then be used to retrieve correct syntax nodes.
      *
      * @param arguments array with arguments and corresponding names. This method strips any
-     *            {@code RMissing} arguments and unrolls all varargs withint arguments array.
+     *            {@code RMissing} arguments and unrolls all varargs within the arguments array.
      */
     public static Supplier<RSyntaxNode> createFromArguments(RFunction function, RArgsValuesAndNames arguments) {
         return createFromArgumentsInternal(function, arguments);
@@ -105,17 +105,50 @@ public final class RCallerHelper {
                 }
                 return syntaxNode;
             }
+        };
+    }
 
-            private RSyntaxNode getArgumentNode(Object arg) {
-                if (arg instanceof RPromise) {
-                    RPromise p = (RPromise) arg;
-                    return p.getRep().asRSyntaxNode();
-                } else if (!(arg instanceof RMissing)) {
-                    return ConstantNode.create(arg);
+    private static RSyntaxNode getArgumentNode(Object arg) {
+        if (arg instanceof RPromise) {
+            RPromise p = (RPromise) arg;
+            return p.getRep().asRSyntaxNode();
+        } else if (!(arg instanceof RMissing)) {
+            return ConstantNode.create(arg);
+        }
+        return null;
+    }
+
+    /**
+     * This method calculates the signature of the permuted arguments lazily.
+     */
+    public static Supplier<RSyntaxNode> createFromArguments(String function, long[] preparePermutation, Object[] suppliedArguments, ArgumentsSignature suppliedSignature) {
+        return new Supplier<RSyntaxNode>() {
+
+            RSyntaxNode syntaxNode = null;
+
+            @Override
+            public RSyntaxNode get() {
+                if (syntaxNode == null) {
+                    Object[] values = new Object[preparePermutation.length];
+                    String[] names = new String[preparePermutation.length];
+                    for (int i = 0; i < values.length; i++) {
+                        long source = preparePermutation[i];
+                        if (!ArgumentsSignature.isVarArgsIndex(source)) {
+                            values[i] = suppliedArguments[(int) source];
+                            names[i] = suppliedSignature.getName((int) source);
+                        } else {
+                            int varArgsIdx = ArgumentsSignature.extractVarArgsIndex(source);
+                            int argsIdx = ArgumentsSignature.extractVarArgsArgumentIndex(source);
+                            RArgsValuesAndNames varargs = (RArgsValuesAndNames) suppliedArguments[varArgsIdx];
+                            values[i] = varargs.getArguments()[argsIdx];
+                            names[i] = varargs.getSignature().getName(argsIdx);
+                        }
+                    }
+                    RArgsValuesAndNames arguments = new RArgsValuesAndNames(values, ArgumentsSignature.get(names));
+                    syntaxNode = createFromArguments(function, arguments).get();
                 }
-                return null;
+                return syntaxNode;
             }
-
         };
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
index e2dd419352a460103589e2582cbc975c66c45f8a..41082f5156c4439809ac6c083d56da246f3f28c3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3FunctionLookupNode.java
@@ -406,7 +406,7 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
         @TruffleBoundary
         private Result executeInternal(String genericName, RStringVector type, String group, MaterializedFrame callerFrame, MaterializedFrame genericDefFrame) {
             LookupOperation op = (lookupFrame, name, inMethodsTable) -> {
-                return ReadVariableNode.lookupFunction(name, lookupFrame, inMethodsTable);
+                return ReadVariableNode.lookupFunction(name, lookupFrame, inMethodsTable, true);
             };
 
             GetMethodsTable getTable = () -> {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentBaseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentBaseNode.java
index b54a2b7daf74a520f530cb33fb92f9dcf08e85e0..ea78fd31ab168ccb3dd5c1bde48c717af525b335 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentBaseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/WrapArgumentBaseNode.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.nodes.function;
 
 import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RS4Object;
@@ -38,6 +39,7 @@ public abstract class WrapArgumentBaseNode extends RNode {
 
     @Child protected RNode operand;
 
+    private final ValueProfile argumentValueProfile;
     private final BranchProfile everSeenVector;
     private final BranchProfile everSeenLanguage;
     private final BranchProfile everSeenFunction;
@@ -49,6 +51,7 @@ public abstract class WrapArgumentBaseNode extends RNode {
     protected WrapArgumentBaseNode(RNode operand, boolean initProfiles) {
         this.operand = operand;
         if (initProfiles) {
+            argumentValueProfile = ValueProfile.createClassProfile();
             everSeenVector = BranchProfile.create();
             everSeenLanguage = BranchProfile.create();
             everSeenFunction = BranchProfile.create();
@@ -56,6 +59,7 @@ public abstract class WrapArgumentBaseNode extends RNode {
             shareable = BranchProfile.create();
             nonShareable = BranchProfile.create();
         } else {
+            argumentValueProfile = null;
             everSeenVector = null;
             everSeenLanguage = null;
             everSeenFunction = null;
@@ -65,7 +69,8 @@ public abstract class WrapArgumentBaseNode extends RNode {
         }
     }
 
-    protected RShareable getShareable(Object result) {
+    protected RShareable getShareable(Object initialResult) {
+        Object result = argumentValueProfile.profile(initialResult);
         if (result instanceof RVector) {
             everSeenVector.enter();
             return (RVector) result;
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 2c468c42b1bbd4b6c25dfb88ff3952db68b03e5c..cf65a15c602e85f5eccf5e005c31402e5c68ff30 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
@@ -101,7 +101,7 @@ public final class RFactorNodes {
             } else {
                 if (castString == null) {
                     CompilerDirectives.transferToInterpreterAndInvalidate();
-                    castString = insert(CastStringNodeGen.create(false, false, false, false));
+                    castString = insert(CastStringNodeGen.create(false, false, false));
                 }
                 RStringVector slevels = (RStringVector) castString.executeString(vec);
                 return RDataFactory.createStringVector(slevels.getDataWithoutCopying(), RDataFactory.COMPLETE_VECTOR);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java
index 116526a385172cdcded48933ec097afb5218560d..7863264c41c0e4d51d6e4b53920933e67a4e983e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/DispatchGeneric.java
@@ -73,8 +73,8 @@ public abstract class DispatchGeneric extends RBaseNode {
             // again
             CompilerDirectives.transferToInterpreterAndInvalidate();
             REnvironment methodsEnv = REnvironment.getRegisteredNamespace("methods");
-            RFunction currentFunction = ReadVariableNode.lookupFunction(".InheritForDispatch", methodsEnv.getFrame(), true);
-            method = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, RASTUtils.getOriginalCall(this)), classes, fdef, mtable);
+            RFunction currentFunction = ReadVariableNode.lookupFunction(".InheritForDispatch", methodsEnv.getFrame(), true, true);
+            method = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, RASTUtils.getOriginalCall(this)), null, classes, fdef, mtable);
         }
         method = loadMethod.executeRFunction(frame, method, fname);
         Object ret = executeMethod.executeObject(frame, method, fname);
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 d4cfc8497ec4751b17cc0654c1149a1a8b5194ca..72a4c84ab2dcfdc2f42b28b3d0a4a3ed09b55855 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
@@ -135,7 +135,7 @@ abstract class LoadMethod extends RBaseNode {
                 ret = (RFunction) loadMethodCall.call(frame, args);
             } else {
                 // slow path
-                ret = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), caller, fdef, fname, REnvironment.frameToEnvironment(frame.materialize()));
+                ret = (RFunction) RContext.getEngine().evalFunction(currentFunction, frame.materialize(), caller, null, fdef, fname, REnvironment.frameToEnvironment(frame.materialize()));
             }
 
         } else {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java
index bc94284a06417fc4261627bda80c3edcacce48f8..9f8baf3851fb8e0f2ea5d8c9ec82a81c04e5bc9c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java
@@ -12,15 +12,14 @@
  */
 package com.oracle.truffle.r.nodes.objects;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.access.AccessSlotNode;
 import com.oracle.truffle.r.nodes.access.AccessSlotNodeGen;
 import com.oracle.truffle.r.nodes.attributes.AttributeAccess;
 import com.oracle.truffle.r.nodes.attributes.AttributeAccessNodeGen;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastLogicalScalarNode;
-import com.oracle.truffle.r.nodes.unary.CastStringScalarNode;
+import com.oracle.truffle.r.nodes.unary.CastNode;
 import com.oracle.truffle.r.nodes.unary.DuplicateNode;
 import com.oracle.truffle.r.nodes.unary.DuplicateNodeGen;
 import com.oracle.truffle.r.runtime.RError;
@@ -36,22 +35,26 @@ public abstract class NewObject extends RExternalBuiltinNode.Arg1 {
     @Child private AccessSlotNode accessSlotVirtual = AccessSlotNodeGen.create(true, null, null);
     @Child private AccessSlotNode accessSlotClassName = AccessSlotNodeGen.create(true, null, null);
     @Child private AccessSlotNode accessSlotPrototypeName = AccessSlotNodeGen.create(true, null, null);
-    @Child private CastStringScalarNode castStringScalar;
-    @Child private CastLogicalScalarNode castLogicalScalar = CastLogicalScalarNode.create();
     @Child private DuplicateNode duplicate = DuplicateNodeGen.create(true);
     @Child private AttributeAccess pckgAttrAccess = AttributeAccessNodeGen.create(RRuntime.PCKG_ATTR_KEY);
 
+    @Child private CastNode castStringScalar;
+    @Child private CastNode castLogicalScalar;
+    {
+        CastBuilder builder = new CastBuilder();
+        builder.arg(0).asStringVector().findFirst(RRuntime.STRING_NA);
+        builder.arg(1).asLogicalVector().findFirst(RRuntime.LOGICAL_NA);
+        castStringScalar = builder.getCasts()[0];
+        castLogicalScalar = builder.getCasts()[1];
+    }
+
     @Specialization(guards = "!isNull(classDef)")
     protected Object doNewObject(Object classDef) {
 
         Object e = accessSlotVirtual.executeAccess(classDef, RRuntime.S_VIRTUAL);
-        if (castLogicalScalar.executeByte(e) != RRuntime.LOGICAL_FALSE) {
-            if (castStringScalar == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                castStringScalar = insert(CastStringScalarNode.create());
-            }
+        if (((byte) castLogicalScalar.execute(e)) != RRuntime.LOGICAL_FALSE) {
             e = accessSlotClassName.executeAccess(classDef, RRuntime.S_CLASSNAME);
-            throw RError.error(this, RError.Message.OBJECT_FROM_VIRTUAL, castStringScalar.executeString(e));
+            throw RError.error(this, RError.Message.OBJECT_FROM_VIRTUAL, castStringScalar.execute(e));
         }
         e = accessSlotClassName.executeAccess(classDef, RRuntime.S_CLASSNAME);
         Object prototype = accessSlotPrototypeName.executeAccess(classDef, RRuntime.S_PROTOTYPE);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastBaseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastBaseNode.java
index 4590f6475ff18b5620d80e531d2701f094114497..9f401aa09158f569c14c17be0220dbd234d13d1e 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastBaseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastBaseNode.java
@@ -22,19 +22,24 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-import com.oracle.truffle.api.dsl.NodeField;
-import com.oracle.truffle.api.dsl.NodeFields;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.NullProfile;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RTypedValue;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.env.REnvironment;
 
-@NodeFields({@NodeField(name = "preserveNames", type = boolean.class), @NodeField(name = "dimensionsPreservation", type = boolean.class), @NodeField(name = "attrPreservation", type = boolean.class)})
 public abstract class CastBaseNode extends CastNode {
 
     private final BranchProfile listCoercionErrorBranch = BranchProfile.create();
@@ -43,11 +48,29 @@ public abstract class CastBaseNode extends CastNode {
     private final NullProfile hasNamesProfile = NullProfile.create();
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
-    protected abstract boolean isPreserveNames();
+    private final boolean preserveNames;
+    private final boolean preserveDimensions;
+    private final boolean preserveAttributes;
 
-    protected abstract boolean isDimensionsPreservation();
+    protected CastBaseNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        this.preserveNames = preserveNames;
+        this.preserveDimensions = preserveDimensions;
+        this.preserveAttributes = preserveAttributes;
+    }
+
+    public boolean preserveNames() {
+        return preserveNames;
+    }
 
-    protected abstract boolean isAttrPreservation();
+    public boolean preserveDimensions() {
+        return preserveDimensions;
+    }
+
+    public boolean preserveAttributes() {
+        return preserveAttributes;
+    }
+
+    protected abstract RType getTargetType();
 
     protected RError throwCannotCoerceListError(String type) {
         listCoercionErrorBranch.enter();
@@ -55,7 +78,7 @@ public abstract class CastBaseNode extends CastNode {
     }
 
     protected int[] getPreservedDimensions(RAbstractContainer operand) {
-        if (isDimensionsPreservation()) {
+        if (preserveDimensions()) {
             return hasDimensionsProfile.profile(operand.getDimensions());
         } else {
             return null;
@@ -63,7 +86,7 @@ public abstract class CastBaseNode extends CastNode {
     }
 
     protected RStringVector getPreservedNames(RAbstractContainer operand) {
-        if (isPreserveNames()) {
+        if (preserveNames()) {
             return hasNamesProfile.profile(operand.getNames(attrProfiles));
         } else {
             return null;
@@ -71,11 +94,26 @@ public abstract class CastBaseNode extends CastNode {
     }
 
     protected void preserveDimensionNames(RAbstractContainer operand, RVector ret) {
-        if (isDimensionsPreservation()) {
+        if (preserveDimensions()) {
             RList dimNames = operand.getDimNames(attrProfiles);
             if (hasDimNamesProfile.profile(dimNames != null)) {
                 ret.setDimNames((RList) dimNames.copy());
             }
         }
     }
+
+    @Fallback
+    @TruffleBoundary
+    protected Object doOther(Object value) {
+        Object mappedValue = RRuntime.asAbstractVector(value);
+        if (mappedValue instanceof REnvironment) {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.ENVIRONMENTS_COERCE);
+        } else if (mappedValue instanceof RTypedValue) {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.CANNOT_COERCE, ((RTypedValue) mappedValue).getRType().getName(), getTargetType().getName());
+        } else if (mappedValue instanceof TruffleObject) {
+            throw RError.error(RError.SHOW_CALLER, RError.Message.CANNOT_COERCE, "truffleobject", getTargetType().getName());
+        } else {
+            throw RInternalError.shouldNotReachHere("unexpected value of type " + (mappedValue == null ? "null" : mappedValue.getClass()));
+        }
+    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastComplexNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastComplexNode.java
index b781b66977c8d4a3fb2fb183bcbe63fd51cd6723..1ff2e13b0783bb648a3e3f246d8722b5a93a46ad 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastComplexNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastComplexNode.java
@@ -24,17 +24,18 @@ package com.oracle.truffle.r.nodes.unary;
 
 import java.util.function.IntFunction;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.CompilerDirectives;
 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.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RRaw;
@@ -42,6 +43,7 @@ import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 import com.oracle.truffle.r.runtime.ops.na.NAProfile;
@@ -60,6 +62,25 @@ public abstract class CastComplexNode extends CastBaseNode {
 
     public abstract Object executeComplex(Object o);
 
+    protected CastComplexNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Child private CastComplexNode recursiveCastComplex;
+
+    private Object castComplexRecursive(Object o) {
+        if (recursiveCastComplex == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            recursiveCastComplex = insert(CastComplexNodeGen.create(preserveNames(), preserveDimensions(), preserveAttributes()));
+        }
+        return recursiveCastComplex.executeComplex(o);
+    }
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.Complex;
+    }
+
     @Specialization
     protected RNull doNull(@SuppressWarnings("unused") RNull operand) {
         return RNull.instance;
@@ -121,7 +142,7 @@ public abstract class CastComplexNode extends CastBaseNode {
         }
         RComplexVector ret = RDataFactory.createComplexVector(ddata, !seenNA, getPreservedDimensions(operand), getPreservedNames(operand));
         preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(operand);
         }
         return ret;
@@ -174,7 +195,7 @@ public abstract class CastComplexNode extends CastBaseNode {
         }
         RComplexVector ret = RDataFactory.createComplexVector(ddata, !seenNA, getPreservedDimensions(operand), getPreservedNames(operand));
         preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(operand);
         }
         return ret;
@@ -190,10 +211,48 @@ public abstract class CastComplexNode extends CastBaseNode {
         return createResultVector(operand, index -> RDataFactory.createComplex(operand.getDataAt(index).getValue(), 0));
     }
 
-    @Fallback
-    @TruffleBoundary
-    protected int doOther(Object operand) {
-        throw new ConversionFailedException(operand.getClass().getName());
+    @Specialization
+    protected RComplexVector doList(RAbstractListVector list) {
+        int length = list.getLength();
+        double[] result = new double[length * 2];
+        boolean seenNA = false;
+        for (int i = 0, j = 0; i < length; i++, j += 2) {
+            Object entry = list.getDataAt(i);
+            if (entry instanceof RList) {
+                result[j] = RRuntime.DOUBLE_NA;
+                result[j + 1] = RRuntime.DOUBLE_NA;
+                seenNA = true;
+            } else {
+                Object castEntry = castComplexRecursive(entry);
+                if (castEntry instanceof RComplex) {
+                    RComplex value = (RComplex) castEntry;
+                    result[j] = value.getRealPart();
+                    result[j + 1] = value.getImaginaryPart();
+                    seenNA = seenNA || RRuntime.isNA(value);
+                } else if (castEntry instanceof RComplexVector) {
+                    RComplexVector complexVector = (RComplexVector) castEntry;
+                    if (complexVector.getLength() == 1) {
+                        RComplex value = complexVector.getDataAt(0);
+                        result[j] = value.getRealPart();
+                        result[j + 1] = value.getImaginaryPart();
+                        seenNA = seenNA || RRuntime.isNA(value);
+                    } else if (complexVector.getLength() == 0) {
+                        result[j] = RRuntime.DOUBLE_NA;
+                        result[j + 1] = RRuntime.DOUBLE_NA;
+                        seenNA = true;
+                    } else {
+                        throw throwCannotCoerceListError("complex");
+                    }
+                } else {
+                    throw throwCannotCoerceListError("complex");
+                }
+            }
+        }
+        RComplexVector ret = RDataFactory.createComplexVector(result, !seenNA);
+        if (preserveAttributes()) {
+            ret.copyRegAttributesFrom(list);
+        }
+        return ret;
     }
 
     public static CastComplexNode create() {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastDoubleBaseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastDoubleBaseNode.java
index 159da03c48d96ea984e285b89d44b3ce3a7ed9ea..c4377e38e2f9e050931b613a7f2e815cb8fb1bac 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastDoubleBaseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastDoubleBaseNode.java
@@ -22,14 +22,13 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-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.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RRaw;
@@ -38,9 +37,18 @@ import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
 public abstract class CastDoubleBaseNode extends CastBaseNode {
 
-    private final NACheck naCheck = NACheck.create();
-    private final NAProfile naProfile = NAProfile.create();
-    private final BranchProfile warningBranch = BranchProfile.create();
+    protected final NACheck naCheck = NACheck.create();
+    protected final NAProfile naProfile = NAProfile.create();
+    protected final BranchProfile warningBranch = BranchProfile.create();
+
+    protected CastDoubleBaseNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.Double;
+    }
 
     public abstract Object executeDouble(int o);
 
@@ -101,11 +109,4 @@ public abstract class CastDoubleBaseNode extends CastBaseNode {
     protected double doRaw(RRaw operand) {
         return RRuntime.raw2double(operand);
     }
-
-    @Fallback
-    @TruffleBoundary
-    protected double doOther(Object operand) {
-        throw new ConversionFailedException(operand.getClass().getName());
-    }
-
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastDoubleNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastDoubleNode.java
index ae8e7d86ba191c6f14f165743912026249fd0e6a..9e5e541a9d2839cd2ac67f88b782a58a864ec385 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastDoubleNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastDoubleNode.java
@@ -27,7 +27,6 @@ import java.util.function.IntToDoubleFunction;
 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.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -39,32 +38,32 @@ import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
+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.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
-import com.oracle.truffle.r.runtime.ops.na.NACheck;
-import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
 public abstract class CastDoubleNode extends CastDoubleBaseNode {
 
-    private final NACheck naCheck = NACheck.create();
-    private final NAProfile naProfile = NAProfile.create();
-    private final BranchProfile warningBranch = BranchProfile.create();
+    protected CastDoubleNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
 
     @Child private CastDoubleNode recursiveCastDouble;
 
     private Object castDoubleRecursive(Object o) {
         if (recursiveCastDouble == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            recursiveCastDouble = insert(CastDoubleNodeGen.create(isPreserveNames(), isDimensionsPreservation(), isAttrPreservation()));
+            recursiveCastDouble = insert(CastDoubleNodeGen.create(preserveNames(), preserveDimensions(), preserveAttributes()));
         }
         return recursiveCastDouble.executeDouble(o);
     }
 
-    private RDoubleVector createResultVector(RAbstractVector operand, double[] ddata) {
-        RDoubleVector ret = RDataFactory.createDoubleVector(ddata, naCheck.neverSeenNA(), getPreservedDimensions(operand), getPreservedNames(operand));
+    private RDoubleVector vectorCopy(RAbstractContainer operand, double[] data, boolean isComplete) {
+        RDoubleVector ret = RDataFactory.createDoubleVector(data, isComplete, getPreservedDimensions(operand), getPreservedNames(operand));
         preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(operand);
         }
         return ret;
@@ -79,12 +78,7 @@ public abstract class CastDoubleNode extends CastDoubleBaseNode {
             ddata[i] = value;
             seenNA = seenNA || naProfile.isNA(value);
         }
-        RDoubleVector ret = RDataFactory.createDoubleVector(ddata, !seenNA, getPreservedDimensions(operand), getPreservedNames(operand));
-        preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
-            ret.copyRegAttributesFrom(operand);
-        }
-        return ret;
+        return vectorCopy(operand, ddata, !seenNA);
     }
 
     @Specialization
@@ -127,7 +121,7 @@ public abstract class CastDoubleNode extends CastDoubleBaseNode {
         }
         RDoubleVector ret = RDataFactory.createDoubleVector(ddata, !seenNA, getPreservedDimensions(operand), getPreservedNames(operand));
         preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(operand);
         }
         return ret;
@@ -149,7 +143,7 @@ public abstract class CastDoubleNode extends CastDoubleBaseNode {
             warningBranch.enter();
             RError.warning(this, RError.Message.IMAGINARY_PARTS_DISCARDED_IN_COERCION);
         }
-        return createResultVector(operand, ddata);
+        return vectorCopy(operand, ddata, naCheck.neverSeenNA());
     }
 
     @Specialization
@@ -163,12 +157,13 @@ public abstract class CastDoubleNode extends CastDoubleBaseNode {
     }
 
     @Specialization
-    protected RDoubleSequence doDoubleSequence(RDoubleSequence operand) {
+    protected RDoubleSequence doDoubleVector(RDoubleSequence operand) {
+        // sequence does not have attributes - nothing to copy or drop
         return operand;
     }
 
     @Specialization
-    protected RDoubleVector doList(RList list) {
+    protected RDoubleVector doList(RAbstractListVector list) {
         int length = list.getLength();
         double[] result = new double[length];
         boolean seenNA = false;
@@ -201,7 +196,7 @@ public abstract class CastDoubleNode extends CastDoubleBaseNode {
             }
         }
         RDoubleVector ret = RDataFactory.createDoubleVector(result, !seenNA);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(list);
         }
         return ret;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastExpressionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastExpressionNode.java
index cd73b7847f815970da3903c065779659dc8c2e8f..10e7fe8bfe7acb9b5388f997afb9859a29b56e53 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastExpressionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastExpressionNode.java
@@ -24,6 +24,7 @@ package com.oracle.truffle.r.nodes.unary;
 
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RExpression;
@@ -39,6 +40,15 @@ public abstract class CastExpressionNode extends CastBaseNode {
 
     public abstract Object executeExpression(Object o);
 
+    protected CastExpressionNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.Expression;
+    }
+
     @Specialization
     protected RExpression doNull(@SuppressWarnings("unused") RNull value) {
         return create(RNull.instance);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerBaseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerBaseNode.java
index 9445804d02df77dbe1d82a72a041bd8228b8b446..5e3b5b5aa16d0650fcd6cd5a22389a84cb0d50e3 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerBaseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerBaseNode.java
@@ -29,12 +29,11 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RComplex;
-import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RRaw;
-import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 
 public abstract class CastIntegerBaseNode extends CastBaseNode {
@@ -44,10 +43,19 @@ public abstract class CastIntegerBaseNode extends CastBaseNode {
 
     @Child private CastIntegerNode recursiveCastInteger;
 
+    protected CastIntegerBaseNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.Integer;
+    }
+
     protected Object castIntegerRecursive(Object o) {
         if (recursiveCastInteger == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            recursiveCastInteger = insert(CastIntegerNodeGen.create(isPreserveNames(), isDimensionsPreservation(), isAttrPreservation()));
+            recursiveCastInteger = insert(CastIntegerNodeGen.create(preserveNames(), preserveDimensions(), preserveAttributes()));
         }
         return recursiveCastInteger.executeInt(o);
     }
@@ -109,14 +117,4 @@ public abstract class CastIntegerBaseNode extends CastBaseNode {
     protected int doRaw(RRaw operand) {
         return RRuntime.raw2int(operand);
     }
-
-    @Specialization
-    protected Object doEnvironment(@SuppressWarnings("unused") REnvironment value) {
-        throw RError.error(RError.SHOW_CALLER, RError.Message.ENVIRONMENTS_COERCE);
-    }
-
-    @Specialization
-    protected Object doFunction(@SuppressWarnings("unused") RFunction value) {
-        throw RError.error(RError.SHOW_CALLER, RError.Message.CLOSURE_COERCE);
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
index f9b782dc101aee43469ef62cc1a73e0acd8a81d0..59245636fa3c80316e0febe0fa94e27645bd9ebe 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerNode.java
@@ -22,9 +22,7 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-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.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RError;
@@ -50,6 +48,10 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
 
     private final NAProfile naProfile = NAProfile.create();
 
+    protected CastIntegerNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
     public abstract Object executeInt(int o);
 
     public abstract Object executeInt(double o);
@@ -58,23 +60,27 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
 
     public abstract Object executeInt(Object o);
 
-    @Child private CastIntegerNode recursiveCastInteger;
+    @Specialization
+    protected RIntVector doIntVector(RIntVector operand) {
+        return operand;
+    }
 
     @Specialization
-    protected RAbstractIntVector doIntVector(RAbstractIntVector operand) {
+    protected RIntSequence doIntVector(RIntSequence operand) {
+        // sequence does not have attributes - nothing to copy or drop
         return operand;
     }
 
     @Specialization
     protected RIntSequence doDoubleSequence(RDoubleSequence operand) {
-        naCheck.enable(operand);
-        return RDataFactory.createIntSequence(naCheck.convertDoubleToInt(operand.getStart()), naCheck.convertDoubleToInt(operand.getStride()), operand.getLength());
+        // start and stride cannot be NA so no point checking
+        return RDataFactory.createIntSequence(RRuntime.double2intNoCheck(operand.getStart()), RRuntime.double2intNoCheck(operand.getStride()), operand.getLength());
     }
 
-    private RIntVector createResultVector(RAbstractVector operand, int[] idata) {
-        RIntVector ret = RDataFactory.createIntVector(idata, naCheck.neverSeenNA(), getPreservedDimensions(operand), getPreservedNames(operand));
+    private RIntVector vectorCopy(RAbstractVector operand, int[] idata, boolean isComplete) {
+        RIntVector ret = RDataFactory.createIntVector(idata, isComplete, getPreservedDimensions(operand), getPreservedNames(operand));
         preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(operand);
         }
         return ret;
@@ -94,12 +100,7 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
             idata[i] = value;
             seenNA = seenNA || naProfile.isNA(value);
         }
-        RIntVector ret = RDataFactory.createIntVector(idata, !seenNA, getPreservedDimensions(operand), getPreservedNames(operand));
-        preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
-            ret.copyRegAttributesFrom(operand);
-        }
-        return ret;
+        return vectorCopy(operand, idata, !seenNA);
     }
 
     @Specialization
@@ -119,7 +120,7 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
             warningBranch.enter();
             RError.warning(this, RError.Message.IMAGINARY_PARTS_DISCARDED_IN_COERCION);
         }
-        return createResultVector(operand, idata);
+        return vectorCopy(operand, idata, naCheck.neverSeenNA());
     }
 
     @Specialization
@@ -150,12 +151,7 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
         if (warning) {
             RError.warning(this, RError.Message.NA_INTRODUCED_COERCION);
         }
-        RIntVector ret = RDataFactory.createIntVector(idata, !seenNA, getPreservedDimensions(operand), getPreservedNames(operand));
-        preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
-            ret.copyRegAttributesFrom(operand);
-        }
-        return ret;
+        return vectorCopy(operand, idata, !seenNA);
     }
 
     @Specialization
@@ -166,7 +162,7 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
     @Specialization
     protected RIntVector doDoubleVector(RAbstractDoubleVector operand) {
         naCheck.enable(operand);
-        return createResultVector(operand, naCheck.convertDoubleVectorToIntData(operand));
+        return vectorCopy(operand, naCheck.convertDoubleVectorToIntData(operand), naCheck.neverSeenNA());
     }
 
     @Specialization
@@ -190,8 +186,8 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
                     int value = (Integer) castEntry;
                     result[i] = value;
                     seenNA = seenNA || RRuntime.isNA(value);
-                } else if (castEntry instanceof RIntVector) {
-                    RIntVector intVector = (RIntVector) castEntry;
+                } else if (castEntry instanceof RAbstractIntVector) {
+                    RAbstractIntVector intVector = (RAbstractIntVector) castEntry;
                     if (intVector.getLength() == 1) {
                         int value = intVector.getDataAt(0);
                         result[i] = value;
@@ -208,7 +204,7 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
             }
         }
         RIntVector ret = RDataFactory.createIntVector(result, !seenNA);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(list);
         }
         return ret;
@@ -224,12 +220,6 @@ public abstract class CastIntegerNode extends CastIntegerBaseNode {
         return arg instanceof RIntVector;
     }
 
-    @Fallback
-    @TruffleBoundary
-    protected int doOther(Object operand) {
-        throw new ConversionFailedException(operand.getClass().getName());
-    }
-
     public static CastIntegerNode create() {
         return CastIntegerNodeGen.create(true, true, true);
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerScalarNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerScalarNode.java
deleted file mode 100644
index dc7525c99bc4d225c1f6ab9deabafedb7dbb07ea..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastIntegerScalarNode.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.unary;
-
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-
-public abstract class CastIntegerScalarNode extends CastIntegerBaseNode {
-
-    public abstract int executeInt(Object o);
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected int doLogicalVector(RLogicalVector operand) {
-        return (int) castIntegerRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected int doIntVector(RAbstractIntVector operand) {
-        return (int) castIntegerRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected int doDoubleVector(RAbstractDoubleVector operand) {
-        return (int) castIntegerRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected int doStringVector(RStringVector operand) {
-        return (int) castIntegerRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected int doComplexVector(RComplexVector operand) {
-        return (int) castIntegerRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected int doRawVectorDims(RRawVector operand) {
-        return (int) castIntegerRecursive(operand.getDataAt(0));
-    }
-
-    @Fallback
-    protected int castInt(@SuppressWarnings("unused") Object o) {
-        // for non-atomic structures and vectors of length 0
-        return RRuntime.INT_NA;
-    }
-
-    public static CastIntegerScalarNode create() {
-        return CastIntegerScalarNodeGen.create(false, false, false);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
index ba4330cc0849dc3ad0093651103740e1cb806bd5..8a67bbad26382d5854dc1d232ea40b558f2bbd9f 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
@@ -27,6 +27,7 @@ import java.util.Iterator;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributes;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
@@ -45,10 +46,17 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 
 public abstract class CastListNode extends CastBaseNode {
 
-    @Child private CastListNode castListRecursive;
-
     public abstract RList executeList(Object o);
 
+    protected CastListNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.List;
+    }
+
     @Specialization
     protected RList doNull(@SuppressWarnings("unused") RNull operand) {
         return RDataFactory.createList();
@@ -72,7 +80,7 @@ public abstract class CastListNode extends CastBaseNode {
         }
         RList ret = RDataFactory.createList(data, getPreservedDimensions(operand), getPreservedNames(operand));
         preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(operand);
         }
         return ret;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalBaseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalBaseNode.java
index 1de63001a8fab2521cd1070037313452976d13c2..03ea9e3eb653978486ba62171af92fc0b7b962b2 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalBaseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalBaseNode.java
@@ -22,9 +22,9 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RRaw;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
@@ -33,14 +33,13 @@ public abstract class CastLogicalBaseNode extends CastBaseNode {
 
     protected final NACheck naCheck = NACheck.create();
 
-    @Child private CastLogicalNode recursiveCastLogical;
+    protected CastLogicalBaseNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
 
-    protected Object castLogicalRecursive(Object o) {
-        if (recursiveCastLogical == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            recursiveCastLogical = insert(CastLogicalNodeGen.create(isPreserveNames(), isDimensionsPreservation(), isAttrPreservation()));
-        }
-        return recursiveCastLogical.execute(o);
+    @Override
+    protected final RType getTargetType() {
+        return RType.Logical;
     }
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalNode.java
index 90fc83be7044eb76e719e4fee206e137914ec1d5..bb333f788f9fe878b320be956239f66f7cf84399 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalNode.java
@@ -24,8 +24,7 @@ package com.oracle.truffle.r.nodes.unary;
 
 import java.util.Arrays;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -40,6 +39,7 @@ import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
@@ -47,6 +47,29 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
 
     private final NAProfile naProfile = NAProfile.create();
 
+    @Child private CastLogicalNode recursiveCastLogical;
+    @Child private InheritsCheckNode inheritsFactorCheck;
+
+    protected CastLogicalNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    protected Object castLogicalRecursive(Object o) {
+        if (recursiveCastLogical == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            recursiveCastLogical = insert(CastLogicalNodeGen.create(preserveNames(), preserveDimensions(), preserveAttributes()));
+        }
+        return recursiveCastLogical.execute(o);
+    }
+
+    protected boolean isFactor(Object o) {
+        if (inheritsFactorCheck == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            inheritsFactorCheck = insert(new InheritsCheckNode(RRuntime.CLASS_FACTOR));
+        }
+        return inheritsFactorCheck.execute(o);
+    }
+
     @Specialization
     protected RNull doNull(@SuppressWarnings("unused") RNull operand) {
         return RNull.instance;
@@ -57,6 +80,15 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
         byte apply(int value);
     }
 
+    private RLogicalVector vectorCopy(RAbstractVector operand, byte[] bdata, boolean isComplete) {
+        RLogicalVector ret = RDataFactory.createLogicalVector(bdata, isComplete, getPreservedDimensions(operand), getPreservedNames(operand));
+        preserveDimensionNames(operand, ret);
+        if (preserveAttributes()) {
+            ret.copyRegAttributesFrom(operand);
+        }
+        return ret;
+    }
+
     private RLogicalVector createResultVector(RAbstractVector operand, IntToByteFunction elementFunction) {
         naCheck.enable(operand);
         byte[] bdata = new byte[operand.getLength()];
@@ -66,12 +98,7 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
             bdata[i] = value;
             seenNA = seenNA || naProfile.isNA(value);
         }
-        RLogicalVector ret = RDataFactory.createLogicalVector(bdata, !seenNA, getPreservedDimensions(operand), getPreservedNames(operand));
-        preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
-            ret.copyRegAttributesFrom(operand);
-        }
-        return ret;
+        return vectorCopy(operand, bdata, !seenNA);
     }
 
     @Specialization
@@ -84,6 +111,13 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
         return createResultVector(operand, index -> naCheck.convertIntToLogical(operand.getDataAt(index)));
     }
 
+    @Specialization(guards = "isFactor(factor)")
+    protected RLogicalVector asLogical(RAbstractIntVector factor) {
+        byte[] data = new byte[factor.getLength()];
+        Arrays.fill(data, RRuntime.LOGICAL_NA);
+        return RDataFactory.createLogicalVector(data, RDataFactory.INCOMPLETE_VECTOR);
+    }
+
     @Specialization
     protected RLogicalVector doDoubleVector(RAbstractDoubleVector operand) {
         return createResultVector(operand, index -> naCheck.convertDoubleToLogical(operand.getDataAt(index)));
@@ -105,7 +139,7 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
     }
 
     @Specialization
-    protected RLogicalVector doList(RList list) {
+    protected RLogicalVector doList(RAbstractListVector list) {
         int length = list.getLength();
         byte[] result = new byte[length];
         boolean seenNA = false;
@@ -138,7 +172,7 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
             }
         }
         RLogicalVector ret = RDataFactory.createLogicalVector(result, !seenNA);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(list);
         }
         return ret;
@@ -154,26 +188,7 @@ public abstract class CastLogicalNode extends CastLogicalBaseNode {
         return missing;
     }
 
-    @Specialization(guards = "isFactor(factor)")
-    protected RLogicalVector asLogical(RAbstractIntVector factor) {
-        byte[] data = new byte[factor.getLength()];
-        Arrays.fill(data, RRuntime.LOGICAL_NA);
-        return RDataFactory.createLogicalVector(data, RDataFactory.INCOMPLETE_VECTOR);
-    }
-
-    @Fallback
-    @TruffleBoundary
-    protected int doOther(Object operand) {
-        throw new ConversionFailedException(operand.getClass().getName());
-    }
-
     public static CastLogicalNode createNonPreserving() {
         return CastLogicalNodeGen.create(false, false, false);
     }
-
-    @Child private InheritsCheckNode inheritsFactorCheck = new InheritsCheckNode(RRuntime.CLASS_FACTOR);
-
-    protected boolean isFactor(Object o) {
-        return inheritsFactorCheck.execute(o);
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalScalarNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalScalarNode.java
deleted file mode 100644
index 71513232170913e47071ab77b61cb01e99424db4..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastLogicalScalarNode.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.unary;
-
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-
-public abstract class CastLogicalScalarNode extends CastLogicalBaseNode {
-
-    public abstract byte executeByte(Object o);
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected byte doLogicalVector(RLogicalVector operand) {
-        return (byte) castLogicalRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected byte doIntVector(RAbstractIntVector operand) {
-        return (byte) castLogicalRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected byte doDoubleVector(RAbstractDoubleVector operand) {
-        return (byte) castLogicalRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected byte doStringVector(RStringVector operand) {
-        return (byte) castLogicalRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected byte doComplexVector(RComplexVector operand) {
-        return (byte) castLogicalRecursive(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected byte doRawVectorDims(RRawVector operand) {
-        return (byte) castLogicalRecursive(operand.getDataAt(0));
-    }
-
-    @Fallback
-    protected byte castLogical(@SuppressWarnings("unused") Object o) {
-        // for non-atomic structures and vectors of length 0
-        return RRuntime.LOGICAL_NA;
-    }
-
-    public static CastLogicalScalarNode create() {
-        return CastLogicalScalarNodeGen.create(false, false, false);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastNode.java
index 573d5823dfd1f9a094b75bfc805509df5e78c058..463283d4f62ec3068a9dc0f947249dc5457121b8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastNode.java
@@ -22,12 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-import java.io.PrintWriter;
-
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 /**
  * Cast nodes behave like unary nodes, but in many cases it is useful to have a specific type for
@@ -36,28 +35,25 @@ import com.oracle.truffle.r.runtime.context.RContext;
 public abstract class CastNode extends UnaryNode {
 
     @TruffleBoundary
-    public static void handleArgumentError(Object arg, CastNode node, RError.Message message, Object[] messageArgs) {
-        if (RContext.getRRuntimeASTAccess() == null) {
+    protected static void handleArgumentError(Object arg, RBaseNode callObj, RError.Message message, Object[] messageArgs) {
+        if (RContext.getInstance() == null) {
             throw new IllegalArgumentException(String.format(message.message, CastBuilder.substituteArgPlaceholder(arg, messageArgs)));
         } else {
-            throw RError.error(node, message, CastBuilder.substituteArgPlaceholder(arg, messageArgs));
+            throw RError.error(callObj, message, CastBuilder.substituteArgPlaceholder(arg, messageArgs));
         }
     }
 
     @TruffleBoundary
-    public static void handleArgumentWarning(Object arg, CastNode node, RError.Message message, Object[] messageArgs, PrintWriter out) {
+    protected static void handleArgumentWarning(Object arg, RBaseNode callObj, RError.Message message, Object[] messageArgs) {
         if (message == null) {
             return;
         }
 
-        if (out != null) {
-            out.printf(message.message, CastBuilder.substituteArgPlaceholder(arg, messageArgs));
-        } else if (RContext.getRRuntimeASTAccess() == null) {
+        if (RContext.getInstance() == null) {
             System.err.println(String.format(message.message, CastBuilder.substituteArgPlaceholder(arg,
                             messageArgs)));
         } else {
-            RError.warning(node, message, CastBuilder.substituteArgPlaceholder(arg, messageArgs));
+            RError.warning(callObj, message, CastBuilder.substituteArgPlaceholder(arg, messageArgs));
         }
     }
-
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastRawNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastRawNode.java
index 120d4232b83f602a30aea46bc8f485cffd7893f6..69d13990142a7dcd8d52515c28c579f65576df7a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastRawNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastRawNode.java
@@ -22,14 +22,14 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.CompilerDirectives;
 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.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -40,6 +40,7 @@ import com.oracle.truffle.r.runtime.data.RRawVector;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractListVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 import com.oracle.truffle.r.runtime.ops.na.NAProfile;
@@ -49,6 +50,25 @@ public abstract class CastRawNode extends CastBaseNode {
     private final NACheck naCheck = NACheck.create();
     private final BranchProfile warningBranch = BranchProfile.create();
 
+    protected CastRawNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Child private CastRawNode recursiveCastRaw;
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.Raw;
+    }
+
+    protected Object castRawRecursive(Object o) {
+        if (recursiveCastRaw == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            recursiveCastRaw = insert(CastRawNodeGen.create(preserveNames(), preserveDimensions(), preserveAttributes()));
+        }
+        return recursiveCastRaw.executeRaw(o);
+    }
+
     public abstract Object executeRaw(int o);
 
     public abstract Object executeRaw(double o);
@@ -127,10 +147,10 @@ public abstract class CastRawNode extends CastBaseNode {
         return RRaw.valueOf((byte) intRawValue);
     }
 
-    private RRawVector createResultVector(RAbstractVector operand, byte[] bdata) {
+    private RRawVector vectorCopy(RAbstractVector operand, byte[] bdata) {
         RRawVector ret = RDataFactory.createRawVector(bdata, getPreservedDimensions(operand), getPreservedNames(operand));
         preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
+        if (preserveAttributes()) {
             ret.copyRegAttributesFrom(operand);
         }
         return ret;
@@ -154,7 +174,7 @@ public abstract class CastRawNode extends CastBaseNode {
         if (warning) {
             RError.warning(this, RError.Message.OUT_OF_RANGE);
         }
-        return createResultVector(operand, bdata);
+        return vectorCopy(operand, bdata);
     }
 
     @Specialization
@@ -174,7 +194,7 @@ public abstract class CastRawNode extends CastBaseNode {
         if (warning) {
             RError.warning(this, RError.Message.OUT_OF_RANGE);
         }
-        return createResultVector(operand, bdata);
+        return vectorCopy(operand, bdata);
     }
 
     @Specialization
@@ -214,7 +234,7 @@ public abstract class CastRawNode extends CastBaseNode {
         if (outOfRangeWarning) {
             RError.warning(this, RError.Message.OUT_OF_RANGE);
         }
-        return createResultVector(operand, bdata);
+        return vectorCopy(operand, bdata);
     }
 
     @Specialization
@@ -242,7 +262,7 @@ public abstract class CastRawNode extends CastBaseNode {
         if (outOfRangeWarning) {
             RError.warning(this, RError.Message.OUT_OF_RANGE);
         }
-        return createResultVector(operand, bdata);
+        return vectorCopy(operand, bdata);
     }
 
     @Specialization
@@ -263,7 +283,7 @@ public abstract class CastRawNode extends CastBaseNode {
         if (warning) {
             RError.warning(this, RError.Message.OUT_OF_RANGE);
         }
-        return createResultVector(operand, bdata);
+        return vectorCopy(operand, bdata);
     }
 
     @Specialization
@@ -271,10 +291,14 @@ public abstract class CastRawNode extends CastBaseNode {
         return operand;
     }
 
-    @Fallback
-    @TruffleBoundary
-    protected int doOther(Object operand) {
-        throw new ConversionFailedException(operand.getClass().getName());
+    @Specialization
+    protected RRawVector doList(RAbstractListVector value) {
+        int length = value.getLength();
+        RRawVector result = RDataFactory.createRawVector(length);
+        for (int i = 0; i < length; i++) {
+            result.updateDataAt(i, (RRaw) castRawRecursive(value.getDataAt(i)));
+        }
+        return result;
     }
 
     public static CastRawNode createNonPreserving() {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringBaseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringBaseNode.java
index 290505762224c50a33ae3632f8a3bec7132e8d2e..9c2b21f546c2cebbf1f14bdb329233844cea2a06 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringBaseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringBaseNode.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.nodes.unary;
 
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RRaw;
 
@@ -30,6 +31,15 @@ public abstract class CastStringBaseNode extends CastBaseNode {
 
     @Child private ToStringNode toString = ToStringNodeGen.create();
 
+    protected CastStringBaseNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.Character;
+    }
+
     @Specialization
     protected String doString(String value) {
         return value;
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 2ad538134cc4987e3f8cc813c10750804f6b2270..53e23e6e8be1b17af91843b85c673e5d2b31b082 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
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-import com.oracle.truffle.api.dsl.NodeField;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -31,11 +30,13 @@ import com.oracle.truffle.r.runtime.data.RNull;
 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;
-import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-@NodeField(name = "emptyVectorConvertedToNull", type = boolean.class)
 public abstract class CastStringNode extends CastStringBaseNode {
 
+    protected CastStringNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
     public abstract Object executeString(int o);
 
     public abstract Object executeString(double o);
@@ -44,24 +45,26 @@ public abstract class CastStringNode extends CastStringBaseNode {
 
     public abstract Object executeString(Object o);
 
-    public abstract boolean isEmptyVectorConvertedToNull();
-
     @Specialization
     protected RNull doNull(@SuppressWarnings("unused") RNull operand) {
         return RNull.instance;
     }
 
-    @Specialization(guards = "vector.getLength() == 0")
-    protected Object doEmptyVector(@SuppressWarnings("unused") RAbstractVector vector) {
-        return isEmptyVectorConvertedToNull() ? RNull.instance : RDataFactory.createStringVector(0);
+    private RStringVector vectorCopy(RAbstractContainer operand, String[] data) {
+        RStringVector ret = RDataFactory.createStringVector(data, operand.isComplete(), getPreservedDimensions(operand), getPreservedNames(operand));
+        preserveDimensionNames(operand, ret);
+        if (preserveAttributes()) {
+            ret.copyRegAttributesFrom(operand);
+        }
+        return ret;
     }
 
-    @Specialization(guards = "vector.getLength() != 0")
+    @Specialization
     protected RStringVector doStringVector(RStringVector vector) {
         return vector;
     }
 
-    @Specialization(guards = "operand.getLength() != 0")
+    @Specialization
     protected RStringVector doIntVector(RAbstractContainer operand) {
         String[] sdata = new String[operand.getLength()];
         // conversions to character will not introduce new NAs
@@ -73,12 +76,7 @@ public abstract class CastStringNode extends CastStringBaseNode {
                 sdata[i] = toString(o);
             }
         }
-        RStringVector ret = RDataFactory.createStringVector(sdata, operand.isComplete(), getPreservedDimensions(operand), getPreservedNames(operand));
-        preserveDimensionNames(operand, ret);
-        if (isAttrPreservation()) {
-            ret.copyRegAttributesFrom(operand);
-        }
-        return ret;
+        return vectorCopy(operand, sdata);
     }
 
     @Specialization
@@ -87,10 +85,10 @@ public abstract class CastStringNode extends CastStringBaseNode {
     }
 
     public static CastStringNode create() {
-        return CastStringNodeGen.create(false, true, true, true);
+        return CastStringNodeGen.create(true, true, true);
     }
 
     public static CastStringNode createNonPreserving() {
-        return CastStringNodeGen.create(false, false, false, false);
+        return CastStringNodeGen.create(false, false, false);
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringScalarNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringScalarNode.java
deleted file mode 100644
index 7a5f961d4fc921bbc5d9c065ab8ae0033650dd5a..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastStringScalarNode.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.unary;
-
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RComplexVector;
-import com.oracle.truffle.r.runtime.data.RLogicalVector;
-import com.oracle.truffle.r.runtime.data.RRawVector;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
-
-public abstract class CastStringScalarNode extends CastStringBaseNode {
-
-    public abstract String executeString(Object o);
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected String doLogicalVector(RLogicalVector operand) {
-        return toString(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected String doIntVector(RAbstractIntVector operand) {
-        return toString(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected String doDoubleVector(RAbstractDoubleVector operand) {
-        return toString(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected String doStringVector(RStringVector operand) {
-        return toString(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected String doComplexVector(RComplexVector operand) {
-        return toString(operand.getDataAt(0));
-    }
-
-    @Specialization(guards = "operand.getLength() > 0")
-    protected String doRawVectorDims(RRawVector operand) {
-        return toString(operand.getDataAt(0));
-    }
-
-    @Fallback
-    protected String castLogical(@SuppressWarnings("unused") Object o) {
-        // for non-atomic structures, vectors of length 0, and NULL
-        return RRuntime.STRING_NA;
-    }
-
-    public static CastStringScalarNode create() {
-        return CastStringScalarNodeGen.create(false, false, false);
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastSymbolNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastSymbolNode.java
index cfd853bfe0b4af5e6e92c4bdf56edb34b6204928..c567944d82dd273656521a82d5dbe8af7d79426c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastSymbolNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastSymbolNode.java
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.unary;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntVector;
@@ -37,6 +38,15 @@ public abstract class CastSymbolNode extends CastBaseNode {
 
     @Child private ToStringNode toString = ToStringNodeGen.create();
 
+    protected CastSymbolNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.Symbol;
+    }
+
     public abstract Object executeSymbol(Object o);
 
     private String toString(Object value) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToAttributableNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToAttributableNode.java
index 89199ea9571e54514ddce53b7972f094ccc74b8d..769ee3d5558aa4b64476b6a7476e3e90c4c7d0a5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToAttributableNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToAttributableNode.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.nodes.unary;
 
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -35,6 +36,15 @@ public abstract class CastToAttributableNode extends CastBaseNode {
 
     public abstract Object executeObject(Object value);
 
+    protected CastToAttributableNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.Any;
+    }
+
     @Specialization
     @SuppressWarnings("unused")
     protected RNull cast(RNull rnull) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java
index 607e59d5c87ebb86ee7d06e551286a4647599100..7bd1fd595cbb70500660a57c559305252bb520e1 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastToContainerNode.java
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.nodes.unary;
 
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RExpression;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RLanguage;
@@ -35,6 +36,15 @@ public abstract class CastToContainerNode extends CastBaseNode {
 
     public abstract Object executeObject(Object value);
 
+    protected CastToContainerNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
+        super(preserveNames, preserveDimensions, preserveAttributes);
+    }
+
+    @Override
+    protected final RType getTargetType() {
+        return RType.Any;
+    }
+
     @Specialization
     @SuppressWarnings("unused")
     protected RNull castNull(RNull rnull) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNode.java
index 958f51bc0104bd4f826770085a94b97bf6b254ef..4d4442785cacde16406952f80b72e079651dc076 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConditionalMapNode.java
@@ -56,11 +56,11 @@ public abstract class ConditionalMapNode extends CastNode {
 
     @Specialization(guards = "doMap(x)")
     protected Object map(Object x) {
-        return trueBranch.execute(x);
+        return trueBranch == null ? x : trueBranch.execute(x);
     }
 
     @Specialization(guards = "!doMap(x)")
     protected Object noMap(Object x) {
-        return x;
+        return falseBranch == null ? x : falseBranch.execute(x);
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConvertInt.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConvertInt.java
deleted file mode 100644
index dbfb7f96d877a673ba19d553cf4776e7e531c4b2..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConvertInt.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2013, 2016, 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.unary;
-
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
-
-public abstract class ConvertInt extends CastNode {
-
-    @Child private ConvertInt convertIntRecursive;
-
-    public abstract int executeInteger(Object operand);
-
-    private int convertIntRecursive(Object operand) {
-        if (convertIntRecursive == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            convertIntRecursive = insert(ConvertIntNodeGen.create());
-        }
-        return executeInteger(operand);
-    }
-
-    @Specialization
-    protected int doInt(int operand) {
-        return operand;
-    }
-
-    @Specialization
-    protected int doDouble(double operand) {
-        return (int) operand;
-    }
-
-    @Specialization
-    protected int doLogical(byte operand) {
-        return RRuntime.logical2int(operand);
-    }
-
-    @Specialization(guards = "operand.getLength() == 1")
-    protected int doLogical(RAbstractContainer operand) {
-        return convertIntRecursive(operand.getDataAtAsObject(0));
-    }
-
-    @Fallback
-    @TruffleBoundary
-    protected int doOther(Object operand) {
-        throw new ConversionFailedException(operand.getClass().getName());
-    }
-}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FilterNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FilterNode.java
index 6ac3cb78287d63133f2e853583dd13b70296018b..9d3d5d5c4031a8a182a18627e492dfa7c40bb4f5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FilterNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FilterNode.java
@@ -22,8 +22,6 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-import java.io.PrintWriter;
-
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
@@ -31,28 +29,29 @@ import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNode;
 import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNodeGen;
 import com.oracle.truffle.r.nodes.builtin.ArgumentFilter;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 @SuppressWarnings({"rawtypes", "unchecked"})
 public abstract class FilterNode extends CastNode {
 
     private final ArgumentFilter filter;
     private final RError.Message message;
+    private final RBaseNode callObj;
     private final Object[] messageArgs;
     private final boolean boxPrimitives;
     private final boolean isWarning;
-    private final PrintWriter out;
 
     private final BranchProfile warningProfile = BranchProfile.create();
 
     @Child private BoxPrimitiveNode boxPrimitiveNode = BoxPrimitiveNodeGen.create();
 
-    protected FilterNode(ArgumentFilter<?, ?> filter, boolean isWarning, RError.Message message, Object[] messageArgs, boolean boxPrimitives, PrintWriter out) {
+    protected FilterNode(ArgumentFilter<?, ?> filter, boolean isWarning, RBaseNode callObj, RError.Message message, Object[] messageArgs, boolean boxPrimitives) {
         this.filter = filter;
         this.isWarning = isWarning;
+        this.callObj = callObj == null ? this : callObj;
         this.message = message;
         this.messageArgs = messageArgs;
         this.boxPrimitives = boxPrimitives;
-        this.out = out;
     }
 
     public ArgumentFilter getFilter() {
@@ -67,10 +66,10 @@ public abstract class FilterNode extends CastNode {
         if (isWarning) {
             if (message != null) {
                 warningProfile.enter();
-                handleArgumentWarning(x, this, message, messageArgs, out);
+                handleArgumentWarning(x, callObj, message, messageArgs);
             }
         } else {
-            handleArgumentError(x, this, message, messageArgs);
+            handleArgumentError(x, callObj, message, messageArgs);
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FindFirstNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FindFirstNode.java
index c6b42843c92a17ea4521cdd1403d21edb8a04320..df6dc13ea2a8cbf28b8cd0ed72d5df0d69d99959 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FindFirstNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/FindFirstNode.java
@@ -22,34 +22,30 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-import java.io.PrintWriter;
-
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
-import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNode;
-import com.oracle.truffle.r.nodes.binary.BoxPrimitiveNodeGen;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 public abstract class FindFirstNode extends CastNode {
 
-    @Child private BoxPrimitiveNode boxPrimitive = BoxPrimitiveNodeGen.create();
     private final Class<?> elementClass;
+    private final RBaseNode callObj;
     private final RError.Message message;
     private final Object[] messageArgs;
-    private final PrintWriter out;
     private final Object defaultValue;
 
     private final BranchProfile warningProfile = BranchProfile.create();
 
-    protected FindFirstNode(Class<?> elementClass, RError.Message message, Object[] messageArgs, PrintWriter out, Object defaultValue) {
+    protected FindFirstNode(Class<?> elementClass, RBaseNode callObj, RError.Message message, Object[] messageArgs, Object defaultValue) {
+        this.callObj = callObj == null ? this : callObj;
         this.elementClass = elementClass;
         this.defaultValue = defaultValue;
         this.message = message;
         this.messageArgs = messageArgs;
-        this.out = out;
     }
 
     protected FindFirstNode(Class<?> elementClass, Object defaultValue) {
@@ -79,7 +75,7 @@ public abstract class FindFirstNode extends CastNode {
         return handleMissingElement(x);
     }
 
-    @Specialization(guards = "!isVector(x)")
+    @Specialization(guards = "nonVector(x)")
     protected Object onNonVector(Object x) {
         return x;
     }
@@ -88,11 +84,11 @@ public abstract class FindFirstNode extends CastNode {
         if (defaultValue != null) {
             if (message != null) {
                 warningProfile.enter();
-                handleArgumentWarning(x, this, message, messageArgs, out);
+                handleArgumentWarning(x, callObj, message, messageArgs);
             }
             return defaultValue;
         } else {
-            handleArgumentError(x, this, message, messageArgs);
+            handleArgumentError(x, callObj, message, messageArgs);
             return null;
         }
     }
@@ -106,8 +102,8 @@ public abstract class FindFirstNode extends CastNode {
         return x.getLength() == 0;
     }
 
-    protected boolean isVector(Object x) {
-        return x instanceof RAbstractVector;
+    protected boolean nonVector(Object x) {
+        return x != RNull.instance && x != RMissing.instance && !(x instanceof RAbstractVector);
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/NonNANode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/NonNANode.java
index b86bda53c67fdc2340dc56695afbaca53293f0cb..25c864c4c355f6d603c889ccd90b8ea817272f5a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/NonNANode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/NonNANode.java
@@ -22,28 +22,27 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
-import java.io.PrintWriter;
-
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 
 public abstract class NonNANode extends CastNode {
 
+    private final RBaseNode callObj;
     private final RError.Message message;
     private final Object[] messageArgs;
-    private final PrintWriter out;
     private final Object naReplacement;
 
     private final BranchProfile warningProfile = BranchProfile.create();
 
-    protected NonNANode(RError.Message message, Object[] messageArgs, PrintWriter out, Object naReplacement) {
+    protected NonNANode(RBaseNode callObj, RError.Message message, Object[] messageArgs, Object naReplacement) {
+        this.callObj = callObj == null ? this : callObj;
         this.message = message;
         this.messageArgs = messageArgs;
-        this.out = out;
         this.naReplacement = naReplacement;
     }
 
@@ -59,11 +58,11 @@ public abstract class NonNANode extends CastNode {
         if (naReplacement != null) {
             if (message != null) {
                 warningProfile.enter();
-                handleArgumentWarning(arg, this, message, messageArgs, out);
+                handleArgumentWarning(arg, callObj, message, messageArgs);
             }
             return naReplacement;
         } else {
-            handleArgumentError(arg, this, message, messageArgs);
+            handleArgumentError(arg, callObj, message, messageArgs);
             return null;
         }
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryNotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryNotNode.java
index 262744ccbc8d1c49b2ff8e24764da72133cc4a8a..f46b860eaa0bb01f6161abce902c1a7bd32ae362 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryNotNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryNotNode.java
@@ -22,15 +22,17 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
+import static com.oracle.truffle.r.runtime.RDispatch.OPS_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.api.dsl.Fallback;
 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.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -45,7 +47,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.ops.na.NACheck;
 import com.oracle.truffle.r.runtime.ops.na.NAProfile;
 
-@RBuiltin(name = "!", kind = RBuiltinKind.PRIMITIVE, parameterNames = {""}, dispatch = RDispatch.OPS_GROUP_GENERIC)
+@RBuiltin(name = "!", kind = PRIMITIVE, parameterNames = {""}, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
 public abstract class UnaryNotNode extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/Load_RFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/Load_RFFIFactory.java
index f368d516c8ab085f9c3cd49c3f2903219ff157ff..f178e6264add01001baa99864f8a290312dc9f6d 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/Load_RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/Load_RFFIFactory.java
@@ -60,7 +60,7 @@ public class Load_RFFIFactory {
                 instance = (RFFIFactory) Class.forName(prop).newInstance();
                 RFFIFactory.setRFFIFactory(instance);
             } catch (Exception ex) {
-                throw Utils.fail("Failed to instantiate class: " + prop + ": " + ex);
+                throw Utils.rSuicide("Failed to instantiate class: " + prop + ": " + ex);
             }
         }
         instance.initialize(runtime);
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java
index 5363006718ac8bdd314f127440bdf86e7ff16c31..a44b8ff62a92abd152f40e7db3f18345c91508fa 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java
@@ -23,8 +23,6 @@
 package com.oracle.truffle.r.runtime.ffi.jnr;
 
 import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.function.Function;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -35,17 +33,16 @@ import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RCleanUp;
-import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.REnvVars;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RSrcref;
 import com.oracle.truffle.r.runtime.RSource;
+import com.oracle.truffle.r.runtime.RSrcref;
+import com.oracle.truffle.r.runtime.RStartParams.SA_TYPE;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.RStartParams.SA_TYPE;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributable;
@@ -278,8 +275,9 @@ public class CallRFFIHelper {
     }
 
     public static Object R_do_MAKE_CLASS(String clazz) {
-        RFunction getClass = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(REnvironment.getRegisteredNamespace("methods").get("getClass"));
-        return RContext.getEngine().evalFunction(getClass, null, RCaller.createInvalid(null), clazz);
+        String name = "getClass";
+        RFunction getClass = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(name, REnvironment.getRegisteredNamespace("methods").get(name));
+        return RContext.getEngine().evalFunction(getClass, null, RCaller.createInvalid(null), null, clazz);
     }
 
     public static Object Rf_findVar(Object symbolArg, Object envArg) {
@@ -894,31 +892,6 @@ public class CallRFFIHelper {
         return result;
     }
 
-    private static Object convertPairList(RPairList list) {
-        try {
-            if (list.getType() == SEXPTYPE.LANGSXP) {
-                RPairList pl = list;
-                Map<String, Object> constants = new HashMap<>();
-                String deparse = RDeparse.deparseDeserialize(constants, pl);
-                Source source = RSource.fromTextInternal(deparse, RSource.Internal.PAIRLIST_DEPARSE);
-                RExpression expr = RContext.getEngine().parse(constants, source);
-                assert expr.getLength() == 1;
-                Object result = expr.getDataAt(0);
-                RAttributes attrs = pl.getAttributes();
-                if (result instanceof RAttributable) {
-                    RAttributes.copyAttributes((RAttributable) result, attrs);
-                }
-                return result;
-
-            } else {
-                throw RInternalError.shouldNotReachHere();
-            }
-        } catch (Throwable x) {
-            throw RInternalError.shouldNotReachHere();
-        }
-
-    }
-
     @TruffleBoundary
     public static Object Rf_eval(Object expr, Object env) {
         if (RFFIUtils.traceEnabled()) {
@@ -927,13 +900,22 @@ public class CallRFFIHelper {
         guarantee(env instanceof REnvironment);
         Object result;
         if (expr instanceof RPromise) {
-            result = RContext.getRRuntimeASTAccess().forcePromise(expr);
+            result = RContext.getRRuntimeASTAccess().forcePromise(null, expr);
         } else if (expr instanceof RExpression) {
-            result = RContext.getEngine().eval((RExpression) expr, (REnvironment) env, RCaller.createInvalid(null));
+            result = RContext.getEngine().eval((RExpression) expr, (REnvironment) env, topLevel);
         } else if (expr instanceof RLanguage) {
-            result = RContext.getEngine().eval((RLanguage) expr, (REnvironment) env, RCaller.createInvalid(null));
+            result = RContext.getEngine().eval((RLanguage) expr, (REnvironment) env, topLevel);
         } else if (expr instanceof RPairList) {
-            result = Rf_eval(convertPairList((RPairList) expr), env);
+            RPairList l = (RPairList) expr;
+            RFunction f = (RFunction) l.car();
+            Object args = l.cdr();
+            if (args == RNull.instance) {
+                result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), topLevel, null, new Object[0]);
+            } else {
+                RList argsList = ((RPairList) args).toRList();
+                result = RContext.getEngine().evalFunction(f, env == REnvironment.globalEnv() ? null : ((REnvironment) env).getFrame(), topLevel, argsList.getNames(), argsList.getDataNonShared());
+            }
+
         } else {
             // just return value
             result = expr;
@@ -1008,7 +990,7 @@ public class CallRFFIHelper {
             RFFIUtils.traceUpCall("R_computeIdentical", x, y, flags);
         }
         RFunction indenticalBuiltin = RContext.lookupBuiltin("identical");
-        Object res = RContext.getEngine().evalFunction(indenticalBuiltin, null, null, x, y, RRuntime.asLogical((!((flags & 1) == 0))),
+        Object res = RContext.getEngine().evalFunction(indenticalBuiltin, null, null, null, x, y, RRuntime.asLogical((!((flags & 1) == 0))),
                         RRuntime.asLogical((!((flags & 2) == 0))), RRuntime.asLogical((!((flags & 4) == 0))), RRuntime.asLogical((!((flags & 8) == 0))), RRuntime.asLogical((!((flags & 16) == 0))));
         return (int) res;
     }
@@ -1276,12 +1258,12 @@ public class CallRFFIHelper {
         }
         RCaller currentCaller = RArguments.getCall(frame);
         while (currentCaller != null) {
-            if (!currentCaller.isPromise()) {
+            if (!currentCaller.isPromise() && currentCaller.isValidCaller()) {
                 break;
             }
             currentCaller = currentCaller.getParent();
         }
-        return currentCaller == null ? RNull.instance : currentCaller;
+        return currentCaller == null || currentCaller == topLevel ? RNull.instance : currentCaller;
     }
 
     public static Object R_getParentFunctionContext(Object c) {
@@ -1291,30 +1273,11 @@ public class CallRFFIHelper {
         RCaller currentCaller = guaranteeInstanceOf(c, RCaller.class);
         while (true) {
             currentCaller = currentCaller.getParent();
-            if (currentCaller == null || !currentCaller.isPromise()) {
+            if (currentCaller == null || (!currentCaller.isPromise() && currentCaller.isValidCaller())) {
                 break;
             }
         }
-        return currentCaller == null ? RNull.instance : currentCaller;
-    }
-
-    public static Object R_getFunctionContext(int depth) {
-        if (RFFIUtils.traceEnabled()) {
-            RFFIUtils.traceUpCall("getFunctionContext", depth);
-        }
-        Frame frame = Utils.getActualCurrentFrame();
-        RCaller currentCaller = RArguments.getCall(frame);
-        int currentDepth = 0;
-        while (currentCaller != null) {
-            if (!currentCaller.isPromise()) {
-                currentDepth++;
-                if (currentDepth >= depth) {
-                    break;
-                }
-            }
-            currentCaller = currentCaller.getParent();
-        }
-        return currentCaller == null ? RNull.instance : currentCaller;
+        return currentCaller == null || currentCaller == topLevel ? RNull.instance : currentCaller;
     }
 
     public static Object R_getContextEnv(Object c) {
@@ -1421,5 +1384,4 @@ public class CallRFFIHelper {
     public static int R_insideBrowser() {
         return RContext.getInstance().stateInstrumentation.getBrowserState().inBrowser() ? 1 : 0;
     }
-
 }
diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java
index 168618a998bd8e666fe27f37136eaf78372965a4..da24f27db4ab82bba8840d97541dffae4e408e88 100644
--- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/JNR_RFFIFactory.java
@@ -22,6 +22,8 @@
  */
 package com.oracle.truffle.r.runtime.ffi.jnr;
 
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.r.runtime.RPlatform;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextState;
@@ -90,111 +92,122 @@ public class JNR_RFFIFactory extends RFFIFactory implements RFFI {
         return this;
     }
 
-    private BaseRFFI baseRFFI;
+    @CompilationFinal private BaseRFFI baseRFFI;
 
     @Override
     public BaseRFFI getBaseRFFI() {
         if (baseRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             baseRFFI = new JNR_Base();
         }
         return baseRFFI;
     }
 
-    private LapackRFFI lapackRFFI;
+    @CompilationFinal private LapackRFFI lapackRFFI;
 
     @Override
     public LapackRFFI getLapackRFFI() {
         if (lapackRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             lapackRFFI = new JNR_Lapack();
         }
         return lapackRFFI;
     }
 
-    private RApplRFFI rApplRFFI;
+    @CompilationFinal private RApplRFFI rApplRFFI;
 
     @Override
     public RApplRFFI getRApplRFFI() {
         if (rApplRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             rApplRFFI = new JNR_RAppl();
         }
         return rApplRFFI;
     }
 
-    private StatsRFFI statsRFFI;
+    @CompilationFinal private StatsRFFI statsRFFI;
 
     @Override
     public StatsRFFI getStatsRFFI() {
         if (statsRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             statsRFFI = new JNR_Stats();
         }
         return statsRFFI;
     }
 
-    private ToolsRFFI toolsRFFI;
+    @CompilationFinal private ToolsRFFI toolsRFFI;
 
     @Override
     public ToolsRFFI getToolsRFFI() {
         if (toolsRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             toolsRFFI = new Generic_Tools();
         }
         return toolsRFFI;
     }
 
-    private GridRFFI gridRFFI;
+    @CompilationFinal private GridRFFI gridRFFI;
 
     @Override
     public GridRFFI getGridRFFI() {
         if (gridRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             gridRFFI = new Generic_Grid();
         }
         return gridRFFI;
     }
 
-    private UserRngRFFI userRngRFFI;
+    @CompilationFinal private UserRngRFFI userRngRFFI;
 
     @Override
     public UserRngRFFI getUserRngRFFI() {
         if (userRngRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             userRngRFFI = new JNR_UserRng();
         }
         return userRngRFFI;
     }
 
-    private CRFFI cRFFI;
+    @CompilationFinal private CRFFI cRFFI;
 
     @Override
     public CRFFI getCRFFI() {
         if (cRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             cRFFI = new CRFFI_JNR_Invoke();
         }
         return cRFFI;
     }
 
-    private CallRFFI callRFFI;
+    @CompilationFinal private CallRFFI callRFFI;
 
     @Override
     public CallRFFI getCallRFFI() {
         if (callRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             callRFFI = new JNI_CallRFFI();
         }
         return callRFFI;
     }
 
-    private ZipRFFI zipRFFI;
+    @CompilationFinal private ZipRFFI zipRFFI;
 
     @Override
     public ZipRFFI getZipRFFI() {
         if (zipRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             zipRFFI = new JNR_Zip();
         }
         return zipRFFI;
     }
 
-    private PCRERFFI pcreRFFI;
+    @CompilationFinal private PCRERFFI pcreRFFI;
 
     @Override
     public PCRERFFI getPCRERFFI() {
         if (pcreRFFI == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
             pcreRFFI = new JNR_PCRE();
         }
         return pcreRFFI;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConversionFailedException.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ExitException.java
similarity index 66%
rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConversionFailedException.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ExitException.java
index 364e2787010541ac8bf8e6da672bcae02a7c5919..53691d5cacc624a75a04234ed39c46cc85973af1 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/ConversionFailedException.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ExitException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,14 +20,22 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.unary;
-
-/** Thrown by a convert node. Indicates that a parent node must rewrite itself. */
-public final class ConversionFailedException extends RuntimeException {
+package com.oracle.truffle.r.runtime;
 
+/**
+ * This exception is thrown when a Polyglot R engine wants to exit, usually via the {@code quit}
+ * builtin. It allows systems using multiple contexts via {@code .fastr.context.op} to handle exits
+ * gracefully.
+ */
+public class ExitException extends RuntimeException {
     private static final long serialVersionUID = 1L;
+    private int status;
+
+    public ExitException(int status) {
+        this.status = status;
+    }
 
-    ConversionFailedException(String message) {
-        super(message);
+    public int getStatus() {
+        return status;
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
index 84a9a54769d6bd21c9ee21bf121b68b03f629850..4df846f7beb1b36cfa4407146931e273abd2fd59 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCleanUp.java
@@ -90,7 +90,7 @@ public class RCleanUp {
         }
         // TODO run exit finalizers (FFI)
         // TODO clean tmpdir
-        Utils.exit(status);
+        throw new ExitException(status);
 
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java
index 9437a68768d529f3211d64ddd7b0f976ff3e7f5b..e65017eed639b37248305c574090978ab300f228 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java
@@ -54,7 +54,7 @@ public final class RCmdOptions {
         EITHER {
             @Override
             public String usage() {
-                throw Utils.fail("can't call usage() on Client.EITHER");
+                throw Utils.rSuicide("can't call usage() on Client.EITHER");
             }
         };
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
index 1c9a2d4d54a118b253be02e4587f821dfa9e5719..50a18c02504f470cbfb08d67dc5d3fba23b240de 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java
@@ -596,7 +596,10 @@ public class RDeparse {
                         append("list(").appendListContents(obj).append(')');
                     }
                 } else if (value instanceof RAbstractVector) {
-                    appendVector((RAbstractVector) value);
+                    RAbstractVector obj = (RAbstractVector) value;
+                    try (C c = withAttributes(obj)) {
+                        appendVector((RAbstractVector) value);
+                    }
                 } else if (value instanceof RNull) {
                     append("NULL");
                 } else if (value instanceof RFunction) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index 15777101afe3c9342eaab71f1313a9f15dda53ae..b1e8b8809410ffaade8698147faf38c847b2b085 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -229,6 +229,7 @@ public final class RError extends RuntimeException {
          * available.
          */
         GENERIC("%s"),
+        TOO_SHORT("'%s' is too short"),
         ARG_RECYCYLED("an argument will be fractionally recycled"),
         LENGTH_GT_1("the condition has length > 1 and only the first element will be used"),
         LENGTH_ZERO("argument is of length zero"),
@@ -248,6 +249,7 @@ public final class RError extends RuntimeException {
         INVALID_ARG_TYPE("invalid argument type"),
         INVALID_ARG_TYPE_UNARY("invalid argument to unary operator"),
         VECTOR_SIZE_NEGATIVE("vector size cannot be negative"),
+        VECTOR_SIZE_NA("vector size cannot be NA"),
         NO_LOOP_FOR_BREAK_NEXT("no loop for break/next, jumping to top level"),
         INVALID_FOR_SEQUENCE("invalid for() loop sequence"),
         NO_NONMISSING_MAX("no non-missing arguments to max; returning -Inf"),
@@ -683,7 +685,10 @@ public final class RError extends RuntimeException {
         CLOSURE_COERCE("cannot coerce type 'closure' to vector of type 'integer'"),
         ROWSUM_NAMES_NOT_CHAR("row names are not character"),
         ROWSUM_NON_NUMERIC("non-numeric matrix in rowsum(): this should not happen"),
-        ARGUMENTS_REQUIRED_COUNT("%d arguments to '%s' which requires %d");
+        ARGUMENTS_REQUIRED_COUNT("%d arguments to '%s' which requires %d"),
+        ARGUMENT_LENGTH_0("argument of length 0"),
+        MUST_BE_VECTOR_BUT_WAS("'%s' must be of a vector type, was '%s'"),
+        CANNOT_BE_LENGTH("'%s' cannot be of length %d");
 
         public final String message;
         final boolean hasArgs;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java
index 911a09d8291a12e6d029ee2120a74c3b58f83c0a..74aff0e6602d52b7bdc9cc54b91f01276aa6dd41 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RErrorHandling.java
@@ -60,7 +60,7 @@ public class RErrorHandling {
     private static final RStringVector RESTART_CLASS = RDataFactory.createStringVectorFromScalar("restart");
 
     private static class Warnings {
-        private static ArrayList<Warning> list = new ArrayList<>();
+        private ArrayList<Warning> list = new ArrayList<>();
 
         int size() {
             return list.size();
@@ -138,8 +138,9 @@ public class RErrorHandling {
         private RFunction getDotSignalSimpleWarning() {
             if (dotSignalSimpleWarning == null) {
                 CompilerDirectives.transferToInterpreter();
-                Object f = REnvironment.baseEnv().findFunction(".signalSimpleWarning");
-                dotSignalSimpleWarning = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(f);
+                String name = ".signalSimpleWarning";
+                Object f = REnvironment.baseEnv().findFunction(name);
+                dotSignalSimpleWarning = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(name, f);
             }
             return dotSignalSimpleWarning;
         }
@@ -340,7 +341,7 @@ public class RErrorHandling {
                     errorcallDfltWithCall(fromCall(call), Message.GENERIC, msg);
                 } else {
                     RFunction hf = (RFunction) h;
-                    RContext.getEngine().evalFunction(hf, null, null, cond);
+                    RContext.getEngine().evalFunction(hf, null, null, null, cond);
                 }
             } else {
                 throw gotoExitingHandler(cond, call, entry);
@@ -503,7 +504,7 @@ public class RErrorHandling {
                             evaluatedArgs[i] = RMissing.instance;
                         }
                     }
-                    RContext.getEngine().evalFunction(errorFunction, null, null, evaluatedArgs);
+                    RContext.getEngine().evalFunction(errorFunction, null, null, null, evaluatedArgs);
                 } else if (errorExpr instanceof RLanguage || errorExpr instanceof RExpression) {
                     if (errorExpr instanceof RLanguage) {
                         RContext.getEngine().eval((RLanguage) errorExpr, materializedFrame);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java
index 17fcab5f0bede00e64fd466d767abf374976494a..153f136796017f523e8898b8aeec7a788645a86f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RInternalError.java
@@ -36,6 +36,7 @@ import java.util.Date;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.runtime.context.ConsoleHandler;
 import com.oracle.truffle.r.runtime.context.RContext;
 
 /**
@@ -128,10 +129,20 @@ public final class RInternalError extends Error {
         }
     }
 
+    @TruffleBoundary
+    public static void reportErrorAndConsoleLog(Throwable throwable, ConsoleHandler consoleHandler, int contextId) {
+        reportError(throwable, consoleHandler, contextId);
+    }
+
     @TruffleBoundary
     public static void reportError(Throwable throwable) {
+        reportError(throwable, null, 0);
+    }
+
+    private static void reportError(Throwable throwable, ConsoleHandler consoleHandler, int contextId) {
         Throwable t = throwable;
         if (FastROptions.PrintErrorStacktracesToFile.getBooleanValue() || FastROptions.PrintErrorStacktraces.getBooleanValue()) {
+
             ByteArrayOutputStream out = new ByteArrayOutputStream();
             t.printStackTrace(new PrintStream(out));
             String verboseStackTrace;
@@ -150,7 +161,8 @@ public final class RInternalError extends Error {
                 System.err.println(verboseStackTrace);
             }
             if (FastROptions.PrintErrorStacktracesToFile.getBooleanValue()) {
-                Path logfile = Utils.getLogPath("fastr_errors.log");
+                String suffix = contextId == 0 ? "" : "-" + Integer.toString(contextId);
+                Path logfile = Utils.getLogPath("fastr_errors.log" + suffix);
                 try (BufferedWriter writer = Files.newBufferedWriter(logfile, StandardCharsets.UTF_8, StandardOpenOption.APPEND,
                                 StandardOpenOption.CREATE)) {
                     writer.append(new Date().toString()).append('\n');
@@ -162,6 +174,9 @@ public final class RInternalError extends Error {
                 if (RContext.isEmbedded()) {
                     Utils.rSuicide("FastR internal error");
                 }
+                if (consoleHandler != null) {
+                    consoleHandler.println("internal error: " + t.getClass().getSimpleName() + " (see fastr_errors.log" + suffix + ")");
+                }
             }
         }
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java
index 87371bdfaeda67218b3f4d2197d31f1030fa9c4a..eca6c3fc56918a49decaa01d7a5cdc277353840d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RPlatform.java
@@ -49,7 +49,7 @@ public class RPlatform {
                 default:
                     osSubDir = null;
                     libExt = null;
-                    Utils.fail("CallRFFI: unsupported OS: " + osName);
+                    Utils.rSuicide("CallRFFI: unsupported OS: " + osName);
             }
         }
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
index 17418510bfaf43251b9bd944c8f300ad2a78c679..2522a7fbeeea4765c298c3010b875d219369c949 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RProfile.java
@@ -86,7 +86,7 @@ public final class RProfile implements RContext.ContextState {
         Path path = FileSystems.getDefault().getPath(REnvVars.rHome(), "library", "base", "R", "Rprofile");
         Source source = getProfile(path.toString());
         if (source == null) {
-            Utils.fail("can't find system profile");
+            Utils.rSuicide("can't find system profile");
         }
         return source;
     }
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 a6f96c9b38872c451e009a865fcec257c5b62413..2e76ac5fd5339dc612c4b842641b8277b85af9b2 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
@@ -134,7 +134,7 @@ public interface RRuntimeASTAccess {
     /**
      * Force a promise by slow-path evaluation.
      */
-    Object forcePromise(Object val);
+    Object forcePromise(String identifier, Object val);
 
     /**
      * Returns the {@link ArgumentsSignature} for {@code f}.
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 3c826891dd099b8bcfe4952440c971abc1bb4f13..0458c3a7369cfffb28cffde127ee8ee907db8eeb 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
@@ -193,8 +193,9 @@ public class RSerialize {
         RFunction getDotDotFindNamespace() {
             if (dotDotFindNamespace == null) {
                 CompilerDirectives.transferToInterpreter();
-                Object f = REnvironment.baseEnv().findFunction("..getNamespace");
-                dotDotFindNamespace = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(f);
+                String name = "..getNamespace";
+                Object f = REnvironment.baseEnv().findFunction(name);
+                dotDotFindNamespace = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(name, f);
             }
             return dotDotFindNamespace;
         }
@@ -445,7 +446,7 @@ public class RSerialize {
                      * only used in a warning message in the unlikely event that the namespace
                      * cannot be found.
                      */
-                    Object r = RContext.getEngine().evalFunction(contextState.getDotDotFindNamespace(), null, null, s, "");
+                    Object r = RContext.getEngine().evalFunction(contextState.getDotDotFindNamespace(), null, null, null, s, "");
                     return checkResult(addReadRef(r));
                 }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java
index 73587432ea044d2e50f23df3ce3fabb08d4c29ac..050047c15b04f035d312d4198db3e90ed37d70d7 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java
@@ -66,7 +66,7 @@ public class TempPathName {
             if (t != null) {
                 tempDirPath = t;
             } else {
-                Utils.fail("cannot create 'R_TempDir'");
+                Utils.rSuicide("cannot create 'R_TempDir'");
             }
             RFFIFactory.getRFFI().getCallRFFI().setTempDir(tempDirPath);
         }
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 74f8ed2ea849735170effb4a05c9e8e50f3d0466..486c4b98687d8b2f54e026a3d432d2ecdb9cd7c0 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
@@ -115,7 +115,7 @@ public final class Utils {
             } catch (IOException ex) {
             }
         }
-        throw Utils.fail("resource " + resourceName + " not found");
+        throw Utils.rSuicide("resource " + resourceName + " not found");
     }
 
     private static String getResourceAsString(InputStream is) throws IOException {
@@ -147,42 +147,38 @@ public final class Utils {
     }
 
     /**
-     * All terminations that happen within a PolyglotEngine context should go through this method.
+     * Called when the system encounters a fatal internal error and must commit suicide (i.e.
+     * terminate). It allows an embedded client to override the default (although they typically
+     * invoke the default eventually).
      */
-    public static RuntimeException exit(int status) {
-        /*
-         * TODO: Ultimately, destroying the context should be triggered from the "outside" of a
-         * PolyglotEngine. This ultimately depends on what the expected semantics of "quit()" in a
-         * polyglot context are.
-         */
-        RPerfStats.report();
-        try {
-            /*
-             * This is not the proper way to dispose a PolyglotEngine, but it doesn't matter since
-             * we're going to System.exit anyway.
-             */
-            RContext.getInstance().destroy();
-        } catch (Throwable t) {
-            // ignore
+    public static RuntimeException rSuicide(String msg) {
+        if (RInterfaceCallbacks.R_Suicide.isOverridden()) {
+            RFFIFactory.getRFFI().getREmbedRFFI().suicide(msg);
         }
-        System.exit(status);
-        return null;
+        throw rSuicideDefault(msg);
     }
 
-    public static RuntimeException fail(String msg) {
-        // CheckStyle: stop system..print check
-        System.err.println("FastR internal error: " + msg);
-        // CheckStyle: resume system..print check
-        throw Utils.exit(2);
+    /**
+     * The default, non-overrideable, suicide call. It prints the message and throws
+     * {@link ExitException}.
+     *
+     * @param msg
+     */
+    public static RuntimeException rSuicideDefault(String msg) {
+        System.err.println("FastR unexpected failure: " + msg);
+        throw new ExitException(2);
     }
 
-    public static RuntimeException rSuicide(String msg) {
-        if (RInterfaceCallbacks.R_Suicide.isOverridden()) {
-            RFFIFactory.getRFFI().getREmbedRFFI().suicide(msg);
-        } else {
-            System.err.println("Fatal error: " + msg);
-        }
-        throw Utils.exit(2);
+    /**
+     * This the real, final, non-overrideable, exit of the entire R system. TODO well, modulo how
+     * quit() is interpreted when R is started implicitly from a Polyglot shell that is running
+     * other languages.
+     *
+     * @param status
+     */
+    public static void systemExit(int status) {
+        RPerfStats.report();
+        System.exit(status);
     }
 
     private static String userHome;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/FastPathFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/FastPathFactory.java
similarity index 97%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/FastPathFactory.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/FastPathFactory.java
index 02ba005555bb2e4552710e62c3c596f7fc780007..59271ee152afc1d898158e4c11f9195f983d8785 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/FastPathFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/FastPathFactory.java
@@ -20,12 +20,11 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.data;
+package com.oracle.truffle.r.runtime.builtins;
 
 import java.util.function.Supplier;
 
 import com.oracle.truffle.r.runtime.FastROptions;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RVisibility;
 import com.oracle.truffle.r.runtime.nodes.RFastPathNode;
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBehavior.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBehavior.java
new file mode 100644
index 0000000000000000000000000000000000000000..47f5423505d2d38e4d39ac2c2ff1140985989275
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBehavior.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016, 2016, 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.builtins;
+
+import com.oracle.truffle.r.runtime.context.RContext;
+
+/**
+ * The different ways in which an {@link RBuiltin} interacts with its parameters and the rest of the
+ * system.
+ */
+public enum RBehavior {
+    /**
+     * This builtin always returns the same result or raises the same error if it is called with the
+     * same arguments. It cannot depend on any external state, on the frame or on IO input.
+     */
+    PURE,
+    /**
+     * This builtin performs IO operations.
+     */
+    IO,
+    /**
+     * This builtin reads from the frame, but does not depend on any other state. It will return the
+     * same result or raise the same error if called with the same arguments, as long as the frame
+     * is not changed.
+     */
+    READS_FRAME,
+    /**
+     * This builtin modifies the frame, changing values or other state that is stored in the frame.
+     * It also depends on the frame, similar to {@link #READS_FRAME}.
+     */
+    MODIFIES_FRAME,
+    /**
+     * This builtin reads from the global state ({@link RContext}, etc.), but not the frame. It will
+     * return the same result or raise the same error if called with the same arguments, as long as
+     * the global state is not changed.
+     */
+    READS_STATE,
+    /**
+     * This builtin modifies the global state ({@link RContext}, etc.), but not the frame. It also
+     * depends on the global state, similar to {@link #READS_STATE}.
+     */
+    MODIFIES_STATE,
+    /**
+     * This builtin has arbitrary effects on the global state, on IO components, the frame, and the
+     * AST itself. It also depends on the global state, IO and the frame.
+     */
+    COMPLEX
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltin.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltin.java
similarity index 84%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltin.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltin.java
index d10c3b0103a06777d39d0d5a33d44786921a2858..e609182b11595ff2f1aac89f4ddf3f1449b2d5e6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltin.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltin.java
@@ -20,11 +20,14 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime;
+package com.oracle.truffle.r.runtime.builtins;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
+import com.oracle.truffle.r.runtime.RDispatch;
+import com.oracle.truffle.r.runtime.RVisibility;
+
 @Retention(RetentionPolicy.RUNTIME)
 public @interface RBuiltin {
 
@@ -64,6 +67,18 @@ public @interface RBuiltin {
      */
     RVisibility visibility() default RVisibility.ON;
 
+    /**
+     * Determines how calls to a builtin should be dispatched, e.g., whether internal or group
+     * generic dispatch should be used.
+     */
+    RDispatch dispatch() default RDispatch.DEFAULT;
+
+    /**
+     * The behavior defines which conditions can be expected to hold for calls to this builtin,
+     * .e.g., whether repeated calls with the same arguments are expected to return the same result.
+     */
+    RBehavior behavior();
+
     /**
      * Indicates whether or not function containing a call of the form
      * <code>.Internal(name(...))</code> should trigger a split of the caller at its direct call
@@ -72,6 +87,4 @@ public @interface RBuiltin {
     boolean splitCaller() default false;
 
     boolean alwaysSplit() default false;
-
-    RDispatch dispatch() default RDispatch.DEFAULT;
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RBuiltinDescriptor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinDescriptor.java
similarity index 93%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RBuiltinDescriptor.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinDescriptor.java
index b305a2d317ea7b374f47a3c138913e025c66ac45..9581723641af720712061a2b12ab6e1a364de750 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RBuiltinDescriptor.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinDescriptor.java
@@ -20,14 +20,13 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime.data;
+package com.oracle.truffle.r.runtime.builtins;
 
 import java.util.Arrays;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.PrimitiveMethodsInfo;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RVisibility;
 
@@ -36,6 +35,8 @@ public abstract class RBuiltinDescriptor {
     private static int primitiveMethodCount;
 
     private final String name;
+    private final Class<?> builtinNodeClass;
+    private final RVisibility visibility;
     private final String[] aliases;
     private final RBuiltinKind kind;
     private final ArgumentsSignature signature;
@@ -43,13 +44,13 @@ public abstract class RBuiltinDescriptor {
     private final boolean splitCaller;
     private final boolean alwaysSplit;
     private final RDispatch dispatch;
+    private final RBehavior behavior;
+
     private final int primitiveMethodIndex;
-    private final RVisibility visibility;
     @CompilationFinal private final boolean[] evaluatesArgument;
-    private final Class<?> builtinNodeClass;
 
     public RBuiltinDescriptor(String name, Class<?> builtinNodeClass, RVisibility visibility, String[] aliases, RBuiltinKind kind, ArgumentsSignature signature, int[] nonEvalArgs, boolean splitCaller,
-                    boolean alwaysSplit, RDispatch dispatch) {
+                    boolean alwaysSplit, RDispatch dispatch, RBehavior behavior) {
         this.name = name.intern();
         this.builtinNodeClass = builtinNodeClass;
         this.visibility = visibility;
@@ -60,6 +61,7 @@ public abstract class RBuiltinDescriptor {
         this.splitCaller = splitCaller;
         this.alwaysSplit = alwaysSplit;
         this.dispatch = dispatch;
+        this.behavior = behavior;
 
         evaluatesArgument = new boolean[signature.getLength()];
         Arrays.fill(evaluatesArgument, true);
@@ -125,4 +127,8 @@ public abstract class RBuiltinDescriptor {
     public Class<?> getBuiltinNodeClass() {
         return builtinNodeClass;
     }
+
+    public RBehavior getBehavior() {
+        return behavior;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinKind.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinKind.java
similarity index 92%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinKind.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinKind.java
index 6ba813ea95639692cc8f179f507db75a304a034a..3cc0bc22485af7f110a621c42d874ec679f0c2af 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinKind.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinKind.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,7 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime;
+package com.oracle.truffle.r.runtime.builtins;
 
 /**
  * The {@link RBuiltin} "kinds".
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookup.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinLookup.java
similarity index 92%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookup.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinLookup.java
index 0da17c6a1ce3158c0d88adc726eff390310d1244..1102940fb2a0c858c01e52e6dc0a4399bc3a30f1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RBuiltinLookup.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RBuiltinLookup.java
@@ -20,9 +20,8 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.runtime;
+package com.oracle.truffle.r.runtime.builtins;
 
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RFunction;
 
 public interface RBuiltinLookup {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
index d67c2e3a8f296e019d2cac129823f8fd40df14bd..a8436ff49f669512583d889c7190c43352383dd5 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/StdConnections.java
@@ -68,7 +68,7 @@ public class StdConnections {
             try {
                 return new ContextStateImpl(new StdinConnection(), new StdoutConnection(consoleHandler), new StderrConnection(consoleHandler));
             } catch (IOException ex) {
-                throw Utils.fail("failed to open stdconnections:");
+                throw Utils.rSuicide("failed to open stdconnections:");
             }
         }
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java
index a780f005b4f26e7522d84d2c66650d9b488277fc..131397b5d5b24fca57d33a6ebe44410d424d863b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/ContextInfo.java
@@ -24,7 +24,6 @@ package com.oracle.truffle.r.runtime.context;
 
 import java.io.IOException;
 import java.util.TimeZone;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.oracle.truffle.api.interop.ForeignAccess;
@@ -43,7 +42,6 @@ import com.oracle.truffle.r.runtime.context.RContext.ContextKind;
 public final class ContextInfo implements TruffleObject {
     public static final String GLOBAL_SYMBOL = "fastrContextInfo";
 
-    private static final ConcurrentHashMap<Integer, ContextInfo> contextInfos = new ConcurrentHashMap<>();
     private static final AtomicInteger contextInfoIds = new AtomicInteger();
 
     private final RStartParams startParams;
@@ -93,16 +91,6 @@ public final class ContextInfo implements TruffleObject {
         return create(startParams, kind, parent, consoleHandler, TimeZone.getDefault());
     }
 
-    public static int createDeferred(RStartParams startParams, ContextKind kind, RContext parent, ConsoleHandler consoleHandler) {
-        ContextInfo info = create(startParams, kind, parent, consoleHandler, TimeZone.getDefault());
-        contextInfos.put(info.id, info);
-        return info.id;
-    }
-
-    public static ContextInfo get(int id) {
-        return contextInfos.get(id);
-    }
-
     public static ContextInfo getContextInfo(PolyglotEngine vm) {
         try {
             return (ContextInfo) vm.findGlobalSymbol(ContextInfo.GLOBAL_SYMBOL).get();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java
index e8363e5a00435d101d9e1a99987924ec2a96fb2b..f6c0f60ed573f8ce488923a6dcb895dc1b938963 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/Engine.java
@@ -35,6 +35,7 @@ import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.data.RExpression;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RLanguage;
+import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 
@@ -167,9 +168,10 @@ public interface Engine {
      * case the current frame is used). In many cases {@code frame} may not represent the current
      * call stack, for example many S4-related evaluations set {@code frame} to the {@code methods}
      * namespace, but the current stack is not empty. So when {@code frame} is not {@code null} a
-     * {@code caller} should be passed to maintain the call stack correctly.
+     * {@code caller} should be passed to maintain the call stack correctly. {@code names} string
+     * vector describing (optional) argument names
      */
-    Object evalFunction(RFunction func, MaterializedFrame frame, RCaller caller, Object... args);
+    Object evalFunction(RFunction func, MaterializedFrame frame, RCaller caller, RStringVector names, Object... args);
 
     /**
      * Checks for the existence of (startup/shutdown) function {@code name} and, if present, invokes
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
index 8e011cfa039c2d2d87b63e33897271cfbb7f0a80..bbc30f71a915fe474e2bb4cb8d801c723a2aeb89 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -41,17 +41,19 @@ import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.nodes.InvalidAssumptionException;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.r.runtime.ExitException;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.LazyDBCache;
 import com.oracle.truffle.r.runtime.PrimitiveMethodsInfo;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RBuiltinLookup;
 import com.oracle.truffle.r.runtime.RCmdOptions;
 import com.oracle.truffle.r.runtime.RCmdOptions.Client;
 import com.oracle.truffle.r.runtime.REnvVars;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RErrorHandling;
 import com.oracle.truffle.r.runtime.RInternalCode.ContextStateImpl;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinLookup;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.ROptions;
 import com.oracle.truffle.r.runtime.RProfile;
@@ -65,7 +67,6 @@ import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
 import com.oracle.truffle.r.runtime.context.Engine.ParseException;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -198,31 +199,44 @@ public final class RContext extends ExecutionContext implements TruffleObject {
                 throw new RInternalError(e1, "error while initializing eval thread");
             }
             try {
-                try {
-                    PolyglotEngine.Value resultValue = vm.eval(source);
-                    evalResult = createEvalResult(resultValue);
-                } catch (ParseException e) {
-                    e.report(info.getConsoleHandler());
-                    evalResult = createErrorResult(e.getMessage());
-                } catch (IOException e) {
-                    Throwable cause = e.getCause();
-                    if (cause instanceof RInternalError) {
-                        info.getConsoleHandler().println("internal error: " + e.getMessage() + " (see fastr_errors.log)");
-                        RInternalError.reportError(e);
-                    }
-                    evalResult = createErrorResult(e.getCause().getMessage());
-                }
+                evalResult = run(vm, info, source);
             } finally {
                 vm.dispose();
                 threads.remove(info.getId());
             }
         }
 
+        /**
+         * Convenience method for {@code .fastr.context.eval} in same thread.
+         */
+        public static RList run(PolyglotEngine vm, ContextInfo info, Source source) {
+            RList evalResult;
+            try {
+                PolyglotEngine.Value resultValue = vm.eval(source);
+                evalResult = createEvalResult(resultValue);
+            } catch (ParseException e) {
+                e.report(info.getConsoleHandler());
+                evalResult = createErrorResult(e.getMessage());
+            } catch (IOException e) {
+                Throwable cause = e.getCause();
+                if (cause instanceof ExitException) {
+                    // termination, treat this as "success"
+                    ExitException exitException = (ExitException) cause;
+                    evalResult = RDataFactory.createList(new Object[]{exitException.getStatus()});
+                } else {
+                    // some internal error
+                    RInternalError.reportErrorAndConsoleLog(cause, info.getConsoleHandler(), info.getId());
+                    evalResult = createErrorResult(cause.getClass().getSimpleName());
+                }
+            }
+            return evalResult;
+        }
+
         /**
          * The result is an {@link RList} contain the value, plus an "error" attribute if the
          * evaluation resulted in an error.
          */
-        public static RList createEvalResult(PolyglotEngine.Value resultValue) throws IOException {
+        private static RList createEvalResult(PolyglotEngine.Value resultValue) throws IOException {
             Object result = resultValue.get();
             Object listResult = result;
             String error = null;
@@ -417,7 +431,7 @@ public final class RContext extends ExecutionContext implements TruffleObject {
 
         this.env = env;
         if (info.getConsoleHandler() == null) {
-            throw Utils.fail("no console handler set");
+            throw Utils.rSuicide("no console handler set");
         }
 
         if (singleContextAssumption.isValid()) {
@@ -701,7 +715,6 @@ public final class RContext extends ExecutionContext implements TruffleObject {
 
     @Override
     public String toString() {
-        new RuntimeException().printStackTrace();
         return "context: " + info.getId();
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e303054edb4f818d2153f011bac4e37a0baebff
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/AgentObjectSizeFactory.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2016, 2016, 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 java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.management.ManagementFactory;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import javax.tools.ToolProvider;
+
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler;
+import com.oracle.truffle.r.runtime.data.RObjectSize.TypeCustomizer;
+
+/**
+ * Uses an instrumentation agent to get an accurate estimate of an objects size, plus reflection to
+ * aggregate the size of object-valued fields. Sharing is not handled in the general case, although
+ * some special cases are handed, such as the fact that {@link RNull} is a singleton.
+ *
+ * In order to satisfy the requirements of the Java instrumentation API, we have to load the agent
+ * from a jar file. The creation and loading is all orchestrated by this class.
+ */
+public class AgentObjectSizeFactory extends ObjectSizeFactory {
+
+    private Map<Class<?>, ArrayList<Field>> objectFieldsMap = new HashMap<>();
+    private static Map<Class<?>, TypeCustomizer> customizerMap = new HashMap<>(); // system wide
+
+    public AgentObjectSizeFactory() {
+        if (!ObjSizeAgent.isInitialized()) {
+            try {
+                createAgentJar();
+            } catch (Exception ex) {
+                // not available
+                Utils.rSuicide("failed to load ObjSizeAgent: " + ex.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Adds the class file bytes for a given class to a JAR stream.
+     */
+    static void add(JarOutputStream jar, Class<?> c) throws IOException {
+        String name = c.getName();
+        String classAsPath = name.replace('.', '/') + ".class";
+        jar.putNextEntry(new JarEntry(classAsPath));
+
+        InputStream stream = c.getClassLoader().getResourceAsStream(classAsPath);
+
+        int nRead;
+        byte[] buf = new byte[1024];
+        while ((nRead = stream.read(buf, 0, buf.length)) != -1) {
+            jar.write(buf, 0, nRead);
+        }
+
+        jar.closeEntry();
+    }
+
+    protected void createAgentJar() throws Exception {
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
+        Attributes mainAttrs = manifest.getMainAttributes();
+        mainAttrs.putValue("Agent-Class", ObjSizeAgent.class.getName());
+        mainAttrs.putValue("Premain-Class", ObjSizeAgent.class.getName());
+
+        Path jar = Files.createTempFile("myagent", ".jar");
+        try {
+            JarOutputStream jarStream = new JarOutputStream(new FileOutputStream(jar.toFile()), manifest);
+            add(jarStream, ObjSizeAgent.class);
+            jarStream.close();
+
+            loadAgent(jar);
+        } finally {
+            Files.deleteIfExists(jar);
+        }
+    }
+
+    public static void loadAgent(Path agent) throws Exception {
+        String vmName = ManagementFactory.getRuntimeMXBean().getName();
+        int p = vmName.indexOf('@');
+        String pid = vmName.substring(0, p);
+        ClassLoader cl = ToolProvider.getSystemToolClassLoader();
+        Class<?> c = Class.forName("com.sun.tools.attach.VirtualMachine", true, cl);
+        Method attach = c.getDeclaredMethod("attach", String.class);
+        Method loadAgent = c.getDeclaredMethod("loadAgent", String.class, String.class);
+        Object vm = attach.invoke(null, pid);
+        loadAgent.invoke(vm, agent.toString(), "");
+    }
+
+    @Override
+    public long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        return getObjectSize(obj, obj, ignoreObjectHandler);
+    }
+
+    private long getObjectSize(Object rootObj, Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        try {
+            long basicSize = ObjSizeAgent.objectSize(obj);
+            long size = basicSize;
+            Class<?> klass = obj.getClass();
+            if (klass.isArray() && !klass.getComponentType().isPrimitive()) {
+                for (int i = 0; i < Array.getLength(obj); i++) {
+                    Object elem = Array.get(obj, i);
+                    if (elem == null || isNa(elem)) {
+                        continue;
+                    } else {
+                        size += getObjectSize(rootObj, elem, ignoreObjectHandler);
+                    }
+                }
+            } else {
+                ArrayList<Field> objectFields = objectFieldsMap.get(klass);
+                if (objectFields == null) {
+                    objectFields = new ArrayList<>();
+                    findObjectFields(obj.getClass(), objectFields);
+                    objectFieldsMap.put(klass, objectFields);
+                }
+                for (Field objectField : objectFields) {
+                    Object fieldObj = objectField.get(obj);
+                    if (fieldObj == null || ignoreObjectHandler.ignore(rootObj, fieldObj)) {
+                        continue;
+                    } else {
+                        TypeCustomizer typeCustomizer = getCustomizer(fieldObj.getClass());
+                        if (typeCustomizer == null) {
+                            size += getObjectSize(rootObj, fieldObj, ignoreObjectHandler);
+                        } else {
+                            size += typeCustomizer.getObjectSize(fieldObj);
+                        }
+                    }
+                }
+            }
+            return size;
+        } catch (Throwable t) {
+            throw RInternalError.shouldNotReachHere(t);
+        }
+
+    }
+
+    private static boolean isNa(Object elem) {
+        String typeName = elem.getClass().getSimpleName();
+        switch (typeName) {
+            case "Integer":
+                return RRuntime.isNA((int) elem);
+            case "Double":
+                return RRuntime.isNA((double) elem);
+            case "String":
+                return RRuntime.isNA((String) elem);
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Walks the superclass hierarchy of {@code klass} and accumulates all object-valued fields in
+     * {@code objectFields}.
+     */
+    private static void findObjectFields(Class<?> klass, ArrayList<Field> objectFields) {
+        if (klass != Object.class) {
+            findObjectFields(klass.getSuperclass(), objectFields);
+            Field[] fields = klass.getDeclaredFields();
+            for (Field field : fields) {
+                Class<?> fieldClass = field.getType();
+                if (fieldClass.isPrimitive()) {
+                    continue;
+                }
+                int modifiers = field.getModifiers();
+                if (Modifier.isStatic(modifiers)) {
+                    continue;
+                }
+                // check for special case of an completely ignored type
+                if (getCustomizer(fieldClass) == RObjectSize.IGNORE) {
+                    continue;
+                }
+                field.setAccessible(true);
+                objectFields.add(field);
+            }
+        }
+    }
+
+    private static TypeCustomizer getCustomizer(Class<?> objClass) {
+        for (Map.Entry<Class<?>, TypeCustomizer> entry : customizerMap.entrySet()) {
+            if (entry.getKey().isAssignableFrom(objClass)) {
+                return entry.getValue();
+            }
+        }
+        return null;
+
+    }
+
+    @Override
+    public void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer) {
+        customizerMap.put(klass, typeCustomizer);
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java
similarity index 56%
rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java
index 05b5db6c2ab0659d63d734052b21dd3216ae1553..f6e7cabae776ea3598ae3034f6ee4da2ee799ca1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryTracer.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/MemoryCopyTracer.java
@@ -23,48 +23,56 @@
 
 package com.oracle.truffle.r.runtime.data;
 
-import com.oracle.truffle.api.Assumption;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.r.runtime.context.RContext;
+import java.util.Deque;
+import java.util.concurrent.ConcurrentLinkedDeque;
+
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.utilities.CyclicAssumption;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 /**
- * Helper for tracing memory related events. All implementors of {@link RAbstractVector} are
- * expected to report to {@link MemoryTracer} and others can listen to them through {@link Listener}
- * interface. Use method {@link #reportEvents()} to start the tracing.
+ * Helper for tracing memory copying events, as used by the {@code tracemem} bultin. All
+ * implementors of {@link RAbstractVector} are expected to report to {@link MemoryCopyTracer} and
+ * others can listen to them through {@link Listener} interface. Use method
+ * {@link #setTracingState(boolean)} to enable/disable the tracing.
  */
-public final class MemoryTracer {
-    private static Listener listener;
-    private static final Assumption noMemoryTracingAssumption = Truffle.getRuntime().createAssumption();
+public final class MemoryCopyTracer {
+    private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>();
+    @CompilationFinal private static boolean enabled;
+
+    private static final CyclicAssumption noMemoryCopyTracingAssumption = new CyclicAssumption("data copying");
 
-    private MemoryTracer() {
+    private MemoryCopyTracer() {
         // only static methods
     }
 
     /**
-     * Sets the listener of memory tracing events. For the time being there can only be one
-     * listener. This can be extended to an array should we need more listeners.
+     * Adds a listener of memory copying events.
      */
-    public static void setListener(Listener newListener) {
-        listener = newListener;
+    public static void addListener(Listener listener) {
+        listeners.addLast(listener);
     }
 
     /**
      * After calling this method memory related events will be reported to the listener. This
      * invalidates global assumption and should be used with caution.
      */
-    public static void reportEvents() {
-        noMemoryTracingAssumption.invalidate();
+    public static void setTracingState(boolean newState) {
+        if (enabled != newState) {
+            noMemoryCopyTracingAssumption.invalidate();
+            enabled = newState;
+        }
     }
 
     /**
      * Reports copy event to the listener. If there are no traced objects, this should turn into
-     * no-op. TODO might be worth interposing on a change in {@code tracingState} to turn off the
-     * collection.
+     * no-op.
      */
     public static void reportCopying(RAbstractVector source, RAbstractVector dest) {
-        if (!noMemoryTracingAssumption.isValid() && listener != null && RContext.getInstance().stateInstrumentation.getTracingState()) {
-            listener.reportCopying(source, dest);
+        if (enabled) {
+            for (Listener listener : listeners) {
+                listener.reportCopying(source, dest);
+            }
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/CastStringNodeGenSampler.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java
similarity index 50%
rename from com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/CastStringNodeGenSampler.java
rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java
index 95814c609349b8e2114f5314a6e5c4bca4518a33..b6756248470bea75bf369bfd2859bd071c11de48 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/unary/CastStringNodeGenSampler.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjSizeAgent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,26 +20,33 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.unary;
+package com.oracle.truffle.r.runtime.data;
 
-import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
-import com.oracle.truffle.r.nodes.casts.TypeExpr;
-import com.oracle.truffle.r.runtime.data.RNull;
+import java.lang.instrument.Instrumentation;
 
-public class CastStringNodeGenSampler extends CastNodeSampler<CastStringNodeGen> {
+/**
+ * This is agent class for object sizing. It has to be separate as it is loaded from a jar file.
+ * This implements the basic call to the JVM for the "struct" part of the object.
+ * {@link AgentObjectSizeFactory} handles the recursive sizing based on the field/array types.
+ *
+ */
+public class ObjSizeAgent {
+    private static Instrumentation instrumentation;
+
+    public static void premain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) {
+        instrumentation = inst;
+    }
+
+    public static void agentmain(@SuppressWarnings("unused") String agentArgs, Instrumentation inst) {
+        instrumentation = inst;
+    }
 
-    public CastStringNodeGenSampler(CastStringNodeGen castNode) {
-        super(castNode);
+    public static long objectSize(Object obj) {
+        return instrumentation.getObjectSize(obj);
     }
 
-    @Override
-    public TypeExpr resultTypes(TypeExpr inputType) {
-        TypeExpr rt = super.resultTypes(inputType);
-        if (castNode.isEmptyVectorConvertedToNull()) {
-            return rt.or(TypeExpr.union(RNull.class));
-        } else {
-            return rt;
-        }
+    static boolean isInitialized() {
+        return instrumentation != null;
     }
 
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..18a0cb472db916172c7386ab5e62fb9334a0552f
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/ObjectSizeFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016, 2016, 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.r.runtime.data.RObjectSize.IgnoreObjectHandler;
+import com.oracle.truffle.r.runtime.data.RObjectSize.TypeCustomizer;
+
+public abstract class ObjectSizeFactory {
+
+    static {
+        final String prop = System.getProperty("fastr.objectsize.factory.class", "com.oracle.truffle.r.runtime.data.AgentObjectSizeFactory");
+        try {
+            theInstance = (ObjectSizeFactory) Class.forName(prop).newInstance();
+        } catch (Exception ex) {
+            // CheckStyle: stop system..print check
+            System.err.println("Failed to instantiate class: " + prop);
+        }
+    }
+
+    private static ObjectSizeFactory theInstance;
+
+    public static ObjectSizeFactory getInstance() {
+        return theInstance;
+    }
+
+    /**
+     * See {@link RObjectSize#getObjectSize}.
+     */
+    public abstract long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler);
+
+    public abstract void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer);
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d7279743c01e4518d20124db73a4676a5c65a0a
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/OutputAgentObjectSizeFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016, 2016, 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 java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.data.RObjectSize.IgnoreObjectHandler;
+
+/**
+ * A debugging tool, logs all calls to {@link #getObjectSize(Object, IgnoreObjectHandler)} to a
+ * file.
+ *
+ */
+public class OutputAgentObjectSizeFactory extends AgentObjectSizeFactory {
+
+    private PrintWriter printWriter;
+
+    public OutputAgentObjectSizeFactory() {
+        try {
+            printWriter = new PrintWriter(new FileWriter(Utils.getLogPath("fastr_objectsize.log").toString()));
+        } catch (IOException ex) {
+            Utils.rSuicide(ex.getMessage());
+        }
+
+    }
+
+    @Override
+    public long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        long size = super.getObjectSize(obj, ignoreObjectHandler);
+        printWriter.printf("%s: %d\n", obj.getClass().getSimpleName(), (int) size);
+        printWriter.flush();
+        return size;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java
index 1bfe70252de40c3162543da9ef30c416b27154b2..430be1e43025c645c81ffbb1c59aa6f756c8bee6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RComplex.java
@@ -68,7 +68,7 @@ public final class RComplex extends RScalarVector implements RAbstractComplexVec
     @Override
     public RComplexVector materialize() {
         RComplexVector result = RDataFactory.createComplexVectorFromScalar(this);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
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 547e402be30f5f69749d0fc093efb87aaa6b877a..dad4afd45a6e0e52823f4e946f459dd9a5514e1e 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
@@ -23,8 +23,10 @@
 package com.oracle.truffle.r.runtime.data;
 
 import java.util.Arrays;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.oracle.truffle.api.Assumption;
@@ -32,12 +34,13 @@ 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.frame.MaterializedFrame;
-import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.utilities.CyclicAssumption;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RCaller;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RPerfStats;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.data.RPromise.Closure;
 import com.oracle.truffle.r.runtime.data.RPromise.EagerFeedback;
 import com.oracle.truffle.r.runtime.data.RPromise.PromiseState;
@@ -48,11 +51,6 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 
 public final class RDataFactory {
 
-    /**
-     * Profile for creation tracing; must precede following declarations.
-     */
-    private static final ConditionProfile statsProfile = ConditionProfile.createBinaryProfile();
-
     public static final boolean INCOMPLETE_VECTOR = false;
     public static final boolean COMPLETE_VECTOR = true;
 
@@ -497,24 +495,79 @@ public final class RDataFactory {
         return traceDataCreated(new RExternalPtr(value, tag, RNull.instance));
     }
 
-    @CompilationFinal private static PerfHandler stats;
+    /*
+     * Support for collecting information on allocations in this class. Rprofmem/Rprof register a
+     * listener when active which, when memory profiling is enabled, is called with the object being
+     * allocated. Owing to the use of the Assumption, there should be no overhead when disabled.
+     */
+
+    private static Deque<Listener> listeners = new ConcurrentLinkedDeque<>();
+    @CompilationFinal private static boolean enabled;
+    private static final CyclicAssumption noAllocationTracingAssumption = new CyclicAssumption("data allocation");
+
+    public static void setAllocationTracing(boolean newState) {
+        if (enabled != newState) {
+            noAllocationTracingAssumption.invalidate();
+            enabled = newState;
+        }
+    }
 
     private static <T> T traceDataCreated(T data) {
-        if (statsProfile.profile(stats != null)) {
-            stats.record(data);
+        if (enabled) {
+            for (Listener listener : listeners) {
+                listener.reportAllocation((RTypedValue) data);
+            }
         }
         return data;
     }
 
+    public interface Listener {
+        /**
+         * Invoked when an instance of an {@link RTypedValue} is created. Note that the initial
+         * state of the complex objects, i.e., those with additional {@code Object} subclass fields,
+         * which may also be {@link RTypedValue} instances is undefined other than by inspection. A
+         * listener that computes the "size" of an object must take into account that
+         * {@link RTypedValue} instances passed to a {@code createXXX} method will already have been
+         * reported, but other data such as {@code int[]} instances for array dimensions will not.
+         */
+        void reportAllocation(RTypedValue data);
+    }
+
+    /**
+     * Sets the listener of memory tracing events. For the time being there can only be one
+     * listener. This can be extended to an array should we need more listeners.
+     */
+    public static void addListener(Listener listener) {
+        listeners.addLast(listener);
+    }
+
+    /*
+     * (Legacy) support for R:PerfStats option. This does produce more information than Rprofmem
+     * regarding the types and length of the objects being allocated, but it does not record where
+     * in R the allocation took place.
+     */
     static {
         RPerfStats.register(new PerfHandler());
     }
 
-    private static class PerfHandler implements RPerfStats.Handler {
+    private static class PerfHandler implements RPerfStats.Handler, Listener {
         private static Map<Class<?>, RPerfStats.Histogram> histMap;
 
+        @Override
+        public void initialize(String optionData) {
+            histMap = new HashMap<>();
+            addListener(this);
+            setAllocationTracing(true);
+        }
+
+        @Override
+        public String getName() {
+            return "datafactory";
+        }
+
+        @Override
         @TruffleBoundary
-        void record(Object data) {
+        public void reportAllocation(RTypedValue data) {
             Class<?> klass = data.getClass();
             boolean isBounded = data instanceof RAbstractVector;
             RPerfStats.Histogram hist = histMap.get(klass);
@@ -526,17 +579,6 @@ public final class RDataFactory {
             hist.inc(length);
         }
 
-        @Override
-        public void initialize(String optionData) {
-            stats = this;
-            histMap = new HashMap<>();
-        }
-
-        @Override
-        public String getName() {
-            return "datafactory";
-        }
-
         @Override
         public void report() {
             RPerfStats.out().println("Scalar types");
@@ -557,5 +599,6 @@ public final class RDataFactory {
             }
             RPerfStats.out().println();
         }
+
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java
index 096356c36aaf5031ea8b85166e820cfbd7d960b5..2caf812292e1c4270b619fe81165d7e1066aa7b2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RDouble.java
@@ -90,7 +90,7 @@ public final class RDouble extends RScalarVector implements RAbstractDoubleVecto
     @Override
     public RDoubleVector materialize() {
         RDoubleVector result = RDataFactory.createDoubleVectorFromScalar(getValue());
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java
index 9726b285457ccf3d7df3895ca8962f5e8cef3194..0ec52202d4806b9cf83be65955d91a1cdf35efd3 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RFunction.java
@@ -25,9 +25,10 @@ package com.oracle.truffle.r.runtime.data;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.r.runtime.RBuiltin;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.VirtualEvalFrame;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributes.RAttribute;
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java
index 075d9ee51126ff8bd297ebffb5b11bed48055b13..33ba1ebd83bac82664770babb137ec2908cf5148 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteger.java
@@ -88,7 +88,7 @@ public final class RInteger extends RScalarVector implements RAbstractIntVector
     @Override
     public RIntVector materialize() {
         RIntVector result = RDataFactory.createIntVectorFromScalar(value);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java
index 05d0e6d1b1d642a39fc13b620d15a3727a3c130d..44e432804a569f815eeb52fe6b9533d3f79c97a4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLogical.java
@@ -94,7 +94,7 @@ public final class RLogical extends RScalarVector implements RAbstractLogicalVec
     @Override
     public RLogicalVector materialize() {
         RLogicalVector result = RDataFactory.createLogicalVectorFromScalar(value);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java
new file mode 100644
index 0000000000000000000000000000000000000000..14b70ab39912d6a656926780f59ffe37d48e3daa
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2016, 2016, 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.Assumption;
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.runtime.RCaller;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.gnur.SEXPTYPE;
+
+/**
+ * Support for the sizing of the objects that flow through the interpreter, i.e., mostly
+ * {@link RTypedValue}, but also including scalar types like {@code String} and dimension data for
+ * arrays, i.e., {@code int[]}.
+ *
+ * The actually implementation is controlled by {@link ObjectSizeFactory} to finesse problems with
+ * Java VMs that do not support reflection.
+ *
+ * Owing to the (implementation) complexity of some of the types, two levels of customization are
+ * provided:
+ * <ol>
+ * <li>A completely custom sizing implementation can be provided for a specific type. This effects
+ * all sizing computations.</li>
+ * <li>In any given call to {@link #getObjectSize} an instance of {@IgnoreObjectHandler} can passed.
+ * This allows some additional dynamic control over certain fields depending of the context of the
+ * call. For example, when tracking the incremental memory allocation via {@link RDataFactory}, we
+ * do not want to (double) count fields of type {@link RTypedValue}. However, when computing the
+ * total size of the object, e.g. for the {@code utils::object.size} builtin, we do want to count
+ * them.</li>
+ * </ol>
+ *
+ */
+public class RObjectSize {
+    public static final int INT_SIZE = 32;
+    public static final int DOUBLE_SIZE = 64;
+    public static final int BYTE_SIZE = 8;
+
+    public interface TypeCustomizer {
+        /**
+         * Allows complete control over sizing of a type registered with
+         * {@link #registerTypeCustomizer}.
+         */
+        long getObjectSize(Object obj);
+    }
+
+    public interface IgnoreObjectHandler {
+        /**
+         * Controls which fields of an object passed to {@link #getObjectSize} will be ignored.
+         * {@code rootObject} is the initiating object and {@code obj} is some field of that object
+         * or one of its components. The return value should be {@code true} if {@code obj} should
+         * be ignored.
+         */
+        boolean ignore(Object rootObject, Object obj);
+    }
+
+    /**
+     * Returns an estimate of the size of the this object, including the size of any object-valued
+     * fields, recursively. Evidently this is a snapshot and the size can change as, e.g.,
+     * attributes are added/removed.
+     *
+     * If called immediately after creation by {@link RDataFactory}, with an
+     * {@link IgnoreObjectHandler} that ignores objects created separately and, it provides an
+     * approximation of the incremental memory usage of the system.
+     *
+     * @param ignoreObjectHandler An object that is called to decide whether to include the
+     *            contribution a field of this object (and its sub-objects) in the result. Passing
+     *            {@code null} includes everything. N.B. {@code obj} is typed as {@code Object} only
+     *            to allow scalar typed such as {@code String} to be passed.
+     *
+     */
+    public static long getObjectSize(Object obj, IgnoreObjectHandler ignoreObjectHandler) {
+        return (int) ObjectSizeFactory.getInstance().getObjectSize(obj, ignoreObjectHandler);
+    }
+
+    /**
+     * Register a {@link TypeCustomizer} for {@code klass} and its subclasses. I.e. and object
+     * {@code obj} is customized iff {@code klass.isAssignableFrom(obj.getClass())}.
+     */
+    public static void registerTypeCustomizer(Class<?> klass, TypeCustomizer typeCustomizer) {
+        ObjectSizeFactory.getInstance().registerTypeCustomizer(klass, typeCustomizer);
+    }
+
+    /**
+     * This denotes a special customizer that completely ignores instances of the type and its
+     * subclasses. It allows a more efficient implementation as the type can be suppressed
+     * completely from the computation at the time fields of a containing type are analyzed.
+     */
+    public static final TypeCustomizer IGNORE = new TypeCustomizer() {
+
+        @Override
+        public long getObjectSize(Object obj) {
+            return 0;
+        }
+
+    };
+
+    // TODO construct proper customizers for some of these.
+    static {
+        registerTypeCustomizer(Frame.class, IGNORE);
+        registerTypeCustomizer(FrameDescriptor.class, IGNORE);
+        registerTypeCustomizer(Node.class, IGNORE);
+        registerTypeCustomizer(CallTarget.class, IGNORE);
+        registerTypeCustomizer(RBuiltinDescriptor.class, IGNORE);
+        registerTypeCustomizer(RPromise.Closure.class, IGNORE);
+        registerTypeCustomizer(Assumption.class, IGNORE);
+        registerTypeCustomizer(RCaller.class, IGNORE);
+        registerTypeCustomizer(SEXPTYPE.class, IGNORE);
+    }
+
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
index 7a1944f3ea0fffa3b0f70da0dc5bc55795507a71..d2f1b99c455dd2b9c2fb9dd0801f9a8fd92ea978 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
@@ -273,6 +273,12 @@ public class RPairList extends RSharingAttributeStorage implements RAbstractCont
             result = (RPairList) result.cdr;
             original = origList.cdr;
         }
+        if (getAttributes() != null) {
+            RAttributes newAttributes = result.initAttributes();
+            for (RAttribute attr : getAttributes()) {
+                newAttributes.put(attr.getName(), attr.getValue());
+            }
+        }
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
index d843cacfb937a8518c91151e91e7e33b3c553076..a18f274d43736e0c7e9936de1eaa8d6adf0aa544 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
@@ -65,7 +65,7 @@ public final class RRaw extends RScalarVector implements RAbstractRawVector {
     @Override
     public RRawVector materialize() {
         RRawVector result = RDataFactory.createRawVector(new byte[]{value});
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java
index 6e4a7ca5d9590b16c937b8bb62608c6f25ef8cd7..e12aed17f8fbb58a7789401701ae49a288788850 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RScalarVector.java
@@ -143,28 +143,28 @@ public abstract class RScalarVector extends RScalar implements RAbstractVector {
     @Override
     public RVector copyResized(int size, boolean fillNA) {
         RVector result = materialize().copyResized(size, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public RAbstractVector copyWithNewDimensions(int[] newDimensions) {
         RAbstractVector result = materialize().copyWithNewDimensions(newDimensions);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public RVector copyResizedWithDimensions(int[] newDimensions, boolean fillNA) {
         RVector result = materialize().copyResizedWithDimensions(newDimensions, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public RAbstractVector copyDropAttributes() {
         RVector result = materialize().copyDropAttributes();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
index 3dcc48a783d72896755c68f58238721596a0d86c..75a022269b5c8b5057b02859a0d6f750d412e76b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RSequence.java
@@ -81,7 +81,7 @@ public abstract class RSequence implements RAbstractVector {
 
     public final RVector createVector() {
         RVector result = internalCreateVector();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java
index 764f4c4b4ba2c88556989eb83461fdf4b8409bc5..af3281e0c19f749edcdbd5f3ab44ed4562315ce6 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RString.java
@@ -74,7 +74,7 @@ public final class RString extends RScalarVector implements RAbstractStringVecto
     @Override
     public RStringVector materialize() {
         RStringVector result = RDataFactory.createStringVector(new String[]{getValue()}, isComplete());
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
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 159addc3e228b3bda6e2e3bbcc9e9504544c5987..a75b54fdb4bd80a721e5dfb173f611368db7cb62 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
@@ -581,19 +581,19 @@ public abstract class RVector extends RSharingAttributeStorage implements RShare
 
     protected final RVector internalCopyAndReport() {
         RVector result = internalCopy();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     protected final RVector internalDeepCopyAndReport() {
         RVector result = internalDeepCopy();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     protected final RVector internalCopyResizedAndReport(int size, boolean fillNA) {
         RVector result = internalCopyResized(size, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
index 0fe0baccfc90e7e0bb0b1a374322b1d431b37cc2..99383ff1a2bf7e9f9c5afd5ed0158a00ffb02835 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/closures/RToVectorClosure.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.data.closures;
 
-import com.oracle.truffle.r.runtime.data.MemoryTracer;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RAttributes;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -129,14 +129,14 @@ abstract class RToVectorClosure implements RAbstractVector {
     @Override
     public final RAbstractVector copy() {
         RAbstractVector result = vector.copy();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
     @Override
     public final RVector copyResized(int size, boolean fillNA) {
         RVector result = vector.copyResized(size, fillNA);
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
@@ -152,7 +152,7 @@ abstract class RToVectorClosure implements RAbstractVector {
     @Override
     public final RAbstractVector copyDropAttributes() {
         RAbstractVector result = vector.copyDropAttributes();
-        MemoryTracer.reportCopying(this, result);
+        MemoryCopyTracer.reportCopying(this, result);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
index a2ae57288b474717f66033b2bacd43a882e5e899..a9b3eebf0606c05d5a434500e994a5ab907cc38c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/model/RAbstractVector.java
@@ -24,11 +24,11 @@ package com.oracle.truffle.r.runtime.data.model;
 
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.MemoryTracer;
+import com.oracle.truffle.r.runtime.data.MemoryCopyTracer;
 import com.oracle.truffle.r.runtime.data.RVector;
 
 /**
- * When implementing, make sure to invoke related {@link MemoryTracer} methods.
+ * When implementing, make sure to invoke related {@link MemoryCopyTracer} methods.
  */
 public interface RAbstractVector extends RAbstractContainer {
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java
index d54831170d0c97c111fdda780c0abe264dd2b5b9..e66aef15b5c364f8e2fc610525b1286ef902d4f1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java
@@ -848,7 +848,7 @@ public abstract class REnvironment extends RAttributeStorage {
         try {
             put(key, value);
         } catch (PutException ex) {
-            Utils.fail("exception in safePut");
+            Utils.rSuicide("exception in safePut");
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
index e34c048fda5393653330a81013f66d3907276fec..cdbe8d0b82ad6d03f6e2848499ccc4550be3d3d0 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIFactory.java
@@ -22,6 +22,7 @@
  */
 package com.oracle.truffle.r.runtime.ffi;
 
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextState;
@@ -36,7 +37,7 @@ import com.oracle.truffle.r.runtime.context.RContext.ContextState;
  */
 public abstract class RFFIFactory {
 
-    protected static RFFI theRFFI;
+    @CompilationFinal protected static RFFI theRFFI;
 
     public static void setRFFIFactory(RFFIFactory factory) {
         RFFIContextStateFactory.registerFactory(factory);
@@ -52,7 +53,7 @@ public abstract class RFFIFactory {
      * Initialize the factory instance. This method will be called immediately after the factory
      * instance is created allowing any additional initialization that could not be done in the
      * constructor.
-     * 
+     *
      * @param runtime {@code true} if the initialization is being done at runtime. An AOT system may
      *            call this twice, once with {@code false} whern an image is being bilt and once
      *            when starting up.
@@ -106,7 +107,7 @@ public abstract class RFFIFactory {
     }
 
     private static RuntimeException missing(String ffi) throws RuntimeException {
-        throw Utils.fail(ffi + " FFI not implemented");
+        throw Utils.rSuicide(ffi + " FFI not implemented");
     }
 
     public abstract ContextState newContext(RContext context);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
index 09f5a10046d079cd9b7697e1d51e4fb172fb4556..0b601ce31970ad98c9347a687fc8de366dbcef6c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/InstrumentationState.java
@@ -68,6 +68,11 @@ public final class InstrumentationState implements RContext.ContextState {
      */
     private final RprofState rprofState;
 
+    /**
+     * The {@link RprofmemState} state, if any, associated with this {@link RContext}.
+     */
+    private final RprofmemState rprofmemState;
+
     private final TracememContext tracememContext;
 
     /**
@@ -96,32 +101,72 @@ public final class InstrumentationState implements RContext.ContextState {
      */
     private boolean debugGloballyDisabled;
 
+    private abstract static class RprofAdapter {
+        protected PrintWriter out;
+
+        /**
+         * Return current output or {@code null} if not profiling.
+         */
+        public PrintWriter out() {
+            return out;
+        }
+
+        public void setOut(PrintWriter out) {
+            this.out = out;
+        }
+    }
+
     /**
      * State used by {@code Rprof}.
      *
      */
-    public static final class RprofState {
-        private PrintWriter out;
+    public static final class RprofState extends RprofAdapter {
         private Thread profileThread;
         private ExecutionEventListener statementListener;
         private long intervalInMillis;
         private boolean lineProfiling;
+        private MemoryQuad memoryQuad;
+
+        public static final class MemoryQuad {
+            public long smallV;
+            public long largeV;
+            public long nodes;
+            public long copied;
+
+            public MemoryQuad copyAndClear() {
+                MemoryQuad result = new MemoryQuad();
+                result.copied = copied;
+                result.largeV = largeV;
+                result.smallV = smallV;
+                result.nodes = nodes;
+                copied = 0;
+                largeV = 0;
+                smallV = 0;
+                nodes = 0;
+                return result;
+            }
+        }
 
         public void initialize(PrintWriter outA, Thread profileThreadA, ExecutionEventListener statementListenerA, long intervalInMillisA,
-                        boolean lineProfilingA) {
+                        boolean lineProfilingA, boolean memoryProfilingA) {
             this.out = outA;
             this.profileThread = profileThreadA;
             this.statementListener = statementListenerA;
             this.intervalInMillis = intervalInMillisA;
             this.lineProfiling = lineProfilingA;
+            this.memoryQuad = memoryProfilingA ? new MemoryQuad() : null;
         }
 
         public boolean lineProfiling() {
             return lineProfiling;
         }
 
-        public PrintWriter out() {
-            return out;
+        public boolean memoryProfiling() {
+            return memoryQuad != null;
+        }
+
+        public MemoryQuad memoryQuad() {
+            return memoryQuad;
         }
 
         public long intervalInMillis() {
@@ -138,6 +183,28 @@ public final class InstrumentationState implements RContext.ContextState {
 
     }
 
+    public static final class RprofmemState extends RprofAdapter {
+        private double threshold;
+        private int pageCount;
+
+        public void initialize(PrintWriter outA, double thresholdA) {
+            this.out = outA;
+            this.threshold = thresholdA;
+        }
+
+        public double threshold() {
+            return threshold;
+        }
+
+        public int pageCount() {
+            return pageCount;
+        }
+
+        public void setPageCount(int pageCount) {
+            this.pageCount = pageCount;
+        }
+    }
+
     public static class BrowserState {
         private boolean inBrowser;
         private String lastEmptyLineCommand = "n";
@@ -162,6 +229,7 @@ public final class InstrumentationState implements RContext.ContextState {
     private InstrumentationState(Instrumenter instrumenter) {
         this.instrumenter = instrumenter;
         this.rprofState = new RprofState();
+        this.rprofmemState = new RprofmemState();
         this.tracememContext = new TracememContext();
         this.browserState = new BrowserState();
     }
@@ -215,6 +283,10 @@ public final class InstrumentationState implements RContext.ContextState {
         return rprofState;
     }
 
+    public RprofmemState getRprofmem() {
+        return rprofmemState;
+    }
+
     public TracememContext getTracemem() {
         return tracememContext;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/RPackageSource.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/RPackageSource.java
index b937742b11fb5c1524d72de496d69909ac9b9626..aa3e750c69c989a167bc13b163e3dde6b52d4b7c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/RPackageSource.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/instrument/RPackageSource.java
@@ -269,7 +269,7 @@ public class RPackageSource {
                 wr.append('\n');
             }
         } catch (IOException ex) {
-            Utils.fail("error writing package source index");
+            Utils.rSuicide("error writing package source index");
         }
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsFastPath.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsFastPath.java
index 361c23ad89ca1f5d71a69fe7739f160e1e8c671a..7485df6bc517b694b0b08604f64d936c11506bed 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsFastPath.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsFastPath.java
@@ -26,7 +26,7 @@ import java.util.Arrays;
 
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.FastROptions;
-import com.oracle.truffle.r.runtime.data.FastPathFactory;
+import com.oracle.truffle.r.runtime.builtins.FastPathFactory;
 
 final class EvaluatedArgumentsFastPath implements FastPathFactory {
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsVisitor.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsVisitor.java
index 12ff1b0a0d00db58df5aef5cf71711e2dff0e66f..d699acf3cd81c4a13016a04a83363776b1dd3642 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsVisitor.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/EvaluatedArgumentsVisitor.java
@@ -28,9 +28,9 @@ import java.util.HashSet;
 import java.util.Set;
 
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RBuiltinDescriptor;
 
 final class Info {
     public static final Info EMPTY = new Info(Collections.emptySet(), Collections.emptySet(), false);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxLookup.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxLookup.java
index 73c5b2bdfd7a88573f03d8102b2335be1044403d..ac2cfe029130c6db14079f91223bc1a623d59354 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxLookup.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RSyntaxLookup.java
@@ -29,6 +29,9 @@ import com.oracle.truffle.api.source.SourceSection;
  */
 public interface RSyntaxLookup extends RSyntaxElement {
 
+    /**
+     * @return The identifier that this lookup represents - this needs to be an interned string.
+     */
     String getIdentifier();
 
     boolean isFunctionLookup();
@@ -60,6 +63,11 @@ public interface RSyntaxLookup extends RSyntaxElement {
             public void setSourceSection(SourceSection src) {
                 // ignored
             }
+
+            @Override
+            public String toString() {
+                return "`" + identifier + "`";
+            }
         };
     }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryArithmetic.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryArithmetic.java
index 9a3f6dcd6b328a9294aa13c908c7f31c96b1fbc5..a6b0a48f0f784e8e01217383e5c743c6ec70c60e 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryArithmetic.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryArithmetic.java
@@ -12,9 +12,12 @@
  */
 package com.oracle.truffle.r.runtime.ops;
 
+import static com.oracle.truffle.r.runtime.RDispatch.OPS_GROUP_GENERIC;
 import static com.oracle.truffle.r.runtime.RRuntime.INT_NA;
 import static com.oracle.truffle.r.runtime.RRuntime.isFinite;
 import static com.oracle.truffle.r.runtime.RRuntime.isNAorNaN;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -24,12 +27,10 @@ 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.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 
@@ -43,31 +44,31 @@ public abstract class BinaryArithmetic extends Operation {
 
     /* Fake RBuiltins to unify the binary operations */
 
-    @RBuiltin(name = "+", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "+", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class AddBuiltin {
     }
 
-    @RBuiltin(name = "-", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "-", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class SubtractBuiltin {
     }
 
-    @RBuiltin(name = "/", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "/", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class DivBuiltin {
     }
 
-    @RBuiltin(name = "%/%", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "%/%", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class IntegerDivBuiltin {
     }
 
-    @RBuiltin(name = "%%", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "%%", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class ModBuiltin {
     }
 
-    @RBuiltin(name = "*", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "*", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class MultiplyBuiltin {
     }
 
-    @RBuiltin(name = "^", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "^", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class PowBuiltin {
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java
index f029799c3d14a2f7923dccd860635c135e275763..19a58782aad3842ee1400bb6be0ef539700daf2f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryCompare.java
@@ -22,10 +22,12 @@
  */
 package com.oracle.truffle.r.runtime.ops;
 
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
+import static com.oracle.truffle.r.runtime.RDispatch.OPS_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RComplex;
 
 /**
@@ -34,27 +36,27 @@ import com.oracle.truffle.r.runtime.data.RComplex;
 public abstract class BinaryCompare extends BooleanOperation {
 
     /* Fake RBuiltins to unify the compare operations */
-    @RBuiltin(name = "==", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "==", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class EqualBuiltin {
     }
 
-    @RBuiltin(name = "!=", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "!=", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class NotEqualBuiltin {
     }
 
-    @RBuiltin(name = ">=", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = ">=", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class GreaterEqualBuiltin {
     }
 
-    @RBuiltin(name = ">", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = ">", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class GreaterBuiltin {
     }
 
-    @RBuiltin(name = "<=", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "<=", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class LessEqualBuiltin {
     }
 
-    @RBuiltin(name = "<", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "<", kind = PRIMITIVE, parameterNames = {"", ""}, alwaysSplit = true, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class LessBuiltin {
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryLogic.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryLogic.java
index 029f92dacfd81c34080abbb439ef7d1b5d80f9be..50a29211528fdd757cdb4d03349ed18998634402 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryLogic.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ops/BinaryLogic.java
@@ -22,11 +22,14 @@
  */
 package com.oracle.truffle.r.runtime.ops;
 
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
-import com.oracle.truffle.r.runtime.RDispatch;
+import static com.oracle.truffle.r.runtime.RDispatch.OPS_GROUP_GENERIC;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
+
 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.RComplex;
 import com.oracle.truffle.r.runtime.data.RLogical;
 
@@ -37,19 +40,19 @@ public abstract class BinaryLogic extends BooleanOperation {
 
     /* Fake RBuiltins to unify the binary operations */
 
-    @RBuiltin(name = "&&", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, nonEvalArgs = {1})
+    @RBuiltin(name = "&&", kind = PRIMITIVE, parameterNames = {"", ""}, nonEvalArgs = {1}, behavior = COMPLEX)
     public static class NonVectorAndBuiltin {
     }
 
-    @RBuiltin(name = "||", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, nonEvalArgs = {1})
+    @RBuiltin(name = "||", kind = PRIMITIVE, parameterNames = {"", ""}, nonEvalArgs = {1}, behavior = COMPLEX)
     public static class NonVectorOrBuiltin {
     }
 
-    @RBuiltin(name = "&", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "&", kind = PRIMITIVE, parameterNames = {"", ""}, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class AndBuiltin {
     }
 
-    @RBuiltin(name = "|", kind = RBuiltinKind.PRIMITIVE, parameterNames = {"", ""}, dispatch = RDispatch.OPS_GROUP_GENERIC)
+    @RBuiltin(name = "|", kind = PRIMITIVE, parameterNames = {"", ""}, dispatch = OPS_GROUP_GENERIC, behavior = PURE)
     public static class OrBuiltin {
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
index 3e233ca9d88e88dcfe70020c916566b8add409d0..3a6e356fcfaeb38539cacd10aeade9d1bf823d61 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/rng/RRNG.java
@@ -310,7 +310,7 @@ public class RRNG {
     private static Object getDotRandomSeed() {
         Object seed = REnvironment.globalEnv().get(RANDOM_SEED);
         if (seed instanceof RPromise) {
-            seed = RContext.getRRuntimeASTAccess().forcePromise(seed);
+            seed = RContext.getRRuntimeASTAccess().forcePromise(RANDOM_SEED, seed);
         }
         return seed;
     }
diff --git a/com.oracle.truffle.r.test.cran/r/install.cran.packages.R b/com.oracle.truffle.r.test.cran/r/install.cran.packages.R
index 21cd5a3d1eb5117ade53b4aa7b5dad141bee04f9..097eab15f6bf8feeb4576609efce8337a4fd018b 100644
--- a/com.oracle.truffle.r.test.cran/r/install.cran.packages.R
+++ b/com.oracle.truffle.r.test.cran/r/install.cran.packages.R
@@ -331,8 +331,27 @@ check.installed.pkgs <- function() {
 # requested set of candidate packages
 # sets global variables avail.pkgs and toinstall.pkgs, the latter being
 # of the same type as avail.pkgs but containing only those packages to install
+# returns a vector of package names to install/test
 get.pkgs <- function() {
-	avail.pkgs <<- available.packages(contriburl=contriburl, type="source")
+	my.warning <- function(war) {
+		if (!quiet) {
+			cat("Fatal error:", war$message, "\n")
+		}
+		quit(save="no", status=100)
+	}
+	tryCatch({
+	    avail.pkgs <<- available.packages(contriburl=contriburl, type="source")
+    }, warning=my.warning)
+
+    # Owing to a FastR bug, we may not invoke the handler above, but
+	# if length(avail.pkgs) == 0, that also means it failed
+	if (length(avail.pkgs) == 0) {
+		if (!quiet) {
+		  print("Fatal error: no packages found in repo")
+    	}
+		quit(save="no", status=100)
+	}
+
 	avail.pkgs.rownames <<- rownames(avail.pkgs)
 	# get/create the blacklist
 	blacklist <- get.blacklist()
@@ -371,7 +390,29 @@ get.pkgs <- function() {
 	}
 	matched.avail.pkgs <- apply(avail.pkgs, 1, match.fun)
 	toinstall.pkgs <<-avail.pkgs[matched.avail.pkgs, , drop=F]
-	toinstall.pkgs
+
+	if (!is.na(random.count)) {
+		# install random.count packages taken at random from toinstall.pkgs
+		test.avail.pkgnames <- rownames(toinstall.pkgs)
+		rands <- sample(1:length(test.avail.pkgnames))
+		test.pkgnames <- character(random.count)
+		for (i in (1:random.count)) {
+			test.pkgnames[[i]] <- test.avail.pkgnames[[rands[[i]]]]
+		}
+	} else {
+		test.pkgnames <- rownames(toinstall.pkgs)
+		if (!is.na(count.daily)) {
+			# extract count from index given by yday
+			npkgs <- length(test.pkgnames)
+			yday <- as.POSIXlt(Sys.Date())$yday
+			chunk <- as.integer(npkgs / count.daily)
+			start <- (yday %% chunk) * count.daily
+			end <- ifelse(start + count.daily > npkgs, npkgs, start + count.daily - 1)
+			test.pkgnames <- test.pkgnames[start:end]
+		}
+	}
+
+	test.pkgnames
 }
 
 # Serially install the packages in pkgnames.
@@ -476,36 +517,15 @@ get.blacklist <- function() {
 
 # performs the installation, or logs what it would install if dry.run = T
 do.it <- function() {
-	get.pkgs()
+	test.pkgnames <- get.pkgs()
 
 	if (list.versions) {
-		for (i in (1:length(rownames(toinstall.pkgs)))) {
-			pkg <- toinstall.pkgs[i, ]
+		for (pkgname in test.pkgnames) {
+			pkg <- toinstall.pkgs[pkgname, ]
 			cat(pkg["Package"], pkg["Version"], paste0(contriburl, "/", pkg["Version"], ".tar.gz"), "\n", sep=",")
 		}
 	}
 
-	if (!is.na(random.count)) {
-		# install random.count packages taken at random from toinstall.pkgs
-		test.avail.pkgnames <- rownames(toinstall.pkgs)
-		rands <- sample(1:length(test.avail.pkgnames))
-		test.pkgnames <- character(random.count)
-		for (i in (1:random.count)) {
-			test.pkgnames[[i]] <- test.avail.pkgnames[[rands[[i]]]]
-		}
-	} else {
-		test.pkgnames <- rownames(toinstall.pkgs)
-		if (!is.na(count.daily)) {
-			# extract count from index given by yday
-			npkgs <- length(test.pkgnames)
-			yday <- as.POSIXlt(Sys.Date())$yday
-			chunk <- as.integer(npkgs / count.daily)
-			start <- (yday %% chunk) * count.daily
-			end <- ifelse(start + count.daily > npkgs, npkgs, start + count.daily - 1)
-			test.pkgnames <- test.pkgnames[start:end]
-		}
-	}
-
 	if (install) {
 		cat("BEGIN package installation\n")
 		install.pkgs(test.pkgnames)
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 14d90aaf402264ac911aea73ede5a64c9976cbd9..01e3fcd48ae14532a5137cb0329f0b4184b4e46e 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
@@ -139,8 +139,8 @@ Class: numeric
 [26] 32 33 34 35 36 37 38 39 40 41 42
 
 ##com.oracle.truffle.r.test.S4.TestS4.testConversions
-#{ asS4(NULL); isS4(NULL }
-Error: unexpected '}' in "{ asS4(NULL); isS4(NULL }"
+#{ asS4(NULL); isS4(NULL) }
+[1] TRUE
 
 ##com.oracle.truffle.r.test.S4.TestS4.testConversions
 #{ isS4(NULL) }
@@ -596,16 +596,17 @@ character(0)
 character(0)
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_Encoding.testEncoding5
-#argv <- list(structure('Type 'demo(PKG::FOO)' to run demonstration 'PKG::FOO'.', .Names = 'demo')); .Internal(Encoding(argv[[1]]))
-Error: unexpected symbol in "argv <- list(structure('Type 'demo"
+#argv <- list(structure('Type demo(PKG::FOO) to run demonstration PKG::FOO.', .Names = 'demo')); .Internal(Encoding(argv[[1]]))
+[1] "unknown"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_Encoding.testEncoding6
-#argv <- list('A shell of class documentation has been written to the file './myTst2/man/DocLink-class.Rd'.\n'); .Internal(Encoding(argv[[1]]))
-Error: unexpected symbol in "argv <- list('A shell of class documentation has been written to the file '."
+#argv <- list('A shell of class documentation has been written to the file ./myTst2/man/DocLink-class.Rd.\n'); .Internal(Encoding(argv[[1]]))
+[1] "unknown"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_Encoding.testEncoding7
-#argv <- list(c('* Edit the help file skeletons in 'man', possibly combining help files for multiple functions.', '* Edit the exports in 'NAMESPACE', and add necessary imports.', '* Put any C/C++/Fortran code in 'src'.', '* If you have compiled code, add a useDynLib() directive to 'NAMESPACE'.', '* Run R CMD build to build the package tarball.', '* Run R CMD check to check the package tarball.', '', 'Read \'Writing R Extensions\' for more information.')); .Internal(Encoding(argv[[1]]))
-Error: unexpected symbol in "argv <- list(c('* Edit the help file skeletons in 'man"
+#argv <- list(c('* Edit the help file skeletons in man, possibly combining help files for multiple functions.', '* Edit the exports in NAMESPACE, and add necessary imports.', '* Put any C/C++/Fortran code in src.', '* If you have compiled code, add a useDynLib() directive to NAMESPACE.', '* Run R CMD build to build the package tarball.', '* Run R CMD check to check the package tarball.', '', 'Read Writing R Extensions for more information.')); .Internal(Encoding(argv[[1]]))
+[1] "unknown" "unknown" "unknown" "unknown" "unknown" "unknown" "unknown"
+[8] "unknown"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_Encoding.testEncoding9
 #argv <- structure(list(x = 'abc'), .Names = 'x');do.call('Encoding', argv)
@@ -1100,6 +1101,26 @@ character(0)
 #argv <- list(structure(list(sec = c(0, 0, 0, 0, 0), min = c(0L, 0L, 0L, 0L, 0L), hour = c(0L, 0L, 0L, 0L, 0L), mday = 22:26, mon = c(3L, 3L, 3L, 3L, 3L), year = c(108L, 108L, 108L, 108L, 108L), wday = 2:6, yday = 112:116, isdst = c(-1L, -1L, -1L, -1L, -1L)), .Names = c('sec', 'min', 'hour', 'mday', 'mon', 'year', 'wday', 'yday', 'isdst'), class = c('POSIXlt', 'POSIXt'), tzone = 'GMT')); .Internal(POSIXlt2Date(argv[[1]]))
 [1] "2008-04-22" "2008-04-23" "2008-04-24" "2008-04-25" "2008-04-26"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive('any')
+function (..., na.rm = FALSE)  .Primitive("any")
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive('complex')
+Error in .Primitive("complex") : no such primitive function
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive('foo')
+Error in .Primitive("foo") : no such primitive function
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive(1)
+Error in .Primitive(1) : string argument required
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
+#.Primitive(c('c', 'b'))
+Error in .Primitive(c("c", "b")) : string argument required
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_Primitive.testPrimitive1
 #argv <- list('c');.Primitive(argv[[1]]);
 function (..., recursive = FALSE)  .Primitive("c")
@@ -2123,6 +2144,46 @@ In all(argv[[1]]) : coercing argument of type 'double' to logical
 #all.names(quote(y ~ ((g1) * exp((log(g2/g1)) * (1 - exp(-k * (x - Ta)))/(1 - exp(-k * (Tb - Ta)))))), FALSE, -1L, TRUE)
 [1] "y"  "g1" "g2" "k"  "x"  "Ta" "Tb"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), functions=F) }
+[1] "x" "y" "x"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), functions=NA) }
+[1] "x" "y" "x"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), functions=NULL) }
+[1] "x" "y" "x"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), functions=T) }
+[1] "sin" "+"   "+"   "x"   "y"   "x"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), max.names=NA) }
+character(0)
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), max.names=NULL) }
+character(0)
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), unique=(NA) }
+Error: unexpected '}' in "{ all.names(expression(sin(x+y+x)), unique=(NA) }"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), unique=F) }
+[1] "sin" "+"   "+"   "x"   "y"   "x"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), unique=NULL) }
+[1] "sin" "+"   "x"   "y"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testAllNames
+#{ all.names(expression(sin(x+y+x)), unique=T) }
+[1] "sin" "+"   "x"   "y"
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_allnames.testallnames1
 #argv <- list(quote(y ~ ((g1) * exp((log(g2/g1)) * (1 - exp(-k * (x - Ta)))/(1 - exp(-k * (Tb - Ta)))))), FALSE, -1L, TRUE); .Internal(all.names(argv[[1]], argv[[2]], argv[[3]], argv[[4]]))
 [1] "y"  "g1" "g2" "k"  "x"  "Ta" "Tb"
@@ -3853,6 +3914,58 @@ NULL
 #argv <- list(structure(numeric(0), .Dim = c(0L, 0L))); .Internal(args(argv[[1]]))
 NULL
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ .Internal(array(1:4, NULL, NULL)) }
+Error: 'dims' cannot be of length 0
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ .Internal(array(NA, 1, NULL)) }
+[1] NA
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ .Internal(array(NULL, 1, NULL)) }
+Error: 'data' must be of a vector type, was 'NULL'
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ array(1:4, 1:2, 4) }
+     [,1] [,2]
+[1,]    1    2
+Warning message:
+In array(1:4, 1:2, 4) :
+  non-list dimnames are disregarded; will be an error in R 3.3.0
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ array(1:4, NULL) }
+Error in array(1:4, NULL) : 'dims' cannot be of length 0
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ array(1:4, c(1+2i, 2+2i)) }
+     [,1] [,2]
+[1,]    1    2
+Warning message:
+In array(1:4, c(1 + (0+2i), 2 + (0+2i))) :
+  imaginary parts discarded in coercion
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ array(1:4, integer()) }
+Error in array(1:4, integer()) : 'dims' cannot be of length 0
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ array(NA) }
+[1] NA
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ array(NULL) }
+Error in array(NULL) : 'data' must be of a vector type, was 'NULL'
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ array(as.raw(1:4)) }
+[1] 01 02 03 04
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testArray
+#{ f<-function() 42; .Internal(array(f, 1, NULL)) }
+Error: 'data' must be of a vector type, was 'closure'
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_array.testarray1
 #argv <- list(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L), 59L, structure(list(dr = c('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59.5')), .Names = 'dr')); .Internal(array(argv[[1]], argv[[2]], argv[[3]]))
 dr
@@ -5007,6 +5120,10 @@ character(0)
 a b
 1 2
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall
+#{ as.call(42) }
+Error in as.call(42) : invalid argument list
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_ascall.testAsCall
 #{ f <- function() 23 ; l <- list(f) ; cl <- as.call(l) ; eval(cl) }
 [1] 23
@@ -5461,6 +5578,20 @@ NAs introduced by coercion
 #{ as.complex(c(0/0, 0/0)) }
 [1] NA NA
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_ascomplex.testAsComplex
+#{ as.complex(list("foo")) }
+[1] NA
+Warning message:
+NAs introduced by coercion
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_ascomplex.testAsComplex
+#{ as.complex(list(42)) }
+[1] 42+0i
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_ascomplex.testAsComplex
+#{ as.complex(list(NULL)) }
+Error: (list) object cannot be coerced to type 'complex'
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_ascomplex.testAsComplex
 #{ x<-c(a=1.1, b=2.2); dim(x)<-c(1,2); attr(x, "foo")<-"foo"; y<-as.complex(x); attributes(y) }
 NULL
@@ -6792,6 +6923,18 @@ Warning messages:
 2: In as.vector("foo", "raw") :
   out-of-range values treated as 0 in coercion to raw
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_asvector.testAsVector
+#{ as.vector(42, NULL) }
+Error in as.vector(x, mode) : invalid 'mode' argument
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_asvector.testAsVector
+#{ as.vector(42, c("character", "character") }
+Error: unexpected '}' in "{ as.vector(42, c("character", "character") }"
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_asvector.testAsVector
+#{ as.vector(42, character())  }
+Error in as.vector(x, mode) : invalid 'mode' argument
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_asvector.testAsVector
 #{ as.vector(NULL) }
 NULL
@@ -9937,8 +10080,13 @@ expression(1, 2)
 [1] "myLib/myTst"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_c.testc10
-#argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = 'supply both 'x' and 'y' or a matrix-like 'x'', call = quote(cor(rnorm(10), NULL))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))), .Names = c('class', 'condition')));c(argv[[1]],argv[[2]]);
-Error: unexpected symbol in "argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = 'supply both 'x"
+#argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = 'supply both x and y or a matrix-like x', call = quote(cor(rnorm(10), NULL))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))), .Names = c('class', 'condition')));c(argv[[1]],argv[[2]]);
+$class
+[1] "try-error"
+
+$condition
+<simpleError in cor(rnorm(10), NULL): supply both x and y or a matrix-like x>
+
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_c.testc11
 #argv <- list(`(Intercept)` = '(Intercept)', structure(list(B = 'B', V = 'V', N = 'N', `V:N` = c('V', 'N'), Residuals = c('B', 'V', 'N', 'Within')), .Names = c('B', 'V', 'N', 'V:N', 'Residuals')));c(argv[[1]],argv[[2]]);
@@ -10050,8 +10198,13 @@ $condition
 
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_c.testc21
-#argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = ''x' is empty', call = quote(cor(Z[, FALSE], use = 'pairwise.complete.obs', method = 'kendall'))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))), .Names = c('class', 'condition')));c(argv[[1]],argv[[2]]);
-Error: unexpected symbol in "argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = ''x"
+#argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = 'x is empty', call = quote(cor(Z[, FALSE], use = 'pairwise.complete.obs', method = 'kendall'))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))), .Names = c('class', 'condition')));c(argv[[1]],argv[[2]]);
+$class
+[1] "try-error"
+
+$condition
+<simpleError in cor(Z[, FALSE], use = "pairwise.complete.obs", method = "kendall"): x is empty>
+
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_c.testc22
 #argv <- list(structure(list(N = structure(c(17, 18, 18, 18), .Dim = 4L, .Dimnames = structure(list(N = c('0.0cwt', '0.2cwt', '0.4cwt', '0.6cwt')), .Names = 'N'))), .Names = 'N'), structure(list(`V:N` = structure(c(6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6), .Dim = 3:4, .Dimnames = structure(list(V = c('Golden.rain', 'Marvellous', 'Victory'), N = c('0.0cwt', '0.2cwt', '0.4cwt', '0.6cwt')), .Names = c('V', 'N')))), .Names = 'V:N'));c(argv[[1]],argv[[2]]);
@@ -13219,6 +13372,11 @@ logical(0)
 #{ complex(3) }
 [1] 0+0i 0+0i 0+0i
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_complex.testComplex
+#{ complex(3, 3, new.env()) }
+Error in complex(3, 3, new.env()) :
+  environments cannot be coerced to other types
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_complex.testComplex
 #{ complex(3, c(1,2), c(4,5,6)) }
 [1] 1+4i 2+5i 1+6i
@@ -13231,6 +13389,15 @@ logical(0)
 #{ complex(3, c(1,2,3), c(4,5,6)) }
 [1] 1+4i 2+5i 3+6i
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_complex.testComplex
+#{ complex(3, new.env()) }
+Error in complex(3, new.env()) :
+  environments cannot be coerced to other types
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_complex.testComplex
+#{ complex(new.env()) }
+Error in complex(new.env()) : invalid length
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_complex.testComplex
 #{ complex(real=1,imag=2) }
 [1] 1+2i
@@ -15243,6 +15410,10 @@ a[a <- TRUE]
 #{ k <- 2 ; deparse(k) }
 [1] "2"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_deparse.testDeparse
+#{ x<-c(a=42, b=7); deparse(x) }
+[1] "structure(c(42, 7), .Names = c(\"a\", \"b\"))"
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_deparse.testDeparse
 #{ x<-expression(1); deparse(x) }
 [1] "expression(1)"
@@ -15280,8 +15451,8 @@ a[a <- TRUE]
 [1] "\"coef.corStruct\""
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_deparse.testdeparse17
-#argv <- list('Version of 'graph' is too old --- no tests done here!\n', 60L, FALSE, 69, -1L); .Internal(deparse(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]]))
-Error: unexpected symbol in "argv <- list('Version of 'graph"
+#argv <- list('Version of graph is too old --- no tests done here!\n', 60L, FALSE, 69, -1L); .Internal(deparse(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]]))
+[1] "\"Version of graph is too old --- no tests done here!\\n\""
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_deparse.testdeparse18
 #argv <- list(Inf, 60L, FALSE, 69, -1L); .Internal(deparse(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]]))
@@ -15579,20 +15750,35 @@ attr(,"class")
 [1] 0
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn1
-#argv <- list(''f' is deprecated.\nUse 'convertY' instead.\nSee help(\'Deprecated\')', NULL); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
-Error: unexpected symbol in "argv <- list(''f"
+#argv <- list('f is deprecated.\nUse convertY instead.\nSee help(Deprecated)', NULL); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
+NULL
+Warning message:
+f is deprecated.
+Use convertY instead.
+See help(Deprecated)
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn10
-#argv <- list(''x' is neither a vector nor a matrix: using as.numeric(x)', quote(dotchart(table(infert$education)))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
-Error: unexpected symbol in "argv <- list(''x"
+#argv <- list('x is neither a vector nor a matrix: using as.numeric(x)', quote(dotchart(table(infert$education)))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
+NULL
+Warning message:
+In dotchart(table(infert$education)) :
+  x is neither a vector nor a matrix: using as.numeric(x)
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn11
-#argv <- list('Invalid file name(s) for R code in ./myTst/R:\n  'file55711ba85492'\n are now renamed to 'z<name>.R'', quote(package.skeleton('myTst', code_files = tmp))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
-Error: unexpected symbol in "argv <- list('Invalid file name(s) for R code in ./myTst/R:\n  'file55711ba85492"
+#argv <- list('Invalid file name(s) for R code in ./myTst/R:\n  file55711ba85492\n are now renamed to z<name>.R', quote(package.skeleton('myTst', code_files = tmp))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
+NULL
+Warning message:
+In package.skeleton("myTst", code_files = tmp) :
+  Invalid file name(s) for R code in ./myTst/R:
+  file55711ba85492
+ are now renamed to z<name>.R
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn12
-#argv <- list('incomplete final line found by readTableHeader on 'foo4'', quote(read.table('foo4', header = TRUE))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
-Error: unexpected symbol in "argv <- list('incomplete final line found by readTableHeader on 'foo4"
+#argv <- list('incomplete final line found by readTableHeader on foo4', quote(read.table('foo4', header = TRUE))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
+NULL
+Warning message:
+In read.table("foo4", header = TRUE) :
+  incomplete final line found by readTableHeader on foo4
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn2
 #argv <- list('bessel_y(2,nu=288.12): precision lost in result', quote(besselY(2, nu = nu <- seq(3, 300, len = 51)))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
@@ -15608,8 +15794,11 @@ Warning message:
 glm.fit: algorithm stopped at boundary value
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn4
-#argv <- list('header and 'col.names' are of different lengths', quote(read.table('foo3', header = TRUE, col.names = letters[1:4]))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
-Error: unexpected symbol in "argv <- list('header and 'col.names"
+#argv <- list('header and col.names are of different lengths', quote(read.table('foo3', header = TRUE, col.names = letters[1:4]))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
+NULL
+Warning message:
+In read.table("foo3", header = TRUE, col.names = letters[1:4]) :
+  header and col.names are of different lengths
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn5
 #argv <- list('‘graphics’ namespace cannot be unloaded:\n  namespace ‘graphics’ is imported by ‘stats’ so cannot be unloaded', NULL); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
@@ -15625,8 +15814,11 @@ Warning message:
 In log(ifelse(y == 0, 1, y/mu)) : NaNs produced
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn7
-#argv <- list(''drop' argument will be ignored', quote(`[.data.frame`(women, 'height', drop = FALSE))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
-Error: unexpected symbol in "argv <- list(''drop"
+#argv <- list('drop argument will be ignored', quote(`[.data.frame`(women, 'height', drop = FALSE))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
+NULL
+Warning message:
+In `[.data.frame`(women, "height", drop = FALSE) :
+  drop argument will be ignored
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dfltWarn.testdfltWarn8
 #argv <- list('prediction from a rank-deficient fit may be misleading', quote(predict.lm(object, newdata, se.fit, scale = residual.scale, type = ifelse(type == 'link', 'response', type), terms = terms, na.action = na.action))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))
@@ -17171,7 +17363,7 @@ attr(,"simpleOnly")
 
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dimnames.testdimnames16
-#argv <- list(structure(c('4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.0-3', '4.0-3', '4.0-3', '4.0-3', '4.0-3', '4.0-2', '4.0-2', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '3.1-55', '3.1-55', '3.1-55', '3.1-54', '3.1-53', '3.1-53', '3.1-52', '3.1-51', NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 'The C and R code has been reformatted for legibility.', 'The old compatibility function rpconvert() has been removed.', 'The cross-validation functions allow for user interrupt at the end\nof evaluating each split.', 'Variable Reliability in data set car90 is corrected to be an\nordered factor, as documented.', 'Surrogate splits are now considered only if they send two or more\ncases _with non-zero weight_ each way.  For numeric/ordinal\nvariables the restriction to non-zero weights is new: for\ncategorical variables this is a new restriction.', 'Surrogate splits which improve only by rounding error over the\ndefault split are no longer returned.  Where weights and missing\nvalues are present, the splits component for some of these was not\nreturned correctly.', 'A fit of class \'rpart\' now contains a component for variable\n‘importance’, which is reported by the summary() method.', 'The text() method gains a minlength argument, like the labels()\nmethod.  This adds finer control: the default remains pretty =\nNULL, minlength = 1L.', 'The handling of fits with zero and fractional weights has been\ncorrected: the results may be slightly different (or even\nsubstantially different when the proportion of zero weights is\nlarge).', 'Some memory leaks have been plugged.', 'There is a second vignette, longintro.Rnw, a version of the\noriginal Mayo Tecnical Report on rpart.', 'Added dataset car90, a corrected version of the S-PLUS dataset\ncar.all (used with permission).', 'This version does not use paste0{} and so works with R 2.14.x.', 'Merged in a set of Splus code changes that had accumulated at Mayo\nover the course of a decade. The primary one is a change in how\nindexing is done in the underlying C code, which leads to a major\nspeed increase for large data sets.  Essentially, for the lower\nleaves all our time used to be eaten up by bookkeeping, and this\nwas replaced by a different approach.  The primary routine also\nuses .Call{} so as to be more memory efficient.', 'The other major change was an error for asymmetric loss matrices,\nprompted by a user query.  With L=loss asymmetric, the altered\npriors were computed incorrectly - they were using L' instead of L.\nUpshot - the tree would not not necessarily choose optimal splits\nfor the given loss matrix.  Once chosen, splits were evaluated\ncorrectly.  The printed “improvement” values are of course the\nwrong ones as well.  It is interesting that for my little test\ncase, with L quite asymmetric, the early splits in the tree are\nunchanged - a good split still looks good.', 'Add the return.all argument to xpred.rpart().', 'Added a set of formal tests, i.e., cases with known answers to\nwhich we can compare.', 'Add a usercode vignette, explaining how to add user defined\nsplitting functions.', 'The class method now also returns the node probability.', 'Add the stagec data set, used in some tests.', 'The plot.rpart routine needs to store a value that will be visible\nto the rpartco routine at a later time.  This is now done in an\nenvironment in the namespace.', 'Force use of registered symbols in R >= 2.16.0', 'Update Polish translations.', 'Work on message formats.', 'Add Polish translations', 'rpart, rpart.matrix: allow backticks in formulae.', 'tests/backtick.R: regession test', 'src/xval.c: ensure unused code is not compiled in.', 'Change description of margin in ?plot.rpart as suggested by Bill\nVenables.'), .Dim = c(29L, 4L)));dimnames(argv[[1]]);
+#argv <- list(structure(c('4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.0-3', '4.0-3', '4.0-3', '4.0-3', '4.0-3', '4.0-2', '4.0-2', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '3.1-55', '3.1-55', '3.1-55', '3.1-54', '3.1-53', '3.1-53', '3.1-52', '3.1-51', NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 'The C and R code has been reformatted for legibility.', 'The old compatibility function rpconvert() has been removed.', 'The cross-validation functions allow for user interrupt at the end\nof evaluating each split.', 'Variable Reliability in data set car90 is corrected to be an\nordered factor, as documented.', 'Surrogate splits are now considered only if they send two or more\ncases _with non-zero weight_ each way.  For numeric/ordinal\nvariables the restriction to non-zero weights is new: for\ncategorical variables this is a new restriction.', 'Surrogate splits which improve only by rounding error over the\ndefault split are no longer returned.  Where weights and missing\nvalues are present, the splits component for some of these was not\nreturned correctly.', 'A fit of class rpart now contains a component for variable\n‘importance’, which is reported by the summary() method.', 'The text() method gains a minlength argument, like the labels()\nmethod.  This adds finer control: the default remains pretty =\nNULL, minlength = 1L.', 'The handling of fits with zero and fractional weights has been\ncorrected: the results may be slightly different (or even\nsubstantially different when the proportion of zero weights is\nlarge).', 'Some memory leaks have been plugged.', 'There is a second vignette, longintro.Rnw, a version of the\noriginal Mayo Tecnical Report on rpart.', 'Added dataset car90, a corrected version of the S-PLUS dataset\ncar.all (used with permission).', 'This version does not use paste0{} and so works with R 2.14.x.', 'Merged in a set of Splus code changes that had accumulated at Mayo\nover the course of a decade. The primary one is a change in how\nindexing is done in the underlying C code, which leads to a major\nspeed increase for large data sets.  Essentially, for the lower\nleaves all our time used to be eaten up by bookkeeping, and this\nwas replaced by a different approach.  The primary routine also\nuses .Call{} so as to be more memory efficient.', 'The other major change was an error for asymmetric loss matrices,\nprompted by a user query.  With L=loss asymmetric, the altered\npriors were computed incorrectly - they were using L' instead of L.\nUpshot - the tree would not not necessarily choose optimal splits\nfor the given loss matrix.  Once chosen, splits were evaluated\ncorrectly.  The printed “improvement” values are of course the\nwrong ones as well.  It is interesting that for my little test\ncase, with L quite asymmetric, the early splits in the tree are\nunchanged - a good split still looks good.', 'Add the return.all argument to xpred.rpart().', 'Added a set of formal tests, i.e., cases with known answers to\nwhich we can compare.', 'Add a usercode vignette, explaining how to add user defined\nsplitting functions.', 'The class method now also returns the node probability.', 'Add the stagec data set, used in some tests.', 'The plot.rpart routine needs to store a value that will be visible\nto the rpartco routine at a later time.  This is now done in an\nenvironment in the namespace.', 'Force use of registered symbols in R >= 2.16.0', 'Update Polish translations.', 'Work on message formats.', 'Add Polish translations', 'rpart, rpart.matrix: allow backticks in formulae.', 'tests/backtick.R: regession test', 'src/xval.c: ensure unused code is not compiled in.', 'Change description of margin in ?plot.rpart as suggested by Bill\nVenables.'), .Dim = c(29L, 4L)));dimnames(argv[[1]]);
 Error: unexpected symbol in "utine also\nuses .Call{} so as to be more memory efficient.', 'The other major change was an error for asymmetric loss matrices,\nprompted by a user query.  With L=loss asymmetric, the altered"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dimnames.testdimnames17
@@ -21028,8 +21220,62 @@ attr(,"Rd_tag")
 [1] NA
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_iconv.testiconv3
-#argv <- list(c('% This file is part of the 'foreign' package for R', '% It is distributed under the GPL version 2 or later', '', '\\name{S3 read functions}', '\\alias{data.restore}', '\\alias{read.S}', '\\title{Read an S3 Binary or data.dump File}', '\\description{', '  Reads binary data files or \\code{data.dump} files that were produced', '  in S version 3.', '}', '\\usage{', '  data.restore(file, print = FALSE, verbose = FALSE, env = .GlobalEnv)', '  read.S(file)', '}', '\\arguments{', '  \\item{file}{the filename of the S-PLUS \\code{data.dump} or binary', '    file.}', '  \\item{print}{whether to print the name of each object as read from the', '    file.}', '  \\item{verbose}{whether to print the name of every subitem within each', '    object.}', '  \\item{env}{environment within which to create the restored object(s).}', '}', '\\value{', '  For \\code{read.S}, an R version of the S3 object.', '', '  For \\code{data.restore}, the name of the file.', '}', '\\details{', '  \\code{read.S} can read the binary files produced in some older', '  versions of S-PLUS on either Windows (versions 3.x, 4.x, 2000) or Unix', '  (version 3.x with 4 byte integers).  It automatically detects whether', '  the file was produced on a big- or little-endian machine and adapts', '  itself accordingly.', '', '  \\code{data.restore} can read a similar range of files produced by', '  \\code{data.dump} and for newer versions of S-PLUS, those from', '  \\code{data.dump(....., oldStyle=TRUE)}.', '', '  Not all S3 objects can be handled in the current version.  The most', '  frequently encountered exceptions are functions and expressions; you', '  will also have trouble with objects that contain model formulas.  In', '  particular, comments will be lost from function bodies, and the', '  argument lists of functions will often be changed.', '}', '\\author{', '  Duncan Murdoch', '}', '\\examples{', '\\dontrun{read.S(file.path(\'_Data\', \'myobj\'))', 'data.restore(\'dumpdata\', print = TRUE)', '}}', '\\keyword{data}', '\\keyword{file}'), '', 'ASCII', NA_character_, TRUE, FALSE); .Internal(iconv(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]]))
-Error: unexpected symbol in "argv <- list(c('% This file is part of the 'foreign"
+#argv <- list(c('% This file is part of the foreign package for R', '% It is distributed under the GPL version 2 or later', '', '\\name{S3 read functions}', '\\alias{data.restore}', '\\alias{read.S}', '\\title{Read an S3 Binary or data.dump File}', '\\description{', '  Reads binary data files or \\code{data.dump} files that were produced', '  in S version 3.', '}', '\\usage{', '  data.restore(file, print = FALSE, verbose = FALSE, env = .GlobalEnv)', '  read.S(file)', '}', '\\arguments{', '  \\item{file}{the filename of the S-PLUS \\code{data.dump} or binary', '    file.}', '  \\item{print}{whether to print the name of each object as read from the', '    file.}', '  \\item{verbose}{whether to print the name of every subitem within each', '    object.}', '  \\item{env}{environment within which to create the restored object(s).}', '}', '\\value{', '  For \\code{read.S}, an R version of the S3 object.', '', '  For \\code{data.restore}, the name of the file.', '}', '\\details{', '  \\code{read.S} can read the binary files produced in some older', '  versions of S-PLUS on either Windows (versions 3.x, 4.x, 2000) or Unix', '  (version 3.x with 4 byte integers).  It automatically detects whether', '  the file was produced on a big- or little-endian machine and adapts', '  itself accordingly.', '', '  \\code{data.restore} can read a similar range of files produced by', '  \\code{data.dump} and for newer versions of S-PLUS, those from', '  \\code{data.dump(....., oldStyle=TRUE)}.', '', '  Not all S3 objects can be handled in the current version.  The most', '  frequently encountered exceptions are functions and expressions; you', '  will also have trouble with objects that contain model formulas.  In', '  particular, comments will be lost from function bodies, and the', '  argument lists of functions will often be changed.', '}', '\\author{', '  Duncan Murdoch', '}', '\\examples{', '\\dontrun{read.S(file.path(\'_Data\', \'myobj\'))', 'data.restore(\'dumpdata\', print = TRUE)', '}}', '\\keyword{data}', '\\keyword{file}'), '', 'ASCII', NA_character_, TRUE, FALSE); .Internal(iconv(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]]))
+ [1] "% This file is part of the foreign package for R"
+ [2] "% It is distributed under the GPL version 2 or later"
+ [3] ""
+ [4] "\\name{S3 read functions}"
+ [5] "\\alias{data.restore}"
+ [6] "\\alias{read.S}"
+ [7] "\\title{Read an S3 Binary or data.dump File}"
+ [8] "\\description{"
+ [9] "  Reads binary data files or \\code{data.dump} files that were produced"
+[10] "  in S version 3."
+[11] "}"
+[12] "\\usage{"
+[13] "  data.restore(file, print = FALSE, verbose = FALSE, env = .GlobalEnv)"
+[14] "  read.S(file)"
+[15] "}"
+[16] "\\arguments{"
+[17] "  \\item{file}{the filename of the S-PLUS \\code{data.dump} or binary"
+[18] "    file.}"
+[19] "  \\item{print}{whether to print the name of each object as read from the"
+[20] "    file.}"
+[21] "  \\item{verbose}{whether to print the name of every subitem within each"
+[22] "    object.}"
+[23] "  \\item{env}{environment within which to create the restored object(s).}"
+[24] "}"
+[25] "\\value{"
+[26] "  For \\code{read.S}, an R version of the S3 object."
+[27] ""
+[28] "  For \\code{data.restore}, the name of the file."
+[29] "}"
+[30] "\\details{"
+[31] "  \\code{read.S} can read the binary files produced in some older"
+[32] "  versions of S-PLUS on either Windows (versions 3.x, 4.x, 2000) or Unix"
+[33] "  (version 3.x with 4 byte integers).  It automatically detects whether"
+[34] "  the file was produced on a big- or little-endian machine and adapts"
+[35] "  itself accordingly."
+[36] ""
+[37] "  \\code{data.restore} can read a similar range of files produced by"
+[38] "  \\code{data.dump} and for newer versions of S-PLUS, those from"
+[39] "  \\code{data.dump(....., oldStyle=TRUE)}."
+[40] ""
+[41] "  Not all S3 objects can be handled in the current version.  The most"
+[42] "  frequently encountered exceptions are functions and expressions; you"
+[43] "  will also have trouble with objects that contain model formulas.  In"
+[44] "  particular, comments will be lost from function bodies, and the"
+[45] "  argument lists of functions will often be changed."
+[46] "}"
+[47] "\\author{"
+[48] "  Duncan Murdoch"
+[49] "}"
+[50] "\\examples{"
+[51] "\\dontrun{read.S(file.path('_Data', 'myobj'))"
+[52] "data.restore('dumpdata', print = TRUE)"
+[53] "}}"
+[54] "\\keyword{data}"
+[55] "\\keyword{file}"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_iconv.testiconv4
 #argv <- list(c('', 'Compute a Survival Curve for Censored Data'), 'UTF-8', '', 'byte', FALSE, FALSE); .Internal(iconv(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]]))
@@ -21055,8 +21301,27 @@ attr(,"Rd_tag")
 [1] "TEXT"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_iconv.testiconv9
-#argv <- list(structure(c('Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance'), .Names = c('Q1_MISSING_NONE', 'Q1_MISSING_1', 'Q1_MISSING_2', 'Q1_MISSING_3', 'Q1_MISSING_RANGE', 'Q1_MISSING_LOW', 'Q1_MISSING_HIGH', 'Q1_MISSING_RANGE_1', 'Q1_MISSING_LOW_1', 'Q1_MISSING_HIGH_1')), 'latin1', '', NA_character_, TRUE, FALSE); .Internal(iconv(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]]))
-Error: unexpected symbol in "argv <- list(structure(c('Q.1 Opinion of president's"
+#argv <- list(structure(c('Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance'), .Names = c('Q1_MISSING_NONE', 'Q1_MISSING_1', 'Q1_MISSING_2', 'Q1_MISSING_3', 'Q1_MISSING_RANGE', 'Q1_MISSING_LOW', 'Q1_MISSING_HIGH', 'Q1_MISSING_RANGE_1', 'Q1_MISSING_LOW_1', 'Q1_MISSING_HIGH_1')), 'latin1', '', NA_character_, TRUE, FALSE); .Internal(iconv(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]]))
+                            Q1_MISSING_NONE
+"Q.1 Opinion of presidents job performance"
+                               Q1_MISSING_1
+"Q.1 Opinion of presidents job performance"
+                               Q1_MISSING_2
+"Q.1 Opinion of presidents job performance"
+                               Q1_MISSING_3
+"Q.1 Opinion of presidents job performance"
+                           Q1_MISSING_RANGE
+"Q.1 Opinion of presidents job performance"
+                             Q1_MISSING_LOW
+"Q.1 Opinion of presidents job performance"
+                            Q1_MISSING_HIGH
+"Q.1 Opinion of presidents job performance"
+                         Q1_MISSING_RANGE_1
+"Q.1 Opinion of presidents job performance"
+                           Q1_MISSING_LOW_1
+"Q.1 Opinion of presidents job performance"
+                          Q1_MISSING_HIGH_1
+"Q.1 Opinion of presidents job performance"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_icuSetCollate.testicuSetCollate1
 # .Internal(icuSetCollate())
@@ -21829,8 +22094,7 @@ Levels: a.b.c a.b.b.c a.c
 #argv <- list(structure('Error in scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings,  : \n  line 1 did not have 4 elements\n', class = 'try-error', condition = structure(list(message = 'line 1 did not have 4 elements', call = quote(scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings, flush, fill, strip.white, quiet, blank.lines.skip, multi.line, comment.char, allowEscapes, encoding))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_invisible.testinvisible16
-#argv <- list(structure('Error in cor(Z[, FALSE], use = \'pairwise.complete.obs\', method = \'kendall\') : \n  'x' is empty\n', class = 'try-error', condition = structure(list(message = ''x' is empty', call = quote(cor(Z[, FALSE], use = 'pairwise.complete.obs', method = 'kendall'))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);
-Error: unexpected symbol in "argv <- list(structure('Error in cor(Z[, FALSE], use = \'pairwise.complete.obs\', method = \'kendall\') : \n  'x"
+#argv <- list(structure('Error in cor(Z[, FALSE], use = pairwise.complete.obs, method = kendall) : \n  x is empty\n', class = 'try-error', condition = structure(list(message = 'x is empty', call = quote(cor(Z[, FALSE], use = 'pairwise.complete.obs', method = 'kendall'))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_invisible.testinvisible17
 #argv <- list(structure(list(A = c(1L, NA, 1L), B = c(1.1, NA, 2), C = c(1.1+0i, NA, 3+0i), D = c(NA, NA, NA), E = c(FALSE, NA, TRUE), F = structure(c(1L, NA, 2L), .Label = c('abc', 'def'), class = 'factor')), .Names = c('A', 'B', 'C', 'D', 'E', 'F'), class = 'data.frame', row.names = c('1', '2', '3')));invisible(argv[[1]]);
@@ -21842,8 +22106,7 @@ Error: unexpected symbol in "argv <- list(structure('Error in cor(Z[, FALSE], us
 #argv <- list(structure('Error in rnorm(2, c(1, NA)) : (converted from warning) NAs produced\n', class = 'try-error', condition = structure(list(message = '(converted from warning) NAs produced', call = quote(rnorm(2, c(1, NA)))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_invisible.testinvisible2
-#argv <- list(structure('Error in cov(rnorm(10), NULL) : \n  supply both 'x' and 'y' or a matrix-like 'x'\n', class = 'try-error', condition = structure(list(message = 'supply both 'x' and 'y' or a matrix-like 'x'', call = quote(cov(rnorm(10), NULL))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);
-Error: unexpected symbol in "argv <- list(structure('Error in cov(rnorm(10), NULL) : \n  supply both 'x"
+#argv <- list(structure('Error in cov(rnorm(10), NULL) : \n  supply both x and y or a matrix-like x\n', class = 'try-error', condition = structure(list(message = 'supply both x and y or a matrix-like x', call = quote(cov(rnorm(10), NULL))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_invisible.testinvisible20
 #argv <- list(structure(list(z = structure(c(1395082040.29392, 1395082040.29392, 1395082040.29392, 1395082040.29392, 1395082040.29392), class = c('AsIs', 'POSIXct', 'POSIXt'))), .Names = 'z', row.names = c(NA, -5L), class = 'data.frame'));invisible(argv[[1]]);
@@ -21936,8 +22199,7 @@ Error: unexpected symbol in "argv <- list(structure('Error in cov(rnorm(10), NUL
 #argv <- list(structure('Error in rnorm(1, sd = Inf) : (converted from warning) NAs produced\n', class = 'try-error', condition = structure(list(message = '(converted from warning) NAs produced', call = quote(rnorm(1, sd = Inf))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_invisible.testinvisible5
-#argv <- list(structure(c('Min.   : 1.000  ', '1st Qu.: 9.000  ', 'Median :18.000  ', 'Mean   :14.742  ', '3rd Qu.:20.000  ', 'Max.   :23.000  ', NA, 'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean   : 56.928  ', '3rd Qu.: 86.750  ', 'Max.   :117.000  ', 'NA's   :16  ', 'Min.   :  0.500  ', '1st Qu.: 11.325  ', 'Median : 23.400  ', 'Mean   : 45.603  ', '3rd Qu.: 47.550  ', 'Max.   :370.000  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')), class = 'table'));invisible(argv[[1]]);
-Error: unexpected symbol in "'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean"
+#argv <- list(structure(c('Min.   : 1.000  ', '1st Qu.: 9.000  ', 'Median :18.000  ', 'Mean   :14.742  ', '3rd Qu.:20.000  ', 'Max.   :23.000  ', NA, 'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean   : 56.928  ', '3rd Qu.: 86.750  ', 'Max.   :117.000  ', 'NAs   :16  ', 'Min.   :  0.500  ', '1st Qu.: 11.325  ', 'Median : 23.400  ', 'Mean   : 45.603  ', '3rd Qu.: 47.550  ', 'Max.   :370.000  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')), class = 'table'));invisible(argv[[1]]);
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_invisible.testinvisible50
 #argv <- list(structure(1395078479.75887, class = c('POSIXct', 'POSIXt')));invisible(argv[[1]]);
@@ -23036,7 +23298,7 @@ Error: unexpected 'if' in "argv <- list(function(x, width = 12, ...) {    if (is
 [1] FALSE
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_isfunction.testisfunction31
-#argv <- list(function(qr, y) {    if (!is.qr(qr)) stop('first argument must be a QR decomposition')    n <- as.integer(nrow(qr$qr))    if (is.na(n)) stop('invalid nrow(qr$qr)')    p <- as.integer(ncol(qr$qr))    if (is.na(p)) stop('invalid ncol(qr$qr)')    k <- as.integer(qr$rank)    if (is.na(k)) stop('invalid ncol(qr$rank)')    im <- is.matrix(y)    if (!im) y <- as.matrix(y)    ny <- as.integer(ncol(y))    if (is.na(ny)) stop('invalid ncol(y)')    if (p == 0L) return(if (im) matrix(0, p, ny) else numeric())    ix <- if (p > n) c(seq_len(n), rep(NA, p - n)) else seq_len(p)    if (is.complex(qr$qr)) {        coef <- matrix(NA_complex_, nrow = p, ncol = ny)        coef[qr$pivot, ] <- .Internal(qr_coef_cmplx(qr, y))[ix,             ]        return(if (im) coef else c(coef))    }    if (isTRUE(attr(qr, 'useLAPACK'))) {        coef <- matrix(NA_real_, nrow = p, ncol = ny)        coef[qr$pivot, ] <- .Internal(qr_coef_real(qr, y))[ix,             ]        return(if (im) coef else c(coef))    }    if (k == 0L) return(if (im) matrix(NA, p, ny) else rep.int(NA,         p))    storage.mode(y) <- 'double'    if (nrow(y) != n) stop(''qr' and 'y' must have the same number of rows')    z <- .Fortran(.F_dqrcf, as.double(qr$qr), n, k, as.double(qr$qraux),         y, ny, coef = matrix(0, nrow = k, ncol = ny), info = integer(1L),         NAOK = TRUE)[c('coef', 'info')]    if (z$info) stop('exact singularity in 'qr.coef'')    if (k < p) {        coef <- matrix(NA_real_, nrow = p, ncol = ny)        coef[qr$pivot[seq_len(k)], ] <- z$coef    } else coef <- z$coef    if (!is.null(nam <- colnames(qr$qr))) if (k < p) rownames(coef)[qr$pivot] <- nam else rownames(coef) <- nam    if (im && !is.null(nam <- colnames(y))) colnames(coef) <- nam    if (im) coef else drop(coef)});do.call('is.function', argv)
+#argv <- list(function(qr, y) {    if (!is.qr(qr)) stop('first argument must be a QR decomposition')    n <- as.integer(nrow(qr$qr))    if (is.na(n)) stop('invalid nrow(qr$qr)')    p <- as.integer(ncol(qr$qr))    if (is.na(p)) stop('invalid ncol(qr$qr)')    k <- as.integer(qr$rank)    if (is.na(k)) stop('invalid ncol(qr$rank)')    im <- is.matrix(y)    if (!im) y <- as.matrix(y)    ny <- as.integer(ncol(y))    if (is.na(ny)) stop('invalid ncol(y)')    if (p == 0L) return(if (im) matrix(0, p, ny) else numeric())    ix <- if (p > n) c(seq_len(n), rep(NA, p - n)) else seq_len(p)    if (is.complex(qr$qr)) {        coef <- matrix(NA_complex_, nrow = p, ncol = ny)        coef[qr$pivot, ] <- .Internal(qr_coef_cmplx(qr, y))[ix,             ]        return(if (im) coef else c(coef))    }    if (isTRUE(attr(qr, 'useLAPACK'))) {        coef <- matrix(NA_real_, nrow = p, ncol = ny)        coef[qr$pivot, ] <- .Internal(qr_coef_real(qr, y))[ix,             ]        return(if (im) coef else c(coef))    }    if (k == 0L) return(if (im) matrix(NA, p, ny) else rep.int(NA,         p))    storage.mode(y) <- 'double'    if (nrow(y) != n) stop('qr and y must have the same number of rows')    z <- .Fortran(.F_dqrcf, as.double(qr$qr), n, k, as.double(qr$qraux),         y, ny, coef = matrix(0, nrow = k, ncol = ny), info = integer(1L),         NAOK = TRUE)[c('coef', 'info')]    if (z$info) stop('exact singularity in qr.coef')    if (k < p) {        coef <- matrix(NA_real_, nrow = p, ncol = ny)        coef[qr$pivot[seq_len(k)], ] <- z$coef    } else coef <- z$coef    if (!is.null(nam <- colnames(qr$qr))) if (k < p) rownames(coef)[qr$pivot] <- nam else rownames(coef) <- nam    if (im && !is.null(nam <- colnames(y))) colnames(coef) <- nam    if (im) coef else drop(coef)});do.call('is.function', argv)
 Error: unexpected symbol in "argv <- list(function(qr, y) {    if (!is.qr(qr)) stop('first argument must be a QR decomposition')    n"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_isfunction.testisfunction32
@@ -23509,8 +23771,8 @@ FALSE FALSE
 [1] FALSE
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_islistfactor.testislistfactor12
-#argv <- list(structure(list(event = c('Min.   : 1.00  ', '1st Qu.: 9.00  ', 'Median :18.00  ', 'Mean   :14.74  ', '3rd Qu.:20.00  ', 'Max.   :23.00  ', NA), mag = c('Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA), station = c('117    :  5  ', '1028   :  4  ', '113    :  4  ', '112    :  3  ', '135    :  3  ', '(Other):147  ', 'NA's   : 16  '), dist = c('Min.   :  0.50  ', '1st Qu.: 11.32  ', 'Median : 23.40  ', 'Mean   : 45.60  ', '3rd Qu.: 47.55  ', 'Max.   :370.00  ', NA), accel = c('Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA)), .Names = c('event', 'mag', 'station', 'dist', 'accel')), TRUE); .Internal(islistfactor(argv[[1]], argv[[2]]))
-Error: unexpected symbol in " ', NA), mag = c('Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA), station = c('117    :  5  ', '1028   :  4  ', '113    : "
+#argv <- list(structure(list(event = c('Min.   : 1.00  ', '1st Qu.: 9.00  ', 'Median :18.00  ', 'Mean   :14.74  ', '3rd Qu.:20.00  ', 'Max.   :23.00  ', NA), mag = c('Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA), station = c('117    :  5  ', '1028   :  4  ', '113    :  4  ', '112    :  3  ', '135    :  3  ', '(Other):147  ', 'NAs   : 16  '), dist = c('Min.   :  0.50  ', '1st Qu.: 11.32  ', 'Median : 23.40  ', 'Mean   : 45.60  ', '3rd Qu.: 47.55  ', 'Max.   :370.00  ', NA), accel = c('Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA)), .Names = c('event', 'mag', 'station', 'dist', 'accel')), TRUE); .Internal(islistfactor(argv[[1]], argv[[2]]))
+[1] FALSE
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_islistfactor.testislistfactor13
 #argv <- list(structure(list(`1` = 1.97626258336499e-323), .Names = '1'), FALSE); .Internal(islistfactor(argv[[1]], argv[[2]]))
@@ -23997,8 +24259,15 @@ N
 [8,] FALSE FALSE FALSE FALSE FALSE
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_isna.testisna34
-#argv <- list(structure(c('Min.   : 1.00  ', '1st Qu.: 9.00  ', 'Median :18.00  ', 'Mean   :14.74  ', '3rd Qu.:20.00  ', 'Max.   :23.00  ', NA, 'Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA, '117    :  5  ', '1028   :  4  ', '113    :  4  ', '112    :  3  ', '135    :  3  ', '(Other):147  ', 'NA's   : 16  ', 'Min.   :  0.50  ', '1st Qu.: 11.32  ', 'Median : 23.40  ', 'Mean   : 45.60  ', '3rd Qu.: 47.55  ', 'Max.   :370.00  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')), class = 'table'));is.na(argv[[1]]);
-Error: unexpected symbol in "00  ', 'Max.   :23.00  ', NA, 'Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA, '117    :  5  ', '1028   :  4  ', '113    : "
+#argv <- list(structure(c('Min.   : 1.00  ', '1st Qu.: 9.00  ', 'Median :18.00  ', 'Mean   :14.74  ', '3rd Qu.:20.00  ', 'Max.   :23.00  ', NA, 'Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA, '117    :  5  ', '1028   :  4  ', '113    :  4  ', '112    :  3  ', '135    :  3  ', '(Other):147  ', 'NAs   : 16  ', 'Min.   :  0.50  ', '1st Qu.: 11.32  ', 'Median : 23.40  ', 'Mean   : 45.60  ', '3rd Qu.: 47.55  ', 'Max.   :370.00  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')), class = 'table'));is.na(argv[[1]]);
+     event      mag    station      dist     accel
+     FALSE    FALSE      FALSE     FALSE     FALSE
+     FALSE    FALSE      FALSE     FALSE     FALSE
+     FALSE    FALSE      FALSE     FALSE     FALSE
+     FALSE    FALSE      FALSE     FALSE     FALSE
+     FALSE    FALSE      FALSE     FALSE     FALSE
+     FALSE    FALSE      FALSE     FALSE     FALSE
+      TRUE     TRUE      FALSE      TRUE      TRUE
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_isna.testisna35
 #argv <- list(NA_complex_);is.na(argv[[1]]);
@@ -30579,6 +30848,10 @@ NULL
 #{ x<-c(1,2,3); dim(x)<-3; dimnames(x)<-list(c(11,12,13)); names(x) }
 [1] "11" "12" "13"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_names.testNames
+#{ y<-c(d="e"); attr(y, "foo")<-"foo"; x<-c(42); names(x)<-y; attributes(names(x)) }
+NULL
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_names.testnames1
 #argv <- list(structure(list(size = 113, isdir = FALSE, mode = structure(436L, class = 'octmode'), mtime = structure(1395082088.72988, class = c('POSIXct', 'POSIXt')), ctime = structure(1395082088.72988, class = c('POSIXct', 'POSIXt')), atime = structure(1395082088.77388, class = c('POSIXct', 'POSIXt')), uid = 1001L, gid = 1001L, uname = 'roman', grname = 'roman'), .Names = c('size', 'isdir', 'mode', 'mtime', 'ctime', 'atime', 'uid', 'gid', 'uname', 'grname'), class = 'data.frame', row.names = '/tmp/RtmptPgrXI/file55711ba85492'));names(argv[[1]]);
  [1] "size"   "isdir"  "mode"   "mtime"  "ctime"  "atime"  "uid"    "gid"
@@ -31823,6 +32096,28 @@ NULL
 [1] "some"
 [1] "test"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testColon
+#8.2:NULL
+Error in 8.2:NULL : argument of length 0
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testColon
+#8.2:c(9,8)
+[1] 8.2
+Warning message:
+In 8.2:c(9, 8) : numerical expression has 2 elements: only the first used
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testColon
+#NULL:5
+Error in NULL:5 : argument of length 0
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testColon
+#new.env():new.env()
+Error in new.env():new.env() : argument of length 0
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testColon
+#numeric(0):numeric(0)
+Error in numeric(0):numeric(0) : argument of length 0
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_operators.testDispatchToOpsSpecializations
 #data.frame(factor(c(1,2,1))) == data.frame(factor(c(1,2,2)))
      factor.c.1..2..1..
@@ -36750,6 +37045,21 @@ integer(0)
 #argv <- structure(list(pkgname = 'stats4', event = 'onLoad'),     .Names = c('pkgname', 'event'));do.call('packageEvent', argv)
 [1] "UserHook::stats4::onLoad"
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_pairlist.testPairList
+#{ x<-7; y<-c(foo=42); z<-pairlist(x, y); list(z, typeof(z)) }
+[[1]]
+[[1]][[1]]
+[1] 7
+
+[[1]][[2]]
+foo
+ 42
+
+
+[[2]]
+[1] "pairlist"
+
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_pairlist.testpairlist1
 #argv <- list();do.call('pairlist', argv)
 NULL
@@ -45686,6 +45996,16 @@ character(0)
 #argv <- list(character(0), 8L); .Internal(strtoi(argv[[1]], argv[[2]]))
 integer(0)
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_strtrim.teststrtrim
+#v <- c('a', 'fooooo', 'bbbbbb', 'cccccccccc', 'dd', NA); names(v) <- as.character(1:6); strtrim(v, c(2, 5))
+      1       2       3       4       5       6
+    "a" "foooo"    "bb" "ccccc"    "dd"      NA
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_strtrim.teststrtrim
+#v <- c('a', 'fooooo', 'bbbbbb', 'cccccccccc', 'dd', NA); names(v) <- as.character(1:6); strtrim(v, c(2L, 5L))
+      1       2       3       4       5       6
+    "a" "foooo"    "bb" "ccccc"    "dd"      NA
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_strtrim.teststrtrim1
 #argv <- list(c('\'time\'', '\'status\''), 128); .Internal(strtrim(argv[[1]], argv[[2]]))
 [1] "'time'"   "'status'"
@@ -47732,6 +48052,39 @@ integer(0)
 #{ u <- function() sys.parents() ; f <- function(x) x ; g <- function(y) f(y) ; h <- function(z=u()) g(z) ; h() }
 [1] 0 1 2 1
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#t(1)
+     [,1]
+[1,]    1
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#t(TRUE)
+     [,1]
+[1,] TRUE
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#t(as.raw(c(1,2,3,4)))
+     [,1] [,2] [,3] [,4]
+[1,]   01   02   03   04
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#t(new.env())
+Error in t.default(new.env()) : argument is not a matrix
+
+##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
+#v <- as.complex(1:50); dim(v) <- c(5,10); dimnames(v) <- list(as.character(40:44), as.character(10:19)); t(v)
+      40    41    42    43    44
+10  1+0i  2+0i  3+0i  4+0i  5+0i
+11  6+0i  7+0i  8+0i  9+0i 10+0i
+12 11+0i 12+0i 13+0i 14+0i 15+0i
+13 16+0i 17+0i 18+0i 19+0i 20+0i
+14 21+0i 22+0i 23+0i 24+0i 25+0i
+15 26+0i 27+0i 28+0i 29+0i 30+0i
+16 31+0i 32+0i 33+0i 34+0i 35+0i
+17 36+0i 37+0i 38+0i 39+0i 40+0i
+18 41+0i 42+0i 43+0i 44+0i 45+0i
+19 46+0i 47+0i 48+0i 49+0i 50+0i
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_t.testTranspose
 #{ m <- double() ; dim(m) <- c(0,4) ; t(m) }
 
@@ -49087,8 +49440,8 @@ Error in typeof(...) : unused arguments (2, 3, 4)
 [1] "list"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_typeof.testtypeof12
-#argv <- list(structure(c('Min.   : 1.000  ', '1st Qu.: 9.000  ', 'Median :18.000  ', 'Mean   :14.742  ', '3rd Qu.:20.000  ', 'Max.   :23.000  ', NA, 'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean   : 56.928  ', '3rd Qu.: 86.750  ', 'Max.   :117.000  ', 'NA's   :16  ', 'Min.   :  0.500  ', '1st Qu.: 11.325  ', 'Median : 23.400  ', 'Mean   : 45.603  ', '3rd Qu.: 47.550  ', 'Max.   :370.000  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')))); .Internal(typeof(argv[[1]]))
-Error: unexpected symbol in "'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean"
+#argv <- list(structure(c('Min.   : 1.000  ', '1st Qu.: 9.000  ', 'Median :18.000  ', 'Mean   :14.742  ', '3rd Qu.:20.000  ', 'Max.   :23.000  ', NA, 'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean   : 56.928  ', '3rd Qu.: 86.750  ', 'Max.   :117.000  ', 'NAs   :16  ', 'Min.   :  0.500  ', '1st Qu.: 11.325  ', 'Median : 23.400  ', 'Mean   : 45.603  ', '3rd Qu.: 47.550  ', 'Max.   :370.000  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')))); .Internal(typeof(argv[[1]]))
+[1] "character"
 
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_typeof.testtypeof13
 #argv <- list(c(2L, 1L, NA)); .Internal(typeof(argv[[1]]))
@@ -106815,26 +107168,38 @@ a b c d
 a b c d e
 1 2 3 4 5
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { 1 } else { .fastr.interop.eval('application/x-r', '1') }
 [1] 1
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { 16 } else { .fastr.interop.eval('application/x-r', '14 + 2') }
 [1] 16
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { 1L } else { .fastr.interop.eval('application/x-r', '1L') }
 [1] 1
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { TRUE } else { .fastr.interop.eval('application/x-r', 'TRUE') }
 [1] TRUE
 
-##com.oracle.truffle.r.test.library.fastr.TestInteropEval.testInteropEval
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropEval
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { as.character(123) } else { .fastr.interop.eval('application/x-r', 'as.character(123)') }
 [1] "123"
 
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropExport
+#if (length(grep("FastR", R.Version()$version.string)) != 1) { invisible() } else { .fastr.interop.export('foo', 'foo') }
+
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropExport
+#if (length(grep("FastR", R.Version()$version.string)) != 1) { invisible() } else { .fastr.interop.export('foo', 14 + 2) }
+
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropExport
+#if (length(grep("FastR", R.Version()$version.string)) != 1) { invisible() } else { .fastr.interop.export('foo', 1:100) }
+
+##com.oracle.truffle.r.test.library.fastr.TestInterop.testInteropExport
+#if (length(grep("FastR", R.Version()$version.string)) != 1) { invisible() } else { .fastr.interop.export('foo', new.env()) }
+
 ##com.oracle.truffle.r.test.library.fastr.TestStateTrans.testTransitions
 #if (length(grep("FastR", R.Version()$version.string)) != 1) { 1 } else { { f<-function(x) .fastr.refcountinfo(x); f(c(1,2)) } }
 [1] 1
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java
index 92e8441ae30fab8143ddffb5d46f4ac408426bde..d903558850aaac15c22318a819ca6b86323b9df3 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/S4/TestS4.java
@@ -77,7 +77,7 @@ public class TestS4 extends TestRBase {
         assertEval("{ x<-42; isS4(x) }");
         assertEval("{ x<-42; y<-asS4(x); isS4(y) }");
         assertEval("{ isS4(NULL) }");
-        assertEval("{ asS4(NULL); isS4(NULL }");
+        assertEval("{ asS4(NULL); isS4(NULL) }");
         assertEval("{  asS4(7:42) }");
     }
 
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
index 257566d7e83fdf6d15a746ff079664c03fdd5138..7b40db76e668e950a4a887f015dafb093c931042 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
@@ -192,8 +192,7 @@ public class TestBase {
                     if (updated) {
                         if (expectedOutputManager.checkOnly) {
                             // fail fast
-                            System.err.println("Test file:" + expectedOutputManager.outputFile + " is out of sync with unit tests");
-                            Utils.exit(1);
+                            Utils.rSuicideDefault("Test file:" + expectedOutputManager.outputFile + " is out of sync with unit tests");
                         }
                         System.out.println("updating " + expectedOutputManager.outputFile);
                     }
@@ -587,7 +586,8 @@ public class TestBase {
         return new CheckResult(ok, result, expected);
     }
 
-    private static String convertReferencesInOutput(String result) {
+    private static String convertReferencesInOutput(String input) {
+        String result = input;
         Matcher matcher = REFERENCE_PATTERN.matcher(result);
         HashMap<String, Integer> idsMap = new HashMap<>();
         int currentId = 1;
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Encoding.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Encoding.java
index 996a121369d4a842e62b78f57bee9f80f774c5e0..e240f384ac98453d7ee2320c9378d34156a50bcb 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Encoding.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Encoding.java
@@ -40,18 +40,18 @@ public class TestBuiltin_Encoding extends TestBase {
 
     @Test
     public void testEncoding5() {
-        assertEval(Ignored.Unknown, "argv <- list(structure('Type 'demo(PKG::FOO)' to run demonstration 'PKG::FOO'.', .Names = 'demo')); .Internal(Encoding(argv[[1]]))");
+        assertEval("argv <- list(structure('Type demo(PKG::FOO) to run demonstration PKG::FOO.', .Names = 'demo')); .Internal(Encoding(argv[[1]]))");
     }
 
     @Test
     public void testEncoding6() {
-        assertEval(Ignored.Unknown, "argv <- list('A shell of class documentation has been written to the file './myTst2/man/DocLink-class.Rd'.\\n'); .Internal(Encoding(argv[[1]]))");
+        assertEval("argv <- list('A shell of class documentation has been written to the file ./myTst2/man/DocLink-class.Rd.\\n'); .Internal(Encoding(argv[[1]]))");
     }
 
     @Test
     public void testEncoding7() {
         assertEval(Ignored.Unknown,
-                        "argv <- list(c('* Edit the help file skeletons in 'man', possibly combining help files for multiple functions.', '* Edit the exports in 'NAMESPACE', and add necessary imports.', '* Put any C/C++/Fortran code in 'src'.', '* If you have compiled code, add a useDynLib() directive to 'NAMESPACE'.', '* Run R CMD build to build the package tarball.', '* Run R CMD check to check the package tarball.', '', 'Read \\\'Writing R Extensions\\\' for more information.')); .Internal(Encoding(argv[[1]]))");
+                        "argv <- list(c('* Edit the help file skeletons in man, possibly combining help files for multiple functions.', '* Edit the exports in NAMESPACE, and add necessary imports.', '* Put any C/C++/Fortran code in src.', '* If you have compiled code, add a useDynLib() directive to NAMESPACE.', '* Run R CMD build to build the package tarball.', '* Run R CMD check to check the package tarball.', '', 'Read Writing R Extensions for more information.')); .Internal(Encoding(argv[[1]]))");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Primitive.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Primitive.java
index dd1cb83bac27a21d515b2dbc9d66231d545b9f4b..0a5be54776f6d0366f142b7175f1fc4fb3284c57 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Primitive.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_Primitive.java
@@ -19,6 +19,13 @@ public class TestBuiltin_Primitive extends TestBase {
 
     @Test
     public void testPrimitive1() {
-        assertEval(Ignored.Unknown, "argv <- list('c');.Primitive(argv[[1]]);");
+        // our c() primitive is missing an argument
+        assertEval(Ignored.ImplementationError, "argv <- list('c');.Primitive(argv[[1]]);");
+
+        assertEval(".Primitive(c('c', 'b'))");
+        assertEval(".Primitive('any')");
+        assertEval(".Primitive('foo')");
+        assertEval(".Primitive('complex')");
+        assertEval(".Primitive(1)");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_allnames.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_allnames.java
index 6b524e0712691b958e123cea58e990d90e51fff3..a0f38363966745b5c8cb15e586280f948a5b79cc 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_allnames.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_allnames.java
@@ -53,5 +53,16 @@ public class TestBuiltin_allnames extends TestBase {
         assertEval("all.names(quote(a+y+z+y*y*function(x=bar/2) baz(1)), functions=TRUE, max.names=10, unique=FALSE)");
         assertEval("all.names(quote(a+y+z+y*y*function(x=bar/2) baz(1)), functions=TRUE, max.names=2, unique=TRUE)");
         assertEval("all.names(quote(a+y+z+y*y*function(x=bar/2) baz(1)), functions=FALSE, max.names=10, unique=TRUE)");
+
+        assertEval("{ all.names(expression(sin(x+y+x)), functions=F) }");
+        assertEval("{ all.names(expression(sin(x+y+x)), functions=T) }");
+        assertEval("{ all.names(expression(sin(x+y+x)), functions=NULL) }");
+        assertEval("{ all.names(expression(sin(x+y+x)), functions=NA) }");
+        assertEval("{ all.names(expression(sin(x+y+x)), max.names=NULL) }");
+        assertEval("{ all.names(expression(sin(x+y+x)), max.names=NA) }");
+        assertEval("{ all.names(expression(sin(x+y+x)), unique=F) }");
+        assertEval("{ all.names(expression(sin(x+y+x)), unique=T) }");
+        assertEval("{ all.names(expression(sin(x+y+x)), unique=NULL) }");
+        assertEval("{ all.names(expression(sin(x+y+x)), unique=(NA) }");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_array.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_array.java
index 7afc488e9daa4c76a19ed3b9c2f78091522d3791..f3bbee15bd4d1f428380499e223d8e54256cb5c0 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_array.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_array.java
@@ -154,4 +154,20 @@ public class TestBuiltin_array extends TestBase {
     public void testarray27() {
         assertEval("argv <- list(-1, c(3L, 2L), list(c('a', 'b', 'c'), NULL)); .Internal(array(argv[[1]], argv[[2]], argv[[3]]))");
     }
+
+    @Test
+    public void testArray() {
+        assertEval(Output.IgnoreWarningContext, "{ array(1:4, 1:2, 4) }");
+        assertEval(Output.IgnoreWarningContext, "{ array(1:4, c(1+2i, 2+2i)) }");
+        assertEval("{ array(as.raw(1:4)) }");
+        assertEval("{ array(1:4, integer()) }");
+        assertEval("{ array(NULL) }");
+        assertEval("{ array(NA) }");
+        assertEval("{ array(1:4, NULL) }");
+        assertEval("{ .Internal(array(1:4, NULL, NULL)) }");
+        assertEval("{ .Internal(array(NULL, 1, NULL)) }");
+        assertEval("{ .Internal(array(NA, 1, NULL)) }");
+        assertEval("{ f<-function() 42; .Internal(array(f, 1, NULL)) }");
+    }
+
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java
index e1803e36a56ea56b8e5d0f603dcf5e3bcabc2d0c..1649f2aae78282d9b7d635e7ca98f41e5d11fd5d 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascall.java
@@ -59,5 +59,7 @@ public class TestBuiltin_ascall extends TestBase {
 
         assertEval("{ f <- function(x) x ; l <- list(f, 42) ; cl <- as.call(l); typeof(cl[[1]]) }");
         assertEval("{ f <- function(x) x ; l <- list(f, 42) ; cl <- as.call(l); typeof(cl[[2]]) }");
+
+        assertEval("{ as.call(42) }");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascomplex.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascomplex.java
index bdd5b4596b80bf4a7e1a0946cb8cc34723925f20..5de58ac5216bb0d7f4864cc708a62889c1dc00bd 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascomplex.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_ascomplex.java
@@ -96,5 +96,9 @@ public class TestBuiltin_ascomplex extends TestBase {
         assertEval(Ignored.Unknown, "{ as.complex(\"-.1e10+5i\") }");
         assertEval(Ignored.Unknown, "{ as.complex(\"1e-2+3i\") }");
         assertEval(Ignored.Unknown, "{ as.complex(\"+.1e+2-3i\") }");
+
+        assertEval("{ as.complex(list(42)) }");
+        assertEval(Output.IgnoreErrorContext, "{ as.complex(list(NULL)) }");
+        assertEval(Output.IgnoreWarningContext, "{ as.complex(list(\"foo\")) }");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_asvector.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_asvector.java
index 98c022e4509dc537db3353e8208aac0f4f5d1372..32836f39c093ee713aa4422d08768ec33416a3a5 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_asvector.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_asvector.java
@@ -421,6 +421,10 @@ public class TestBuiltin_asvector extends TestBase {
 
         assertEval("as.vector(x~z)");
         assertEval(Ignored.Unimplemented, "as.vector(file(''))");
+
+        assertEval(Output.IgnoreErrorContext, "{ as.vector(42, NULL) }");
+        assertEval("{ as.vector(42, c(\"character\", \"character\") }");
+        assertEval(Output.IgnoreErrorContext, "{ as.vector(42, character())  }");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_c.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_c.java
index b972661904eccb4e49daa0755387d353cb8a4cef..675c028cf17f9094cfe57ba181536738ab49672a 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_c.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_c.java
@@ -68,7 +68,7 @@ public class TestBuiltin_c extends TestBase {
     @Test
     public void testc10() {
         assertEval(Ignored.Unknown,
-                        "argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = 'supply both 'x' and 'y' or a matrix-like 'x'', call = quote(cor(rnorm(10), NULL))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))), .Names = c('class', 'condition')));c(argv[[1]],argv[[2]]);");
+                        "argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = 'supply both x and y or a matrix-like x', call = quote(cor(rnorm(10), NULL))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))), .Names = c('class', 'condition')));c(argv[[1]],argv[[2]]);");
     }
 
     @Test
@@ -127,7 +127,7 @@ public class TestBuiltin_c extends TestBase {
     @Test
     public void testc21() {
         assertEval(Ignored.Unknown,
-                        "argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = \''x' is empty', call = quote(cor(Z[, FALSE], use = 'pairwise.complete.obs', method = 'kendall'))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))), .Names = c('class', 'condition')));c(argv[[1]],argv[[2]]);");
+                        "argv <- list(NULL, structure(list(class = 'try-error', condition = structure(list(message = 'x is empty', call = quote(cor(Z[, FALSE], use = 'pairwise.complete.obs', method = 'kendall'))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))), .Names = c('class', 'condition')));c(argv[[1]],argv[[2]]);");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_complex.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_complex.java
index 166495ab7772c50e59904a86b027f24455ac6a93..9698cfdb0b5fe5ac45effa9dbb8fc9bd044df5b3 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_complex.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_complex.java
@@ -24,13 +24,12 @@ public class TestBuiltin_complex extends TestBase {
 
     @Test
     public void testcomplex2() {
-        assertEval(Ignored.Unknown, "argv <- list(FALSE, FALSE, numeric(0)); .Internal(complex(argv[[1]], argv[[2]], argv[[3]]))");
+        assertEval("argv <- list(FALSE, FALSE, numeric(0)); .Internal(complex(argv[[1]], argv[[2]], argv[[3]]))");
     }
 
     @Test
     public void testcomplex3() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(0L, 1:10, c(1, 1.4142135623731, 1.73205080756888, 2, 2.23606797749979, 2.44948974278318, 2.64575131106459, 2.82842712474619, 3, 3.16227766016838)); .Internal(complex(argv[[1]], argv[[2]], argv[[3]]))");
+        assertEval("argv <- list(0L, 1:10, c(1, 1.4142135623731, 1.73205080756888, 2, 2.23606797749979, 2.44948974278318, 2.64575131106459, 2.82842712474619, 3, 3.16227766016838)); .Internal(complex(argv[[1]], argv[[2]], argv[[3]]))");
     }
 
     @Test
@@ -50,7 +49,7 @@ public class TestBuiltin_complex extends TestBase {
 
     @Test
     public void testcomplex7() {
-        assertEval(Ignored.Unknown, "argv <- list(0L, NULL, numeric(0)); .Internal(complex(argv[[1]], argv[[2]], argv[[3]]))");
+        assertEval("argv <- list(0L, NULL, numeric(0)); .Internal(complex(argv[[1]], argv[[2]], argv[[3]]))");
     }
 
     @Test
@@ -58,6 +57,9 @@ public class TestBuiltin_complex extends TestBase {
         assertEval("{ complex(real=1,imaginary=2) }");
         assertEval("{ complex(real=1,imag=2) }");
         assertEval("{ complex(3) }");
+        assertEval(Output.IgnoreErrorMessage, "{ complex(new.env()) }");
+        assertEval("{ complex(3, new.env()) }");
+        assertEval("{ complex(3, 3, new.env()) }");
         assertEval("{ complex(3, c(1,2,3), c(4,5,6)) }");
         assertEval("{ complex(3, c(1,2,3), c(4,5)) }");
         assertEval("{ complex(3, c(1,2), c(4,5,6)) }");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_deparse.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_deparse.java
index 8a8b2d5ef5f58711e052ff74d34266dc699895e1..a8749fef6f05bb5dd1b7de8c1dc323d4a8b6db34 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_deparse.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_deparse.java
@@ -104,8 +104,7 @@ public class TestBuiltin_deparse extends TestBase {
 
     @Test
     public void testdeparse17() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list('Version of 'graph' is too old --- no tests done here!\\n', 60L, FALSE, 69, -1L); .Internal(deparse(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]]))");
+        assertEval("argv <- list('Version of graph is too old --- no tests done here!\\n', 60L, FALSE, 69, -1L); .Internal(deparse(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]]))");
     }
 
     @Test
@@ -173,7 +172,7 @@ public class TestBuiltin_deparse extends TestBase {
 
     @Test
     public void testdeparse30() {
-        assertEval(Ignored.Unknown, "argv <- list(structure(FALSE, .Dim = 1L), 60L, FALSE, 69, -1L); .Internal(deparse(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]]))");
+        assertEval("argv <- list(structure(FALSE, .Dim = 1L), 60L, FALSE, 69, -1L); .Internal(deparse(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]]))");
     }
 
     @Test
@@ -319,6 +318,8 @@ public class TestBuiltin_deparse extends TestBase {
 
         assertEval("unserialize(serialize(quote(!(a <- TRUE)), NULL))");
         assertEval("unserialize(serialize(quote(a[a <- TRUE]), NULL))");
+
+        assertEval("{ x<-c(a=42, b=7); deparse(x) }");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dfltWarn.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dfltWarn.java
index 5467dcff061e5de0fd987324ad38bb0dd205f4d6..f03a54da486fd77971c41815798d66ad92d11141 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dfltWarn.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dfltWarn.java
@@ -19,7 +19,7 @@ public class TestBuiltin_dfltWarn extends TestBase {
 
     @Test
     public void testdfltWarn1() {
-        assertEval(Ignored.Unknown, "argv <- list(\''f' is deprecated.\\nUse 'convertY' instead.\\nSee help(\\\'Deprecated\\\')', NULL); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
+        assertEval("argv <- list('f is deprecated.\\nUse convertY instead.\\nSee help(Deprecated)', NULL); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
     }
 
     @Test
@@ -35,8 +35,8 @@ public class TestBuiltin_dfltWarn extends TestBase {
 
     @Test
     public void testdfltWarn4() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list('header and 'col.names' are of different lengths', quote(read.table('foo3', header = TRUE, col.names = letters[1:4]))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
+        assertEval(Output.IgnoreWarningContext,
+                        "argv <- list('header and col.names are of different lengths', quote(read.table('foo3', header = TRUE, col.names = letters[1:4]))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
     }
 
     @Test
@@ -51,7 +51,7 @@ public class TestBuiltin_dfltWarn extends TestBase {
 
     @Test
     public void testdfltWarn7() {
-        assertEval(Ignored.Unknown, "argv <- list(\''drop' argument will be ignored', quote(`[.data.frame`(women, 'height', drop = FALSE))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
+        assertEval(Output.IgnoreWarningContext, "argv <- list(\'drop argument will be ignored', quote(`[.data.frame`(women, 'height', drop = FALSE))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
     }
 
     @Test
@@ -67,17 +67,19 @@ public class TestBuiltin_dfltWarn extends TestBase {
 
     @Test
     public void testdfltWarn10() {
-        assertEval(Ignored.Unknown, "argv <- list(\''x' is neither a vector nor a matrix: using as.numeric(x)', quote(dotchart(table(infert$education)))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
+        assertEval(Output.IgnoreWarningContext,
+                        "argv <- list(\'x is neither a vector nor a matrix: using as.numeric(x)', quote(dotchart(table(infert$education)))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
     }
 
     @Test
     public void testdfltWarn11() {
         assertEval(Ignored.Unknown,
-                        "argv <- list('Invalid file name(s) for R code in ./myTst/R:\\n  'file55711ba85492'\\n are now renamed to 'z<name>.R'', quote(package.skeleton('myTst', code_files = tmp))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
+                        "argv <- list('Invalid file name(s) for R code in ./myTst/R:\\n  file55711ba85492\\n are now renamed to z<name>.R', quote(package.skeleton('myTst', code_files = tmp))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
     }
 
     @Test
     public void testdfltWarn12() {
-        assertEval(Ignored.Unknown, "argv <- list('incomplete final line found by readTableHeader on 'foo4'', quote(read.table('foo4', header = TRUE))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
+        assertEval(Output.IgnoreWarningContext,
+                        "argv <- list('incomplete final line found by readTableHeader on foo4', quote(read.table('foo4', header = TRUE))); .Internal(.dfltWarn(argv[[1]], argv[[2]]))");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dimnames.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dimnames.java
index c6e6ef0e8969dac351325d9d866e235b3ad848da..0699ce3303ebe8b7963b4565c4c004423a86ddff 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dimnames.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dimnames.java
@@ -95,7 +95,7 @@ public class TestBuiltin_dimnames extends TestBase {
     @Test
     public void testdimnames16() {
         assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c('4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.0-3', '4.0-3', '4.0-3', '4.0-3', '4.0-3', '4.0-2', '4.0-2', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '3.1-55', '3.1-55', '3.1-55', '3.1-54', '3.1-53', '3.1-53', '3.1-52', '3.1-51', NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 'The C and R code has been reformatted for legibility.', 'The old compatibility function rpconvert() has been removed.', 'The cross-validation functions allow for user interrupt at the end\\nof evaluating each split.', 'Variable Reliability in data set car90 is corrected to be an\\nordered factor, as documented.', 'Surrogate splits are now considered only if they send two or more\\ncases _with non-zero weight_ each way.  For numeric/ordinal\\nvariables the restriction to non-zero weights is new: for\\ncategorical variables this is a new restriction.', 'Surrogate splits which improve only by rounding error over the\\ndefault split are no longer returned.  Where weights and missing\\nvalues are present, the splits component for some of these was not\\nreturned correctly.', 'A fit of class \\\'rpart\\\' now contains a component for variable\\n‘importance’, which is reported by the summary() method.', 'The text() method gains a minlength argument, like the labels()\\nmethod.  This adds finer control: the default remains pretty =\\nNULL, minlength = 1L.', 'The handling of fits with zero and fractional weights has been\\ncorrected: the results may be slightly different (or even\\nsubstantially different when the proportion of zero weights is\\nlarge).', 'Some memory leaks have been plugged.', 'There is a second vignette, longintro.Rnw, a version of the\\noriginal Mayo Tecnical Report on rpart.', 'Added dataset car90, a corrected version of the S-PLUS dataset\\ncar.all (used with permission).', 'This version does not use paste0{} and so works with R 2.14.x.', 'Merged in a set of Splus code changes that had accumulated at Mayo\\nover the course of a decade. The primary one is a change in how\\nindexing is done in the underlying C code, which leads to a major\\nspeed increase for large data sets.  Essentially, for the lower\\nleaves all our time used to be eaten up by bookkeeping, and this\\nwas replaced by a different approach.  The primary routine also\\nuses .Call{} so as to be more memory efficient.', 'The other major change was an error for asymmetric loss matrices,\\nprompted by a user query.  With L=loss asymmetric, the altered\\npriors were computed incorrectly - they were using L' instead of L.\\nUpshot - the tree would not not necessarily choose optimal splits\\nfor the given loss matrix.  Once chosen, splits were evaluated\\ncorrectly.  The printed “improvement” values are of course the\\nwrong ones as well.  It is interesting that for my little test\\ncase, with L quite asymmetric, the early splits in the tree are\\nunchanged - a good split still looks good.', 'Add the return.all argument to xpred.rpart().', 'Added a set of formal tests, i.e., cases with known answers to\\nwhich we can compare.', 'Add a usercode vignette, explaining how to add user defined\\nsplitting functions.', 'The class method now also returns the node probability.', 'Add the stagec data set, used in some tests.', 'The plot.rpart routine needs to store a value that will be visible\\nto the rpartco routine at a later time.  This is now done in an\\nenvironment in the namespace.', 'Force use of registered symbols in R >= 2.16.0', 'Update Polish translations.', 'Work on message formats.', 'Add Polish translations', 'rpart, rpart.matrix: allow backticks in formulae.', 'tests/backtick.R: regession test', 'src/xval.c: ensure unused code is not compiled in.', 'Change description of margin in ?plot.rpart as suggested by Bill\\nVenables.'), .Dim = c(29L, 4L)));dimnames(argv[[1]]);");
+                        "argv <- list(structure(c('4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.1-0', '4.0-3', '4.0-3', '4.0-3', '4.0-3', '4.0-3', '4.0-2', '4.0-2', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '4.0-1', '3.1-55', '3.1-55', '3.1-55', '3.1-54', '3.1-53', '3.1-53', '3.1-52', '3.1-51', NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 'The C and R code has been reformatted for legibility.', 'The old compatibility function rpconvert() has been removed.', 'The cross-validation functions allow for user interrupt at the end\\nof evaluating each split.', 'Variable Reliability in data set car90 is corrected to be an\\nordered factor, as documented.', 'Surrogate splits are now considered only if they send two or more\\ncases _with non-zero weight_ each way.  For numeric/ordinal\\nvariables the restriction to non-zero weights is new: for\\ncategorical variables this is a new restriction.', 'Surrogate splits which improve only by rounding error over the\\ndefault split are no longer returned.  Where weights and missing\\nvalues are present, the splits component for some of these was not\\nreturned correctly.', 'A fit of class rpart now contains a component for variable\\n‘importance’, which is reported by the summary() method.', 'The text() method gains a minlength argument, like the labels()\\nmethod.  This adds finer control: the default remains pretty =\\nNULL, minlength = 1L.', 'The handling of fits with zero and fractional weights has been\\ncorrected: the results may be slightly different (or even\\nsubstantially different when the proportion of zero weights is\\nlarge).', 'Some memory leaks have been plugged.', 'There is a second vignette, longintro.Rnw, a version of the\\noriginal Mayo Tecnical Report on rpart.', 'Added dataset car90, a corrected version of the S-PLUS dataset\\ncar.all (used with permission).', 'This version does not use paste0{} and so works with R 2.14.x.', 'Merged in a set of Splus code changes that had accumulated at Mayo\\nover the course of a decade. The primary one is a change in how\\nindexing is done in the underlying C code, which leads to a major\\nspeed increase for large data sets.  Essentially, for the lower\\nleaves all our time used to be eaten up by bookkeeping, and this\\nwas replaced by a different approach.  The primary routine also\\nuses .Call{} so as to be more memory efficient.', 'The other major change was an error for asymmetric loss matrices,\\nprompted by a user query.  With L=loss asymmetric, the altered\\npriors were computed incorrectly - they were using L' instead of L.\\nUpshot - the tree would not not necessarily choose optimal splits\\nfor the given loss matrix.  Once chosen, splits were evaluated\\ncorrectly.  The printed “improvement” values are of course the\\nwrong ones as well.  It is interesting that for my little test\\ncase, with L quite asymmetric, the early splits in the tree are\\nunchanged - a good split still looks good.', 'Add the return.all argument to xpred.rpart().', 'Added a set of formal tests, i.e., cases with known answers to\\nwhich we can compare.', 'Add a usercode vignette, explaining how to add user defined\\nsplitting functions.', 'The class method now also returns the node probability.', 'Add the stagec data set, used in some tests.', 'The plot.rpart routine needs to store a value that will be visible\\nto the rpartco routine at a later time.  This is now done in an\\nenvironment in the namespace.', 'Force use of registered symbols in R >= 2.16.0', 'Update Polish translations.', 'Work on message formats.', 'Add Polish translations', 'rpart, rpart.matrix: allow backticks in formulae.', 'tests/backtick.R: regession test', 'src/xval.c: ensure unused code is not compiled in.', 'Change description of margin in ?plot.rpart as suggested by Bill\\nVenables.'), .Dim = c(29L, 4L)));dimnames(argv[[1]]);");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dput.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dput.java
index 1b52a00a5b212b6feb9af291b2186895c564179e..0ee4403023b8d66ab9991baaacb9ccfe4c1e83d1 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dput.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dput.java
@@ -24,7 +24,7 @@ public class TestBuiltin_dput extends TestBase {
 
     @Test
     public void testdput2() {
-        assertEval(Ignored.Unimplemented, "argv <- list(structure(1, .Dim = 1L), structure(1L, class = c('terminal', 'connection')), 95); .Internal(dput(argv[[1]], argv[[2]], argv[[3]]))");
+        assertEval("argv <- list(structure(1, .Dim = 1L), structure(1L, class = c('terminal', 'connection')), 95); .Internal(dput(argv[[1]], argv[[2]], argv[[3]]))");
     }
 
     @Test
@@ -34,8 +34,7 @@ public class TestBuiltin_dput extends TestBase {
 
     @Test
     public void testdput4() {
-        assertEval(Ignored.Unimplemented,
-                        "argv <- list(structure(numeric(0), .Dim = c(0L, 0L)), structure(1L, class = c('terminal', 'connection')), 69); .Internal(dput(argv[[1]], argv[[2]], argv[[3]]))");
+        assertEval("argv <- list(structure(numeric(0), .Dim = c(0L, 0L)), structure(1L, class = c('terminal', 'connection')), 69); .Internal(dput(argv[[1]], argv[[2]], argv[[3]]))");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_iconv.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_iconv.java
index 03e3b887da67b8ddb8b18b44f12a5f0f183cfad4..0ce698940de8f884e5b26023f95f9249796a32d5 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_iconv.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_iconv.java
@@ -29,8 +29,7 @@ public class TestBuiltin_iconv extends TestBase {
 
     @Test
     public void testiconv3() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(c('% This file is part of the 'foreign' package for R', '% It is distributed under the GPL version 2 or later', '', '\\\\name{S3 read functions}', '\\\\alias{data.restore}', '\\\\alias{read.S}', '\\\\title{Read an S3 Binary or data.dump File}', '\\\\description{', '  Reads binary data files or \\\\code{data.dump} files that were produced', '  in S version 3.', '}', '\\\\usage{', '  data.restore(file, print = FALSE, verbose = FALSE, env = .GlobalEnv)', '  read.S(file)', '}', '\\\\arguments{', '  \\\\item{file}{the filename of the S-PLUS \\\\code{data.dump} or binary', '    file.}', '  \\\\item{print}{whether to print the name of each object as read from the', '    file.}', '  \\\\item{verbose}{whether to print the name of every subitem within each', '    object.}', '  \\\\item{env}{environment within which to create the restored object(s).}', '}', '\\\\value{', '  For \\\\code{read.S}, an R version of the S3 object.', '', '  For \\\\code{data.restore}, the name of the file.', '}', '\\\\details{', '  \\\\code{read.S} can read the binary files produced in some older', '  versions of S-PLUS on either Windows (versions 3.x, 4.x, 2000) or Unix', '  (version 3.x with 4 byte integers).  It automatically detects whether', '  the file was produced on a big- or little-endian machine and adapts', '  itself accordingly.', '', '  \\\\code{data.restore} can read a similar range of files produced by', '  \\\\code{data.dump} and for newer versions of S-PLUS, those from', '  \\\\code{data.dump(....., oldStyle=TRUE)}.', '', '  Not all S3 objects can be handled in the current version.  The most', '  frequently encountered exceptions are functions and expressions; you', '  will also have trouble with objects that contain model formulas.  In', '  particular, comments will be lost from function bodies, and the', '  argument lists of functions will often be changed.', '}', '\\\\author{', '  Duncan Murdoch', '}', '\\\\examples{', '\\\\dontrun{read.S(file.path(\\\'_Data\\\', \\\'myobj\\\'))', 'data.restore(\\\'dumpdata\\\', print = TRUE)', '}}', '\\\\keyword{data}', '\\\\keyword{file}'), '', 'ASCII', NA_character_, TRUE, FALSE); .Internal(iconv(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]]))");
+        assertEval("argv <- list(c('% This file is part of the foreign package for R', '% It is distributed under the GPL version 2 or later', '', '\\\\name{S3 read functions}', '\\\\alias{data.restore}', '\\\\alias{read.S}', '\\\\title{Read an S3 Binary or data.dump File}', '\\\\description{', '  Reads binary data files or \\\\code{data.dump} files that were produced', '  in S version 3.', '}', '\\\\usage{', '  data.restore(file, print = FALSE, verbose = FALSE, env = .GlobalEnv)', '  read.S(file)', '}', '\\\\arguments{', '  \\\\item{file}{the filename of the S-PLUS \\\\code{data.dump} or binary', '    file.}', '  \\\\item{print}{whether to print the name of each object as read from the', '    file.}', '  \\\\item{verbose}{whether to print the name of every subitem within each', '    object.}', '  \\\\item{env}{environment within which to create the restored object(s).}', '}', '\\\\value{', '  For \\\\code{read.S}, an R version of the S3 object.', '', '  For \\\\code{data.restore}, the name of the file.', '}', '\\\\details{', '  \\\\code{read.S} can read the binary files produced in some older', '  versions of S-PLUS on either Windows (versions 3.x, 4.x, 2000) or Unix', '  (version 3.x with 4 byte integers).  It automatically detects whether', '  the file was produced on a big- or little-endian machine and adapts', '  itself accordingly.', '', '  \\\\code{data.restore} can read a similar range of files produced by', '  \\\\code{data.dump} and for newer versions of S-PLUS, those from', '  \\\\code{data.dump(....., oldStyle=TRUE)}.', '', '  Not all S3 objects can be handled in the current version.  The most', '  frequently encountered exceptions are functions and expressions; you', '  will also have trouble with objects that contain model formulas.  In', '  particular, comments will be lost from function bodies, and the', '  argument lists of functions will often be changed.', '}', '\\\\author{', '  Duncan Murdoch', '}', '\\\\examples{', '\\\\dontrun{read.S(file.path(\\\'_Data\\\', \\\'myobj\\\'))', 'data.restore(\\\'dumpdata\\\', print = TRUE)', '}}', '\\\\keyword{data}', '\\\\keyword{file}'), '', 'ASCII', NA_character_, TRUE, FALSE); .Internal(iconv(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]]))");
     }
 
     @Test
@@ -62,6 +61,6 @@ public class TestBuiltin_iconv extends TestBase {
     @Test
     public void testiconv9() {
         assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c('Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance', 'Q.1 Opinion of president's job performance'), .Names = c('Q1_MISSING_NONE', 'Q1_MISSING_1', 'Q1_MISSING_2', 'Q1_MISSING_3', 'Q1_MISSING_RANGE', 'Q1_MISSING_LOW', 'Q1_MISSING_HIGH', 'Q1_MISSING_RANGE_1', 'Q1_MISSING_LOW_1', 'Q1_MISSING_HIGH_1')), 'latin1', '', NA_character_, TRUE, FALSE); .Internal(iconv(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]]))");
+                        "argv <- list(structure(c('Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance', 'Q.1 Opinion of presidents job performance'), .Names = c('Q1_MISSING_NONE', 'Q1_MISSING_1', 'Q1_MISSING_2', 'Q1_MISSING_3', 'Q1_MISSING_RANGE', 'Q1_MISSING_LOW', 'Q1_MISSING_HIGH', 'Q1_MISSING_RANGE_1', 'Q1_MISSING_LOW_1', 'Q1_MISSING_HIGH_1')), 'latin1', '', NA_character_, TRUE, FALSE); .Internal(iconv(argv[[1]], argv[[2]], argv[[3]], argv[[4]], argv[[5]], argv[[6]]))");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_invisible.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_invisible.java
index f25adfe7555c9a500ff47a08174095392bad2d3e..8c1beff46aae94a14376b86bb6d937b7fecf9da9 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_invisible.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_invisible.java
@@ -24,8 +24,7 @@ public class TestBuiltin_invisible extends TestBase {
 
     @Test
     public void testinvisible2() {
-        assertEval(Output.IgnoreErrorContext,
-                        "argv <- list(structure('Error in cov(rnorm(10), NULL) : \\n  supply both 'x' and 'y' or a matrix-like 'x'\\n', class = 'try-error', condition = structure(list(message = 'supply both 'x' and 'y' or a matrix-like 'x'', call = quote(cov(rnorm(10), NULL))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);");
+        assertEval("argv <- list(structure('Error in cov(rnorm(10), NULL) : \\n  supply both x and y or a matrix-like x\\n', class = 'try-error', condition = structure(list(message = 'supply both x and y or a matrix-like x', call = quote(cov(rnorm(10), NULL))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);");
     }
 
     @Test
@@ -40,8 +39,7 @@ public class TestBuiltin_invisible extends TestBase {
 
     @Test
     public void testinvisible5() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c('Min.   : 1.000  ', '1st Qu.: 9.000  ', 'Median :18.000  ', 'Mean   :14.742  ', '3rd Qu.:20.000  ', 'Max.   :23.000  ', NA, 'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean   : 56.928  ', '3rd Qu.: 86.750  ', 'Max.   :117.000  ', 'NA's   :16  ', 'Min.   :  0.500  ', '1st Qu.: 11.325  ', 'Median : 23.400  ', 'Mean   : 45.603  ', '3rd Qu.: 47.550  ', 'Max.   :370.000  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')), class = 'table'));invisible(argv[[1]]);");
+        assertEval("argv <- list(structure(c('Min.   : 1.000  ', '1st Qu.: 9.000  ', 'Median :18.000  ', 'Mean   :14.742  ', '3rd Qu.:20.000  ', 'Max.   :23.000  ', NA, 'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean   : 56.928  ', '3rd Qu.: 86.750  ', 'Max.   :117.000  ', 'NAs   :16  ', 'Min.   :  0.500  ', '1st Qu.: 11.325  ', 'Median : 23.400  ', 'Mean   : 45.603  ', '3rd Qu.: 47.550  ', 'Max.   :370.000  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')), class = 'table'));invisible(argv[[1]]);");
     }
 
     @Test
@@ -96,8 +94,7 @@ public class TestBuiltin_invisible extends TestBase {
 
     @Test
     public void testinvisible16() {
-        assertEval(Output.IgnoreErrorContext,
-                        "argv <- list(structure('Error in cor(Z[, FALSE], use = \\\'pairwise.complete.obs\\\', method = \\\'kendall\\\') : \\n  'x' is empty\\n', class = 'try-error', condition = structure(list(message = \''x' is empty', call = quote(cor(Z[, FALSE], use = 'pairwise.complete.obs', method = 'kendall'))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);");
+        assertEval("argv <- list(structure('Error in cor(Z[, FALSE], use = pairwise.complete.obs, method = kendall) : \\n  x is empty\\n', class = 'try-error', condition = structure(list(message = \'x is empty', call = quote(cor(Z[, FALSE], use = 'pairwise.complete.obs', method = 'kendall'))), .Names = c('message', 'call'), class = c('simpleError', 'error', 'condition'))));invisible(argv[[1]]);");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isfunction.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isfunction.java
index f15a31f34a90722fb2122b786ac77df30e1569d3..790bd8e0c9183f5f406b5d7f5da323e7215edcc8 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isfunction.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isfunction.java
@@ -173,7 +173,7 @@ public class TestBuiltin_isfunction extends TestBase {
     @Test
     public void testisfunction31() {
         assertEval(Ignored.Unknown,
-                        "argv <- list(function(qr, y) {    if (!is.qr(qr)) stop('first argument must be a QR decomposition')    n <- as.integer(nrow(qr$qr))    if (is.na(n)) stop('invalid nrow(qr$qr)')    p <- as.integer(ncol(qr$qr))    if (is.na(p)) stop('invalid ncol(qr$qr)')    k <- as.integer(qr$rank)    if (is.na(k)) stop('invalid ncol(qr$rank)')    im <- is.matrix(y)    if (!im) y <- as.matrix(y)    ny <- as.integer(ncol(y))    if (is.na(ny)) stop('invalid ncol(y)')    if (p == 0L) return(if (im) matrix(0, p, ny) else numeric())    ix <- if (p > n) c(seq_len(n), rep(NA, p - n)) else seq_len(p)    if (is.complex(qr$qr)) {        coef <- matrix(NA_complex_, nrow = p, ncol = ny)        coef[qr$pivot, ] <- .Internal(qr_coef_cmplx(qr, y))[ix,             ]        return(if (im) coef else c(coef))    }    if (isTRUE(attr(qr, 'useLAPACK'))) {        coef <- matrix(NA_real_, nrow = p, ncol = ny)        coef[qr$pivot, ] <- .Internal(qr_coef_real(qr, y))[ix,             ]        return(if (im) coef else c(coef))    }    if (k == 0L) return(if (im) matrix(NA, p, ny) else rep.int(NA,         p))    storage.mode(y) <- 'double'    if (nrow(y) != n) stop(\''qr' and 'y' must have the same number of rows')    z <- .Fortran(.F_dqrcf, as.double(qr$qr), n, k, as.double(qr$qraux),         y, ny, coef = matrix(0, nrow = k, ncol = ny), info = integer(1L),         NAOK = TRUE)[c('coef', 'info')]    if (z$info) stop('exact singularity in 'qr.coef'')    if (k < p) {        coef <- matrix(NA_real_, nrow = p, ncol = ny)        coef[qr$pivot[seq_len(k)], ] <- z$coef    } else coef <- z$coef    if (!is.null(nam <- colnames(qr$qr))) if (k < p) rownames(coef)[qr$pivot] <- nam else rownames(coef) <- nam    if (im && !is.null(nam <- colnames(y))) colnames(coef) <- nam    if (im) coef else drop(coef)});" +
+                        "argv <- list(function(qr, y) {    if (!is.qr(qr)) stop('first argument must be a QR decomposition')    n <- as.integer(nrow(qr$qr))    if (is.na(n)) stop('invalid nrow(qr$qr)')    p <- as.integer(ncol(qr$qr))    if (is.na(p)) stop('invalid ncol(qr$qr)')    k <- as.integer(qr$rank)    if (is.na(k)) stop('invalid ncol(qr$rank)')    im <- is.matrix(y)    if (!im) y <- as.matrix(y)    ny <- as.integer(ncol(y))    if (is.na(ny)) stop('invalid ncol(y)')    if (p == 0L) return(if (im) matrix(0, p, ny) else numeric())    ix <- if (p > n) c(seq_len(n), rep(NA, p - n)) else seq_len(p)    if (is.complex(qr$qr)) {        coef <- matrix(NA_complex_, nrow = p, ncol = ny)        coef[qr$pivot, ] <- .Internal(qr_coef_cmplx(qr, y))[ix,             ]        return(if (im) coef else c(coef))    }    if (isTRUE(attr(qr, 'useLAPACK'))) {        coef <- matrix(NA_real_, nrow = p, ncol = ny)        coef[qr$pivot, ] <- .Internal(qr_coef_real(qr, y))[ix,             ]        return(if (im) coef else c(coef))    }    if (k == 0L) return(if (im) matrix(NA, p, ny) else rep.int(NA,         p))    storage.mode(y) <- 'double'    if (nrow(y) != n) stop('qr and y must have the same number of rows')    z <- .Fortran(.F_dqrcf, as.double(qr$qr), n, k, as.double(qr$qraux),         y, ny, coef = matrix(0, nrow = k, ncol = ny), info = integer(1L),         NAOK = TRUE)[c('coef', 'info')]    if (z$info) stop('exact singularity in qr.coef')    if (k < p) {        coef <- matrix(NA_real_, nrow = p, ncol = ny)        coef[qr$pivot[seq_len(k)], ] <- z$coef    } else coef <- z$coef    if (!is.null(nam <- colnames(qr$qr))) if (k < p) rownames(coef)[qr$pivot] <- nam else rownames(coef) <- nam    if (im && !is.null(nam <- colnames(y))) colnames(coef) <- nam    if (im) coef else drop(coef)});" +
                                         "do.call('is.function', argv)");
     }
 
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_islistfactor.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_islistfactor.java
index 92ab4c440a4a300310b9e641e32c1a0e159973f5..407f1c5319495385436c0911f10e66cc479f0b3c 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_islistfactor.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_islistfactor.java
@@ -75,7 +75,7 @@ public class TestBuiltin_islistfactor extends TestBase {
     @Test
     public void testislistfactor12() {
         assertEval(Output.IgnoreErrorMessage,
-                        "argv <- list(structure(list(event = c('Min.   : 1.00  ', '1st Qu.: 9.00  ', 'Median :18.00  ', 'Mean   :14.74  ', '3rd Qu.:20.00  ', 'Max.   :23.00  ', NA), mag = c('Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA), station = c('117    :  5  ', '1028   :  4  ', '113    :  4  ', '112    :  3  ', '135    :  3  ', '(Other):147  ', 'NA's   : 16  '), dist = c('Min.   :  0.50  ', '1st Qu.: 11.32  ', 'Median : 23.40  ', 'Mean   : 45.60  ', '3rd Qu.: 47.55  ', 'Max.   :370.00  ', NA), accel = c('Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA)), .Names = c('event', 'mag', 'station', 'dist', 'accel')), TRUE); .Internal(islistfactor(argv[[1]], argv[[2]]))");
+                        "argv <- list(structure(list(event = c('Min.   : 1.00  ', '1st Qu.: 9.00  ', 'Median :18.00  ', 'Mean   :14.74  ', '3rd Qu.:20.00  ', 'Max.   :23.00  ', NA), mag = c('Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA), station = c('117    :  5  ', '1028   :  4  ', '113    :  4  ', '112    :  3  ', '135    :  3  ', '(Other):147  ', 'NAs   : 16  '), dist = c('Min.   :  0.50  ', '1st Qu.: 11.32  ', 'Median : 23.40  ', 'Mean   : 45.60  ', '3rd Qu.: 47.55  ', 'Max.   :370.00  ', NA), accel = c('Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA)), .Names = c('event', 'mag', 'station', 'dist', 'accel')), TRUE); .Internal(islistfactor(argv[[1]], argv[[2]]))");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isna.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isna.java
index 4fa4917ba84d833dd5686af2620e4b790639393f..68b9a80272e0df9d2732cbd8b702aeea6439d482 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isna.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_isna.java
@@ -178,7 +178,7 @@ public class TestBuiltin_isna extends TestBase {
     @Test
     public void testisna34() {
         assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c('Min.   : 1.00  ', '1st Qu.: 9.00  ', 'Median :18.00  ', 'Mean   :14.74  ', '3rd Qu.:20.00  ', 'Max.   :23.00  ', NA, 'Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA, '117    :  5  ', '1028   :  4  ', '113    :  4  ', '112    :  3  ', '135    :  3  ', '(Other):147  ', 'NA's   : 16  ', 'Min.   :  0.50  ', '1st Qu.: 11.32  ', 'Median : 23.40  ', 'Mean   : 45.60  ', '3rd Qu.: 47.55  ', 'Max.   :370.00  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')), class = 'table'));is.na(argv[[1]]);");
+                        "argv <- list(structure(c('Min.   : 1.00  ', '1st Qu.: 9.00  ', 'Median :18.00  ', 'Mean   :14.74  ', '3rd Qu.:20.00  ', 'Max.   :23.00  ', NA, 'Min.   :5.000  ', '1st Qu.:5.300  ', 'Median :6.100  ', 'Mean   :6.084  ', '3rd Qu.:6.600  ', 'Max.   :7.700  ', NA, '117    :  5  ', '1028   :  4  ', '113    :  4  ', '112    :  3  ', '135    :  3  ', '(Other):147  ', 'NAs   : 16  ', 'Min.   :  0.50  ', '1st Qu.: 11.32  ', 'Median : 23.40  ', 'Mean   : 45.60  ', '3rd Qu.: 47.55  ', 'Max.   :370.00  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')), class = 'table'));is.na(argv[[1]]);");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_names.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_names.java
index 7a1ba027b7e2836032a9fc06232474901b940c51..cd568a219dfdb357ab3e656147f43c3f6ef9d095 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_names.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_names.java
@@ -203,6 +203,7 @@ public class TestBuiltin_names extends TestBase {
         assertEval("v <- parse(text=\"useDynLib(digest, digest_impl=digest)\"); names(v[[1]][[3]])");
         assertEval("{ x<-c(1,2,3); dim(x)<-3; dimnames(x)<-list(c(11,12,13)); names(x) }");
         assertEval("{ symNames <- c(\"foobar\", \"bar\"); names(symNames) = symNames; names(names(symNames)); }");
+        assertEval("{ y<-c(d=\"e\"); attr(y, \"foo\")<-\"foo\"; x<-c(42); names(x)<-y; attributes(names(x)) }");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_operators.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_operators.java
index b7bbc8a6b8992576fcdae3d4042a9ceacb35a3ee..3f34620f9e2e627694684588e82f94439904bce0 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_operators.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_operators.java
@@ -1971,4 +1971,13 @@ public class TestBuiltin_operators extends TestBase {
         assertEval("{ c(\"hello\", \"say\") %in% c(\"I\", \"say\", \"hello\", \"world\") }");
         assertEval("{ `%in%`(2,c(1,2,3)) }");
     }
+
+    @Test
+    public void testColon() {
+        assertEval("NULL:5");
+        assertEval("8.2:NULL");
+        assertEval("8.2:c(9,8)");
+        assertEval("new.env():new.env()");
+        assertEval("numeric(0):numeric(0)");
+    }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_pairlist.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_pairlist.java
index bcf662251692789948b05cf944e737d65507f520..5c44cfe13b3b3ffc5126d1bea67ea46041fb5854 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_pairlist.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_pairlist.java
@@ -22,4 +22,10 @@ public class TestBuiltin_pairlist extends TestBase {
     public void testpairlist1() {
         assertEval("argv <- list();do.call('pairlist', argv)");
     }
+
+    @Test
+    public void testPairList() {
+        assertEval("{ x<-7; y<-c(foo=42); z<-pairlist(x, y); list(z, typeof(z)) }");
+    }
+
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtrim.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtrim.java
index 6c566f53a4cd418c70b1f9cf22aeb9019ebc992c..00fc2dc52149e953dff0057469a515912a4decb2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtrim.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_strtrim.java
@@ -51,4 +51,10 @@ public class TestBuiltin_strtrim extends TestBase {
     public void teststrtrim8() {
         assertEval("argv <- list(character(0), 40L); .Internal(strtrim(argv[[1]], argv[[2]]))");
     }
+
+    @Test
+    public void teststrtrim() {
+        assertEval("v <- c('a', 'fooooo', 'bbbbbb', 'cccccccccc', 'dd', NA); names(v) <- as.character(1:6); strtrim(v, c(2L, 5L))");
+        assertEval("v <- c('a', 'fooooo', 'bbbbbb', 'cccccccccc', 'dd', NA); names(v) <- as.character(1:6); strtrim(v, c(2, 5))");
+    }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_t.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_t.java
index 3d851f24c7e6da980f8521ef2ff438e066cf4420..693cde32eb6e252583908b569377a1a8b142a8c2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_t.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_t.java
@@ -40,5 +40,11 @@ public class TestBuiltin_t extends TestBase {
         assertEval("{ t(t(matrix(1:4, nrow=2))) }");
 
         assertEval("{ x<-matrix(1:2, ncol=2, dimnames=list(\"a\", c(\"b\", \"c\"))); t(x) }");
+
+        assertEval("t(new.env())");
+        assertEval("v <- as.complex(1:50); dim(v) <- c(5,10); dimnames(v) <- list(as.character(40:44), as.character(10:19)); t(v)");
+        assertEval("t(1)");
+        assertEval("t(TRUE)");
+        assertEval("t(as.raw(c(1,2,3,4)))");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tolower.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tolower.java
index 8b0588b2bdd7b3dbe88f9dcc5db85d1dbc65c20f..f216fb17383d13f45e41e65f7a756002c2cc96a6 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tolower.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_tolower.java
@@ -39,7 +39,7 @@ public class TestBuiltin_tolower extends TestBase {
 
     @Test
     public void testtolower5() {
-        assertEval(Ignored.Unimplemented, "argv <- list(structure('base', .Names = 'Priority')); .Internal(tolower(argv[[1]]))");
+        assertEval("argv <- list(structure('base', .Names = 'Priority')); .Internal(tolower(argv[[1]]))");
     }
 
     @Test
@@ -57,8 +57,9 @@ public class TestBuiltin_tolower extends TestBase {
         assertEval("{ tolower(c(\"Hello\",\"ByE\")) }");
         assertEval("{ tolower(c()) }");
 
+        // double-to-string conversion problem
         assertEval(Ignored.OutputFormatting, "{ tolower(1E100) }");
-        assertEval(Ignored.Unimplemented, "{ tolower(c(a=\"HI\", \"HELlo\")) }");
+        assertEval("{ tolower(c(a=\"HI\", \"HELlo\")) }");
         assertEval("{ tolower(NA) }");
 
         assertEval("tolower(c('NA', 'na'))");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_toupper.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_toupper.java
index d00b82523eba4d16051e8f3682407f4e391229be..a6a2008deac27591833cea3559e7b408bbcffd1f 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_toupper.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_toupper.java
@@ -34,8 +34,7 @@ public class TestBuiltin_toupper extends TestBase {
 
     @Test
     public void testtoupper4() {
-        assertEval(Ignored.Unimplemented,
-                        "argv <- list(structure(c('BasicClasses', 'Classes', 'Documentation', 'environment-class', 'GenericFunctions', 'language-class', 'LinearMethodsList-class', 'MethodDefinition-class', 'MethodWithNext-class', 'Methods', 'MethodsList-class', 'callNextMethod', 'ObjectsWithPackage-class', 'S3Part', 'S4groupGeneric', 'SClassExtension-class', 'StructureClasses', 'TraceClasses', 'as', 'callGeneric', 'canCoerce', 'cbind2', 'className', 'classRepresentation-class', 'classesToAM', 'dotsMethods', 'evalSource', 'findClass', 'findMethods', 'fixPre1.8', 'genericFunction-class', 'getClass', 'getMethod', 'getPackageName', 'hasArg', 'implicitGeneric', 'inheritedSlotNames', 'initialize-methods', 'is', 'isSealedMethod', 'LocalReferenceClasses', 'method.skeleton', 'new', 'nonStructure-class', 'promptClass', 'promptMethods', 'ReferenceClasses', 'representation', 'selectSuperClasses', 'setClass', 'setClassUnion', 'setGeneric', 'setLoadActions', 'setMethod', 'setOldClass', 'makeClassRepresentation', 'show', 'showMethods', 'signature-class', 'slot', 'envRefClass-class', 'testInheritedMethods', 'validObject', '.BasicFunsList'), .Names = c('/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/BasicClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Classes.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Documentation.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/EnvironmentClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/GenericFunctions.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/LanguageClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/LinearMethodsList-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodDefinition-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodWithNext-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Methods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodsList-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/NextMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/ObjectsWithPackage-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/S3Part.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/S4groupGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/SClassExtension-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/StructureClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/TraceClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/as.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/callGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/canCoerce.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/cbind2.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/className.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/classRepresentation-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/classesToAM.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/dotsMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/evalSource.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/findClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/findMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/fixPrevious.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/genericFunction-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getPackageName.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/hasArg.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/implicitGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/inheritedSlotNames.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/initialize-methods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/is.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/isSealedMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/localRefClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/method.skeleton.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/new.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/nonStructure-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/promptClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/promptMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/refClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/representation.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/selectSuperClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setClassUnion.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setLoadActions.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setOldClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setSClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/show.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/showMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/signature-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/slot.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/stdRefClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/testInheritedMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/validObject.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/zBasicFunsList.tex'))); .Internal(toupper(argv[[1]]))");
+        assertEval("argv <- list(structure(c('BasicClasses', 'Classes', 'Documentation', 'environment-class', 'GenericFunctions', 'language-class', 'LinearMethodsList-class', 'MethodDefinition-class', 'MethodWithNext-class', 'Methods', 'MethodsList-class', 'callNextMethod', 'ObjectsWithPackage-class', 'S3Part', 'S4groupGeneric', 'SClassExtension-class', 'StructureClasses', 'TraceClasses', 'as', 'callGeneric', 'canCoerce', 'cbind2', 'className', 'classRepresentation-class', 'classesToAM', 'dotsMethods', 'evalSource', 'findClass', 'findMethods', 'fixPre1.8', 'genericFunction-class', 'getClass', 'getMethod', 'getPackageName', 'hasArg', 'implicitGeneric', 'inheritedSlotNames', 'initialize-methods', 'is', 'isSealedMethod', 'LocalReferenceClasses', 'method.skeleton', 'new', 'nonStructure-class', 'promptClass', 'promptMethods', 'ReferenceClasses', 'representation', 'selectSuperClasses', 'setClass', 'setClassUnion', 'setGeneric', 'setLoadActions', 'setMethod', 'setOldClass', 'makeClassRepresentation', 'show', 'showMethods', 'signature-class', 'slot', 'envRefClass-class', 'testInheritedMethods', 'validObject', '.BasicFunsList'), .Names = c('/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/BasicClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Classes.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Documentation.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/EnvironmentClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/GenericFunctions.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/LanguageClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/LinearMethodsList-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodDefinition-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodWithNext-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/Methods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/MethodsList-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/NextMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/ObjectsWithPackage-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/S3Part.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/S4groupGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/SClassExtension-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/StructureClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/TraceClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/as.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/callGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/canCoerce.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/cbind2.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/className.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/classRepresentation-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/classesToAM.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/dotsMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/evalSource.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/findClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/findMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/fixPrevious.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/genericFunction-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/getPackageName.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/hasArg.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/implicitGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/inheritedSlotNames.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/initialize-methods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/is.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/isSealedMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/localRefClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/method.skeleton.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/new.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/nonStructure-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/promptClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/promptMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/refClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/representation.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/selectSuperClasses.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setClassUnion.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setGeneric.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setLoadActions.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setMethod.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setOldClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/setSClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/show.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/showMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/signature-class.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/slot.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/stdRefClass.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/testInheritedMethods.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/validObject.tex', '/home/lzhao/tmp/RtmpZy1R7l/ltx5573594f0cc9/zBasicFunsList.tex'))); .Internal(toupper(argv[[1]]))");
     }
 
     @Test
@@ -51,6 +50,6 @@ public class TestBuiltin_toupper extends TestBase {
 
         assertEval(Ignored.OutputFormatting, "{ toupper(1E100) }");
         assertEval("{ m <- matrix(\"hi\") ; toupper(m) }");
-        assertEval(Ignored.Unimplemented, "{ toupper(c(a=\"hi\", \"hello\")) }");
+        assertEval("{ toupper(c(a=\"hi\", \"hello\")) }");
     }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_typeof.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_typeof.java
index 538c7491212d008c22fb772352a6a0fdce639bf8..d1ed3f5fcc3c5d4effa1d444c3162f6cb2bb1caf 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_typeof.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_typeof.java
@@ -74,8 +74,7 @@ public class TestBuiltin_typeof extends TestBase {
 
     @Test
     public void testtypeof12() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c('Min.   : 1.000  ', '1st Qu.: 9.000  ', 'Median :18.000  ', 'Mean   :14.742  ', '3rd Qu.:20.000  ', 'Max.   :23.000  ', NA, 'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean   : 56.928  ', '3rd Qu.: 86.750  ', 'Max.   :117.000  ', 'NA's   :16  ', 'Min.   :  0.500  ', '1st Qu.: 11.325  ', 'Median : 23.400  ', 'Mean   : 45.603  ', '3rd Qu.: 47.550  ', 'Max.   :370.000  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')))); .Internal(typeof(argv[[1]]))");
+        assertEval("argv <- list(structure(c('Min.   : 1.000  ', '1st Qu.: 9.000  ', 'Median :18.000  ', 'Mean   :14.742  ', '3rd Qu.:20.000  ', 'Max.   :23.000  ', NA, 'Min.   :5.0000  ', '1st Qu.:5.3000  ', 'Median :6.1000  ', 'Mean   :6.0841  ', '3rd Qu.:6.6000  ', 'Max.   :7.7000  ', NA, 'Min.   :  1.000  ', '1st Qu.: 24.250  ', 'Median : 56.500  ', 'Mean   : 56.928  ', '3rd Qu.: 86.750  ', 'Max.   :117.000  ', 'NAs   :16  ', 'Min.   :  0.500  ', '1st Qu.: 11.325  ', 'Median : 23.400  ', 'Mean   : 45.603  ', '3rd Qu.: 47.550  ', 'Max.   :370.000  ', NA, 'Min.   :0.00300  ', '1st Qu.:0.04425  ', 'Median :0.11300  ', 'Mean   :0.15422  ', '3rd Qu.:0.21925  ', 'Max.   :0.81000  ', NA), .Dim = c(7L, 5L), .Dimnames = list(c('', '', '', '', '', '', ''), c('    event', '     mag', '   station', '     dist', '    accel')))); .Internal(typeof(argv[[1]]))");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmax.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmax.java
index 2fe9d80df244312a224515e494fa1fc7ced6783c..ddf4c669f82f8a705cacddb8ba2f8b227097aad2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmax.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmax.java
@@ -24,7 +24,7 @@ public class TestBuiltin_whichmax extends TestBase {
 
     @Test
     public void testwhichmax2() {
-        assertEval(Ignored.Unknown, "argv <- list(structure(c(TRUE, FALSE), .Names = c('d', 'I(as.numeric(d)^2)'))); .Internal(which.max(argv[[1]]))");
+        assertEval("argv <- list(structure(c(TRUE, FALSE), .Names = c('d', 'I(as.numeric(d)^2)'))); .Internal(which.max(argv[[1]]))");
     }
 
     @Test
@@ -34,23 +34,22 @@ public class TestBuiltin_whichmax extends TestBase {
 
     @Test
     public void testwhichmax4() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c(NA, 87, 82, 75, 63, 50, 43, 32, 35, 60, 54, 55, 36, 39, NA, NA, 69, 57, 57, 51, 45, 37, 46, 39, 36, 24, 32, 23, 25, 32, NA, 32, 59, 74, 75, 60, 71, 61, 71, 57, 71, 68, 79, 73, 76, 71, 67, 75, 79, 62, 63, 57, 60, 49, 48, 52, 57, 62, 61, 66, 71, 62, 61, 57, 72, 83, 71, 78, 79, 71, 62, 74, 76, 64, 62, 57, 80, 73, 69, 69, 71, 64, 69, 62, 63, 46, 56, 44, 44, 52, 38, 46, 36, 49, 35, 44, 59, 65, 65, 56, 66, 53, 61, 52, 51, 48, 54, 49, 49, 61, NA, NA, 68, 44, 40, 27, 28, 25, 24, 24), .Tsp = c(1945, 1974.75, 4), class = 'ts')); .Internal(which.max(argv[[1]]))");
+        assertEval("argv <- list(structure(c(NA, 87, 82, 75, 63, 50, 43, 32, 35, 60, 54, 55, 36, 39, NA, NA, 69, 57, 57, 51, 45, 37, 46, 39, 36, 24, 32, 23, 25, 32, NA, 32, 59, 74, 75, 60, 71, 61, 71, 57, 71, 68, 79, 73, 76, 71, 67, 75, 79, 62, 63, 57, 60, 49, 48, 52, 57, 62, 61, 66, 71, 62, 61, 57, 72, 83, 71, 78, 79, 71, 62, 74, 76, 64, 62, 57, 80, 73, 69, 69, 71, 64, 69, 62, 63, 46, 56, 44, 44, 52, 38, 46, 36, 49, 35, 44, 59, 65, 65, 56, 66, 53, 61, 52, 51, 48, 54, 49, 49, 61, NA, NA, 68, 44, 40, 27, 28, 25, 24, 24), .Tsp = c(1945, 1974.75, 4), class = 'ts')); .Internal(which.max(argv[[1]]))");
     }
 
     @Test
     public void testwhichmax5() {
-        assertEval(Ignored.Unknown, "argv <- list(NULL); .Internal(which.max(argv[[1]]))");
+        assertEval("argv <- list(NULL); .Internal(which.max(argv[[1]]))");
     }
 
     @Test
     public void testwhichmax6() {
-        assertEval(Ignored.Unknown, "argv <- list(list()); .Internal(which.max(argv[[1]]))");
+        assertEval("argv <- list(list()); .Internal(which.max(argv[[1]]))");
     }
 
     @Test
     public void testwhichmax8() {
-        assertEval(Ignored.Unknown, "argv <- structure(list(x = c(NA, NA)), .Names = 'x');do.call('which.max', argv)");
+        assertEval("argv <- structure(list(x = c(NA, NA)), .Names = 'x');do.call('which.max', argv)");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmin.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmin.java
index c68a12f92aeb6613580324898e4074e1212331ef..8dbadd6a6face7e01fb9f1830fe277a4b5186edd 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmin.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_whichmin.java
@@ -19,41 +19,37 @@ public class TestBuiltin_whichmin extends TestBase {
 
     @Test
     public void testwhichmin1() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c(345595, 172795, 69115, 34555, 23035, 11515, 5755, 2875, 1147, 571, 379, 187, 91, 27, 11, 3, 1, 3, 4.42857142857143, 4.73716632443532, 4.86858316221766, 4.95619438740589, 4.97809719370294, 4.98904859685147, 4.99452429842574, 4.99780971937029, 4.99890485968515, 4.99945242984257, 4.99978097193703, 4.99989048596851, 4.99994524298426, 4.9999780971937, 4.99998904859685), .Names = c('1 sec', '2 secs', '5 secs', '10 secs', '15 secs', '30 secs', '1 min', '2 mins', '5 mins', '10 mins', '15 mins', '30 mins', '1 hour', '3 hours', '6 hours', '12 hours', '1 DSTday', '2 DSTdays', '1 week', 'halfmonth', '1 month', '3 months', '6 months', '1 year', '2 years', '5 years', '10 years', '20 years', '50 years', '100 years', '200 years', '500 years', '1000 years'))); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(structure(c(345595, 172795, 69115, 34555, 23035, 11515, 5755, 2875, 1147, 571, 379, 187, 91, 27, 11, 3, 1, 3, 4.42857142857143, 4.73716632443532, 4.86858316221766, 4.95619438740589, 4.97809719370294, 4.98904859685147, 4.99452429842574, 4.99780971937029, 4.99890485968515, 4.99945242984257, 4.99978097193703, 4.99989048596851, 4.99994524298426, 4.9999780971937, 4.99998904859685), .Names = c('1 sec', '2 secs', '5 secs', '10 secs', '15 secs', '30 secs', '1 min', '2 mins', '5 mins', '10 mins', '15 mins', '30 mins', '1 hour', '3 hours', '6 hours', '12 hours', '1 DSTday', '2 DSTdays', '1 week', 'halfmonth', '1 month', '3 months', '6 months', '1 year', '2 years', '5 years', '10 years', '20 years', '50 years', '100 years', '200 years', '500 years', '1000 years'))); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin2() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c(295, 145, 55, 25, 15, 5, 0, 2.5, 4, 4.5, 4.66666666666667, 4.83333333333333, 4.91666666666667, 4.97222222222222, 4.98611111111111, 4.99305555555556, 4.99652777777778, 4.99826388888889, 4.99950396825397, 4.99977184576774, 4.99988592288387, 4.99996197429462, 4.99998098714731, 4.99999049357366, 4.99999524678683, 4.99999809871473, 4.99999904935737, 4.99999952467868, 4.99999980987147, 4.99999990493574, 4.99999995246787, 4.99999998098715, 4.99999999049357), .Names = c('1 sec', '2 secs', '5 secs', '10 secs', '15 secs', '30 secs', '1 min', '2 mins', '5 mins', '10 mins', '15 mins', '30 mins', '1 hour', '3 hours', '6 hours', '12 hours', '1 DSTday', '2 DSTdays', '1 week', 'halfmonth', '1 month', '3 months', '6 months', '1 year', '2 years', '5 years', '10 years', '20 years', '50 years', '100 years', '200 years', '500 years', '1000 years'))); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(structure(c(295, 145, 55, 25, 15, 5, 0, 2.5, 4, 4.5, 4.66666666666667, 4.83333333333333, 4.91666666666667, 4.97222222222222, 4.98611111111111, 4.99305555555556, 4.99652777777778, 4.99826388888889, 4.99950396825397, 4.99977184576774, 4.99988592288387, 4.99996197429462, 4.99998098714731, 4.99999049357366, 4.99999524678683, 4.99999809871473, 4.99999904935737, 4.99999952467868, 4.99999980987147, 4.99999990493574, 4.99999995246787, 4.99999998098715, 4.99999999049357), .Names = c('1 sec', '2 secs', '5 secs', '10 secs', '15 secs', '30 secs', '1 min', '2 mins', '5 mins', '10 mins', '15 mins', '30 mins', '1 hour', '3 hours', '6 hours', '12 hours', '1 DSTday', '2 DSTdays', '1 week', 'halfmonth', '1 month', '3 months', '6 months', '1 year', '2 years', '5 years', '10 years', '20 years', '50 years', '100 years', '200 years', '500 years', '1000 years'))); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin3() {
-        assertEval(Ignored.Unknown, "argv <- list(NULL); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(NULL); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin4() {
-        assertEval(Ignored.Unknown, "argv <- list(list()); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(list()); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin5() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(c(NA, 0.951840581382975, 0.805577027554469, 0.663985017923499, 0.53717416750558, 0.496765449963868, 0.472038350505409, 0.463306413812878, 0.485896454097402, 0.520777596351646, 0.524391122960607, 0.492063804965834, 0.513821989320989, 0.521702559081969, 0.533525525673351)); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(c(NA, 0.951840581382975, 0.805577027554469, 0.663985017923499, 0.53717416750558, 0.496765449963868, 0.472038350505409, 0.463306413812878, 0.485896454097402, 0.520777596351646, 0.524391122960607, 0.492063804965834, 0.513821989320989, 0.521702559081969, 0.533525525673351)); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin6() {
-        assertEval(Ignored.Unknown,
-                        "argv <- list(structure(c(NA, 87, 82, 75, 63, 50, 43, 32, 35, 60, 54, 55, 36, 39, NA, NA, 69, 57, 57, 51, 45, 37, 46, 39, 36, 24, 32, 23, 25, 32, NA, 32, 59, 74, 75, 60, 71, 61, 71, 57, 71, 68, 79, 73, 76, 71, 67, 75, 79, 62, 63, 57, 60, 49, 48, 52, 57, 62, 61, 66, 71, 62, 61, 57, 72, 83, 71, 78, 79, 71, 62, 74, 76, 64, 62, 57, 80, 73, 69, 69, 71, 64, 69, 62, 63, 46, 56, 44, 44, 52, 38, 46, 36, 49, 35, 44, 59, 65, 65, 56, 66, 53, 61, 52, 51, 48, 54, 49, 49, 61, NA, NA, 68, 44, 40, 27, 28, 25, 24, 24), .Tsp = c(1945, 1974.75, 4), class = 'ts')); .Internal(which.min(argv[[1]]))");
+        assertEval("argv <- list(structure(c(NA, 87, 82, 75, 63, 50, 43, 32, 35, 60, 54, 55, 36, 39, NA, NA, 69, 57, 57, 51, 45, 37, 46, 39, 36, 24, 32, 23, 25, 32, NA, 32, 59, 74, 75, 60, 71, 61, 71, 57, 71, 68, 79, 73, 76, 71, 67, 75, 79, 62, 63, 57, 60, 49, 48, 52, 57, 62, 61, 66, 71, 62, 61, 57, 72, 83, 71, 78, 79, 71, 62, 74, 76, 64, 62, 57, 80, 73, 69, 69, 71, 64, 69, 62, 63, 46, 56, 44, 44, 52, 38, 46, 36, 49, 35, 44, 59, 65, 65, 56, 66, 53, 61, 52, 51, 48, 54, 49, 49, 61, NA, NA, 68, 44, 40, 27, 28, 25, 24, 24), .Tsp = c(1945, 1974.75, 4), class = 'ts')); .Internal(which.min(argv[[1]]))");
     }
 
     @Test
     public void testwhichmin8() {
-        assertEval(Ignored.Unknown, "argv <- structure(list(x = c(NA, NA, Inf)), .Names = 'x');do.call('which.min', argv)");
+        assertEval("argv <- structure(list(x = c(NA, NA, Inf)), .Names = 'x');do.call('which.min', argv)");
     }
 
     @Test
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels1.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels1.R
index 0cd0e317758b7432f90b3a28c8c3646bb6ebe08c..2fcc1bef21fae4cb7df0bfd0111301fe4b16714c 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels1.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels1.R
@@ -2,9 +2,8 @@
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
     ch <- .fastr.channel.create(1L)
-    cx <- .fastr.context.create("SHARED_NOTHING")
     code <- "ch <- .fastr.channel.get(1L); x<-.fastr.channel.receive(ch); x[1]<-7; .fastr.channel.send(ch, x)"
-    .fastr.context.spawn(cx, code)
+    cx <- .fastr.context.spawn(code)
     y<-c(42)
     .fastr.channel.send(ch, y)
     x<-.fastr.channel.receive(ch)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels2.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels2.R
index 3dad930c90bf637dbee58fa8b95ab791bab93b1b..1879ea428d7f3aa5464ba037403ac00025d1a2ce 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels2.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels2.R
@@ -2,9 +2,8 @@
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
     ch <- .fastr.channel.create(1L)
-    cx <- .fastr.context.create("SHARED_NOTHING")
     code <- "ch <- .fastr.channel.get(1L); x<-.fastr.channel.receive(ch); x[1][1]<-7; .fastr.channel.send(ch, x)"
-    .fastr.context.spawn(cx, code)
+    cx <- .fastr.context.spawn(code)
     y<-list(c(42))
     .fastr.channel.send(ch, y)
     x<-fastr.channel.receive(ch)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels3.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels3.R
index 21066855d7154826ea64e20c0415466bb9f32b05..67a6a97e1a45dfe92ae303748acb706a7a9e7ef1 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels3.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels3.R
@@ -2,9 +2,8 @@
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
     ch <- .fastr.channel.create(1L)
-    cx <- .fastr.context.create("SHARED_NOTHING")
     code <- "ch <- .fastr.channel.get(1L); f<-.fastr.channel.receive(ch); x<-f(7); .fastr.channel.send(ch, x)"
-    .fastr.context.spawn(cx, code)
+    cx <- .fastr.context.spawn(code)
     mul<-function(y) y*y
     .fastr.channel.send(ch, mul)
     x<-.fastr.channel.receive(ch)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels4.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels4.R
index fad902306100ac9fea6f57263d74fc1cfe58e0a1..abf201a00e9e9dc670404e8fb31809e52d95237d 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels4.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels4.R
@@ -2,9 +2,8 @@
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
     ch <- .fastr.channel.create(1L)
-    cx <- .fastr.context.create("SHARED_NOTHING")
     code <- "ch <- .fastr.channel.get(1L); env<-.fastr.channel.receive(ch); assign('y', 7, pos=env); .fastr.channel.send(ch, y)"
-    .fastr.context.spawn(cx, code)
+    cx <- .fastr.context.spawn(code)
     .fastr.channel.send(ch, .GlobalEnv)
     x<-.fastr.channel.receive(ch)
     .fastr.context.join(cx)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels5.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels5.R
index a176be9ef11ab09bdc47fbb7b839fdca257c44f8..3a87fe57a2fe9bda3f32b44991fbf59dd488ba38 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels5.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels5.R
@@ -2,9 +2,8 @@
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
     ch <- .fastr.channel.create(1L)
-    cx <- .fastr.context.create("SHARED_NOTHING")
     code <- "ch <- .fastr.channel.get(1L); msg<-.fastr.channel.receive(ch); env<-attr(msg, 'GLOBAL'); assign('y', 7, pos=env); .fastr.channel.send(ch, y)"
-    .fastr.context.spawn(cx, code)
+    cx <- .fastr.context.spawn(code)
     l<-list(c(42))
     attr(l, 'GLOBAL')<-.GlobalEnv
     .fastr.channel.send(ch, l)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels6.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels6.R
index c2994bddfd5356a1d04ee3a6bb6704b3e0a6b0e7..0f860c85d88aba9db43c304f768b4cc1f426a7d9 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels6.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels6.R
@@ -4,9 +4,8 @@
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
     ch <- .fastr.channel.create(1L)
-    cx <- .fastr.context.create("SHARED_NOTHING")
     code <- "ch <- .fastr.channel.get(1L); msg<-.fastr.channel.receive(ch); env<-attr(attr(msg, 'LIST'), 'GLOBAL'); assign('y', 7, pos=env); .fastr.channel.send(ch, y)"
-    .fastr.context.spawn(cx, code)
+    cx <- .fastr.context.spawn(code)
     l2<-list(c(42))
     l<-list(c(7))
     attr(l, 'GLOBAL')<-.GlobalEnv
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels7.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels7.R
index 216630c64a80dc10a78abc7bd139db043ae0e819..24addfd020342ed6cae99aa3439bacdf9741a120 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels7.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels7.R
@@ -2,9 +2,8 @@
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
     ch <- .fastr.channel.create(1L)
-    cx <- .fastr.context.create("SHARED_NOTHING")
     code <- "ch <- .fastr.channel.get(1L); x<-.fastr.channel.receive(ch); y<-x[1]; z<-.fastr.identity(x); .fastr.channel.send(ch, z)"
-    .fastr.context.spawn(cx, code)
+    cx <- .fastr.context.spawn(code)
     y<-c(7, 42)
     z<-.fastr.identity(y)
     .fastr.channel.send(ch, y)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels8.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels8.R
index f8982e2fc8ac27eb84567edde0b22152312932fe..baab3f7bb24cda7b2b7d282e321544e27085d6ca 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels8.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels8.R
@@ -2,9 +2,8 @@
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
     ch <- .fastr.channel.create(1L)
-    cx <- .fastr.context.create("SHARED_NOTHING")
     code <- "ch <- .fastr.channel.get(1L); x<-.fastr.channel.receive(ch); y<-x[[1]][1]; z<-.fastr.identity(x[[1]]); .fastr.channel.send(ch, z)"
-    .fastr.context.spawn(cx, code)
+    cx <- .fastr.context.spawn(code)
     y<-list(c(7, 42), 1)
     z<-.fastr.identity(y[[1]])
     .fastr.channel.send(ch, y)
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels9.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels9.R
index d15979ec4ca5b8519b0ec1a2bf84041bf7c96b55..bdd1b853797f334beb40d55c57b5c459d11e7e73 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels9.R
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/channels/R/channels9.R
@@ -3,9 +3,8 @@
 
 if (length(grep("FastR", R.Version()$version.string)) == 1) {
     ch <- .fastr.channel.create(1L)
-    cx <- .fastr.context.create("SHARED_NOTHING")
     code <- "ch <- .fastr.channel.get(1L); x<-.fastr.channel.receive(ch); y<-x[[1]][1]; z<-c(.fastr.identity(x[[1]]), .fastr.identity(x[[2]])) ; .fastr.channel.send(ch, z)"
-    .fastr.context.spawn(cx, code)
+    cx <- .fastr.context.spawn(code)
     y<-list(c(7, 42), function(x) 1)
     z<-.fastr.identity(y[[1]])
     w<-.fastr.identity(y[[2]])
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInteropEval.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInterop.java
similarity index 79%
rename from com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInteropEval.java
rename to com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInterop.java
index f866e6f2e6f40e52d0b39934bb32c6aeef5dc77e..0f8ad2f8dd8531ed611b23c3a89d9173f3f1d0fe 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInteropEval.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastr/TestInterop.java
@@ -26,7 +26,7 @@ import org.junit.Test;
 
 import com.oracle.truffle.r.test.TestBase;
 
-public class TestInteropEval extends TestBase {
+public class TestInterop extends TestBase {
 
     @Test
     public void testInteropEval() {
@@ -36,4 +36,12 @@ public class TestInteropEval extends TestBase {
         assertEvalFastR(".fastr.interop.eval('application/x-r', 'TRUE')", "TRUE");
         assertEvalFastR(".fastr.interop.eval('application/x-r', 'as.character(123)')", "as.character(123)");
     }
+
+    @Test
+    public void testInteropExport() {
+        assertEvalFastR(".fastr.interop.export('foo', 14 + 2)", "invisible()");
+        assertEvalFastR(".fastr.interop.export('foo', 'foo')", "invisible()");
+        assertEvalFastR(".fastr.interop.export('foo', 1:100)", "invisible()");
+        assertEvalFastR(".fastr.interop.export('foo', new.env())", "invisible()");
+    }
 }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RBuiltinCheck.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RBuiltinCheck.java
index 2ce13c5c6a2a97403a8dfae84a64514e047f9e53..c27e21ee1fc8fa968c9769373a87b7e281349a98 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RBuiltinCheck.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/RBuiltinCheck.java
@@ -28,10 +28,10 @@ import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.function.BiPredicate;
 import java.util.function.Function;
 import java.util.regex.Matcher;
@@ -41,9 +41,10 @@ import java.util.stream.Stream;
 
 import com.oracle.truffle.r.nodes.builtin.RBuiltinFactory;
 import com.oracle.truffle.r.nodes.builtin.base.BasePackage;
-import com.oracle.truffle.r.runtime.RBuiltin;
-import com.oracle.truffle.r.runtime.RBuiltinKind;
 import com.oracle.truffle.r.runtime.RVisibility;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
+import com.oracle.truffle.r.test.TestBase.Ignored;
 
 /**
  * Utility to analyze builtins implemented in FastR vs. builtins available in GNU R. The GNU R
@@ -62,6 +63,7 @@ import com.oracle.truffle.r.runtime.RVisibility;
 public final class RBuiltinCheck {
 
     private static final String DEFAULT_NAMESC = "com.oracle.truffle.r.native/gnur/R-3.2.4/src/main/names.c";
+    private static final String BUILTIN_TEST_PATH = "com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_%s.java";
 
     // old-style code annotation to get rid of javadoc error.
     /**
@@ -170,7 +172,7 @@ public final class RBuiltinCheck {
             return; // so that gnur can be final
         }
 
-        Set<String> both = new HashSet<>(fastr.keySet());
+        Set<String> both = new TreeSet<>(fastr.keySet());
         both.retainAll(gnur.keySet());
 
         Set<String> fastrOnly = keysWithout(fastr, both);
@@ -181,14 +183,14 @@ public final class RBuiltinCheck {
         both.removeAll(descriptorsDiff.keySet());
 
         // Print the results
-        printOutput(show, both, descriptorsDiff, fastrOnly, gnurOnly);
+        printOutput(suitePath, show, both, descriptorsDiff, fastrOnly, gnurOnly);
     }
 
-    private static void printOutput(EnumSet<FilterOption> show, Set<String> both, Map<String, BuiltinTuple> descriptorsDiff, Set<String> fastrOnly, Set<String> gnurOnly) {
+    private static void printOutput(String suitePath, EnumSet<FilterOption> show, Set<String> both, Map<String, BuiltinTuple> descriptorsDiff, Set<String> fastrOnly, Set<String> gnurOnly) {
         String prefix = "";
         if (show.contains(FilterOption.BOTH)) {
             prefix = printHeader(prefix, show, "Builtins in both GNU R and FastR with matching descriptors: " + both.size());
-            both.stream().forEach(System.out::println);
+            both.stream().forEach(b -> printBuiltinWithTest(b, suitePath));
         }
 
         if (show.contains(FilterOption.BOTH_DIFF)) {
@@ -220,6 +222,24 @@ public final class RBuiltinCheck {
         }
     }
 
+    private static void printBuiltinWithTest(String builtin, String suitePath) {
+        String name = builtin.replace(".", "");
+        name = name.replace("<-", "assign");
+        try {
+            String content = Files.lines(Paths.get(suitePath, String.format(BUILTIN_TEST_PATH, name))).collect(Collectors.joining("\n"));
+            int tests = content.split("assertEval").length - 1;
+            int ignoredTests = content.split(Ignored.class.getSimpleName()).length - 1;
+            if (tests > 0) {
+                String testDetails = String.format("%3d%% (%d/%d)", (tests - ignoredTests) * 100 / tests, tests - ignoredTests, tests);
+                System.out.printf("%-15s %s%n", builtin, testDetails);
+                return;
+            }
+        } catch (IOException e) {
+            // nothing to do
+        }
+        System.out.printf("%-20s (no tests found)%n", builtin);
+    }
+
     private static String formatTableCell(String format, BuiltinTuple tuple, Function<BuiltinInfo, Object> selector, BiPredicate<BuiltinInfo, BuiltinInfo> cmp) {
         String result = String.format(format, selector.apply(tuple.fastr), selector.apply(tuple.gnur));
         if (!cmp.test(tuple.fastr, tuple.gnur)) {
@@ -235,19 +255,18 @@ public final class RBuiltinCheck {
         if (show.size() > 1) {
             System.out.println(prefix + header);
         }
-
         return "\n\n";
     }
 
     private static Set<String> keysWithout(Map<String, BuiltinInfo> map, Set<String> both) {
-        Set<String> mapOnly = new HashSet<>(map.keySet());
+        Set<String> mapOnly = new TreeSet<>(map.keySet());
         mapOnly.removeAll(both);
         return mapOnly;
     }
 
     private static Map<String, BuiltinInfo> extractFastRBuiltins() {
         BasePackage base = new BasePackage();
-        HashMap<String, BuiltinInfo> result = new HashMap<>();
+        Map<String, BuiltinInfo> result = new TreeMap<>();
         for (Map.Entry<String, RBuiltinFactory> builtin : base.getBuiltins().entrySet()) {
             Class<?> clazz = builtin.getValue().getBuiltinNodeClass();
             RBuiltin annotation = clazz.getAnnotation(RBuiltin.class);
@@ -264,7 +283,7 @@ public final class RBuiltinCheck {
     private static Map<String, BuiltinInfo> extractGnuRBuiltins(String suiteDir) throws IOException {
         String content = Files.lines(Paths.get(suiteDir, DEFAULT_NAMESC)).collect(Collectors.joining("\n"));
         Matcher matcher = Pattern.compile(FUNTAB_REGEXP).matcher(content);
-        HashMap<String, BuiltinInfo> result = new HashMap<>();
+        Map<String, BuiltinInfo> result = new TreeMap<>();
         while (matcher.find()) {
             // normalize eval to three digits, e.g. '2' to '002'
             String eval = String.format("%1$3s", matcher.group("eval")).replace(' ', '0');
diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py
index dd65727a8202fb78c7388cd8e15755401064208c..424c5057371abdfc34e6ce8c098acd2c367e6635 100644
--- a/mx.fastr/mx_fastr.py
+++ b/mx.fastr/mx_fastr.py
@@ -110,7 +110,7 @@ def _sanitize_vmArgs(jdk, vmArgs):
     chosen jdk. It is easier to allow clients to set anything they want and filter them
     out here.
     '''
-    jvmci_jdk = jdk.tag == 'jvmci'
+    jvmci_jdk = jdk.tag is not None and 'jvmci' in jdk.tag
     jvmci_disabled = '-XX:-EnableJVMCI' in vmArgs
 
     xargs = []
@@ -440,9 +440,12 @@ def rbcheck(args):
 def rbdiag(args):
     '''Diagnoses FastR builtins
 
-	-v	Verbose output including the list of unimplemented specializations
-	-n	Ignore RNull as an argument type
-	-m	Ignore RMissing as an argument type
+	-v		Verbose output including the list of unimplemented specializations
+	-n		Ignore RNull as an argument type
+	-m		Ignore RMissing as an argument type
+    --sweep		Performs the 'chimney-sweeping'. The sample combination selection method is determined automatically.
+    --sweep-lite	Performs the 'chimney-sweeping'. The diagonal sample selection method is used.
+    --sweep-total	Performs the 'chimney-sweeping'. The total sample selection method is used.
 
 	If no builtin is specified, all registered builtins are diagnosed.
 
@@ -451,8 +454,15 @@ def rbdiag(args):
     	mx rbdiag
 		mx rbdiag colSums colMeans -v
 		mx rbdiag scan -m -n
+    	mx rbdiag colSums --sweep
     '''
     cp = mx.classpath('com.oracle.truffle.r.nodes.test')
+
+    setREnvironment()
+    os.environ["FASTR_TESTGEN_GNUR"] = "internal"
+    # this should work for Linux and Mac:
+    os.environ["TZDIR"] = "/usr/share/zoneinfo/"
+
     mx.run_java(['-cp', cp, 'com.oracle.truffle.r.nodes.test.RBuiltinDiagnostics'] + args)
 
 def rcmplib(args):
@@ -508,7 +518,7 @@ _commands = {
     'junitnoapps' : [junit_noapps, ['options']],
     'unittest' : [unittest, ['options']],
     'rbcheck' : [rbcheck, '--filter [gnur-only,fastr-only,both,both-diff]'],
-    'rbdiag' : [rbdiag, '(builtin)* [-v] [-n] [-m]'],
+    'rbdiag' : [rbdiag, '(builtin)* [-v] [-n] [-m] [--sweep | --sweep-lite | --sweep-total'],
     'rcmplib' : [rcmplib, ['options']],
     'pkgtest' : [mx_fastr_pkgs.pkgtest, ['options']],
     'rrepl' : [rrepl, '[options]'],
diff --git a/mx.fastr/mx_fastr_pkgs.py b/mx.fastr/mx_fastr_pkgs.py
index eca0d4bf72f6bef420844c839831b5429417da48..334b4c3e9a4b935b38cb3e0ec3e621c681c5be02 100644
--- a/mx.fastr/mx_fastr_pkgs.py
+++ b/mx.fastr/mx_fastr_pkgs.py
@@ -133,7 +133,11 @@ def pkgtest(args):
 
     _log_step('BEGIN', 'install/test', 'FastR')
     # Currently installpkgs does not set a return code (in install.cran.packages.R)
-    mx_fastr._installpkgs(stacktrace_args + install_args, nonZeroIsFatal=False, env=env, out=out, err=out)
+    rc = mx_fastr._installpkgs(stacktrace_args + install_args, nonZeroIsFatal=False, env=env, out=out, err=out)
+    if rc == 100:
+        # fatal error connecting to package repo
+        mx.abort(rc)
+
     rc = 0
     for status in out.install_status.itervalues():
         if not status:
@@ -186,7 +190,7 @@ def _get_test_outputs(suite, pkg_name, test_info):
             test_info[pkg_name] = TestStatus()
         for f in files:
             ext = os.path.splitext(f)[1]
-            if ext == '.R' or ext == '.prev':
+            if f == 'test_time' or ext == '.R' or ext == '.prev':
                 continue
             # suppress .pdf's for now (we can't compare them)
             if ext == '.pdf':
@@ -265,26 +269,37 @@ def _set_test_status(fastr_test_info):
         gnur_outputs = gnur_test_status.testfile_outputs
         fastr_outputs = fastr_test_status.testfile_outputs
         if _failed_outputs(gnur_outputs):
+            # What this likely means is that some native package is not
+            # installed on the system so GNUR can't run the tests.
+            # Ideally this never happens.
             print "{0}: GnuR test had .fail outputs".format(pkg)
-            continue
 
         if _failed_outputs(fastr_outputs):
+            # In addition to the similar comment for GNU R, this can happen
+            # if, say, the JVM crashes (possible with native code packages)
             print "{0}: FastR test had .fail outputs".format(pkg)
             fastr_test_status.status = "FAILED"
-            continue
-
 
+        # Now for each successful GNU R output we compare content (assuming FastR didn't fail)
         for gnur_test_output_relpath, gnur_testfile_status in gnur_outputs.iteritems():
+            # Can't compare if either GNUR or FastR failed
+            if gnur_testfile_status.status == "FAILED":
+                break
+
             if not gnur_test_output_relpath in fastr_outputs:
+                # FastR crashed on this test
                 fastr_test_status.status = "FAILED"
                 print "{0}: FastR is missing output file: {1}".format(pkg, gnur_test_output_relpath)
                 break
 
+            fastr_testfile_status = fastr_outputs[gnur_test_output_relpath]
+            if fastr_testfile_status.status == "FAILED":
+                break
+
             gnur_content = None
             with open(gnur_testfile_status.abspath) as f:
                 gnur_content = f.readlines()
             fastr_content = None
-            fastr_testfile_status = fastr_outputs[gnur_test_output_relpath]
             with open(fastr_testfile_status.abspath) as f:
                 fastr_content = f.readlines()
 
@@ -295,11 +310,27 @@ def _set_test_status(fastr_test_info):
                 break
             if result != 0:
                 fastr_test_status.status = "FAILED"
+                fastr_testfile_status.status = "FAILED"
                 print "{0}: FastR output mismatch: {1}".format(pkg, gnur_test_output_relpath)
                 break
         # we started out as UNKNOWN
         if not (fastr_test_status.status == "INDETERMINATE" or fastr_test_status.status == "FAILED"):
             fastr_test_status.status = "OK"
+
+        # write out a file with the test status for each output (that exists)
+        with open(join(_pkg_testdir('fastr', pkg), 'testfile_status'), 'w') as f:
+            for fastr_relpath, fastr_testfile_status in fastr_outputs.iteritems():
+                if fastr_testfile_status.status == "FAILED":
+                    relpath = fastr_relpath + ".fail"
+                else:
+                    relpath = fastr_relpath
+
+                if os.path.exists(join(_pkg_testdir('fastr', pkg), relpath)):
+                    f.write(relpath)
+                    f.write(' ')
+                    f.write(fastr_testfile_status.status)
+                    f.write('\n')
+
         print 'END checking ' + pkg
 
 def _find_start(content):
diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py
index c8de59b9adc32b88b093e0edb259ab209aa4fff8..bd1837a79e77c0bce1b99dd90e6a170a69d8f94d 100644
--- a/mx.fastr/suite.py
+++ b/mx.fastr/suite.py
@@ -21,14 +21,14 @@
 # questions.
 #
 suite = {
-  "mxversion" : "5.31.0",
+  "mxversion" : "5.34.4",
   "name" : "fastr",
   "versionConflictResolution" : "latest",
   "imports" : {
     "suites" : [
             {
                "name" : "truffle",
-               "version" : "91254ff6b437dbfbb4cf8f063abfb880c3ff3e2f",
+               "version" : "bd163128ec958b97ebc68b33ac5b4fae376a37b5",
                "urls" : [
                     {"url" : "https://github.com/graalvm/truffle", "kind" : "git"},
                     {"url" : "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind" : "binary"},