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 3877ee3ed298131c8b17a8f571954206be9a0cbe..f52c8e64823a2448a5245895889ef20965acb89f 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
@@ -332,7 +332,7 @@ public final class REngine implements RContext.Engine {
         } catch (UnsupportedSpecializationException use) {
             ConsoleHandler ch = singleton.context.getConsoleHandler();
             ch.println("Unsupported specialization in node " + use.getNode().getClass().getSimpleName() + " - supplied values: " +
-                            Arrays.asList(use.getSuppliedValues()).stream().map(v -> v.getClass().getSimpleName()).collect(Collectors.toList()));
+                            Arrays.asList(use.getSuppliedValues()).stream().map(v -> v == null ? "null" : v.getClass().getSimpleName()).collect(Collectors.toList()));
             throw use;
         } catch (DebugExitException | BrowserQuitException e) {
             throw e;
@@ -453,10 +453,10 @@ public final class REngine implements RContext.Engine {
             if (loadBase) {
                 Object printMethod = REnvironment.globalEnv().findFunction("print");
                 RFunction function = (RFunction) (printMethod instanceof RPromise ? PromiseHelperNode.evaluateSlowPath(null, (RPromise) printMethod) : printMethod);
-                function.getTarget().call(RArguments.create(function, null, 1, new Object[]{resultValue, RMissing.instance}));
+                function.getTarget().call(RArguments.create(function, null, REnvironment.baseEnv().getFrame(), 1, new Object[]{resultValue, RMissing.instance}));
             } else {
                 // we only have the .Internal print.default method available
-                getPrintInternal().getTarget().call(RArguments.create(printInternal, null, 1, new Object[]{resultValue}));
+                getPrintInternal().getTarget().call(RArguments.create(printInternal, null, REnvironment.baseEnv().getFrame(), 1, new Object[]{resultValue}));
             }
         }
     }
@@ -491,7 +491,7 @@ public final class REngine implements RContext.Engine {
     private static void reportImplementationError(Throwable e) {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         e.printStackTrace(new PrintStream(out));
-        singleton.context.getConsoleHandler().printErrorln(RRuntime.toString(out));
+        singleton.context.getConsoleHandler().printErrorln(out.toString());
         // R suicide, unless, e.g., we are running units tests.
         // We don't call quit as the system is broken.
         if (singleton.crashOnFatalError) {
diff --git a/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R b/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R
index 484f91baf83a6b5a7f7b4b93a40ee55990c21257..6d20654b777ab456b7b11687cbc56422bec513e6 100644
--- a/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R
+++ b/com.oracle.truffle.r.native/library/fastr/src/R/fastr.R
@@ -25,9 +25,9 @@ fastr.createcc <- function(func) .FastR(.NAME="createcc", func)
 
 fastr.getcc <- function(func) .FastR(.NAME="getcc", func)
 
-fastr.compile <- function(func, background=TRUE) .FastR(.NAME="getcc", func, background)
+fastr.compile <- function(func, background=TRUE) .FastR(.NAME="compile", func, background)
 
-fastr.dumptrees <- function(func, igvDump=FALSE, verbose=FALSE) .FastR(.NAME="getcc", func, igvDump, verbose)
+fastr.dumptrees <- function(func, igvDump=FALSE, verbose=FALSE) .FastR(.NAME="dumptrees", func, igvDump, verbose)
 
 fastr.source <- function(func) .FastR(.NAME="source", func)
 
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 0ebe26709dbca92cbf48e2c30ad3d8ac9da5e8e2..e18239d4f012f1beb39b6c06eb5433f133f594c2 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
@@ -70,7 +70,7 @@ public abstract class Args extends RBuiltinNode {
         FunctionBodyNode newBody = new FunctionBodyNode(SaveArgumentsNode.NO_ARGS, nullBody);
         String newDesc = "args(" + rootNode.getDescription() + ")";
         FunctionDefinitionNode newNode = new FunctionDefinitionNode(null, rootNode.getFrameDescriptor(), newBody, formals, newDesc, false);
-        return RDataFactory.createFunction(newDesc, Truffle.getRuntime().createCallTarget(newNode), REnvironment.globalEnv().getFrame());
+        return RDataFactory.createFunction(newDesc, Truffle.getRuntime().createCallTarget(newNode), REnvironment.globalEnv().getFrame(), false);
     }
 
     @Fallback
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 fe03675beebd301941d5861c38095c8635e4b475..b2b8e8783a8b6fbf17479c8e7630f746e7e5ce6f 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
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.nodes.*;
@@ -44,24 +45,16 @@ public abstract class DoCall extends RBuiltinNode {
     @Child private GetFunctions.Get getNode;
 
     @Child private PromiseHelperNode promiseHelper = new PromiseHelperNode();
+    @CompilationFinal private boolean needsCallerFrame;
 
     @Specialization(guards = "lengthOne")
     protected Object doDoCall(VirtualFrame frame, RAbstractStringVector fname, RList argsAsList, REnvironment env) {
-        /*
-         * TODO this is only necessary to find builtins that are (currently) not available via the
-         * standard lookup; it's very dangerous if it happens to find a .Internal, as that cannot be
-         * called directly with the same semantics!
-         */
-        RFunction func = RContext.getEngine().lookupBuiltin(fname.getDataAt(0));
-        if (func == null || func.getRBuiltin().kind() == RBuiltinKind.INTERNAL) {
-            if (getNode == null) {
-                CompilerDirectives.transferToInterpreterAndInvalidate();
-                getNode = insert(GetFactory.create(new RNode[4], this.getBuiltin(), getSuppliedArgsNames()));
-            }
-            func = (RFunction) getNode.execute(frame, fname, env, RType.Function.getName(), RRuntime.LOGICAL_TRUE);
+        if (getNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            getNode = insert(GetFactory.create(new RNode[4], this.getBuiltin(), getSuppliedArgsNames()));
         }
-        Object result = doDoCall(frame, func, argsAsList, env);
-        return result;
+        RFunction func = (RFunction) getNode.execute(frame, fname, env, RType.Function.getName(), RRuntime.LOGICAL_TRUE);
+        return doDoCall(frame, func, argsAsList, env);
     }
 
     @Specialization()
@@ -71,7 +64,12 @@ public abstract class DoCall extends RBuiltinNode {
         String[] argNames = n == null ? null : n.getDataNonShared();
         EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, argNames);
         EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(frame, func, evaledArgs, getEncapsulatingSourceSection(), promiseHelper, false);
-        Object[] callArgs = RArguments.create(func, callCache.getSourceSection(), RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
+        if (!needsCallerFrame && func.containsDispatch()) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            needsCallerFrame = true;
+        }
+        MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
+        Object[] callArgs = RArguments.create(func, callCache.getSourceSection(), callerFrame, RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
         RArguments.setIsIrregular(callArgs, true);
         return callCache.execute(frame, func.getTarget(), callArgs);
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java
index ac4967a715f770025acf01712c9d5532441d67ad..9bb359813bab260b78ccd41e20e5ac55de0f5bf1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ForeignFunctions.java
@@ -14,7 +14,8 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import java.io.*;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.utilities.*;
@@ -464,7 +465,7 @@ public class ForeignFunctions {
 
         @SuppressWarnings("unused")
         @Specialization(guards = "isFlushconsole")
-        protected RNull flushConsole(RList f, Object[] args, RMissing packageName) {
+        protected RNull flushConsole(RList f, RArgsValuesAndNames args, RMissing packageName) {
             return RNull.instance;
         }
 
@@ -516,7 +517,7 @@ public class ForeignFunctions {
 
         @SuppressWarnings("unused")
         @Specialization(guards = "isMakeQuartzDefault")
-        protected byte makeQuartzDefault(RList f, Object[] args, RMissing packageName) {
+        protected byte makeQuartzDefault(RList f, RArgsValuesAndNames args, RMissing packageName) {
             return RRuntime.LOGICAL_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 68d89050d8e351a33c0fdd2879faa00a5115976a..8db0007c768b5b2785efb18d291d86fccdcb9807 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
@@ -24,6 +24,8 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.utilities.*;
@@ -127,6 +129,8 @@ public class GetFunctions {
 
         @Child private CallInlineCacheNode callCache = CallInlineCacheNode.create(3);
 
+        @CompilationFinal private boolean needsCallerFrame;
+
         @SuppressWarnings("unused")
         public static boolean isInherits(RStringVector xv, REnvironment envir, RAbstractStringVector mode, RList ifNotFound, byte inherits) {
             return inherits == RRuntime.LOGICAL_TRUE;
@@ -238,7 +242,12 @@ public class GetFunctions {
         }
 
         private Object call(VirtualFrame frame, RFunction ifnFunc, String x) {
-            Object[] callArgs = RArguments.create(ifnFunc, callCache.getSourceSection(), RArguments.getDepth(frame) + 1, new Object[]{x}, new String[0]);
+            if (!needsCallerFrame && ifnFunc.containsDispatch()) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                needsCallerFrame = true;
+            }
+            MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
+            Object[] callArgs = RArguments.create(ifnFunc, callCache.getSourceSection(), callerFrame, RArguments.getDepth(frame) + 1, new Object[]{x}, new String[0]);
             return callCache.execute(frame, ifnFunc.getTarget(), callArgs);
         }
 
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 da19ceebcb840ca1e1a1355343bbf716264cc715..028ba4684436b8848d81dc9a152c3390c2f20f58 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
@@ -202,7 +202,7 @@ public class HiddenInternalFunctions {
                     RSerialize.CallHook callHook = new RSerialize.CallHook() {
 
                         public Object eval(Object arg) {
-                            Object[] callArgs = RArguments.create(envhook, callCache.getSourceSection(), RArguments.getDepth(frame) + 1, new Object[]{arg}, new String[0]);
+                            Object[] callArgs = RArguments.create(envhook, callCache.getSourceSection(), null, RArguments.getDepth(frame) + 1, new Object[]{arg}, new String[0]);
                             // TODO this cast is problematic
                             return callCache.execute((VirtualFrame) frame, envhook.getTarget(), callArgs);
                         }
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 66712b3ecc38d8c7544921da6a5b172c1ecf5fc4..11d1904d5ac854487edfdeafb332a4c0af598663 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, 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
@@ -49,7 +49,7 @@ public abstract class Recall extends RBuiltinNode {
         }
 
         // Use arguments in "..." as arguments for RECALL call
-        Object[] argsObject = RArguments.create(function, callCache.getSourceSection(), RArguments.getDepth(frame) + 1, args.getValues());
+        Object[] argsObject = RArguments.create(function, callCache.getSourceSection(), null, RArguments.getDepth(frame) + 1, args.getValues());
         return callCache.execute(frame, function.getTarget(), argsObject);
     }
 
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 56d80f9a72ae799720cf8ad50035befc83f62334..dec2c924c1dc9c46625ddcc70abdd96504226256 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
@@ -24,7 +24,7 @@ package com.oracle.truffle.r.nodes.builtin.base;
 
 import static com.oracle.truffle.r.runtime.RBuiltinKind.*;
 
-import java.util.*;
+import java.util.function.*;
 
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.utilities.*;
@@ -34,14 +34,12 @@ import com.oracle.truffle.r.nodes.unary.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
-import com.oracle.truffle.r.runtime.ops.na.*;
 
 @RBuiltin(name = "rep.int", kind = INTERNAL, parameterNames = {"x", "times"})
 public abstract class RepeatInternal extends RBuiltinNode {
 
-    private final ConditionProfile timesOne = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile timesOneProfile = ConditionProfile.createBinaryProfile();
     private final BranchProfile errorProfile = BranchProfile.create();
-    private final NACheck naCheck = NACheck.create();
 
     @CreateCast("arguments")
     protected RNode[] castStatusArgument(RNode[] arguments) {
@@ -50,144 +48,80 @@ public abstract class RepeatInternal extends RBuiltinNode {
         return arguments;
     }
 
-    @Specialization
-    protected RDoubleVector repInt(double value, int times) {
-        controlVisibility();
-        double[] array = new double[times];
-        Arrays.fill(array, value);
-        naCheck.enable(value);
-        return RDataFactory.createDoubleVector(array, !naCheck.check(value));
+    @FunctionalInterface
+    private interface ArrayUpdateFunction<ValueT, ArrayT> {
+        void update(ArrayT array, int pos, ValueT value, int index);
     }
 
-    @Specialization
-    protected RRawVector repInt(RRaw value, int times) {
-        controlVisibility();
-        byte[] array = new byte[times];
-        Arrays.fill(array, value.getValue());
-        return RDataFactory.createRawVector(array);
+    @FunctionalInterface
+    private interface CreateResultFunction<ResultT, ArrayT> {
+        ResultT create(ArrayT array, boolean complete);
     }
 
-    @Specialization
-    protected RIntVector repInt(RIntSequence value, int times) {
+    private <ValueT extends RAbstractVector, ResultT extends ValueT, ArrayT> ResultT repInt(ValueT value, RAbstractIntVector times, IntFunction<ArrayT> arrayConstructor,
+                    ArrayUpdateFunction<ValueT, ArrayT> arrayUpdate, CreateResultFunction<ResultT, ArrayT> createResult) {
         controlVisibility();
-        int oldLength = value.getLength();
-        int length = oldLength * times;
-        int[] array = new int[length];
-        for (int i = 0; i < times; i++) {
-            for (int j = 0; j < oldLength; ++j) {
-                array[i * oldLength + j] = value.getDataAt(j);
+        ArrayT result;
+        int timesLength = times.getLength();
+        int valueLength = value.getLength();
+        if (timesOneProfile.profile(timesLength == 1)) {
+            int timesValue = times.getDataAt(0);
+            int count = timesValue * valueLength;
+            result = arrayConstructor.apply(count);
+            int pos = 0;
+            for (int i = 0; i < timesValue; i++) {
+                for (int j = 0; j < valueLength; j++) {
+                    arrayUpdate.update(result, pos++, value, j);
+                }
             }
-        }
-        return RDataFactory.createIntVector(array, value.isComplete());
-    }
-
-    @Specialization
-    protected RDoubleVector repInt(RDoubleVector value, int times) {
-        controlVisibility();
-        int oldLength = value.getLength();
-        int length = value.getLength() * times;
-        double[] array = new double[length];
-        for (int i = 0; i < times; i++) {
-            for (int j = 0; j < oldLength; ++j) {
-                array[i * oldLength + j] = value.getDataAt(j);
+        } else if (timesLength == valueLength) {
+            int count = 0;
+            for (int i = 0; i < timesLength; i++) {
+                int data = times.getDataAt(i);
+                if (data < 0) {
+                    errorProfile.enter();
+                    RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_VALUE, "times");
+                }
+                count += data;
             }
-        }
-        return RDataFactory.createDoubleVector(array, value.isComplete());
-    }
-
-    @Specialization(guards = "isTimesValid")
-    protected RDoubleVector repInt(RAbstractDoubleVector value, RIntVector times) {
-        controlVisibility();
-        List<Double> result = new ArrayList<>();
-        for (int i = 0; i < value.getLength(); i++) {
-            for (int j = 0; j < times.getDataAt(i); ++j) {
-                result.add(value.getDataAt(i));
+            result = arrayConstructor.apply(count);
+            int pos = 0;
+            for (int i = 0; i < valueLength; i++) {
+                int num = times.getDataAt(i);
+                for (int j = 0; j < num; j++) {
+                    arrayUpdate.update(result, pos++, value, i);
+                }
             }
+        } else {
+            errorProfile.enter();
+            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_VALUE, "times");
         }
-        double[] ans = new double[result.size()];
-        for (int i = 0; i < ans.length; ++i) {
-            ans[i] = result.get(i);
-        }
-        return RDataFactory.createDoubleVector(ans, value.isComplete());
+        return createResult.create(result, value.isComplete());
     }
 
-    @Specialization(guards = "isTimesValid")
-    protected RIntVector repInt(RAbstractIntVector value, RIntVector times) {
-        controlVisibility();
-        List<Integer> result = new ArrayList<>();
-        for (int i = 0; i < value.getLength(); i++) {
-            for (int j = 0; j < times.getDataAt(i); ++j) {
-                result.add(value.getDataAt(i));
-            }
-        }
-        int[] ans = new int[result.size()];
-        for (int i = 0; i < ans.length; ++i) {
-            ans[i] = result.get(i);
-        }
-        return RDataFactory.createIntVector(ans, value.isComplete());
+    @Specialization
+    protected RDoubleVector repInt(RAbstractDoubleVector value, RAbstractIntVector times) {
+        return repInt(value, times, double[]::new, (array, pos, val, index) -> array[pos] = val.getDataAt(index), RDataFactory::createDoubleVector);
     }
 
     @Specialization
-    protected RIntVector repInt(int value, int times) {
-        controlVisibility();
-        int[] array = new int[times];
-        Arrays.fill(array, value);
-        naCheck.enable(value);
-        return RDataFactory.createIntVector(array, !naCheck.check(value));
+    protected RIntVector repInt(RAbstractIntVector value, RAbstractIntVector times) {
+        return repInt(value, times, int[]::new, (array, pos, val, index) -> array[pos] = val.getDataAt(index), RDataFactory::createIntVector);
     }
 
     @Specialization
-    protected RLogicalVector repInt(byte value, int times) {
-        controlVisibility();
-        byte[] array = new byte[times];
-        Arrays.fill(array, value);
-        naCheck.enable(value);
-        return RDataFactory.createLogicalVector(array, !naCheck.check(value));
+    protected RLogicalVector repInt(RAbstractLogicalVector value, RAbstractIntVector times) {
+        return repInt(value, times, byte[]::new, (array, pos, val, index) -> array[pos] = val.getDataAt(index), RDataFactory::createLogicalVector);
     }
 
     @Specialization
-    protected RStringVector repInt(String value, int times) {
-        controlVisibility();
-        String[] array = new String[times];
-        Arrays.fill(array, value);
-        naCheck.enable(value);
-        return RDataFactory.createStringVector(array, !naCheck.check(value));
+    protected RStringVector repInt(RAbstractStringVector value, RAbstractIntVector times) {
+        return repInt(value, times, String[]::new, (array, pos, val, index) -> array[pos] = val.getDataAt(index), RDataFactory::createStringVector);
     }
 
     @Specialization
-    protected RStringVector repInt(RStringVector value, RIntVector timesVec) {
-        controlVisibility();
-        int valueLength = value.getLength();
-        int times = timesVec.getLength();
-        if (!(times == 1 || times == valueLength)) {
-            errorProfile.enter();
-            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_TIMES_ARG);
-        }
-        String[] array;
-        if (timesOne.profile(times == 1)) {
-            int length = value.getLength() * times;
-            array = new String[length];
-            for (int i = 0; i < times; i++) {
-                for (int j = 0; j < valueLength; ++j) {
-                    array[i * valueLength + j] = value.getDataAt(j);
-                }
-            }
-        } else {
-            int length = 0;
-            for (int k = 0; k < times; k++) {
-                length = length + timesVec.getDataAt(k);
-            }
-            array = new String[length];
-            int arrayIndex = 0;
-            for (int i = 0; i < valueLength; i++) {
-                String s = value.getDataAt(i);
-                int timesLen = timesVec.getDataAt(i);
-                for (int k = 0; k < timesLen; k++) {
-                    array[arrayIndex++] = s;
-                }
-            }
-        }
-        return RDataFactory.createStringVector(array, value.isComplete());
+    protected RRawVector repInt(RAbstractRawVector value, RAbstractIntVector times) {
+        return repInt(value, times, byte[]::new, (array, pos, val, index) -> array[pos] = val.getDataAt(index).getValue(), (array, complete) -> RDataFactory.createRawVector(array));
     }
 
     @Specialization
@@ -203,12 +137,4 @@ public abstract class RepeatInternal extends RBuiltinNode {
         }
         return RDataFactory.createList(array);
     }
-
-    protected boolean isTimesValid(RAbstractVector value, RIntVector times) {
-        if (value.getLength() != times.getLength()) {
-            errorProfile.enter();
-            throw RError.error(getEncapsulatingSourceSection(), RError.Message.INVALID_VALUE, "times");
-        }
-        return true;
-    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java
index 6fcd6023411e6847f2eacd4b22d5a99356914b81..9ad69a27f9f2b6e16ffa1f71171db2372f41a639 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RTruffleVisitor.java
@@ -145,6 +145,7 @@ public final class RTruffleVisitor extends BasicVisitor<RNode> {
             String[] argumentNames = new String[argumentsList.size()];
             RNode[] defaultValues = new RNode[argumentsList.size()];
             SaveArgumentsNode saveArguments;
+            AccessArgumentNode[] argAccessNodes = new AccessArgumentNode[argumentsList.size()];
             if (!argumentsList.isEmpty()) {
                 RNode[] init = new RNode[argumentsList.size()];
                 int index = 0;
@@ -159,7 +160,9 @@ public final class RTruffleVisitor extends BasicVisitor<RNode> {
                     }
 
                     // Create an initialization statement
-                    init[index] = WriteVariableNode.create(arg.getName(), AccessArgumentNode.create(index), true, false);
+                    AccessArgumentNode accessArg = AccessArgumentNode.create(index);
+                    argAccessNodes[index] = accessArg;
+                    init[index] = WriteVariableNode.create(arg.getName(), accessArg, true, false);
 
                     // Store formal arguments
                     argumentNames[index] = arg.getName();
@@ -178,6 +181,9 @@ public final class RTruffleVisitor extends BasicVisitor<RNode> {
                 statements.assignSourceSection(astBody.getSource());
             }
             FormalArguments formals = FormalArguments.create(argumentNames, defaultValues);
+            for (AccessArgumentNode access : argAccessNodes) {
+                access.setFormals(formals);
+            }
 
             FrameDescriptor descriptor = new FrameDescriptor();
             FrameSlotChangeMonitor.initializeFrameDescriptor(descriptor, false);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
index e131aa90a8a475298e3c6f0cca487f1c67ba2375..92e8cbc2b0f00df907c2cdf100ac1354caf087f6 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/AccessArgumentNode.java
@@ -82,12 +82,12 @@ public abstract class AccessArgumentNode extends RNode {
         defaultArgCanBeOptimized = prev.defaultArgCanBeOptimized;
     }
 
-    @Override
-    protected void onAdopt() {
-        formals = ((RRootNode) getRootNode()).getFormalArguments();
+    public void setFormals(FormalArguments formals) {
+        CompilerAsserts.neverPartOfCompilation();
+        assert this.formals == null;
+        this.formals = formals;
         hasDefaultArg = formals.getDefaultArg(getIndex()) != null;
         isVarArgIndex = formals.getVarArgIndex() == getIndex();
-        super.onAdopt();
     }
 
     /**
@@ -136,27 +136,27 @@ public abstract class AccessArgumentNode extends RNode {
         return argMissing;
     }
 
-    @Specialization(guards = {"hasDefaultArg", "canBeOptimized"})
-    public Object doArgumentEagerDefaultArg(VirtualFrame frame, RMissing argMissing) {
-        // Insert default value
-        checkPromiseFactory();
-        if (!checkInsertOptDefaultArg()) {
-            // Default arg cannot be optimized: Rewrite to default and assure that we don't take
-            // this path again
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            defaultArgCanBeOptimized = false;
-            return doArgumentDefaultArg(frame, argMissing);
-        }
-        Object result = optDefaultArgNode.execute(frame);
-        RArguments.setArgument(frame, index, result);   // Update RArguments for S3 dispatch to work
-        return result;
-    }
-
-    @Specialization(guards = {"hasDefaultArg", "!canBeOptimized"})
+    @Specialization(guards = {"hasDefaultArg"})
     public Object doArgumentDefaultArg(VirtualFrame frame, @SuppressWarnings("unused") RMissing argMissing) {
+        Object result;
+        if (canBeOptimized()) {
+            // Insert default value
+            checkPromiseFactory();
+            if (checkInsertOptDefaultArg()) {
+                result = optDefaultArgNode.execute(frame);
+                // Update RArguments for S3 dispatch to work
+                RArguments.setArgument(frame, index, result);
+                return result;
+            } else {
+                // Default arg cannot be optimized: Rewrite to default and assure that we don't take
+                // this path again
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                defaultArgCanBeOptimized = false;
+            }
+        }
         // Insert default value
         checkPromiseFactory();
-        RPromise result = factory.createPromise(frame.materialize());
+        result = factory.createPromise(frame.materialize());
         RArguments.setArgument(frame, index, result);   // Update RArguments for S3 dispatch to work
         return result;
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/read/PositionsArrayNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/read/PositionsArrayNode.java
index 803b5ddbcb41523e701ce88d4ff3a41ee8a2fc8d..654172afed95502e642e942a453976c006e6312d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/read/PositionsArrayNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/read/PositionsArrayNode.java
@@ -83,19 +83,24 @@ public class PositionsArrayNode extends RNode {
     }
 
     @ExplodeLoop
-    public Object executeEvalNoVarArg(VirtualFrame frame, Object vector, Object exact) {
-        int length = conversionAdapter.getLength();
+    public static Object[] explodeLoopNoVarArg(VirtualFrame frame, PositionsArrayNodeAdapter positionsAdapter, int length) {
         Object[] evaluatedElements = new Object[length];
         for (int i = 0; i < length; i++) {
             evaluatedElements[i] = positionsAdapter.executePos(frame, i);
         }
-        executeEvalInternal(frame, vector, exact, evaluatedElements);
+        return evaluatedElements;
+    }
+
+    public Object executeEvalNoVarArg(VirtualFrame frame, Object vector, Object exact) {
+        int length = conversionAdapter.getLength();
+        Object[] evaluatedElements = explodeLoopNoVarArg(frame, positionsAdapter, length);
+        length = conversionAdapter.getLength(); // could have changed
+        executeEvalInternal(frame, vector, exact, evaluatedElements, length);
         return conversionAdapter.getLength() == 1 ? evaluatedElements[0] : evaluatedElements;
     }
 
     @ExplodeLoop
-    public Object executeEvalVarArg(VirtualFrame frame, Object vector, Object exact) {
-        int length = conversionAdapter.getLength();
+    public static Object[] explodeLoopVarArg(VirtualFrame frame, PositionsArrayNodeAdapter positionsAdapter, int length, PromiseHelperNode promiseHelper) {
         Object[] evaluatedElements = new Object[length];
         int ind = 0;
         for (int i = 0; i < length; i++) {
@@ -108,17 +113,24 @@ public class PositionsArrayNode extends RNode {
                 evaluatedElements[ind++] = p;
             }
         }
+        return evaluatedElements;
+    }
+
+    public Object executeEvalVarArg(VirtualFrame frame, Object vector, Object exact) {
+        int length = conversionAdapter.getLength();
+        Object[] evaluatedElements = explodeLoopVarArg(frame, positionsAdapter, length, promiseHelper);
         if (evaluatedElements.length != conversionAdapter.getLength()) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            this.conversionAdapter = new PositionsArrayConversionNodeMultiDimAdapter(this.conversionAdapter.isSubset(), evaluatedElements.length);
+            this.conversionAdapter = insert(new PositionsArrayConversionNodeMultiDimAdapter(this.conversionAdapter.isSubset(), evaluatedElements.length));
         }
-        executeEvalInternal(frame, vector, exact, evaluatedElements);
+        length = conversionAdapter.getLength(); // could have changed
+        executeEvalInternal(frame, vector, exact, evaluatedElements, length);
         return conversionAdapter.getLength() == 1 ? evaluatedElements[0] : evaluatedElements;
     }
 
     @ExplodeLoop
-    public void executeEvalInternal(VirtualFrame frame, Object vector, Object exact, Object[] evaluatedElements) {
-        for (int i = 0; i < evaluatedElements.length; i++) {
+    public void executeEvalInternal(VirtualFrame frame, Object vector, Object exact, Object[] evaluatedElements, int length) {
+        for (int i = 0; i < length; i++) {
             Object convertedOperator = conversionAdapter.executeConvert(frame, vector, evaluatedElements[i], exact, i);
             evaluatedElements[i] = conversionAdapter.executeArg(frame, vector, convertedOperator, i);
             if (conversionAdapter.multiDimOperatorConverters != null) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/PositionsArrayNodeValue.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/PositionsArrayNodeValue.java
index 1607f44952d56e9dcad746bc5b7657fd8111151e..7634ee3fc9539e30f87fa39ec85007c73464304a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/PositionsArrayNodeValue.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/array/write/PositionsArrayNodeValue.java
@@ -31,7 +31,6 @@ import com.oracle.truffle.r.nodes.access.array.read.*;
 import com.oracle.truffle.r.nodes.function.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.RDeparse.*;
-import com.oracle.truffle.r.runtime.data.*;
 
 public class PositionsArrayNodeValue extends RNode {
 
@@ -62,41 +61,29 @@ public class PositionsArrayNodeValue extends RNode {
         }
     }
 
+    @ExplodeLoop
     public Object executeEvalNoVarArg(VirtualFrame frame, Object vector, Object value) {
         int length = conversionAdapter.getLength();
-        Object[] evaluatedElements = new Object[length];
-        for (int i = 0; i < length; i++) {
-            evaluatedElements[i] = positionsAdapter.executePos(frame, i);
-        }
-        executeEvalInternal(frame, vector, value, evaluatedElements);
+        Object[] evaluatedElements = PositionsArrayNode.explodeLoopNoVarArg(frame, positionsAdapter, length);
+        executeEvalInternal(frame, vector, value, evaluatedElements, length);
         return conversionAdapter.getLength() == 1 ? evaluatedElements[0] : evaluatedElements;
     }
 
     public Object executeEvalVarArg(VirtualFrame frame, Object vector, Object value) {
         int length = conversionAdapter.getLength();
-        Object[] evaluatedElements = new Object[length];
-        int ind = 0;
-        for (int i = 0; i < length; i++) {
-            Object p = positionsAdapter.executePos(frame, i);
-            if (p instanceof RArgsValuesAndNames) {
-                RArgsValuesAndNames varArg = (RArgsValuesAndNames) p;
-                evaluatedElements = PositionsArrayNode.expandVarArg(frame, varArg, ind, evaluatedElements, promiseHelper);
-                ind += varArg.length();
-            } else {
-                evaluatedElements[ind++] = p;
-            }
-        }
+        Object[] evaluatedElements = PositionsArrayNode.explodeLoopVarArg(frame, positionsAdapter, length, promiseHelper);
         if (evaluatedElements.length != conversionAdapter.getLength()) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
-            this.conversionAdapter = new PositionsArrayConversionValueNodeMultiDimAdapter(this.conversionAdapter.isSubset(), evaluatedElements.length);
+            this.conversionAdapter = insert(new PositionsArrayConversionValueNodeMultiDimAdapter(this.conversionAdapter.isSubset(), evaluatedElements.length));
         }
-        executeEvalInternal(frame, vector, value, evaluatedElements);
+        length = conversionAdapter.getLength(); // could have changed
+        executeEvalInternal(frame, vector, value, evaluatedElements, length);
         return conversionAdapter.getLength() == 1 ? evaluatedElements[0] : evaluatedElements;
     }
 
     @ExplodeLoop
-    public void executeEvalInternal(VirtualFrame frame, Object vector, Object value, Object[] evaluatedElements) {
-        for (int i = 0; i < conversionAdapter.getLength(); i++) {
+    public void executeEvalInternal(VirtualFrame frame, Object vector, Object value, Object[] evaluatedElements, int length) {
+        for (int i = 0; i < length; i++) {
             Object convertedOperator = conversionAdapter.executeConvert(frame, vector, evaluatedElements[i], RRuntime.LOGICAL_TRUE, i);
             evaluatedElements[i] = conversionAdapter.executeArg(frame, vector, convertedOperator, i);
             if (conversionAdapter.multiDimOperatorConverters != null) {
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 66d3f418dd892221d10c9fce2fec058785919f98..4ecc1657b7c6dd97167918f9d708e8eb1ce0d736 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
@@ -160,7 +160,7 @@ public abstract class RBuiltinNode extends LeafCallNode implements VisibilityCon
     static RootCallTarget createArgumentsCallTarget(RBuiltinFactory builtin) {
         // Create function initialization
         RNode[] argAccessNodes = createAccessArgumentsNodes(builtin);
-        RBuiltinNode node = createNode(builtin, argAccessNodes, null);
+        RBuiltinNode node = createNode(builtin, argAccessNodes.clone(), null);
 
         // Create formal arguments
         // TODO We only call getParameterNames to support overrides
@@ -176,6 +176,9 @@ public abstract class RBuiltinNode extends LeafCallNode implements VisibilityCon
             names[i] = nameObj.isEmpty() ? null : nameObj;
         }
         FormalArguments formals = FormalArguments.create(names, node.getParameterValues());
+        for (RNode access : argAccessNodes) {
+            ((AccessArgumentNode) access).setFormals(formals);
+        }
 
         // Setup
         FrameDescriptor frameDescriptor = new FrameDescriptor();
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 70029ab22c30ced7d50231e8c078232cf74a8058..076b6beed317e6807051643b2fc9832a25a73454 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
@@ -23,9 +23,9 @@
 package com.oracle.truffle.r.nodes.function;
 
 import java.util.*;
+import java.util.function.*;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
@@ -98,8 +98,8 @@ import com.oracle.truffle.r.runtime.data.RPromise.RPromiseFactory;
  *
  * f <- function(...) g(...); g <- function(a,b) { a - b }; f(b=1,2)
  *
- * Consequently, "non-executed" ... arguments are represented as {@link VarArgsNode}-s (inheriting
- * from {@link RNode}) and "executed" .. arguments are represented as a language level value of type
+ * Consequently, "non-executed" ... arguments are represented as VarArgsNodes (inheriting from
+ * {@link RNode}) and "executed" .. arguments are represented as a language level value of type
  * {@link RArgsValuesAndNames}, which can be passes directly in the {@link RArguments} object and
  * whose type is understood by the language's builtins (both representations are name-preserving).
  * </p>
@@ -121,8 +121,8 @@ public class ArgumentMatcher {
      *      ClosureCache, boolean)
      */
     public static MatchedArguments matchArguments(RFunction function, UnmatchedArguments suppliedArgs, SourceSection callSrc, SourceSection argsSrc, boolean noOpt) {
-        FormalArguments formals = ((RRootNode) function.getTarget().getRootNode()).getFormalArguments();
         RNode[] wrappedArgs = matchNodes(function, suppliedArgs.getArguments(), suppliedArgs.getNames(), callSrc, argsSrc, false, suppliedArgs, noOpt);
+        FormalArguments formals = ((RRootNode) function.getTarget().getRootNode()).getFormalArguments();
         return MatchedArguments.create(wrappedArgs, formals.getNames());
     }
 
@@ -164,8 +164,35 @@ public class ArgumentMatcher {
                     boolean forNextMethod) {
         RRootNode rootNode = (RRootNode) function.getTarget().getRootNode();
         FormalArguments formals = rootNode.getFormalArguments();
-        Object[] evaledArgs = permuteArguments(function, evaluatedArgs.getEvaluatedArgs(), evaluatedArgs.getNames(), formals, new VarArgsAsObjectArrayFactory(), new ObjectArrayFactory(), callSrc,
-                        null, forNextMethod);
+        MatchPermutation match = permuteArguments(function, evaluatedArgs.getNames(), formals, callSrc, null, forNextMethod, index -> {
+            throw Utils.nyi("S3Dispatch should not have arg length mismatch");
+        }, index -> evaluatedArgs.getNames()[index]);
+
+        Object[] evaledArgs = new Object[match.resultPermutation.length];
+
+        for (int formalIndex = 0; formalIndex < match.resultPermutation.length; formalIndex++) {
+            int suppliedIndex = match.resultPermutation[formalIndex];
+
+            // Has varargs? Unfold!
+            if (suppliedIndex == VARARGS) {
+                int varArgsLen = match.varargsPermutation.length;
+                Object[] newVarArgs = new Object[varArgsLen];
+                boolean nonNull = false;
+                for (int i = 0; i < varArgsLen; i++) {
+                    newVarArgs[i] = evaluatedArgs.arguments[match.varargsPermutation[i]];
+                    nonNull |= newVarArgs[i] != null;
+                }
+                if (nonNull) {
+                    evaledArgs[formalIndex] = new RArgsValuesAndNames(newVarArgs, match.varargsNames);
+                } else {
+                    evaledArgs[formalIndex] = RArgsValuesAndNames.EMPTY;
+                }
+            } else if (suppliedIndex == UNMATCHED) {
+                // nothing to do... (resArgs[formalIndex] == null)
+            } else {
+                evaledArgs[formalIndex] = evaluatedArgs.arguments[suppliedIndex];
+            }
+        }
 
         // Replace RMissing with default value!
         RNode[] defaultArgs = formals.getDefaultArgs();
@@ -219,8 +246,6 @@ public class ArgumentMatcher {
      *
      * @return A list of {@link RNode}s which consist of the given arguments in the correct order
      *         and wrapped into the proper {@link PromiseNode}s
-     * @see #permuteArguments(RFunction, Object[], String[], FormalArguments, VarArgsFactory,
-     *      ArrayFactory, SourceSection, SourceSection, boolean)
      */
     private static RNode[] matchNodes(RFunction function, RNode[] suppliedArgs, String[] suppliedNames, SourceSection callSrc, SourceSection argsSrc, boolean isForInlinedBuiltin,
                     ClosureCache closureCache, boolean noOpt) {
@@ -229,272 +254,259 @@ public class ArgumentMatcher {
         FormalArguments formals = ((RRootNode) function.getTarget().getRootNode()).getFormalArguments();
 
         // Rearrange arguments
-        RNode[] resultArgs = permuteArguments(function, suppliedArgs, suppliedNames, formals, new VarArgsAsObjectArrayNodeFactory(), new RNodeArrayFactory(), callSrc, argsSrc, false);
+        MatchPermutation match = permuteArguments(function, suppliedNames, formals, callSrc, argsSrc, false, index -> ArgumentsTrait.isVarArg(RMissingHelper.unwrapName(suppliedArgs[index])),
+                        index -> suppliedArgs[index].getSourceSection().getCode());
+
+        RNode[] defaultArgs = formals.getDefaultArgs();
+        RNode[] resArgs = new RNode[match.resultPermutation.length];
+
+        /**
+         * Walks a list of given arguments ({@link RNode}s) and wraps them in {@link PromiseNode}s
+         * individually by using promiseWrapper (unfolds varargs, too!) if necessary.
+         *
+         * @param function The function which is to be called
+         * @param arguments The arguments passed to the function call, already in correct order
+         * @param formals The {@link FormalArguments} for the given function
+         * @param promiseWrapper The {@link PromiseWrapper} implementation which handles the
+         *            wrapping of individual arguments
+         * @param closureCache The {@link ClosureCache} for the supplied arguments
+         * @return A list of {@link RNode} wrapped in {@link PromiseNode}s
+         */
+
+        // Check whether this is a builtin
+        RootNode rootNode = function.getTarget().getRootNode();
+        RBuiltinRootNode builtinRootNode = rootNode instanceof RBuiltinRootNode ? (RBuiltinRootNode) rootNode : null;
+
+        // int logicalIndex = 0; As our builtin's 'evalsArgs' is meant for FastR arguments (which
+        // take "..." as one), we don't need a logicalIndex
+        for (int formalIndex = 0; formalIndex < match.resultPermutation.length; formalIndex++) {
+            int suppliedIndex = match.resultPermutation[formalIndex];
 
-        PromiseWrapper wrapper = isForInlinedBuiltin ? new BuiltinInitPromiseWrapper(noOpt) : new DefaultPromiseWrapper(noOpt);
-        return wrapInPromises(function, resultArgs, formals, wrapper, closureCache, callSrc);
+            // Has varargs? Unfold!
+            if (suppliedIndex == VARARGS) {
+                int varArgsLen = match.varargsPermutation.length;
+                String[] newNames = match.varargsNames;
+                RNode[] newVarArgs = new RNode[varArgsLen];
+                int index = 0;
+                for (int i = 0; i < varArgsLen; i++) {
+                    RNode varArg = suppliedArgs[match.varargsPermutation[i]];
+                    if (varArg == null) {
+                        if (newNames[i] == null) {
+                            // Skip all missing values (important for detection of emtpy "...",
+                            // which consequently collapse
+                            continue;
+                        } else {
+                            // But do not skip parameters ala "[...], builtins =, [...]"
+                            varArg = ConstantNode.create(RMissing.instance);
+                        }
+                    }
+                    newNames[index] = newNames[i];
+                    newVarArgs[index] = varArg;
+                    index++;
+                }
+
+                // "Delete and shrink": Shrink only if necessary
+                int newLength = index;
+                if (newLength == 0) {
+                    // Corner case: "f <- function(...) g(...); g <- function(...)"
+                    // Insert correct "missing"!
+                    resArgs[formalIndex] = wrap(formals, builtinRootNode, closureCache, null, null, formalIndex, isForInlinedBuiltin, noOpt);
+                    continue;
+                }
+                if (newNames.length > newLength) {
+                    newNames = Arrays.copyOf(newNames, newLength);
+                    newVarArgs = Arrays.copyOf(newVarArgs, newLength);
+                }
+
+                EvalPolicy evalPolicy = getEvalPolicy(builtinRootNode, formalIndex);
+                resArgs[formalIndex] = PromiseNode.createVarArgs(null, evalPolicy, newVarArgs, newNames, closureCache, callSrc);
+            } else {
+                RNode defaultArg = formalIndex < defaultArgs.length ? defaultArgs[formalIndex] : null;
+                RNode suppliedArg = suppliedIndex == UNMATCHED ? null : suppliedArgs[suppliedIndex];
+                resArgs[formalIndex] = wrap(formals, builtinRootNode, closureCache, suppliedArg, defaultArg, formalIndex, isForInlinedBuiltin, noOpt);
+            }
+        }
+        return resArgs;
+    }
+
+    private static final class MatchPermutation {
+        private final int[] resultPermutation;
+        private final int[] varargsPermutation;
+        private final String[] varargsNames;
+
+        public MatchPermutation(int[] resultPermutation, int[] varargsPermutation, String[] varargsNames) {
+            this.resultPermutation = resultPermutation;
+            this.varargsPermutation = varargsPermutation;
+            this.varargsNames = varargsNames;
+        }
     }
 
+    private static final int UNMATCHED = -1;
+    private static final int VARARGS = -2;
+
     /**
      * /** This method does the heavy lifting of re-arranging arguments by their names and position,
      * also handling varargs.
      *
      * @param function The function which should be called
-     * @param suppliedArgs The arguments given to this function call
      * @param suppliedNames The names the arguments might have
      * @param formals The {@link FormalArguments} this function has
-     * @param listFactory An abstraction for the creation of list of different types
-     * @param arrFactory An abstraction for the generic creation of type safe arrays
      * @param callSrc The source of the function call currently executed
      * @param argsSrc The source code encapsulating the arguments, for debugging purposes
      * @param forNextMethod matching when evaluating NextMethod
      *
-     * @param <T> The type of the given arguments
      * @return An array of type <T> with the supplied arguments in the correct order
      */
     @TruffleBoundary
-    private static <T> T[] permuteArguments(RFunction function, T[] suppliedArgs, String[] suppliedNames, FormalArguments formals, VarArgsFactory<T> listFactory, ArrayFactory<T> arrFactory,
-                    SourceSection callSrc, SourceSection argsSrc, boolean forNextMethod) {
-        String[] formalNames = formals.getNames();
+    private static MatchPermutation permuteArguments(RFunction function, String[] suppliedNames, FormalArguments formals, SourceSection callSrc, SourceSection argsSrc, boolean forNextMethod,
+                    IntPredicate isVarSuppliedVarargs, IntFunction<String> errorString) {
+        // assert Arrays.stream(suppliedNames).allMatch(name -> name == null || !name.isEmpty());
 
         // Preparations
         int varArgIndex = formals.getVarArgIndex();
         boolean hasVarArgs = varArgIndex != FormalArguments.NO_VARARG;
 
         // MATCH by exact name
-        T[] resultArgs = arrFactory.newArray(formalNames.length);
-        BitSet matchedSuppliedArgs = new BitSet(suppliedNames.length);
-        BitSet matchedFormalArgs = new BitSet(formalNames.length);
-        int unmatchedNameCount = 0; // The nr of named supplied args that do not match
-        // si = suppliedIndex, fi = formalIndex
-        for (int si = 0; si < suppliedNames.length; si++) {
-            if (suppliedNames[si] == null || suppliedNames[si].isEmpty()) {
+        int[] resultPermutation = new int[formals.getNames().length];
+        Arrays.fill(resultPermutation, UNMATCHED);
+
+        boolean[] matchedSuppliedArgs = new boolean[suppliedNames.length];
+        for (int suppliedIndex = 0; suppliedIndex < suppliedNames.length; suppliedIndex++) {
+            if (suppliedNames[suppliedIndex] == null || suppliedNames[suppliedIndex].isEmpty()) {
                 continue;
             }
 
             // Search for argument name inside formal arguments
-            int fi = findParameterPosition(formalNames, suppliedNames[si], matchedFormalArgs, si, hasVarArgs, suppliedArgs[si], callSrc, argsSrc, varArgIndex, forNextMethod);
-            if (fi >= 0) {
-                resultArgs[fi] = suppliedArgs[si];
-                matchedSuppliedArgs.set(si);
-            } else {
-                // Named supplied arg that has no match: Vararg candidate!
-                unmatchedNameCount++;
+            int formalIndex = findParameterPosition(formals.getNames(), suppliedNames[suppliedIndex], resultPermutation, suppliedIndex, hasVarArgs, callSrc, argsSrc, varArgIndex, forNextMethod,
+                            errorString);
+            if (formalIndex != UNMATCHED) {
+                resultPermutation[formalIndex] = suppliedIndex;
+                matchedSuppliedArgs[suppliedIndex] = true;
             }
         }
 
-        // TODO MATCH by partial name
+        // TODO MATCH by partial name (up to the vararg, which consumes all non-exact matches)
 
         // MATCH by position
-        UnmatchedSuppliedIterator<T> siCursor = new UnmatchedSuppliedIterator<>(suppliedArgs, matchedSuppliedArgs);
-        for (int fi = 0; fi < resultArgs.length; fi++) {
+        int suppliedIndex = -1;
+        int regularArgumentCount = hasVarArgs ? varArgIndex : formals.getNames().length;
+        outer: for (int formalIndex = 0; formalIndex < regularArgumentCount; formalIndex++) {
             // Unmatched?
-            if (!matchedFormalArgs.get(fi)) {
-                while (siCursor.hasNext() && siCursor.nextIndex() < suppliedNames.length && suppliedNames[siCursor.nextIndex()] != null && !suppliedNames[siCursor.nextIndex()].isEmpty() &&
-                                !forNextMethod) {
-                    // Slide over named parameters and find subsequent location of unnamed parameter
-                    // (if processing args for NextMethod, try to match yet unmatched named
-                    // parameters - do not slide over them)
-                    siCursor.next();
+            if (resultPermutation[formalIndex] == UNMATCHED) {
+                while (true) {
+                    suppliedIndex++;
+                    if (suppliedIndex == suppliedNames.length) {
+                        // no more unmatched supplied arguments
+                        break outer;
+                    }
+                    if (!matchedSuppliedArgs[suppliedIndex]) {
+                        if (forNextMethod) {
+                            // for NextMethod, unused parameters are matched even when named
+                            break;
+                        }
+                        if (suppliedNames[suppliedIndex] == null || suppliedNames[suppliedIndex].isEmpty()) {
+                            // unnamed parameter, match by position
+                            break;
+                        }
+                    }
                 }
-                boolean followsDots = hasVarArgs && fi >= varArgIndex;
-                if (siCursor.hasNext() && !followsDots) {
-                    resultArgs[fi] = siCursor.next();
+                resultPermutation[formalIndex] = suppliedIndex;
 
-                    // set formal status AND "remove" supplied arg from list
-                    matchedFormalArgs.set(fi);
-                    siCursor.remove();
-                }
+                // set formal status AND "remove" supplied arg from list
+                matchedSuppliedArgs[suppliedIndex] = true;
             }
         }
 
         // MATCH rest to vararg "..."
         if (hasVarArgs) {
-            assert listFactory != null;
-            int varArgCount = suppliedArgs.length - matchedSuppliedArgs.cardinality();
-
-            // Create vararg array (+ names if necessary)
-            T[] varArgsArray = arrFactory.newArray(varArgCount);
-            String[] namesArray = null;
-            if (unmatchedNameCount != 0) {
-                namesArray = new String[varArgCount];
-            }
+            int varArgCount = suppliedNames.length - cardinality(matchedSuppliedArgs);
+
+            // Create vararg array
+            int[] varArgsPermutation = new int[varArgCount];
+            String[] namesArray = new String[varArgCount];
 
             // Add every supplied argument that has not been matched
             int pos = 0;
-            UnmatchedSuppliedIterator<T> si = new UnmatchedSuppliedIterator<>(suppliedArgs, matchedSuppliedArgs);
-            while (si.hasNext()) {
-                T arg = si.next();
-                si.remove();
-                if (arrFactory.isMissing(arg)) {
-                    // do not fold missing arguments into ...
-                    varArgsArray = Utils.resizeArray(varArgsArray, varArgsArray.length - 1);
-                    if (namesArray != null) {
-                        namesArray = Utils.resizeArray(namesArray, namesArray.length - 1);
-                    }
-                    continue;
-                }
-                varArgsArray[pos] = arg;
-                if (namesArray != null) {
-                    String suppliedName = suppliedNames[si.lastIndex()];
-                    namesArray[pos] = suppliedName;
-                }
-                pos++;
-            }
-            resultArgs[varArgIndex] = listFactory.makeList(varArgsArray, namesArray);
-        }
-
-        // Error check: Unused argument?
-        int leftoverCount = suppliedArgs.length - matchedSuppliedArgs.cardinality();
-        if (leftoverCount > 0) {
-            // Check if this is really an error. Might be an inlined "..."!
-            UnmatchedSuppliedIterator<T> si = new UnmatchedSuppliedIterator<>(suppliedArgs, matchedSuppliedArgs);
-            if (leftoverCount == 1) {
-                T arg = si.next();
-                if (arrFactory.isVararg(arg)) {
-                    return resultArgs;
+            for (suppliedIndex = 0; suppliedIndex < suppliedNames.length; suppliedIndex++) {
+                if (!matchedSuppliedArgs[suppliedIndex]) {
+                    matchedSuppliedArgs[suppliedIndex] = true;
+                    varArgsPermutation[pos] = suppliedIndex;
+                    namesArray[pos] = suppliedNames[suppliedIndex];
+                    pos++;
                 }
             }
 
-            // Definitely an error: Prepare error message
-            si.reset();
-            throwUnusedArgumentError(leftoverCount, si, arrFactory, callSrc);
-        }
-
-        return resultArgs;
-    }
-
-    @TruffleBoundary
-    private static <T> void throwUnusedArgumentError(int leftoverCount, UnmatchedSuppliedIterator<T> si, ArrayFactory<T> arrFactory, SourceSection callSrc) {
-        // UNUSED_ARGUMENT(S)?
-        if (leftoverCount == 1) {
-            CompilerDirectives.transferToInterpreter();
-            String argStr = arrFactory.debugString(si.next());
-            throw RError.error(callSrc, RError.Message.UNUSED_ARGUMENT, argStr);
-        }
-
-        // Create error message
-        T[] debugArgs = arrFactory.newArray(leftoverCount);
-        int pos = 0;
-        while (si.hasNext()) {
-            debugArgs[pos++] = si.next();
-        }
-
-        CompilerDirectives.transferToInterpreter();
-        String argStr = arrFactory.debugString(debugArgs);
-        throw RError.error(callSrc, RError.Message.UNUSED_ARGUMENTS, argStr);
-    }
-
-    /**
-     * Used in
-     * {@link ArgumentMatcher#permuteArguments(RFunction, Object[], String[], FormalArguments, VarArgsFactory, ArrayFactory, SourceSection, SourceSection, boolean)}
-     * for iteration over suppliedArgs.
-     *
-     * @param <T>
-     */
-    private static class UnmatchedSuppliedIterator<T> implements Iterator<T> {
-        private static final int NO_MORE_ARGS = -1;
-        private int si;
-        private int lastSi;
-        @CompilationFinal private final T[] suppliedArgs;
-        private final BitSet matchedSuppliedArgs;
-
-        public UnmatchedSuppliedIterator(T[] suppliedArgs, BitSet matchedSuppliedArgs) {
-            this.suppliedArgs = suppliedArgs;
-            this.matchedSuppliedArgs = matchedSuppliedArgs;
-            reset();
-        }
-
-        public void reset() {
-            si = 0;
-            lastSi = 0;
-        }
+            resultPermutation[varArgIndex] = VARARGS;
+            return new MatchPermutation(resultPermutation, varArgsPermutation, namesArray);
+        } else {
+            // Error check: Unused argument? (can only happen when there are no varargs)
 
-        /**
-         * @return Index of the argument returned by the last {@link #next()} call.
-         */
-        public int lastIndex() {
-            return lastSi;
-        }
-
-        /**
-         * @return The argument which is going to be returned from the next {@link #next()} call.
-         * @throws NoSuchElementException If {@link #hasNext()} == true!
-         */
-        public int nextIndex() {
-            int next = getNextIndex(si);
-            if (next == NO_MORE_ARGS) {
-                throw new NoSuchElementException();
+            suppliedIndex = 0;
+            while (suppliedIndex < suppliedNames.length && matchedSuppliedArgs[suppliedIndex]) {
+                suppliedIndex++;
             }
-            return next;
-        }
 
-        @Override
-        public boolean hasNext() {
-            return getNextIndex(si) != NO_MORE_ARGS;
-        }
+            if (suppliedIndex < suppliedNames.length) {
+                int leftoverCount = suppliedNames.length - cardinality(matchedSuppliedArgs);
+                if (leftoverCount == 1) {
+                    if (isVarSuppliedVarargs.test(suppliedIndex)) {
+                        return new MatchPermutation(resultPermutation, null, null);
+                    }
 
-        private int getNextIndex(int from) {
-            if (from == NO_MORE_ARGS) {
-                return NO_MORE_ARGS;
-            }
-            int next = matchedSuppliedArgs.nextClearBit(from);
-            if (next == NO_MORE_ARGS || next >= suppliedArgs.length) {
-                return NO_MORE_ARGS;
-            }
-            return next;
-        }
+                    // one unused argument
+                    CompilerDirectives.transferToInterpreter();
+                    throw RError.error(callSrc, RError.Message.UNUSED_ARGUMENT, errorString.apply(suppliedIndex));
+                }
 
-        @Override
-        public T next() {
-            int next = getNextIndex(si);
-            if (next == NO_MORE_ARGS) {
-                throw new NoSuchElementException();
+                CompilerDirectives.transferToInterpreter();
+                // multiple unused arguments
+                StringBuilder str = new StringBuilder();
+                int cnt = 0;
+                for (; suppliedIndex < suppliedNames.length; suppliedIndex++) {
+                    if (!matchedSuppliedArgs[suppliedIndex]) {
+                        if (cnt++ > 0) {
+                            str.append(", ");
+                        }
+                        str.append(errorString.apply(suppliedIndex));
+                    }
+                }
+                throw RError.error(callSrc, RError.Message.UNUSED_ARGUMENTS, str);
             }
-            lastSi = next;
-            si = getNextIndex(next + 1);
-            return suppliedArgs[lastSi];
+            return new MatchPermutation(resultPermutation, null, null);
         }
+    }
 
-        @Override
-        public void remove() {
-            matchedSuppliedArgs.set(lastSi);
+    private static int cardinality(boolean[] array) {
+        int sum = 0;
+        for (boolean b : array) {
+            if (b) {
+                sum++;
+            }
         }
+        return sum;
     }
 
     /**
      * Searches for suppliedName inside formalNames and returns its (formal) index.
      *
-     * @param formalNames
-     * @param suppliedName
-     * @param matchedSuppliedArgs
-     * @param suppliedIndex
-     * @param hasVarArgs
-     * @param debugArgNode
-     * @param callSrc
-     * @param argsSrc
-     * @param varArgIndex
-     * @param forNextMethod
-     *
      * @return The position of the given suppliedName inside the formalNames. Throws errors if the
      *         argument has been matched before
      */
-    private static <T> int findParameterPosition(String[] formalNames, String suppliedName, BitSet matchedSuppliedArgs, int suppliedIndex, boolean hasVarArgs, T debugArgNode, SourceSection callSrc,
-                    SourceSection argsSrc, int varArgIndex, boolean forNextMethod) {
-        int found = -1;
+    private static <T> int findParameterPosition(String[] formalNames, String suppliedName, int[] resultPermutation, int suppliedIndex, boolean hasVarArgs, SourceSection callSrc,
+                    SourceSection argsSrc, int varArgIndex, boolean forNextMethod, IntFunction<String> errorString) {
+        int found = UNMATCHED;
         for (int i = 0; i < formalNames.length; i++) {
             if (formalNames[i] == null) {
                 continue;
             }
 
-            final String formalName = formalNames[i];
+            String formalName = formalNames[i];
             if (formalName.equals(suppliedName)) {
                 found = i;
-                if (matchedSuppliedArgs.get(found)) {
+                if (resultPermutation[found] != UNMATCHED) {
                     // Has already been matched: Error!
                     throw RError.error(argsSrc, RError.Message.FORMAL_MATCHED_MULTIPLE, formalName);
                 }
-                matchedSuppliedArgs.set(found);
                 break;
             } else if (!suppliedName.isEmpty() && formalName.startsWith(suppliedName) && ((varArgIndex != FormalArguments.NO_VARARG && i < varArgIndex) || varArgIndex == FormalArguments.NO_VARARG)) {
                 // partial-match only if the formal argument is positioned before ...
@@ -502,260 +514,57 @@ public class ArgumentMatcher {
                     throw RError.error(argsSrc, RError.Message.ARGUMENT_MATCHES_MULTIPLE, 1 + suppliedIndex);
                 }
                 found = i;
-                if (matchedSuppliedArgs.get(found)) {
+                if (resultPermutation[found] != UNMATCHED) {
                     throw RError.error(argsSrc, RError.Message.FORMAL_MATCHED_MULTIPLE, formalName);
                 }
-                matchedSuppliedArgs.set(found);
             }
         }
         if (found >= 0 || hasVarArgs || forNextMethod) {
             return found;
         }
-        // Error!
-        String debugSrc = suppliedName;
-        if (debugArgNode instanceof RNode) {
-            SourceSection ss = ((RNode) debugArgNode).getSourceSection();
-            if (ss != null && ss.getCode() != null) {
-                debugSrc = ((RNode) debugArgNode).getSourceSection().getCode();
-            }
-        }
-        throw RError.error(callSrc, RError.Message.UNUSED_ARGUMENT, debugSrc);
-    }
-
-    /**
-     * Walks a list of given arguments ({@link RNode}s) and wraps them in {@link PromiseNode}s
-     * individually by using promiseWrapper (unfolds varargs, too!) if necessary.
-     *
-     * @param function The function which is to be called
-     * @param arguments The arguments passed to the function call, already in correct order
-     * @param formals The {@link FormalArguments} for the given function
-     * @param promiseWrapper The {@link PromiseWrapper} implementation which handles the wrapping of
-     *            individual arguments
-     * @param closureCache The {@link ClosureCache} for the supplied arguments
-     * @return A list of {@link RNode} wrapped in {@link PromiseNode}s
-     */
-    @TruffleBoundary
-    private static RNode[] wrapInPromises(RFunction function, RNode[] arguments, FormalArguments formals, PromiseWrapper promiseWrapper, ClosureCache closureCache, SourceSection callSrc) {
-        RNode[] defaultArgs = formals.getDefaultArgs();
-        RNode[] resArgs = arguments;
-
-        // Check whether this is a builtin
-        RootNode rootNode = function.getTarget().getRootNode();
-        final RBuiltinRootNode builtinRootNode = rootNode instanceof RBuiltinRootNode ? (RBuiltinRootNode) rootNode : null;
-
-        // int logicalIndex = 0; As our builtin's 'evalsArgs' is meant for FastR arguments (which
-        // take "..." as one), we don't need a logicalIndex
-        for (int fi = 0; fi < arguments.length; fi++) {
-            RNode arg = arguments[fi];  // arg may be null, which denotes 'no arg supplied'
-
-            // Has varargs? Unfold!
-            if (arg instanceof VarArgsAsObjectArrayNode) {
-                VarArgsAsObjectArrayNode varArgs = (VarArgsAsObjectArrayNode) arg;
-                int varArgsLen = varArgs.getArgumentNodes().length;
-                String[] newNames = varArgs.getNames() == null ? new String[varArgsLen] : Arrays.copyOf(varArgs.getNames(), varArgsLen);
-                RNode[] newVarArgs = Utils.resizeArray(varArgs.getArgumentNodes(), varArgsLen);
-                int index = 0;
-                for (int i = 0; i < varArgs.getArgumentNodes().length; i++) {
-                    RNode varArg = varArgs.getArgumentNodes()[i];
-                    if (varArg == null) {
-                        if (newNames[i] == null) {
-                            // Skip all missing values (important for detection of emtpy "...",
-                            // which consequently collapse
-                            continue;
-                        } else {
-                            // But do not skip parameters ala "[...], builtins =, [...]"
-                            varArg = ConstantNode.create(RMissing.instance);
-                        }
-                    }
-                    newNames[index] = varArgs.getNames() == null ? null : varArgs.getNames()[i];
-                    newVarArgs[index] = varArg;
-                    index++;
-                }
-
-                // "Delete and shrink": Shrink only if necessary
-                int newLength = index;
-                if (newLength == 0) {
-                    // Corner case: "f <- function(...) g(...); g <- function(...)"
-                    // Insert correct "missing"!
-                    resArgs[fi] = promiseWrapper.wrap(function, formals, builtinRootNode, closureCache, null, null, fi);
-                    continue;
-                }
-                if (newNames.length > newLength) {
-                    newNames = Arrays.copyOf(newNames, newLength);
-                    newVarArgs = Arrays.copyOf(newVarArgs, newLength);
-                }
-
-                EvalPolicy evalPolicy = promiseWrapper.getEvalPolicy(function, builtinRootNode, fi);
-                resArgs[fi] = PromiseNode.createVarArgs(varArgs.getSourceSection(), evalPolicy, newVarArgs, newNames, closureCache, callSrc);
-            } else {
-                // Normal argument: just wrap in promise
-                RNode defaultArg = fi < defaultArgs.length ? defaultArgs[fi] : null;
-                resArgs[fi] = promiseWrapper.wrap(function, formals, builtinRootNode, closureCache, arg, defaultArg, fi);
-            }
-        }
-        return resArgs;
+        throw RError.error(callSrc, RError.Message.UNUSED_ARGUMENT, errorString.apply(suppliedIndex));
     }
 
     /**
-     * Interface for trading the cost of using reflection.
-     *
-     * <pre>
-     * Class<?> argClass = suppliedArgs.getClass().getComponentClass();
-     * @SuppressWarning("unchecked")
-     * T[] resultArgs = (T[]) Array.newInstance(argClass, size)
-     * </pre>
-     *
-     * against a type safe virtual function call.
-     *
-     * @param <T> The component type of the arrays to be created
+     * @param builtinRootNode The {@link RBuiltinRootNode} of the function
+     * @param formalIndex The formalIndex of this argument
+     * @return A single suppliedArg and its corresponding defaultValue wrapped up into a
+     *         {@link PromiseNode}
      */
-    private interface ArrayFactory<T> {
-        /**
-         * @param length
-         * @return A fresh (type safe) array of type T
-         */
-        T[] newArray(int length);
-
-        /**
-         * @param arg
-         * @return Whether arg represents a <i>formal</i> "..." which carries no content
-         */
-        default boolean isVararg(T arg) {
-            throw Utils.nyi("S3Dispatch should not have arg length mismatch!?");
-        }
-
-        /**
-         * @param arg
-         * @return Whether arg represents a missing argument
-         */
-        default boolean isMissing(T arg) {
-            throw RInternalError.shouldNotReachHere();
-        }
-
-        /**
-         * @param args
-         * @return A {@link String} containing debug names of all given args
-         */
-        String debugString(T[] args);
-
-        @TruffleBoundary
-        default String debugString(T arg) {
-            T[] args = newArray(1);
-            args[0] = arg;
-            return debugString(args);
-        }
-    }
-
-    /**
-     * {@link ArrayFactory} implementation for {@link RNode}.
-     */
-    private static class RNodeArrayFactory implements ArrayFactory<RNode> {
-        public RNode[] newArray(int length) {
-            return new RNode[length];
-        }
-
-        @Override
-        public boolean isVararg(RNode arg) {
-            // Empty varargs get passed in as "...", and not unrolled. Thus we only have to check
-            // the RVNs name
-            String name = RMissingHelper.unwrapName(arg);
-            return name != null && ArgumentsTrait.isVarArg(name);
-        }
-
-        @Override
-        public boolean isMissing(RNode arg) {
-            return false;
-        }
-
-        @TruffleBoundary
-        public String debugString(RNode[] args) {
-            SourceSection src = Utils.sourceBoundingBox(args);
-            return String.valueOf(src);
-        }
+    public static EvalPolicy getEvalPolicy(RBuiltinRootNode builtinRootNode, int formalIndex) {
+        // This is for actual function calls. However, if the arguments are meant for a
+        // builtin, we have to consider whether they should be forced or not!
+        return builtinRootNode != null && builtinRootNode.evaluatesArg(formalIndex) ? EvalPolicy.INLINED : EvalPolicy.PROMISED;
     }
 
     /**
-     * {@link ArrayFactory} implementation for {@link Object}.
+     * @param formals {@link FormalArguments} as {@link ClosureCache}
+     * @param builtinRootNode The {@link RBuiltinRootNode} of the function
+     * @param closureCache {@link ClosureCache}
+     * @param suppliedArg The argument supplied for this parameter
+     * @param defaultValue The default value for this argument
+     * @param formalIndex The logicalIndex of this argument, also counting individual arguments in
+     *            varargs
+     * @param isBuiltin
+     * @param noOpt
+     * @return Either suppliedArg or its defaultValue wrapped up into a {@link PromiseNode} (or
+     *         {@link RMissing} in case neither is present!
      */
-    private static class ObjectArrayFactory implements ArrayFactory<Object> {
-        public Object[] newArray(int length) {
-            return new Object[length];
-        }
-
-        @Override
-        public boolean isMissing(Object arg) {
-            return arg == RMissing.instance;
-        }
-
-        @TruffleBoundary
-        public String debugString(Object[] args) {
-            StringBuilder b = new StringBuilder();
-            for (int i = 0; i < args.length; i++) {
-                b.append(String.valueOf(args[i]));
-                if (i != args.length - 1) {
-                    b.append(", ");
-                }
-            }
-            return b.toString();
-        }
-    }
-
-    /**
-     * This interface was introduced to reuse
-     * {@link ArgumentMatcher#wrapInPromises(RFunction, RNode[], FormalArguments, PromiseWrapper, ClosureCache, SourceSection)}
-     * and encapsulates the wrapping of a single argument into a {@link PromiseNode}.
-     */
-    private interface PromiseWrapper {
-        /**
-         * @param function the {@link RFunction} being called
-         * @param builtinRootNode The {@link RBuiltinRootNode} of the function
-         * @param formalIndex The formalIndex of this argument
-         * @return A single suppliedArg and its corresponding defaultValue wrapped up into a
-         *         {@link PromiseNode}
-         */
-        EvalPolicy getEvalPolicy(RFunction function, RBuiltinRootNode builtinRootNode, int formalIndex);
-
-        /**
-         * @param function The function this argument is wrapped for
-         * @param formals {@link FormalArguments} as {@link ClosureCache}
-         * @param builtinRootNode The {@link RBuiltinRootNode} of the function
-         * @param closureCache {@link ClosureCache}
-         * @param suppliedArg The argument supplied for this parameter
-         * @param defaultValue The default value for this argument
-         * @param formalIndex The logicalIndex of this argument, also counting individual arguments
-         *            in varargs
-         * @return Either suppliedArg or its defaultValue wrapped up into a {@link PromiseNode} (or
-         *         {@link RMissing} in case neither is present!
-         */
-        RNode wrap(RFunction function, FormalArguments formals, RBuiltinRootNode builtinRootNode, ClosureCache closureCache, RNode suppliedArg, RNode defaultValue, int formalIndex);
-    }
-
-    /**
-     * {@link PromiseWrapper} implementation for 'normal' function calls.
-     */
-    private static class DefaultPromiseWrapper implements PromiseWrapper {
-
-        private final boolean noOpt;
-
-        public DefaultPromiseWrapper(boolean noOpt) {
-            this.noOpt = noOpt;
-        }
-
-        public EvalPolicy getEvalPolicy(RFunction function, RBuiltinRootNode builtinRootNode, int formalIndex) {
-            // This is for actual function calls. However, if the arguments are meant for a builtin,
-            // we have to consider whether they should be forced or not!
-            return builtinRootNode != null && builtinRootNode.evaluatesArg(formalIndex) ? EvalPolicy.INLINED : EvalPolicy.PROMISED;
-        }
-
-        @TruffleBoundary
-        public RNode wrap(RFunction function, FormalArguments formals, RBuiltinRootNode builtinRootNode, ClosureCache closureCache, RNode suppliedArg, RNode defaultValue, int formalIndex) {
-            // Determine whether to choose supplied argument or default value
-            RNode expr = null;
-            PromiseType promiseType = null;
-            if (suppliedArg != null) {
-                // Supplied arg
-                expr = suppliedArg;
-                promiseType = PromiseType.ARG_SUPPLIED;
+    @TruffleBoundary
+    public static RNode wrap(FormalArguments formals, RBuiltinRootNode builtinRootNode, ClosureCache closureCache, RNode suppliedArg, RNode defaultValue, int formalIndex, boolean isBuiltin,
+                    boolean noOpt) {
+        // Determine whether to choose supplied argument or default value
+        RNode expr = null;
+        PromiseType promiseType = null;
+        if (suppliedArg != null) {
+            // Supplied arg
+            expr = suppliedArg;
+            promiseType = PromiseType.ARG_SUPPLIED;
+        } else {
+            // Default value
+            if (isBuiltin && defaultValue != null) {
+                expr = defaultValue;
+                promiseType = PromiseType.ARG_DEFAULT;
             } else {
                 if (formals.getVarArgIndex() == formalIndex) {
                     // "...", but empty
@@ -765,170 +574,12 @@ public class ArgumentMatcher {
                     return ConstantNode.create(RMissing.instance);
                 }
             }
-
-            // Create promise
-            EvalPolicy evalPolicy = getEvalPolicy(function, builtinRootNode, formalIndex);
-            Closure closure = closureCache.getOrCreateClosure(expr);
-            Closure defaultClosure = formals.getOrCreateClosure(defaultValue);
-            return PromiseNode.create(expr.getSourceSection(), RPromiseFactory.create(evalPolicy, promiseType, closure, defaultClosure), noOpt);
-        }
-    }
-
-    /**
-     * {@link PromiseWrapper} implementation for arguments that are going to be used for 'inlined'
-     * builtins.
-     *
-     * @see RBuiltinRootNode#inline(InlinedArguments)
-     */
-    private static class BuiltinInitPromiseWrapper implements PromiseWrapper {
-
-        private final boolean noOpt;
-
-        public BuiltinInitPromiseWrapper(boolean noOpt) {
-            this.noOpt = noOpt;
-        }
-
-        public EvalPolicy getEvalPolicy(RFunction function, RBuiltinRootNode builtinRootNode, int formalIndex) {
-            // This is used for arguments that are going inlined for builtins
-            return !builtinRootNode.evaluatesArg(formalIndex) ? EvalPolicy.PROMISED : EvalPolicy.INLINED;
-        }
-
-        /**
-         * @param function The function this argument is wrapped for
-         * @param formals {@link FormalArguments} as {@link ClosureCache}
-         * @param builtinRootNode The {@link RBuiltinRootNode} of the function
-         * @param closureCache {@link ClosureCache}
-         * @param suppliedArg The argument supplied for this parameter
-         * @param defaultValue The default value for this argument
-         * @param formalIndex The logicalIndex of this argument, also counting individual arguments
-         *            in varargs
-         * @return Either suppliedArg or its defaultValue wrapped up into a {@link PromiseNode} (or
-         *         {@link RMissing} in case neither is present!
-         */
-        @TruffleBoundary
-        public RNode wrap(RFunction function, FormalArguments formals, RBuiltinRootNode builtinRootNode, ClosureCache closureCache, RNode suppliedArg, RNode defaultValue, int formalIndex) {
-            // Determine whether to choose supplied argument or default value
-            RNode expr = null;
-            PromiseType promiseType = null;
-            if (suppliedArg != null) {
-                // Supplied arg
-                expr = suppliedArg;
-                promiseType = PromiseType.ARG_SUPPLIED;
-            } else {
-                // Default value
-                if (defaultValue != null) {
-                    expr = defaultValue;
-                    promiseType = PromiseType.ARG_DEFAULT;
-                } else {
-                    if (formals.getVarArgIndex() == formalIndex) {
-                        // "...", but empty
-                        return ConstantNode.create(RArgsValuesAndNames.EMPTY);
-                    } else {
-                        // In this case, we simply return RMissing (like R)
-                        return ConstantNode.create(RMissing.instance);
-                    }
-                }
-            }
-
-            // Create promise
-            EvalPolicy evalPolicy = getEvalPolicy(function, builtinRootNode, formalIndex);
-            Closure closure = closureCache.getOrCreateClosure(expr);
-            Closure defaultClosure = formals.getOrCreateClosure(defaultValue);
-            return PromiseNode.create(expr.getSourceSection(), RPromiseFactory.create(evalPolicy, promiseType, closure, defaultClosure), noOpt);
         }
-    }
 
-    /**
-     * Abstraction for the generation of varargs.
-     *
-     * @param <T> The type of the resulting vararg
-     */
-    public interface VarArgsFactory<T> {
-        T makeList(T[] elements, String[] names);
-    }
-
-    /**
-     * {@link VarArgsFactory} implementation that returns varargs as <code>Object[]</code>.
-     *
-     */
-    public static final class VarArgsAsObjectArrayFactory implements VarArgsFactory<Object> {
-        /**
-         * The call of {@link #nonNull} and the assertion in the "else" clause prevents the creation
-         * of an {@link RArgsValuesAndNames} containing any {@code null} values. Experimentally,
-         * only length 1 arrays ever contain {@code null} (from the conversion of {@link RMissing}
-         * into {@code null} in {@link S3DispatchNode#addArg}). Should this ever change perhaps
-         * these should be turned back into {@link RMissing}. Ideally, this invariant should be
-         * enforced by the caller(s).
-         */
-        public Object makeList(Object[] elements, String[] names) {
-            if (elements.length > 0 && nonNull(elements)) {
-                return new RArgsValuesAndNames(elements, names);
-            } else {
-                assert elements.length == 0 || elements.length == 1;
-                return RArgsValuesAndNames.EMPTY;   // RMissing.instance;
-            }
-        }
-
-        private static boolean nonNull(Object[] elements) {
-            for (int i = 0; i < elements.length; i++) {
-                if (elements[i] == null) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    /**
-     * A {@link RNode} that encapsulates a list of varargs (as {@link RNode}).
-     */
-    public abstract static class VarArgsNode extends RNode {
-        @Children protected final RNode[] elementNodes;
-
-        protected VarArgsNode(RNode[] elements) {
-            elementNodes = elements;
-        }
-
-        public final RNode[] getArgumentNodes() {
-            return elementNodes;
-        }
-    }
-
-    /**
-     * {@link VarArgsFactory} implementation that returns varargs as
-     * {@link VarArgsAsObjectArrayNode}.
-     */
-    public static final class VarArgsAsObjectArrayNodeFactory implements VarArgsFactory<RNode> {
-        public RNode makeList(RNode[] elements, String[] names) {
-            if (elements.length > 0) {
-                return new VarArgsAsObjectArrayNode(elements, names);
-            } else {
-                // STRICT: This has to be revised!
-                return null;    // ConstantNode.create(RMissing.instance);
-            }
-        }
-    }
-
-    /**
-     * {@link VarArgsNode} that executes all its elements and returns the resulting value array.
-     */
-    public static final class VarArgsAsObjectArrayNode extends VarArgsNode {
-        private String[] names;
-
-        public VarArgsAsObjectArrayNode(RNode[] elements, String[] names) {
-            super(elements);
-            this.names = names;
-        }
-
-        public String[] getNames() {
-            return names;
-        }
-
-        @Override
-        @Deprecated
-        public Object execute(VirtualFrame frame) {
-            // Simple container
-            throw new UnsupportedOperationException();
-        }
+        // Create promise
+        EvalPolicy evalPolicy = getEvalPolicy(builtinRootNode, formalIndex);
+        Closure closure = closureCache.getOrCreateClosure(expr);
+        Closure defaultClosure = formals.getOrCreateClosure(defaultValue);
+        return PromiseNode.create(expr.getSourceSection(), RPromiseFactory.create(evalPolicy, promiseType, closure, defaultClosure), noOpt);
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentsTrait.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentsTrait.java
index ce15734730b70bb3126b8fe4e3852bc29c1b90e7..43f101bb98306be41a9f9f1b0ef52e91b46d6833 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentsTrait.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentsTrait.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2015, 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
@@ -95,7 +95,7 @@ public interface ArgumentsTrait {
     }
 
     static boolean isVarArg(String name) {
-        return name.equals(VARARG_NAME);
+        return name != null && name.equals(VARARG_NAME);
     }
 
     static boolean isVarArgGetter(String name) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchNode.java
index a7fdfd52c5ae50b5584118911fbad77e45267725..72a5c3c29b3390fd41080f2f6ecbb629c6ad99d5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchNode.java
@@ -35,10 +35,10 @@ public abstract class DispatchNode extends RNode {
         }
     }
 
-    public abstract Object execute(VirtualFrame frame, RStringVector aType);
+    public abstract Object executeGeneric(VirtualFrame frame, RStringVector aType);
 
     @SuppressWarnings("unused")
-    public Object executeInternal(VirtualFrame frame, RStringVector aType, Object[] args) {
+    public Object executeInternalGeneric(VirtualFrame frame, RStringVector aType, Object[] args) {
         throw RInternalError.shouldNotReachHere();
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
index 18a5d781b7d77f74fcedb8fe2ed15d9a8bfed23b..69b3825e07b5ce228aa7263026cb6a066a616a78 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/DispatchedCallNode.java
@@ -103,15 +103,14 @@ public abstract class DispatchedCallNode extends RNode {
         private DispatchedCallNode specialize(RStringVector type) {
             CompilerAsserts.neverPartOfCompilation();
             if (depth < INLINE_CACHE_SIZE) {
-                final DispatchNode current = createCurrentNode(type);
-                final DispatchedCallNode cachedNode = new CachedNode(current, new UninitializedDispatchedCallNode(this, this.depth + 1), type);
-                this.replace(cachedNode);
-                return cachedNode;
+                DispatchNode current = createCurrentNode(type);
+                return replace(new CachedNode(current, new UninitializedDispatchedCallNode(this, depth + 1), type));
             }
+            RError.performanceWarning("S3 method dispatch fallback to generic");
             return this.replace(new GenericDispatchNode(createCurrentNode(type)));
         }
 
-        protected DispatchNode createCurrentNode(RStringVector type) {
+        private DispatchNode createCurrentNode(RStringVector type) {
             switch (dispatchType) {
                 case NextMethod:
                     return new NextMethodDispatchNode(genericName, type, args, argNames, enclosingName);
@@ -133,12 +132,12 @@ public abstract class DispatchedCallNode extends RNode {
 
         @Override
         public Object execute(VirtualFrame frame, RStringVector type) {
-            return dcn.execute(frame, type);
+            return dcn.executeGeneric(frame, type);
         }
 
         @Override
         public Object executeInternal(VirtualFrame frame, RStringVector type, Object[] args) {
-            return dcn.executeInternal(frame, type, args);
+            return dcn.executeInternalGeneric(frame, type, args);
         }
     }
 
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 8ef7c3318313e02082e332746e47d598f1a3fbbb..2a81a8e002ee0ce3c70b347e4549040fa6c05e37 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
@@ -25,7 +25,7 @@ package com.oracle.truffle.r.nodes.function;
 import java.util.*;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.CompilerDirectives.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.source.*;
@@ -50,6 +50,15 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
     @Child private InlineCacheNode<VirtualFrame, RNode> onExitExpressionCache;
     private final ConditionProfile onExitProfile = ConditionProfile.createBinaryProfile();
 
+    private final ConditionProfile s3SlotsProfile = ConditionProfile.createBinaryProfile();
+    @CompilationFinal private BranchProfile invalidateFrameSlotProfile;
+    @Child private FrameSlotNode dotGenericSlot;
+    @Child private FrameSlotNode dotMethodSlot;
+    @Child private FrameSlotNode dotClassSlot;
+    @Child private FrameSlotNode dotGenericCallEnvSlot;
+    @Child private FrameSlotNode dotGenericCallDefSlot;
+    @Child private FrameSlotNode dotGroupSlot;
+
     /**
      * An instance of this node may be called from with the intention to have its execution leave a
      * footprint behind in a specific frame/environment, e.g., during library loading, commands from
@@ -105,6 +114,9 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         VirtualFrame vf = substituteFrame ? (VirtualFrame) frame.getArguments()[0] : frame;
         try {
             verifyEnclosingAssumptions(vf);
+            if (s3SlotsProfile.profile(RArguments.hasS3Args(vf))) {
+                setupS3Slots(vf);
+            }
             return body.execute(vf);
         } catch (ReturnException ex) {
             returnProfile.enter();
@@ -130,6 +142,26 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
         }
     }
 
+    private void setupS3Slots(VirtualFrame frame) {
+        if (dotGenericSlot == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            assert invalidateFrameSlotProfile == null && dotMethodSlot == null && dotClassSlot == null && dotGenericCallEnvSlot == null && dotGenericCallDefSlot == null && dotGroupSlot == null;
+            invalidateFrameSlotProfile = BranchProfile.create();
+            dotGenericSlot = insert(FrameSlotNode.create(RRuntime.RDotGeneric, true));
+            dotMethodSlot = insert(FrameSlotNode.create(RRuntime.RDotMethod, true));
+            dotClassSlot = insert(FrameSlotNode.create(RRuntime.RDotClass, true));
+            dotGenericCallEnvSlot = insert(FrameSlotNode.create(RRuntime.RDotGenericCallEnv, true));
+            dotGenericCallDefSlot = insert(FrameSlotNode.create(RRuntime.RDotGenericDefEnv, true));
+            dotGroupSlot = insert(FrameSlotNode.create(RRuntime.RDotGroup, true));
+        }
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericSlot.executeFrameSlot(frame), RArguments.getS3Generic(frame), false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotMethodSlot.executeFrameSlot(frame), RArguments.getS3Method(frame), false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotClassSlot.executeFrameSlot(frame), RArguments.getS3Class(frame), false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallEnvSlot.executeFrameSlot(frame), RArguments.getS3CallEnv(frame), false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGenericCallDefSlot.executeFrameSlot(frame), RArguments.getS3DefEnv(frame), false, invalidateFrameSlotProfile);
+        FrameSlotChangeMonitor.setObjectAndInvalidate(frame, dotGroupSlot.executeFrameSlot(frame), RArguments.getS3Group(frame), false, invalidateFrameSlotProfile);
+    }
+
     @SuppressWarnings("unchecked")
     private static ArrayList<Object> getCurrentOnExitList(VirtualFrame frame, FrameSlot slot) {
         try {
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 4186e79886227a42547281628f4a3ae73f5fd3e2..06b4de13d045b951f10f121badbd6d04cc7d3306 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
@@ -24,7 +24,10 @@ package com.oracle.truffle.r.nodes.function;
 
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.nodes.NodeUtil.NodeCountFilter;
 import com.oracle.truffle.r.nodes.*;
+import com.oracle.truffle.r.nodes.access.variables.*;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode.*;
 import com.oracle.truffle.r.nodes.function.opt.*;
 import com.oracle.truffle.r.nodes.instrument.*;
@@ -32,85 +35,66 @@ import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.env.*;
 
-public abstract class FunctionExpressionNode extends RNode {
-
-    @Override
-    public final RFunction execute(VirtualFrame frame) {
-        return executeFunction(frame);
-    }
-
-    @Override
-    public abstract RFunction executeFunction(VirtualFrame frame);
-
-    public static FunctionExpressionNode create(RFunction function) {
-        return new StaticFunctionExpressionNode(function);
-    }
+public final class FunctionExpressionNode extends RNode {
 
     public static FunctionExpressionNode create(RootCallTarget callTarget) {
-        return new DynamicFunctionExpressionNode(callTarget);
+        return new FunctionExpressionNode(callTarget);
     }
 
-    public static final class StaticFunctionExpressionNode extends FunctionExpressionNode {
+    private final RootCallTarget callTarget;
+    private final PromiseDeoptimizeFrameNode deoptFrameNode;
+    private final boolean containsDispatch;
 
-        private final RFunction function;
+    public FunctionExpressionNode(RootCallTarget callTarget) {
+        this.callTarget = callTarget;
+        this.deoptFrameNode = EagerEvalHelper.optExprs() || EagerEvalHelper.optVars() ? new PromiseDeoptimizeFrameNode() : null;
 
-        public StaticFunctionExpressionNode(RFunction function) {
-            // TODO DEOPT needed here?
-            this.function = function;
-        }
-
-        @Override
-        public RFunction executeFunction(VirtualFrame frame) {
-            return function;
-        }
-
-        public RFunction getFunction() {
-            return function;
-        }
+        NodeCountFilter dispatchingMethodsFilter = node -> {
+            if (node instanceof ReadVariableNode) {
+                String identifier = ((ReadVariableNode) node).getIdentifier();
+                return "UseMethod".equals(identifier) || "NextMethod".equals(identifier);
+            }
+            return false;
+        };
+        this.containsDispatch = NodeUtil.countNodes(callTarget.getRootNode(), dispatchingMethodsFilter) > 0;
     }
 
-    public static final class DynamicFunctionExpressionNode extends FunctionExpressionNode {
-
-        private final RootCallTarget callTarget;
-        private final PromiseDeoptimizeFrameNode deoptFrameNode;
+    @Override
+    public RFunction execute(VirtualFrame frame) {
+        return executeFunction(frame);
+    }
 
-        public DynamicFunctionExpressionNode(RootCallTarget callTarget) {
-            this.callTarget = callTarget;
-            this.deoptFrameNode = EagerEvalHelper.optExprs() || EagerEvalHelper.optVars() ? new PromiseDeoptimizeFrameNode() : null;
+    @Override
+    public RFunction executeFunction(VirtualFrame frame) {
+        MaterializedFrame matFrame = frame.materialize();
+        if (deoptFrameNode != null) {
+            // Deoptimize every promise which is now in this frame, as it might leave it's stack
+            deoptFrameNode.deoptimizeFrame(matFrame);
         }
-
-        @Override
-        public RFunction executeFunction(VirtualFrame frame) {
-            MaterializedFrame matFrame = frame.materialize();
-            if (deoptFrameNode != null) {
-                // Deoptimize every promise which is now in this frame, as it might leave it's stack
-                deoptFrameNode.deoptimizeFrame(matFrame);
-            }
-            RFunction func = RDataFactory.createFunction("", callTarget, matFrame);
-            if (RInstrument.instrumentingEnabled()) {
-                RInstrument.checkDebugRequested(callTarget.toString(), func);
-            }
-            return func;
+        RFunction func = RDataFactory.createFunction("", callTarget, matFrame, containsDispatch);
+        if (RInstrument.instrumentingEnabled()) {
+            RInstrument.checkDebugRequested(callTarget.toString(), func);
         }
+        return func;
+    }
 
-        public RootCallTarget getCallTarget() {
-            return callTarget;
-        }
+    public RootCallTarget getCallTarget() {
+        return callTarget;
+    }
 
-        @Override
-        public boolean isSyntax() {
-            return true;
-        }
+    @Override
+    public boolean isSyntax() {
+        return true;
+    }
 
-        @Override
-        public void deparse(State state) {
-            ((FunctionDefinitionNode) callTarget.getRootNode()).deparse(state);
-        }
+    @Override
+    public void deparse(State state) {
+        ((FunctionDefinitionNode) callTarget.getRootNode()).deparse(state);
+    }
 
-        @Override
-        public RNode substitute(REnvironment env) {
-            FunctionDefinitionNode fdn = ((FunctionDefinitionNode) callTarget.getRootNode()).substituteFDN(env);
-            return new DynamicFunctionExpressionNode(Truffle.getRuntime().createCallTarget(fdn));
-        }
+    @Override
+    public RNode substitute(REnvironment env) {
+        FunctionDefinitionNode fdn = ((FunctionDefinitionNode) callTarget.getRootNode()).substituteFDN(env);
+        return new FunctionExpressionNode(Truffle.getRuntime().createCallTarget(fdn));
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java
index bbea2c08f5402f0ba98a5da3d4a83f6d2b1257b9..9844957a5770ce89f23085cb98eddb75bc910481 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/GroupDispatchCallNode.java
@@ -26,7 +26,6 @@ import com.oracle.truffle.r.runtime.RDeparse.State;
 import com.oracle.truffle.r.runtime.data.*;
 import com.oracle.truffle.r.runtime.data.model.*;
 import com.oracle.truffle.r.runtime.env.*;
-import com.oracle.truffle.r.runtime.env.frame.*;
 
 import edu.umd.cs.findbugs.annotations.*;
 
@@ -75,7 +74,7 @@ public abstract class GroupDispatchCallNode extends RNode {
 
     @Override
     public void deparse(State state) {
-        String name = this.getGenericName();
+        String name = getGenericName();
         RDeparse.Func func = RDeparse.getFunc(name);
         if (func != null) {
             // infix operator
@@ -98,14 +97,14 @@ public abstract class GroupDispatchCallNode extends RNode {
         @CompilationFinal private final String genericName;
         private final int depth;
 
-        public UninitializedGroupDispatchCallNode(final String aGenericName, final String groupName, final CallArgumentsNode callArgNode) {
+        public UninitializedGroupDispatchCallNode(String aGenericName, String groupName, CallArgumentsNode callArgNode) {
             this.genericName = aGenericName;
             this.groupName = groupName;
             this.callArgsNode = callArgNode;
             this.depth = 0;
         }
 
-        private UninitializedGroupDispatchCallNode(final UninitializedGroupDispatchCallNode copy, final int depth) {
+        private UninitializedGroupDispatchCallNode(UninitializedGroupDispatchCallNode copy, int depth) {
             this.genericName = copy.genericName;
             this.groupName = copy.groupName;
             this.callArgsNode = copy.callArgsNode;
@@ -137,7 +136,7 @@ public abstract class GroupDispatchCallNode extends RNode {
             return this.replace(new GenericDispatchNode(createGenericNode(argAndNames.getValues())));
         }
 
-        private GroupDispatchNode createGenericNode(final Object[] evaluatedArgs) {
+        private GroupDispatchNode createGenericNode(Object[] evaluatedArgs) {
             if (this.groupName == RGroupGenerics.GROUP_OPS) {
                 if (evaluatedArgs.length == 1) {
                     return new GenericUnaryOpsGroupDispatchNode(this.genericName, this.callArgsNode.containsVarArgsSymbol(), this.getSourceSection(), this.callArgsNode.getEncapsulatingSourceSection());
@@ -157,7 +156,7 @@ public abstract class GroupDispatchCallNode extends RNode {
         }
 
         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "GROUP_OPS is intended to be used as an identity")
-        protected GroupDispatchNode createCurrentNode(final Object[] evaluatedArgs) {
+        protected GroupDispatchNode createCurrentNode(Object[] evaluatedArgs) {
             if (this.groupName == RGroupGenerics.GROUP_OPS) {
                 if (evaluatedArgs.length == 1) {
                     return new UnaryOpsGroupDispatchNode(this.genericName, this.callArgsNode.containsVarArgsSymbol(), this.getSourceSection(), this.callArgsNode.getEncapsulatingSourceSection());
@@ -190,7 +189,7 @@ public abstract class GroupDispatchCallNode extends RNode {
         @Child private GroupDispatchCallNode nextNode;
         @Child private GroupDispatchNode currentNode;
 
-        CachedNode(final GroupDispatchNode currentNode, final GroupDispatchCallNode nextNode, final CallArgumentsNode callArgsNode) {
+        CachedNode(final GroupDispatchNode currentNode, GroupDispatchCallNode nextNode, CallArgumentsNode callArgsNode) {
             this.nextNode = nextNode;
             this.currentNode = currentNode;
             this.callArgsNode = callArgsNode;
@@ -206,7 +205,7 @@ public abstract class GroupDispatchCallNode extends RNode {
         }
 
         @Override
-        public Object execute(VirtualFrame frame, final RArgsValuesAndNames argAndNames) {
+        public Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames) {
             if (currentNode.isSameType(argAndNames.getValues())) {
                 return currentNode.execute(frame, argAndNames);
             }
@@ -238,7 +237,7 @@ public abstract class GroupDispatchCallNode extends RNode {
         }
 
         @Override
-        public Object execute(VirtualFrame frame, final RArgsValuesAndNames argAndNames) {
+        public Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames) {
             return gdn.execute(frame, argAndNames);
         }
 
@@ -273,7 +272,7 @@ class GroupDispatchNode extends S3DispatchNode {
     protected final SourceSection argSrc;
 
     @Override
-    public Object execute(VirtualFrame frame, RStringVector aType) {
+    public Object executeGeneric(VirtualFrame frame, RStringVector aType) {
         throw new AssertionError();
     }
 
@@ -303,7 +302,7 @@ class GroupDispatchNode extends S3DispatchNode {
     }
 
     protected void findTargetFunction(VirtualFrame frame) {
-        final String[] prefix = {genericName, groupName};
+        String[] prefix = {genericName, groupName};
         for (int i = 0; i < this.type.getLength(); ++i) {
             for (int j = 0; j < prefix.length; ++j) {
                 findFunction(prefix[j], this.type.getDataAt(i), frame);
@@ -328,7 +327,7 @@ class GroupDispatchNode extends S3DispatchNode {
         }
     }
 
-    public Object execute(VirtualFrame frame, final RArgsValuesAndNames argAndNames) {
+    public Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames) {
         Object[] evaluatedArgs = argAndNames.getValues();
         String[] argNames = argAndNames.getNames();
         if (!isExecuted) {
@@ -348,33 +347,22 @@ class GroupDispatchNode extends S3DispatchNode {
         return executeHelper(frame, evaluatedArgs, argNames);
     }
 
-    protected Object callBuiltin(VirtualFrame frame, final Object[] evaluatedArgs, final String[] argNames) {
+    protected Object callBuiltin(VirtualFrame frame, Object[] evaluatedArgs, String[] argNames) {
         initBuiltin(frame);
         EvaluatedArguments reorderedArgs = reorderArgs(frame, builtinFunc, evaluatedArgs, argNames, this.hasVararg, this.callSrc);
-        Object[] argObject = RArguments.create(builtinFunc, this.callSrc, RArguments.getDepth(frame), reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
+        Object[] argObject = RArguments.create(builtinFunc, this.callSrc, null, RArguments.getDepth(frame), reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
         indirectCallNode.assignSourceSection(this.callSrc);
         return indirectCallNode.call(frame, builtinFunc.getTarget(), argObject);
     }
 
-    protected Object executeHelper(VirtualFrame frame, final Object[] evaluatedArgs, final String[] argNames) {
+    protected Object executeHelper(VirtualFrame frame, Object[] evaluatedArgs, String[] argNames) {
         EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, evaluatedArgs, argNames, this.hasVararg, this.callSrc);
-        Object[] argObject = RArguments.createS3Args(targetFunction, this.callSrc, RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
-        // todo: cannot create frame descriptors in compiled code
-        FrameDescriptor s3VarDefFrameDescriptor = new FrameDescriptor();
-        FrameSlotChangeMonitor.initializeFrameDescriptor(s3VarDefFrameDescriptor, true);
-        VirtualFrame s3VarDefFrame = Truffle.getRuntime().createVirtualFrame(RArguments.create(null, null, RArguments.getDepth(frame) + 1), s3VarDefFrameDescriptor);
-        // todo: cannot create frame descriptors in compiled code
-        FrameDescriptor argFrameDescriptor = new FrameDescriptor();
-        FrameSlotChangeMonitor.initializeFrameDescriptor(argFrameDescriptor, true);
-        VirtualFrame argFrame = Truffle.getRuntime().createVirtualFrame(argObject, argFrameDescriptor);
-        genCallEnv = frame;
-        defineVarsAsArguments(argFrame);
-        defineVarsInFrame(s3VarDefFrame);
-        wvnMethod = defineVarInFrame(s3VarDefFrame, wvnMethod, RRuntime.RDotMethod, dotMethod);
-        RArguments.setS3Method(argFrame, targetFunctionName);
+        Object[] argObject = RArguments.createS3Args(targetFunction, this.callSrc, null, RArguments.getDepth(frame) + 1, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
+        genCallEnv = frame.materialize();
+        defineVarsAsArguments(argObject);
+        RArguments.setS3Method(argObject, dotMethod);
         if (writeGroup) {
-            wvnGroup = defineVarInFrame(s3VarDefFrame, wvnGroup, RRuntime.RDotGroup, groupName);
-            RArguments.setS3Group(argFrame, groupName);
+            RArguments.setS3Group(argObject, groupName);
         }
         indirectCallNode.assignSourceSection(this.callSrc);
         /*
@@ -385,15 +373,7 @@ class GroupDispatchNode extends S3DispatchNode {
          * s3VarDefFrame. After the function returns reset the enclosing frame of the target
          * function.
          */
-        MaterializedFrame enclosingFrame = targetFunction.getEnclosingFrame();
-        MaterializedFrame mFrame = s3VarDefFrame.materialize();
-        RArguments.setEnclosingFrame(mFrame, enclosingFrame);
-        targetFunction.setEnclosingFrame(mFrame);
         Object result = indirectCallNode.call(frame, targetFunction.getTarget(), argObject);
-        targetFunction.setEnclosingFrame(enclosingFrame);
-        RArguments.setEnclosingFrame(mFrame, null);
-        removeVars(mFrame);
-        removeVar(mFrame.getFrameDescriptor(), RRuntime.RDotGroup);
         return result;
     }
 
@@ -412,12 +392,12 @@ class GroupDispatchNode extends S3DispatchNode {
 
 class GenericGroupDispatchNode extends GroupDispatchNode {
 
-    protected GenericGroupDispatchNode(String aGenericName, String groupName, final boolean hasVarArg, SourceSection callSrc, SourceSection argSrc) {
+    protected GenericGroupDispatchNode(String aGenericName, String groupName, boolean hasVarArg, SourceSection callSrc, SourceSection argSrc) {
         super(aGenericName, groupName, hasVarArg, callSrc, argSrc);
     }
 
     @Override
-    public Object execute(VirtualFrame frame, final RArgsValuesAndNames argAndNames) {
+    public Object execute(VirtualFrame frame, RArgsValuesAndNames argAndNames) {
         Object[] evaluatedArgs = argAndNames.getValues();
         String[] argNames = argAndNames.getNames();
         this.type = getArgClass(evaluatedArgs[0]);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java
index f4c68606fa1d3afce1c047c7199629c62d07230b..700cccf3bd9beb3b849605bd3533325047c06f6c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/NextMethodDispatchNode.java
@@ -14,13 +14,11 @@ package com.oracle.truffle.r.nodes.function;
 import java.util.*;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.r.nodes.access.*;
 import com.oracle.truffle.r.nodes.access.variables.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
-import com.oracle.truffle.r.runtime.env.frame.*;
 
 public class NextMethodDispatchNode extends S3DispatchNode {
 
@@ -59,7 +57,7 @@ public class NextMethodDispatchNode extends S3DispatchNode {
     }
 
     @Override
-    public Object execute(VirtualFrame frame, final RStringVector aType) {
+    public Object executeGeneric(VirtualFrame frame, RStringVector aType) {
         readGenericVars(frame);
         findTargetFunction(frame);
         storeValues();
@@ -132,19 +130,15 @@ public class NextMethodDispatchNode extends S3DispatchNode {
 
     private Object executeHelper(VirtualFrame frame) {
         EvaluatedArguments evaledArgs = processArgs(frame);
-        Object[] argObject = RArguments.createS3Args(targetFunction, getSourceSection(), RArguments.getDepth(frame) + 1, evaledArgs.getEvaluatedArgs(), evaledArgs.getNames());
-        // todo: cannot create frame descriptors in compiled code
-        FrameDescriptor frameDescriptor = new FrameDescriptor();
-        FrameSlotChangeMonitor.initializeFrameDescriptor(frameDescriptor, true);
-        final VirtualFrame newFrame = Truffle.getRuntime().createVirtualFrame(argObject, frameDescriptor);
-        defineVarsAsArguments(newFrame);
+        Object[] argObject = RArguments.createS3Args(targetFunction, getSourceSection(), null, RArguments.getDepth(frame) + 1, evaledArgs.getEvaluatedArgs(), evaledArgs.getNames());
+        defineVarsAsArguments(argObject);
         if (storedFunctionName != null) {
-            RArguments.setS3Method(newFrame, storedFunctionName);
+            RArguments.setS3Method(argObject, storedFunctionName);
         } else {
-            RArguments.setS3Method(newFrame, targetFunctionName);
+            RArguments.setS3Method(argObject, targetFunctionName);
         }
         if (hasGroup) {
-            RArguments.setS3Group(newFrame, this.group);
+            RArguments.setS3Group(argObject, this.group);
         }
         return indirectCallNode.call(frame, targetFunction.getTarget(), argObject);
     }
@@ -221,7 +215,16 @@ public class NextMethodDispatchNode extends S3DispatchNode {
         } else {
             handlePresentGroup();
         }
-        String functionName = RArguments.getS3Method(frame);
+
+        Object method = RArguments.getS3Method(frame);
+        String functionName;
+        if (method == null) {
+            functionName = null;
+        } else if (method instanceof String) {
+            functionName = (String) method;
+        } else {
+            functionName = ((RStringVector) method).getDataAt(0);
+        }
         if (functionName != null) {
             storedFunctionName = functionName;
         }
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 6f5d84e26facd2505cf0e6f8ef0b624797b5578c..1bc8944bdcd34344ac357dcac0615b4e4239dee9 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
@@ -23,6 +23,7 @@
 package com.oracle.truffle.r.nodes.function;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
@@ -78,13 +79,13 @@ import com.oracle.truffle.r.runtime.env.*;
  *  U = {@link UninitializedCallNode}: Forms the uninitialized end of the function PIC
  *  D = {@link DispatchedCallNode}: Function fixed, no varargs
  *  G = {@link GenericCallNode}: Function arbitrary, no varargs (generic case)
- * 
+ *
  *  UV = {@link UninitializedCallNode} with varargs,
  *  UVC = {@link UninitializedVarArgsCacheCallNode} with varargs, for varargs cache
  *  DV = {@link DispatchedVarArgsCallNode}: Function fixed, with cached varargs
  *  DGV = {@link DispatchedGenericVarArgsCallNode}: Function fixed, with arbitrary varargs (generic case)
  *  GV = {@link GenericVarArgsCallNode}: Function arbitrary, with arbitrary varargs (generic case)
- * 
+ *
  * (RB = {@link RBuiltinNode}: individual functions that are builtins are represented by this node
  * which is not aware of caching). Due to {@link CachedCallNode} (see below) this is transparent to
  * the cache and just behaves like a D/DGV)
@@ -97,11 +98,11 @@ import com.oracle.truffle.r.runtime.env.*;
  * non varargs, max depth:
  * |
  * D-D-D-U
- * 
+ *
  * no varargs, generic (if max depth is exceeded):
  * |
  * D-D-D-D-G
- * 
+ *
  * varargs:
  * |
  * DV-DV-UV         <- function call target identity level cache
@@ -109,7 +110,7 @@ import com.oracle.truffle.r.runtime.env.*;
  *    DV
  *    |
  *    UVC           <- varargs signature level cache
- * 
+ *
  * varargs, max varargs depth exceeded:
  * |
  * DV-DV-UV
@@ -121,7 +122,7 @@ import com.oracle.truffle.r.runtime.env.*;
  *    DV
  *    |
  *    DGV
- * 
+ *
  * varargs, max function depth exceeded:
  * |
  * DV-DV-DV-DV-GV
@@ -581,14 +582,25 @@ public abstract class RCallNode extends RNode {
         @Child private DirectCallNode call;
         @Child private MatchedArgumentsNode matchedArgs;
 
+        private final boolean needsCallerFrame;
+        @CompilationFinal private boolean needsSplitting;
+
         DispatchedCallNode(RFunction function, MatchedArguments matchedArgs) {
             this.matchedArgs = matchedArgs.createNode();
             this.call = Truffle.getRuntime().createDirectCallNode(function.getTarget());
+            this.needsCallerFrame = function.containsDispatch();
+            this.needsSplitting = function.containsDispatch();
         }
 
         @Override
         public Object execute(VirtualFrame frame, RFunction currentFunction) {
-            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), RArguments.getDepth(frame) + 1, matchedArgs.executeArray(frame), matchedArgs.getNames());
+            if (needsSplitting) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                needsSplitting = false;
+                call.cloneCallTarget();
+            }
+            MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
+            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), callerFrame, RArguments.getDepth(frame) + 1, matchedArgs.executeArray(frame), matchedArgs.getNames());
             return call.call(frame, argsObject);
         }
 
@@ -620,7 +632,7 @@ public abstract class RCallNode extends RNode {
 
             if (lastCallTarget == currentFunction.getTarget() && lastMatchedArgs != null) {
                 // poor man's caching succeeded - same function: no re-match needed
-                Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), RArguments.getDepth(frame) + 1, lastMatchedArgs.doExecuteArray(frame), lastMatchedArgs.getNames());
+                Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), null, RArguments.getDepth(frame) + 1, lastMatchedArgs.doExecuteArray(frame), lastMatchedArgs.getNames());
                 return indirectCall.call(frame, currentFunction.getTarget(), argsObject);
             }
 
@@ -628,7 +640,7 @@ public abstract class RCallNode extends RNode {
             this.lastMatchedArgs = matchedArgs;
             this.lastCallTarget = currentFunction.getTarget();
 
-            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), RArguments.getDepth(frame) + 1, matchedArgs.doExecuteArray(frame), matchedArgs.getNames());
+            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), null, RArguments.getDepth(frame) + 1, matchedArgs.doExecuteArray(frame), matchedArgs.getNames());
             return indirectCall.call(frame, currentFunction.getTarget(), argsObject);
         }
     }
@@ -700,6 +712,8 @@ public abstract class RCallNode extends RNode {
         @Child private MatchedArgumentsNode matchedArgs;
 
         private final VarArgsSignature cachedSignature;
+        private final boolean needsCallerFrame;
+        @CompilationFinal private boolean needsSplitting;
 
         /**
          * Whether this [DV] node is the root of the varargs sub-cache (cmp. {@link RCallNode})
@@ -719,6 +733,12 @@ public abstract class RCallNode extends RNode {
             this.cachedSignature = varArgsSignature;
             this.matchedArgs = matchedArgs.createNode();
             this.isVarArgsRoot = isVarArgsRoot;
+            this.needsCallerFrame = function.containsDispatch();
+            /*
+             * this is a simple heuristic - methods that need a caller frame should have call site -
+             * specific versions
+             */
+            this.needsSplitting = function.containsDispatch();
         }
 
         protected static DispatchedVarArgsCallNode create(VirtualFrame frame, CallArgumentsNode args, VarArgsCacheCallNode next, SourceSection callSrc, RFunction function,
@@ -745,7 +765,14 @@ public abstract class RCallNode extends RNode {
             }
 
             // Our cached function and matched arguments do match, simply execute!
-            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), RArguments.getDepth(frame) + 1, matchedArgs.executeArray(frame), matchedArgs.getNames());
+
+            if (needsSplitting) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                needsSplitting = false;
+                call.cloneCallTarget();
+            }
+            MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
+            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), callerFrame, RArguments.getDepth(frame) + 1, matchedArgs.executeArray(frame), matchedArgs.getNames());
             return call.call(frame, argsObject);
         }
 
@@ -766,6 +793,8 @@ public abstract class RCallNode extends RNode {
         @Child private DirectCallNode call;
         @Child private CallArgumentsNode suppliedArgs;
 
+        @CompilationFinal private boolean needsCallerFrame;
+
         DispatchedGenericVarArgsCallNode(RFunction function, CallArgumentsNode suppliedArgs) {
             this.call = Truffle.getRuntime().createDirectCallNode(function.getTarget());
             this.suppliedArgs = suppliedArgs;
@@ -779,7 +808,12 @@ public abstract class RCallNode extends RNode {
             UnrolledVariadicArguments argsValuesAndNames = suppliedArgs.executeFlatten(frame);
             MatchedArguments matchedArgs = ArgumentMatcher.matchArguments(currentFunction, argsValuesAndNames, getSourceSection(), getEncapsulatingSourceSection(), true);
 
-            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), RArguments.getDepth(frame) + 1, matchedArgs.doExecuteArray(frame), matchedArgs.getNames());
+            if (!needsCallerFrame && currentFunction.containsDispatch()) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                needsCallerFrame = true;
+            }
+            MaterializedFrame callerFrame = needsCallerFrame ? frame.materialize() : null;
+            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), callerFrame, RArguments.getDepth(frame) + 1, matchedArgs.doExecuteArray(frame), matchedArgs.getNames());
             return call.call(frame, argsObject);
         }
     }
@@ -805,7 +839,7 @@ public abstract class RCallNode extends RNode {
             UnrolledVariadicArguments argsValuesAndNames = args.executeFlatten(frame);
             MatchedArguments matchedArgs = ArgumentMatcher.matchArguments(currentFunction, argsValuesAndNames, getSourceSection(), getEncapsulatingSourceSection(), true);
 
-            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), RArguments.getDepth(frame) + 1, matchedArgs.doExecuteArray(frame), matchedArgs.getNames());
+            Object[] argsObject = RArguments.create(currentFunction, getSourceSection(), null, RArguments.getDepth(frame) + 1, matchedArgs.doExecuteArray(frame), matchedArgs.getNames());
             return indirectCall.call(frame, currentFunction.getTarget(), argsObject);
         }
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java
index 8ee6d6f623424035658c3a78cf9516c186040e75..6da31ee3a0428e487d7b6c1b0806d1f4b596f509 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/S3DispatchNode.java
@@ -11,36 +11,31 @@
 
 package com.oracle.truffle.r.nodes.function;
 
-import static com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor.*;
-
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.r.nodes.access.*;
+import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.nodes.access.variables.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
 
 public abstract class S3DispatchNode extends DispatchNode {
 
+    protected final BranchProfile errorProfile = BranchProfile.create();
+
     @Child private ReadVariableNode lookup;
     @CompilationFinal private String lastFun;
-    @Child private WriteVariableNode wvnCallEnv;
-    @Child private WriteVariableNode wvnGeneric;
-    @Child private WriteVariableNode wvnClass;
-    @Child protected WriteVariableNode wvnMethod;
-    @Child private WriteVariableNode wvnDefEnv;
     @Child protected PromiseHelperNode promiseHelper = new PromiseHelperNode();
     @Child protected IndirectCallNode indirectCallNode = Truffle.getRuntime().createIndirectCallNode();
     protected String targetFunctionName;
     protected RFunction targetFunction;
     protected RStringVector klass;
     protected FunctionCall funCall;
-    protected Frame genCallEnv;
-    protected Frame genDefEnv;
+    protected MaterializedFrame genCallEnv;
+    protected MaterializedFrame genDefEnv;
     protected boolean isFirst;
 
     // TODO: the executeHelper methods share quite a bit of code, but is it better or worse from
@@ -152,83 +147,11 @@ public abstract class S3DispatchNode extends DispatchNode {
         return new StringBuilder(generic).append(RRuntime.RDOT).append(className).toString();
     }
 
-    protected WriteVariableNode initWvn(WriteVariableNode wvn, String name) {
-        WriteVariableNode node = wvn;
-        if (node == null) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            node = WriteVariableNode.create(name, null, false, false);
-            insert(node);
-        }
-        return node;
-    }
-
-    protected WriteVariableNode defineVarInFrame(VirtualFrame frame, WriteVariableNode wvn, String varName, Object value) {
-        addVar(frame, varName);
-        WriteVariableNode wvnCopy = initWvn(wvn, varName);
-        wvnCopy.execute(frame, value);
-        return wvnCopy;
-    }
-
-    private static void addVar(VirtualFrame frame, final String varName) {
-        addVarHelper(frame.getFrameDescriptor(), varName);
-    }
-
-    @TruffleBoundary
-    private static void addVarHelper(FrameDescriptor frameDescriptor, final String varName) {
-        findOrAddFrameSlot(frameDescriptor, varName);
-    }
-
-    protected void defineVarsInFrame(VirtualFrame frame) {
-        addVars(frame);
-        wvnGeneric = defineVarInFrame(frame, wvnGeneric, RRuntime.RDotGeneric, genericName);
-        wvnClass = defineVarInFrame(frame, wvnClass, RRuntime.RDotClass, klass);
-        wvnCallEnv = defineVarInFrame(frame, wvnCallEnv, RRuntime.RDotGenericCallEnv, genCallEnv);
-        wvnDefEnv = defineVarInFrame(frame, wvnDefEnv, RRuntime.RDotGenericDefEnv, genDefEnv);
-    }
-
-    protected void defineVarsAsArguments(VirtualFrame frame) {
-        RArguments.setS3Generic(frame, genericName);
-        RArguments.setS3Class(frame, klass);
-        RArguments.setS3CallEnv(frame, genCallEnv);
-        RArguments.setS3DefEnv(frame, genDefEnv);
-    }
-
-    protected void addVars(VirtualFrame frame) {
-        addVars0(frame.getFrameDescriptor());
-    }
-
-    @TruffleBoundary
-    private static void addVars0(FrameDescriptor fDesc) {
-        findOrAddFrameSlot(fDesc, RRuntime.RDotGeneric);
-        findOrAddFrameSlot(fDesc, RRuntime.RDotMethod);
-        findOrAddFrameSlot(fDesc, RRuntime.RDotClass);
-        findOrAddFrameSlot(fDesc, RRuntime.RDotGenericCallEnv);
-        findOrAddFrameSlot(fDesc, RRuntime.RDotGenericDefEnv);
-    }
-
-    protected void removeVars(Frame frame) {
-        removeVar(frame.getFrameDescriptor(), RRuntime.RDotGeneric);
-        removeVar(frame.getFrameDescriptor(), RRuntime.RDotMethod);
-        removeVar(frame.getFrameDescriptor(), RRuntime.RDotClass);
-        removeVar(frame.getFrameDescriptor(), RRuntime.RDotGenericCallEnv);
-        removeVar(frame.getFrameDescriptor(), RRuntime.RDotGenericDefEnv);
-
-    }
-
-    @TruffleBoundary
-    private static void removeVars0(FrameDescriptor fDesc) {
-        fDesc.removeFrameSlot(RRuntime.RDotGeneric);
-        fDesc.removeFrameSlot(RRuntime.RDotMethod);
-        fDesc.removeFrameSlot(RRuntime.RDotClass);
-        fDesc.removeFrameSlot(RRuntime.RDotGenericCallEnv);
-        fDesc.removeFrameSlot(RRuntime.RDotGenericDefEnv);
-    }
-
-    @TruffleBoundary
-    protected static void removeVar(FrameDescriptor fDesc, final String varName) {
-        if (fDesc.findFrameSlot(varName) != null) {
-            fDesc.removeFrameSlot(varName);
-        }
+    protected void defineVarsAsArguments(Object[] args) {
+        RArguments.setS3Generic(args, genericName);
+        RArguments.setS3Class(args, klass);
+        RArguments.setS3CallEnv(args, genCallEnv);
+        RArguments.setS3DefEnv(args, genDefEnv);
     }
 
     private void checkLength(final String className, final String generic) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnaryOpsGroupDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnaryOpsGroupDispatchNode.java
index 52e67541768268873be27717c1bbdeefd5cfb54f..ab54468f8ca7e517a8129e4dccf3898abcbd64a0 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnaryOpsGroupDispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UnaryOpsGroupDispatchNode.java
@@ -27,7 +27,7 @@ public class UnaryOpsGroupDispatchNode extends GroupDispatchNode {
     @Override
     protected Object callBuiltin(VirtualFrame frame, Object[] evaluatedArgs, String[] argNames) {
         initBuiltin(frame);
-        Object[] argObject = RArguments.create(builtinFunc, this.callSrc, RArguments.getDepth(frame) + 1, new Object[]{evaluatedArgs[0], RMissing.instance});
+        Object[] argObject = RArguments.create(builtinFunc, callSrc, null, RArguments.getDepth(frame) + 1, new Object[]{evaluatedArgs[0], RMissing.instance});
         return indirectCallNode.call(frame, builtinFunc.getTarget(), argObject);
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java
index bad5e48cdf749059528a71120ce654cd048a3131..3db86d0fc0c3390f8380a5069d6fa884c5e10df5 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodDispatchNode.java
@@ -13,14 +13,13 @@ package com.oracle.truffle.r.nodes.function;
 
 import java.util.*;
 
-import com.oracle.truffle.api.CompilerDirectives.*;
-import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.r.runtime.*;
 import com.oracle.truffle.r.runtime.data.*;
-import com.oracle.truffle.r.runtime.env.frame.*;
 
 /**
  * {@code UseMethod} is typically called like this:
@@ -35,8 +34,8 @@ import com.oracle.truffle.r.runtime.env.frame.*;
  */
 public class UseMethodDispatchNode extends S3DispatchNode {
 
-    private final BranchProfile errorProfile = BranchProfile.create();
     private final ConditionProfile topLevelFrameProfile = ConditionProfile.createBinaryProfile();
+    private final ConditionProfile callerFrameSlotPath = ConditionProfile.createBinaryProfile();
 
     @CompilationFinal private final String[] suppliedArgNames;
 
@@ -46,13 +45,19 @@ public class UseMethodDispatchNode extends S3DispatchNode {
         this.suppliedArgNames = evaledArgNames;
     }
 
+    private Frame getCallerFrame(VirtualFrame frame) {
+        Frame funFrame = RArguments.getCallerFrame(frame);
+        if (callerFrameSlotPath.profile(funFrame == null)) {
+            funFrame = Utils.getCallerFrame(frame, FrameAccess.MATERIALIZE);
+            RError.performanceWarning("slow caller frame access in UseMethod dispatch");
+        }
+        // S3 method can be dispatched from top-level where there is no caller frame
+        return topLevelFrameProfile.profile(funFrame == null) ? frame : funFrame;
+    }
+
     @Override
     public Object execute(VirtualFrame frame) {
-        Frame funFrame = Utils.getCallerFrame(frame, FrameAccess.MATERIALIZE);
-        // S3 method can be dispatched from top-level where there is no caller frame
-        if (topLevelFrameProfile.profile(funFrame == null)) {
-            funFrame = frame;
-        }
+        Frame funFrame = getCallerFrame(frame);
         if (targetFunction == null) {
             findTargetFunction(RArguments.getEnclosingFrame(frame));
         }
@@ -60,13 +65,9 @@ public class UseMethodDispatchNode extends S3DispatchNode {
     }
 
     @Override
-    public Object execute(VirtualFrame frame, RStringVector aType) {
+    public Object executeGeneric(VirtualFrame frame, RStringVector aType) {
         this.type = aType;
-        Frame funFrame = Utils.getCallerFrame(frame, FrameAccess.MATERIALIZE);
-        // S3 method can be dispatched from top-level where there is no caller frame
-        if (funFrame == null) {
-            funFrame = frame;
-        }
+        Frame funFrame = getCallerFrame(frame);
         findTargetFunction(RArguments.getEnclosingFrame(frame));
         return executeHelper(frame, funFrame);
     }
@@ -81,7 +82,7 @@ public class UseMethodDispatchNode extends S3DispatchNode {
     }
 
     @Override
-    public Object executeInternal(VirtualFrame frame, RStringVector aType, Object[] args) {
+    public Object executeInternalGeneric(VirtualFrame frame, RStringVector aType, Object[] args) {
         this.type = aType;
         // TBD getEnclosing?
         findTargetFunction(frame);
@@ -103,7 +104,7 @@ public class UseMethodDispatchNode extends S3DispatchNode {
             }
         }
         EvaluatedArguments reorderedArgs = reorderArgs(frame, targetFunction, argValues, argNames, false, getSourceSection());
-        return executeHelper2(callerFrame, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
+        return executeHelper2(frame, callerFrame.materialize(), reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
     }
 
     private Object executeHelper(VirtualFrame callerFrame, Object[] args) {
@@ -148,7 +149,7 @@ public class UseMethodDispatchNode extends S3DispatchNode {
         EvaluatedArguments evaledArgs = EvaluatedArguments.create(argValues, argNames);
         // ...to match them against the chosen function's formal arguments
         EvaluatedArguments reorderedArgs = ArgumentMatcher.matchArgumentsEvaluated(callerFrame, targetFunction, evaledArgs, getEncapsulatingSourceSection(), promiseHelper, false);
-        return executeHelper2(callerFrame, reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
+        return executeHelper2(callerFrame, callerFrame.materialize(), reorderedArgs.getEvaluatedArgs(), reorderedArgs.getNames());
     }
 
     private static void addArg(Object[] values, Object value, int index) {
@@ -159,17 +160,13 @@ public class UseMethodDispatchNode extends S3DispatchNode {
         }
     }
 
-    @TruffleBoundary
-    private Object executeHelper2(Frame callerFrame, Object[] arguments, String[] argNames) {
-        Object[] argObject = RArguments.createS3Args(targetFunction, getSourceSection(), RArguments.getDepth(callerFrame) + 1, arguments, argNames);
+    private Object executeHelper2(VirtualFrame frame, MaterializedFrame callerFrame, Object[] arguments, String[] argNames) {
+        Object[] argObject = RArguments.createS3Args(targetFunction, getSourceSection(), null, RArguments.getDepth(callerFrame) + 1, arguments, argNames);
         // todo: cannot create frame descriptors in compiled code
-        FrameDescriptor frameDescriptor = new FrameDescriptor();
-        FrameSlotChangeMonitor.initializeFrameDescriptor(frameDescriptor, true);
-        VirtualFrame newFrame = Truffle.getRuntime().createVirtualFrame(argObject, frameDescriptor);
         genCallEnv = callerFrame;
-        defineVarsAsArguments(newFrame);
-        RArguments.setS3Method(newFrame, targetFunctionName);
-        return indirectCallNode.call(newFrame, targetFunction.getTarget(), argObject);
+        defineVarsAsArguments(argObject);
+        RArguments.setS3Method(argObject, targetFunctionName);
+        return indirectCallNode.call(frame, targetFunction.getTarget(), argObject);
     }
 
     private void findTargetFunction(Frame callerFrame) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
index c20d6dda6a7872deac4799f413bf5259fd60210d..769660da7dd73c629c70e6b621fc4f6e4c5d998d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RArguments.java
@@ -86,14 +86,13 @@ public final class RArguments {
     private static final int INDEX_ENVIRONMENT = 0;
     private static final int INDEX_FUNCTION = 1;
     private static final int INDEX_CALL_SRC = 2;
-    private static final int INDEX_ENCLOSING_FRAME = 3;
-    private static final int INDEX_N_ARGS = 4;
-    private static final int INDEX_DEPTH = 5;
-    private static final int INDEX_IS_IRREGULAR = 6;
-    private static final int INDEX_N_NAMES = 7;
-    private static final int INDEX_ARGUMENTS = 8;
-
-    private static final int S3_VAR_COUNT = 9;
+    private static final int INDEX_CALLER_FRAME = 3;
+    private static final int INDEX_ENCLOSING_FRAME = 4;
+    private static final int INDEX_N_ARGS = 5;
+    private static final int INDEX_DEPTH = 6;
+    private static final int INDEX_IS_IRREGULAR = 7;
+    private static final int INDEX_N_NAMES = 8;
+    private static final int INDEX_ARGUMENTS = 9;
     /*
      * These indices are relative to INDEX_ARGUMENTS + nArgs+ nNames
      */
@@ -103,6 +102,7 @@ public final class RArguments {
     private static final int S3_INDEX_CALL_ENV = 3;
     private static final int S3_INDEX_DEF_ENV = 4;
     private static final int S3_INDEX_GROUP = 5;
+    private static final int S3_VAR_COUNT = 6;
 
     /**
      * At the least, the array contains the function, enclosing frame, and numbers of arguments and
@@ -127,7 +127,6 @@ public final class RArguments {
         } else {
             return arguments;
         }
-
     }
 
     private static int getNArgs(Frame frame) {
@@ -142,10 +141,12 @@ public final class RArguments {
         return INDEX_ARGUMENTS + (int) args[INDEX_N_ARGS] + (int) args[INDEX_N_NAMES];
     }
 
-    private static void createHelper(Object[] a, REnvironment env, RFunction functionObj, SourceSection callSrc, int depth, MaterializedFrame enclosingFrame, Object[] evaluatedArgs, String[] names) {
+    private static void createHelper(Object[] a, REnvironment env, RFunction functionObj, SourceSection callSrc, MaterializedFrame callerFrame, int depth, MaterializedFrame enclosingFrame,
+                    Object[] evaluatedArgs, String[] names) {
         a[INDEX_ENVIRONMENT] = env;
         a[INDEX_FUNCTION] = functionObj;
         a[INDEX_CALL_SRC] = callSrc;
+        a[INDEX_CALLER_FRAME] = callerFrame;
         a[INDEX_ENCLOSING_FRAME] = enclosingFrame;
         a[INDEX_DEPTH] = depth;
         a[INDEX_IS_IRREGULAR] = false;
@@ -186,30 +187,31 @@ public final class RArguments {
         return a;
     }
 
-    public static Object[] create(RFunction functionObj, SourceSection callSrc, int depth) {
-        return create(functionObj, callSrc, depth, EMPTY_OBJECT_ARRAY);
+    public static Object[] create(RFunction functionObj, SourceSection callSrc, MaterializedFrame callerFrame, int depth) {
+        return create(functionObj, callSrc, callerFrame, depth, EMPTY_OBJECT_ARRAY);
     }
 
-    public static Object[] create(RFunction functionObj, SourceSection callSrc, int depth, Object[] evaluatedArgs) {
+    public static Object[] create(RFunction functionObj, SourceSection callSrc, MaterializedFrame callerFrame, int depth, Object[] evaluatedArgs) {
         if (functionObj != null) {
-            return create(null, functionObj, callSrc, depth, functionObj.getEnclosingFrame(), evaluatedArgs, EMPTY_STRING_ARRAY);
+            return create(null, functionObj, callSrc, callerFrame, depth, functionObj.getEnclosingFrame(), evaluatedArgs, EMPTY_STRING_ARRAY);
         }
-        return create(null, functionObj, callSrc, depth, null, evaluatedArgs, EMPTY_STRING_ARRAY);
+        return create(null, functionObj, callSrc, callerFrame, depth, null, evaluatedArgs, EMPTY_STRING_ARRAY);
     }
 
-    public static Object[] create(RFunction functionObj, SourceSection callSrc, int depth, Object[] evaluatedArgs, String[] names) {
-        return create(null, functionObj, callSrc, depth, functionObj.getEnclosingFrame(), evaluatedArgs, names);
+    public static Object[] create(RFunction functionObj, SourceSection callSrc, MaterializedFrame callerFrame, int depth, Object[] evaluatedArgs, String[] names) {
+        return create(null, functionObj, callSrc, callerFrame, depth, functionObj.getEnclosingFrame(), evaluatedArgs, names);
     }
 
-    public static Object[] create(REnvironment env, RFunction functionObj, SourceSection callSrc, int depth, MaterializedFrame enclosingFrame, Object[] evaluatedArgs, String[] names) {
+    public static Object[] create(REnvironment env, RFunction functionObj, SourceSection callSrc, MaterializedFrame callerFrame, int depth, MaterializedFrame enclosingFrame, Object[] evaluatedArgs,
+                    String[] names) {
         Object[] a = new Object[MINIMAL_ARRAY_LENGTH + evaluatedArgs.length + names.length];
-        createHelper(a, env, functionObj, callSrc, depth, enclosingFrame, evaluatedArgs, names);
+        createHelper(a, env, functionObj, callSrc, callerFrame, depth, enclosingFrame, evaluatedArgs, names);
         return a;
     }
 
-    public static Object[] createS3Args(RFunction functionObj, SourceSection callSrc, int depth, Object[] evaluatedArgs, String[] names) {
+    public static Object[] createS3Args(RFunction functionObj, SourceSection callSrc, MaterializedFrame callerFrame, int depth, Object[] evaluatedArgs, String[] names) {
         Object[] a = new Object[MINIMAL_ARRAY_LENGTH + evaluatedArgs.length + names.length + S3_VAR_COUNT];
-        createHelper(a, null, functionObj, callSrc, depth, functionObj.getEnclosingFrame(), evaluatedArgs, names);
+        createHelper(a, null, functionObj, callSrc, callerFrame, depth, functionObj.getEnclosingFrame(), evaluatedArgs, names);
         return a;
     }
 
@@ -230,13 +232,17 @@ public final class RArguments {
         return (String) args[s3StartIndex + S3_INDEX_GENERIC];
     }
 
-    public static void setS3Generic(Frame frame, final String generic) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
+    public static void setS3Generic(Object[] args, String generic) {
         int s3StartIndex = getS3StartIndex(args);
         assert (args.length > s3StartIndex);
         args[s3StartIndex + S3_INDEX_GENERIC] = generic;
     }
 
+    public static MaterializedFrame getCallerFrame(Frame frame) {
+        Object[] args = getArgumentsWithEvalCheck(frame);
+        return (MaterializedFrame) args[INDEX_CALLER_FRAME];
+    }
+
     public static RStringVector getS3Class(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
@@ -247,59 +253,55 @@ public final class RArguments {
         }
     }
 
-    public static void setS3Class(Frame frame, final RStringVector klass) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
+    public static void setS3Class(Object[] args, RStringVector klass) {
         int s3StartIndex = getS3StartIndex(args);
         assert (args.length > s3StartIndex);
         args[s3StartIndex + S3_INDEX_CLASS] = klass;
     }
 
-    public static String getS3Method(Frame frame) {
+    public static Object getS3Method(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
         if (args.length <= s3StartIndex) {
             return null;
         } else {
-            return (String) args[s3StartIndex + S3_INDEX_METHOD];
+            return args[s3StartIndex + S3_INDEX_METHOD];
         }
     }
 
-    public static void setS3Method(Frame frame, final String method) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
+    public static void setS3Method(Object[] args, Object method) {
         int s3StartIndex = getS3StartIndex(args);
         assert (args.length > s3StartIndex);
         args[s3StartIndex + S3_INDEX_METHOD] = method;
     }
 
-    public static Frame getS3DefEnv(Frame frame) {
+    public static MaterializedFrame getS3DefEnv(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
         if (args.length <= s3StartIndex) {
             return null;
         } else {
-            return (Frame) args[s3StartIndex + S3_INDEX_DEF_ENV];
+            return (MaterializedFrame) args[s3StartIndex + S3_INDEX_DEF_ENV];
         }
     }
 
-    public static void setS3DefEnv(Frame frame, Frame defEnv) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
+    public static void setS3DefEnv(Object[] args, MaterializedFrame defEnv) {
         int s3StartIndex = getS3StartIndex(args);
         assert (args.length > s3StartIndex);
         args[s3StartIndex + S3_INDEX_DEF_ENV] = defEnv;
     }
 
-    public static Frame getS3CallEnv(Frame frame) {
+    public static MaterializedFrame getS3CallEnv(Frame frame) {
         Object[] args = getArgumentsWithEvalCheck(frame);
         int s3StartIndex = getS3StartIndex(args);
         if (args.length <= s3StartIndex) {
             return null;
         } else {
-            return (Frame) args[s3StartIndex + S3_INDEX_CALL_ENV];
+            return (MaterializedFrame) args[s3StartIndex + S3_INDEX_CALL_ENV];
         }
     }
 
-    public static void setS3CallEnv(Frame frame, Frame callEnv) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
+    public static void setS3CallEnv(Object[] args, MaterializedFrame callEnv) {
         int s3StartIndex = getS3StartIndex(args);
         assert (args.length > s3StartIndex);
         args[s3StartIndex + S3_INDEX_CALL_ENV] = callEnv;
@@ -315,8 +317,7 @@ public final class RArguments {
         }
     }
 
-    public static void setS3Group(Frame frame, final String group) {
-        Object[] args = getArgumentsWithEvalCheck(frame);
+    public static void setS3Group(Object[] args, String group) {
         int s3StartIndex = getS3StartIndex(args);
         assert (args.length > s3StartIndex);
         args[s3StartIndex + S3_INDEX_GROUP] = group;
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 41b27d75272f328e3ce5843170fff7baaea470b1..581f9e37b47aef1dd1df672f2ecfaaa8a32893a5 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
@@ -398,12 +398,12 @@ public final class RDataFactory {
         return traceDataCreated(new RPairList(car, cdr, tag, type));
     }
 
-    public static RFunction createFunction(String name, RootCallTarget target, MaterializedFrame enclosingFrame) {
-        return traceDataCreated(new RFunction(name, target, null, enclosingFrame));
+    public static RFunction createFunction(String name, RootCallTarget target, MaterializedFrame enclosingFrame, boolean containsDispatch) {
+        return traceDataCreated(new RFunction(name, target, null, enclosingFrame, containsDispatch));
     }
 
     public static RFunction createFunction(String name, RootCallTarget target, RBuiltin builtin, MaterializedFrame enclosingFrame) {
-        return traceDataCreated(new RFunction(name, target, builtin, enclosingFrame));
+        return traceDataCreated(new RFunction(name, target, builtin, enclosingFrame, false));
     }
 
     public static REnvironment createNewEnv(REnvironment parent, int size) {
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 13b1b1f5e74884d91488ce34954b915bd32b39e8..321f1fc4b79a99f250827dac9a07e32728b573a2 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
@@ -45,13 +45,16 @@ public final class RFunction extends RScalar implements RAttributable {
     private final String name;
     private final RootCallTarget target;
     private final RBuiltin builtin;
+    private final boolean containsDispatch;
+
     @CompilationFinal private StableValue<MaterializedFrame> enclosingFrame;
     protected RAttributes attributes;
 
-    RFunction(String name, RootCallTarget target, RBuiltin builtin, MaterializedFrame enclosingFrame) {
+    RFunction(String name, RootCallTarget target, RBuiltin builtin, MaterializedFrame enclosingFrame, boolean containsDispatch) {
         this.name = name;
         this.target = target;
         this.builtin = builtin;
+        this.containsDispatch = containsDispatch;
         this.enclosingFrame = new StableValue<>(enclosingFrame, "RFunction enclosing frame");
     }
 
@@ -63,6 +66,10 @@ public final class RFunction extends RScalar implements RAttributable {
         return builtin;
     }
 
+    public boolean containsDispatch() {
+        return containsDispatch;
+    }
+
     public String getName() {
         return name;
     }
@@ -96,7 +103,7 @@ public final class RFunction extends RScalar implements RAttributable {
     }
 
     public RFunction copy() {
-        return new RFunction(name, target, builtin, enclosingFrame.getValue());
+        return new RFunction(name, target, builtin, enclosingFrame.getValue(), containsDispatch);
     }
 
     public RAttributes initAttributes() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/REnvMaterializedFrame.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/REnvMaterializedFrame.java
index e7f9e8c27d01e83bfd07d2dd48f53f97ddb0ad0c..0bef9b0f45dd95ee16b3bbe1fd04fc6c61826a5a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/REnvMaterializedFrame.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/frame/REnvMaterializedFrame.java
@@ -107,76 +107,91 @@ public class REnvMaterializedFrame implements MaterializedFrame {
         return arguments;
     }
 
+    @TruffleBoundary
     public Object getObject(FrameSlot slot) throws FrameSlotTypeException {
         verifyGet(slot, FrameSlotKind.Object);
         return map.get(slot.getIdentifier());
     }
 
+    @TruffleBoundary
     public void setObject(FrameSlot slot, Object value) {
         verifySet(slot, FrameSlotKind.Object);
         map.put(slot.getIdentifier(), value);
     }
 
+    @TruffleBoundary
     public byte getByte(FrameSlot slot) throws FrameSlotTypeException {
         verifyGet(slot, FrameSlotKind.Byte);
         return (byte) map.get(slot.getIdentifier());
     }
 
+    @TruffleBoundary
     public void setByte(FrameSlot slot, byte value) {
         verifySet(slot, FrameSlotKind.Byte);
         map.put(slot.getIdentifier(), value);
     }
 
+    @TruffleBoundary
     public boolean getBoolean(FrameSlot slot) throws FrameSlotTypeException {
         verifyGet(slot, FrameSlotKind.Boolean);
         return (boolean) map.get(slot.getIdentifier());
     }
 
+    @TruffleBoundary
     public void setBoolean(FrameSlot slot, boolean value) {
         verifySet(slot, FrameSlotKind.Boolean);
         map.put(slot.getIdentifier(), value);
     }
 
+    @TruffleBoundary
     public int getInt(FrameSlot slot) throws FrameSlotTypeException {
         verifyGet(slot, FrameSlotKind.Int);
         return (int) map.get(slot.getIdentifier());
     }
 
+    @TruffleBoundary
     public void setInt(FrameSlot slot, int value) {
         verifySet(slot, FrameSlotKind.Int);
         map.put(slot.getIdentifier(), value);
     }
 
+    @TruffleBoundary
     public long getLong(FrameSlot slot) throws FrameSlotTypeException {
         verifyGet(slot, FrameSlotKind.Long);
         return (long) map.get(slot.getIdentifier());
     }
 
+    @TruffleBoundary
     public void setLong(FrameSlot slot, long value) {
         verifySet(slot, FrameSlotKind.Long);
         map.put(slot.getIdentifier(), value);
     }
 
+    @TruffleBoundary
     public float getFloat(FrameSlot slot) throws FrameSlotTypeException {
         verifyGet(slot, FrameSlotKind.Float);
         return (float) map.get(slot.getIdentifier());
     }
 
+    @TruffleBoundary
     public void setFloat(FrameSlot slot, float value) {
         verifySet(slot, FrameSlotKind.Float);
         map.put(slot.getIdentifier(), value);
     }
 
+    @TruffleBoundary
     public double getDouble(FrameSlot slot) throws FrameSlotTypeException {
         verifyGet(slot, FrameSlotKind.Double);
         return (double) map.get(slot.getIdentifier());
     }
 
+    @TruffleBoundary
     public void setDouble(FrameSlot slot, double value) {
         verifySet(slot, FrameSlotKind.Double);
         map.put(slot.getIdentifier(), value);
     }
 
+    @TruffleBoundary
     @Override
     public Object getValue(FrameSlot slot) {
         int slotIndex = slot.getIndex();
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 a98e97d0bda8adb64d2edb0fd2e4250139ba0e6c..058623b032a3cb5d9a7eea35c837dc59ce9cfe01 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
@@ -65211,6 +65211,10 @@ character(0)
 [1] "Australia" "UK"        "UK"        "US"        "US"        "Australia"
 [7] NA
 
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinascharacter.testascharacter24
+#argv <- list(structure(list(4L), class = c('package_version', 'numeric_version')));as.character(argv[[1]]);
+[1] "4"
+
 ##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinascharacter.testascharacter25
 #argv <- list(c(-Inf, NaN, Inf));as.character(argv[[1]]);
 [1] "-Inf" "NaN"  "Inf"
@@ -65271,6 +65275,10 @@ character(0)
 #argv <- list(c(34L, -45L));as.character(argv[[1]]);
 [1] "34"  "-45"
 
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinascharacter.testascharacter40
+#argv <- list(structure(list(), class = 'numeric_version'));as.character(argv[[1]]);
+character(0)
+
 ##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinascharacter.testascharacter41
 #argv <- list(structure(list(c0 = structure(character(0), class = 'AsIs')), .Names = 'c0', row.names = character(0), class = 'data.frame'));as.character(argv[[1]]);
 [1] "character(0)"
@@ -75635,6 +75643,10 @@ logical(0)
 #argv <- list(1, 6); .Internal(rep.int(argv[[1]], argv[[2]]))
 [1] 1 1 1 1 1 1
 
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint10
+#argv <- list(c(1L, 1L, 2L, 2L), 6); .Internal(rep.int(argv[[1]], argv[[2]]))
+ [1] 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 2 1 1 2 2
+
 ##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint11
 #argv <- list(NA_character_, 3L); .Internal(rep.int(argv[[1]], argv[[2]]))
 [1] NA NA NA
@@ -75651,6 +75663,10 @@ logical(0)
 #argv <- list(0.8625, 2); .Internal(rep.int(argv[[1]], argv[[2]]))
 [1] 0.8625 0.8625
 
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint17
+#argv <- list(FALSE, FALSE); .Internal(rep.int(argv[[1]], argv[[2]]))
+logical(0)
+
 ##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint19
 #argv <- list(structure(c(1974, 1974.08333333333, 1974.16666666667, 1974.25, 1974.33333333333, 1974.41666666667, 1974.5, 1974.58333333333, 1974.66666666667, 1974.75, 1974.83333333333, 1974.91666666667, 1975, 1975.08333333333, 1975.16666666667, 1975.25, 1975.33333333333, 1975.41666666667, 1975.5, 1975.58333333333, 1975.66666666667, 1975.75, 1975.83333333333, 1975.91666666667, 1976, 1976.08333333333, 1976.16666666667, 1976.25, 1976.33333333333, 1976.41666666667, 1976.5, 1976.58333333333, 1976.66666666667, 1976.75, 1976.83333333333, 1976.91666666667, 1977, 1977.08333333333, 1977.16666666667, 1977.25, 1977.33333333333, 1977.41666666667, 1977.5, 1977.58333333333, 1977.66666666667, 1977.75, 1977.83333333333, 1977.91666666667, 1978, 1978.08333333333, 1978.16666666667, 1978.25, 1978.33333333333, 1978.41666666667, 1978.5, 1978.58333333333, 1978.66666666667, 1978.75, 1978.83333333333, 1978.91666666667, 1979, 1979.08333333333, 1979.16666666667, 1979.25, 1979.33333333333, 1979.41666666667, 1979.5, 1979.58333333333, 1979.66666666667, 1979.75, 1979.83333333333, 1979.91666666667), .Tsp = c(1974, 1979.91666666667, 12), class = 'ts'), 3L); .Internal(rep.int(argv[[1]], argv[[2]]))
   [1] 1974.000 1974.083 1974.167 1974.250 1974.333 1974.417 1974.500 1974.583
@@ -75685,6 +75701,10 @@ logical(0)
 #argv <- list(NA_integer_, 1L); .Internal(rep.int(argv[[1]], argv[[2]]))
 [1] NA
 
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint20
+#argv <- list(NA, 10L); .Internal(rep.int(argv[[1]], argv[[2]]))
+ [1] NA NA NA NA NA NA NA NA NA NA
+
 ##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint21
 #argv <- list(c('C', 'A', 'B'), structure(list(C = 1L, A = 1L, B = 1L), .Names = c('C', 'A', 'B'))); .Internal(rep.int(argv[[1]], argv[[2]]))
 [1] "C" "A" "B"
@@ -75702,6 +75722,14 @@ logical(0)
 [37] 0.26784 0.26784 0.26784 0.26784 0.26784 0.26784 0.26784 0.26784 0.26784
 [46] 0.26784 0.26784 0.26784 0.26784
 
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint25
+#argv <- list(NA, 5L); .Internal(rep.int(argv[[1]], argv[[2]]))
+[1] NA NA NA NA NA
+
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint26
+#argv <- list(TRUE, 6L); .Internal(rep.int(argv[[1]], argv[[2]]))
+[1] TRUE TRUE TRUE TRUE TRUE TRUE
+
 ##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint27
 #argv <- list(structure(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, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101), .Tsp = c(1, 101, 1), class = 'ts'), 3L); .Internal(rep.int(argv[[1]], argv[[2]]))
   [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18
@@ -75726,10 +75754,22 @@ logical(0)
 #argv <- list(1L, 4L); .Internal(rep.int(argv[[1]], argv[[2]]))
 [1] 1 1 1 1
 
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint4
+#argv <- list(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), 1); .Internal(rep.int(argv[[1]], argv[[2]]))
+ [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
+
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint5
+#argv <- list(FALSE, 0L); .Internal(rep.int(argv[[1]], argv[[2]]))
+logical(0)
+
 ##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint6
 #argv <- list('', 2L); .Internal(rep.int(argv[[1]], argv[[2]]))
 [1] "" ""
 
+##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint7
+#argv <- list(TRUE, 1L); .Internal(rep.int(argv[[1]], argv[[2]]))
+[1] TRUE
+
 ##com.oracle.truffle.r.test.testrgen.TestrGenBuiltinrepint.testrepint8
 #argv <- list('   ', 8L); .Internal(rep.int(argv[[1]], argv[[2]]))
 [1] "   " "   " "   " "   " "   " "   " "   " "   "
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java
index 7e5deca932de4d9157242c19e319a94e704236e8..2f6789f30298cb6f03d80a4ced82a5dfe7941492 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/failing/FailingTests.java
@@ -3249,12 +3249,6 @@ public class FailingTests extends TestBase {
         check("TestrGenBuiltinascharacter_testascharacter22_57067e2c0d6c38282b57535adb101e14");
     }
 
-    @Test
-    public void TestrGenBuiltinascharacter_testascharacter24_fa6e2ffc98f3b422be091992652cc063() {
-        assertEval("argv <- list(structure(list(4L), class = c('package_version', 'numeric_version')));as.character(argv[[1]]);");
-        check("TestrGenBuiltinascharacter_testascharacter24_fa6e2ffc98f3b422be091992652cc063");
-    }
-
     @Test
     public void TestrGenBuiltinascharacter_testascharacter28_be987cc5e86462dab4ec1574a5c96efd() {
         assertEval("argv <- list(structure(c(11323, 11330, 11337, 11344, 11351, 11358, 11365, 11372, 11379, 11386), class = 'Date'));as.character(argv[[1]]);");
@@ -3303,12 +3297,6 @@ public class FailingTests extends TestBase {
         check("TestrGenBuiltinascharacter_testascharacter4_b0efabdea893c5e3c24e0d077282fc6b");
     }
 
-    @Test
-    public void TestrGenBuiltinascharacter_testascharacter40_2b19c43c5220d4933e35afa1bcc7236c() {
-        assertEval("argv <- list(structure(list(), class = 'numeric_version'));as.character(argv[[1]]);");
-        check("TestrGenBuiltinascharacter_testascharacter40_2b19c43c5220d4933e35afa1bcc7236c");
-    }
-
     @Test
     public void TestrGenBuiltinascharacter_testascharacter42_4cd3aed711e78cfcab749a3f20dca4ba() {
         assertEval("argv <- list(structure(c(12784, 13879), class = 'Date'));as.character(argv[[1]]);");
@@ -10785,12 +10773,6 @@ public class FailingTests extends TestBase {
         check("TestrGenBuiltinrep_testrep9_3cc63793e525424d43ae3ba907a85d7a");
     }
 
-    @Test
-    public void TestrGenBuiltinrepint_testrepint10_dde17f705e40806f5b55beae204599b7() {
-        assertEval("argv <- list(c(1L, 1L, 2L, 2L), 6); .Internal(rep.int(argv[[1]], argv[[2]]))");
-        check("TestrGenBuiltinrepint_testrepint10_dde17f705e40806f5b55beae204599b7");
-    }
-
     @Test
     public void TestrGenBuiltinrepint_testrepint13_d31b70d4e01d550889ad6d9cb34b03cf() {
         assertEval("argv <- list(structure(1:4, .Label = c('A', 'B', 'C', 'D'), class = 'factor', .Names = c('a', 'b', 'c', 'd')), 2); .Internal(rep.int(argv[[1]], argv[[2]]))");
@@ -10803,60 +10785,18 @@ public class FailingTests extends TestBase {
         check("TestrGenBuiltinrepint_testrepint14_eb20631d35776b024d237f0e37558b93");
     }
 
-    @Test
-    public void TestrGenBuiltinrepint_testrepint17_7a1213eafa82541b8a351bd5838a5af6() {
-        assertEval("argv <- list(FALSE, FALSE); .Internal(rep.int(argv[[1]], argv[[2]]))");
-        check("TestrGenBuiltinrepint_testrepint17_7a1213eafa82541b8a351bd5838a5af6");
-    }
-
     @Test
     public void TestrGenBuiltinrepint_testrepint18_89d713aa24d3bbc416b09d5b3a7c0fcb() {
         assertEval("argv <- list(c(-1.74520963996789, -1.58308930128988, NA), 100L); .Internal(rep.int(argv[[1]], argv[[2]]))");
         check("TestrGenBuiltinrepint_testrepint18_89d713aa24d3bbc416b09d5b3a7c0fcb");
     }
 
-    @Test
-    public void TestrGenBuiltinrepint_testrepint20_491b0390b66db6a67cedcc2a42873e5a() {
-        assertEval("argv <- list(NA, 10L); .Internal(rep.int(argv[[1]], argv[[2]]))");
-        check("TestrGenBuiltinrepint_testrepint20_491b0390b66db6a67cedcc2a42873e5a");
-    }
-
     @Test
     public void TestrGenBuiltinrepint_testrepint24_8280666f60f8465e6eefa702e913cf63() {
         assertEval("argv <- list(3.1e-06, 49); .Internal(rep.int(argv[[1]], argv[[2]]))");
         check("TestrGenBuiltinrepint_testrepint24_8280666f60f8465e6eefa702e913cf63");
     }
 
-    @Test
-    public void TestrGenBuiltinrepint_testrepint25_c8eaf452f45b00ae42d98c734a5cfffd() {
-        assertEval("argv <- list(NA, 5L); .Internal(rep.int(argv[[1]], argv[[2]]))");
-        check("TestrGenBuiltinrepint_testrepint25_c8eaf452f45b00ae42d98c734a5cfffd");
-    }
-
-    @Test
-    public void TestrGenBuiltinrepint_testrepint26_ab3b24b0c9725d38798a7ad9e4076d5e() {
-        assertEval("argv <- list(TRUE, 6L); .Internal(rep.int(argv[[1]], argv[[2]]))");
-        check("TestrGenBuiltinrepint_testrepint26_ab3b24b0c9725d38798a7ad9e4076d5e");
-    }
-
-    @Test
-    public void TestrGenBuiltinrepint_testrepint4_20bb6f9af6b07b4403c6affda51252a6() {
-        assertEval("argv <- list(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), 1); .Internal(rep.int(argv[[1]], argv[[2]]))");
-        check("TestrGenBuiltinrepint_testrepint4_20bb6f9af6b07b4403c6affda51252a6");
-    }
-
-    @Test
-    public void TestrGenBuiltinrepint_testrepint5_cb990b8179a6fb9eaae1bb541f1d9486() {
-        assertEval("argv <- list(FALSE, 0L); .Internal(rep.int(argv[[1]], argv[[2]]))");
-        check("TestrGenBuiltinrepint_testrepint5_cb990b8179a6fb9eaae1bb541f1d9486");
-    }
-
-    @Test
-    public void TestrGenBuiltinrepint_testrepint7_6887e9ead03307d35952a52816eb9b4c() {
-        assertEval("argv <- list(TRUE, 1L); .Internal(rep.int(argv[[1]], argv[[2]]))");
-        check("TestrGenBuiltinrepint_testrepint7_6887e9ead03307d35952a52816eb9b4c");
-    }
-
     @Test
     public void TestrGenBuiltinretracemem_testretracemem1_2962f142b007b2052ba94b324866fab4() {
         assertEval("argv <- list(FALSE, FALSE);retracemem(argv[[1]],argv[[2]]);");
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java
index 235e9dcc301fb181fbc1d03f81c661db6464bcd6..23f5d45c72cad0a2e10a80aef6532c79f0acb7b1 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java
@@ -37,7 +37,7 @@ public final class FastRSession implements RSession {
      * comparison. It does not separate error output as the test analysis doesn't need it.
      */
     private static class ConsoleHandler implements RContext.ConsoleHandler {
-        private StringBuilder buffer = new StringBuilder();
+        private final StringBuilder buffer = new StringBuilder();
 
         @TruffleBoundary
         public void println(String s) {
@@ -111,7 +111,11 @@ public final class FastRSession implements RSession {
         Load_RFFIFactory.initialize();
         FastROptions.initialize();
         REnvVars.initialize();
-        REngine.initialize(new String[0], consoleHandler, false, false);
+        try {
+            REngine.initialize(new String[0], consoleHandler, false, false);
+        } finally {
+            System.out.print(consoleHandler.buffer.toString());
+        }
     }
 
     @SuppressWarnings("deprecation")
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/testrgen/TestrGenBuiltinascharacter.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/testrgen/TestrGenBuiltinascharacter.java
index 8636363a7656b2d739edeb996a13578e468ac556..4b7516036bf70e101e3f419db88052d0c7035c0e 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/testrgen/TestrGenBuiltinascharacter.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/testrgen/TestrGenBuiltinascharacter.java
@@ -143,7 +143,6 @@ public class TestrGenBuiltinascharacter extends TestBase {
     }
 
     @Test
-    @Ignore
     public void testascharacter24() {
         assertEval("argv <- list(structure(list(4L), class = c(\'package_version\', \'numeric_version\')));as.character(argv[[1]]);");
     }
@@ -231,7 +230,6 @@ public class TestrGenBuiltinascharacter extends TestBase {
     }
 
     @Test
-    @Ignore
     public void testascharacter40() {
         assertEval("argv <- list(structure(list(), class = \'numeric_version\'));as.character(argv[[1]]);");
     }
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/testrgen/TestrGenBuiltinrepint.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/testrgen/TestrGenBuiltinrepint.java
index fb2a20725d7bf89067fb56c5a6a8f18da8102b67..f0c6d123256818699dfb1fac5913ccb66e901b90 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/testrgen/TestrGenBuiltinrepint.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/testrgen/TestrGenBuiltinrepint.java
@@ -33,13 +33,11 @@ public class TestrGenBuiltinrepint extends TestBase {
     }
 
     @Test
-    @Ignore
     public void testrepint4() {
         assertEval("argv <- list(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), 1); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
 
     @Test
-    @Ignore
     public void testrepint5() {
         assertEval("argv <- list(FALSE, 0L); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
@@ -50,7 +48,6 @@ public class TestrGenBuiltinrepint extends TestBase {
     }
 
     @Test
-    @Ignore
     public void testrepint7() {
         assertEval("argv <- list(TRUE, 1L); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
@@ -66,7 +63,6 @@ public class TestrGenBuiltinrepint extends TestBase {
     }
 
     @Test
-    @Ignore
     public void testrepint10() {
         assertEval("argv <- list(c(1L, 1L, 2L, 2L), 6); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
@@ -88,7 +84,7 @@ public class TestrGenBuiltinrepint extends TestBase {
     }
 
     @Test
-    @Ignore
+    @Ignore("formatting")
     public void testrepint14() {
         assertEval("argv <- list(2e-08, 9); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
@@ -104,13 +100,12 @@ public class TestrGenBuiltinrepint extends TestBase {
     }
 
     @Test
-    @Ignore
     public void testrepint17() {
         assertEval("argv <- list(FALSE, FALSE); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
 
     @Test
-    @Ignore
+    @Ignore("formatting")
     public void testrepint18() {
         assertEval("argv <- list(c(-1.74520963996789, -1.58308930128988, NA), 100L); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
@@ -121,7 +116,6 @@ public class TestrGenBuiltinrepint extends TestBase {
     }
 
     @Test
-    @Ignore
     public void testrepint20() {
         assertEval("argv <- list(NA, 10L); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
@@ -148,13 +142,11 @@ public class TestrGenBuiltinrepint extends TestBase {
     }
 
     @Test
-    @Ignore
     public void testrepint25() {
         assertEval("argv <- list(NA, 5L); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
 
     @Test
-    @Ignore
     public void testrepint26() {
         assertEval("argv <- list(TRUE, 6L); .Internal(rep.int(argv[[1]], argv[[2]]))");
     }
diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py
index 6f9812af15e02b1357be8b7e87bc42f974668748..38b17da9965298c5e06792fba8d24b40acb10913 100644
--- a/mx.fastr/suite.py
+++ b/mx.fastr/suite.py
@@ -220,6 +220,7 @@ suite = {
           "com.oracle.truffle.r.nodes.wrapper.processor"
       ],
       "workingSets" : "Truffle,FastR",
+      "jacoco" : "include",
     },
 
     "com.oracle.truffle.r.nodes.builtin" : {
@@ -237,6 +238,7 @@ suite = {
         "com.oracle.truffle.r.nodes.wrapper.processor",
       ],
       "workingSets" : "Truffle,FastR",
+      "jacoco" : "include",
     },
 
     "com.oracle.truffle.r.test.ignore.processor" : {
@@ -260,6 +262,7 @@ suite = {
       "javaCompliance" : "1.8",
       "annotationProcessors" : ["com.oracle.truffle.r.test.ignore.processor"],
       "workingSets" : "Truffle,FastR,Test",
+      "jacoco" : "include",
     },
 
     "com.oracle.truffle.r.test.native" : {
@@ -277,6 +280,7 @@ suite = {
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
       "workingSets" : "Truffle,FastR",
+      "jacoco" : "include",
     },
 
     "com.oracle.truffle.r.shell" : {
@@ -288,6 +292,7 @@ suite = {
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
       "workingSets" : "Truffle,FastR",
+      "jacoco" : "include",
     },
 
     "com.oracle.truffle.r.runtime" : {
@@ -300,6 +305,7 @@ suite = {
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
       "workingSets" : "Truffle,FastR",
+      "jacoco" : "include",
     },
 
     "com.oracle.truffle.r.runtime.ffi" : {
@@ -327,6 +333,7 @@ suite = {
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
       "workingSets" : "Truffle,FastR",
+      "jacoco" : "include",
     },
 
     "com.oracle.truffle.r.native" : {
@@ -343,7 +350,7 @@ suite = {
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
       "workingSets" : "FastR",
-
+      "jacoco" : "include",
     },
 
     "com.oracle.truffle.r.library" : {
@@ -354,6 +361,7 @@ suite = {
       "checkstyle" : "com.oracle.truffle.r.runtime",
       "javaCompliance" : "1.8",
       "workingSets" : "FastR",
+      "jacoco" : "include",
 
     },