changeset 20940:476374f3fe9a

Truffle-DSL: generate better polymorphic execute signatures
author Christian Humer <christian.humer@gmail.com>
date Tue, 14 Apr 2015 15:12:48 +0200
parents f83fd99b2962
children 4f45e4d3361c
files graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteGroupingTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteMethodTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NoTypeSystemTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestHelper.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemNodeFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/ElementUtils.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTreeBuilder.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ExecutableTypeData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/MessageContainer.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ParameterSpec.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TemplateMethod.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/GenericParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/MethodSpecParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeMethodParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/SpecializationMethodParser.java
diffstat 22 files changed, 1158 insertions(+), 516 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteGroupingTest.java	Tue Apr 14 15:12:48 2015 +0200
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.dsl.test;
+
+import org.junit.*;
+import org.junit.experimental.theories.*;
+import org.junit.runner.*;
+import org.junit.runners.Parameterized.Parameters;
+
+import static org.junit.Assert.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.test.ExecuteGroupingTestFactory.ExecuteGrouping1NodeGen;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+/*
+ * This test aims to test the reuse of execute methods with evaluated parameters as much as possible.
+ */
+@SuppressWarnings("unused")
+@RunWith(Theories.class)
+public class ExecuteGroupingTest {
+
+    @DataPoints public static final Object[] parameters = new Object[]{1, 2};
+
+    static final class ExecuteGroupingChild extends Node {
+
+        int invocationCount = 0;
+
+        private final Object returnValue;
+
+        public ExecuteGroupingChild(Object returnValue) {
+            this.returnValue = returnValue;
+        }
+
+        Object execute() {
+            invocationCount++;
+            return returnValue;
+        }
+
+    }
+
+    @Theory
+    public void testExecuteGrouping1Node(Object a, Object b, Object c) throws UnexpectedResultException {
+        ExecuteGroupingChild child0 = new ExecuteGroupingChild(a);
+        ExecuteGroupingChild child1 = new ExecuteGroupingChild(b);
+        ExecuteGroupingChild child2 = new ExecuteGroupingChild(c);
+
+        int result = ((int) a) + ((int) b) + ((int) c);
+
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(child0, child1, child2)).execute());
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(child0, child1, child2)).execute((VirtualFrame) null));
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, child1, child2)).execute(a));
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, child1, child2)).executeInt(a));
+
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).execute(a, b));
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).execute((int) a, b));
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).execute(a, (int) b));
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).execute((int) a, (int) b));
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).executeInt((int) a, (int) b));
+
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, null)).execute(a, b, c));
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, null)).execute((int) a, (int) b, c));
+        assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, null)).execute((int) a, (int) b, (int) c));
+
+    }
+
+    @NodeChildren({@NodeChild(type = ExecuteGroupingChild.class), @NodeChild(type = ExecuteGroupingChild.class), @NodeChild(type = ExecuteGroupingChild.class)})
+    abstract static class ExecuteGrouping1Node extends Node {
+
+        abstract Object execute();
+
+        int executeInt() throws UnexpectedResultException {
+            Object value = execute();
+            if (value instanceof Integer) {
+                return (int) value;
+            }
+            throw new UnexpectedResultException(value);
+        }
+
+        abstract double executeDouble() throws UnexpectedResultException;
+
+        abstract Object execute(VirtualFrame frame);
+
+        abstract Object execute(Object o1);
+
+        abstract int executeInt(Object o1) throws UnexpectedResultException;
+
+        abstract Object execute(Object o1, Object o2);
+
+        abstract Object execute(int o1, int o2);
+
+        abstract Object execute(int o1, int o2, Object o3);
+
+        abstract int executeInt(int o1, int o2) throws UnexpectedResultException;
+
+        abstract Object execute(Object o1, int o2);
+
+        abstract Object execute(int o1, Object o2);
+
+        abstract Object execute(Object o1, Object o2, Object o3);
+
+        abstract Object execute(int o1, int o2, int o3);
+
+        @Specialization
+        int s1(int a, int b, int c) {
+            return a + b + c;
+        }
+
+        @Specialization
+        int s2(Object a, Object b, Object c) {
+            return ((int) a) + ((int) b) + ((int) c);
+        }
+
+    }
+
+    abstract static class StrangeReturnCase extends Node {
+
+        // we don't know how to implement executeDouble
+        public abstract double executeDouble();
+
+        public int executeInt() {
+            return 42;
+        }
+
+        @Specialization(rewriteOn = RuntimeException.class)
+        int s1() {
+            return 42;
+        }
+
+        @Specialization
+        int s2() {
+            return 42;
+        }
+
+    }
+
+    @ExpectError("Incompatible abstract execute methods found [executeDouble(), executeInt()].%")
+    abstract static class IncompatibleAbstract1 extends Node {
+
+        // we don't know how to implement executeDouble
+        abstract double executeDouble();
+
+        abstract int executeInt();
+
+        @Specialization
+        int s1() {
+            return 42;
+        }
+
+    }
+
+    abstract static class IncompatibleAbstract2 extends Node {
+
+        abstract double executeDouble();
+
+        // we can resolve duplicate path errors by making an execute method final
+        @SuppressWarnings("static-method")
+        public final int executeInt() {
+            return 42;
+        }
+
+        @ExpectError("The provided return type \"int\" does not match expected return type \"double\".%")
+        @Specialization(rewriteOn = RuntimeException.class)
+        int s1() {
+            return 42;
+        }
+
+        @Specialization
+        double s2() {
+            return 42;
+        }
+
+    }
+
+}
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteMethodTest.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteMethodTest.java	Tue Apr 14 15:12:48 2015 +0200
@@ -137,7 +137,6 @@
 
     @TypeSystemReference(ExecuteMethodTypes.class)
     @NodeChild(value = "a", type = ChildNoFrame.class)
-    @ExpectError("Multiple accessible and overridable generic execute methods found [executeInt(), executeObject()]. Remove all but one or mark all but one as final.")
     abstract static class ExecuteThis8 extends Node {
 
         abstract int executeInt();
@@ -196,7 +195,6 @@
 
     @TypeSystemReference(ExecuteMethodTypes.class)
     @NodeChild(value = "a", type = ChildNoFrame.class)
-    @ExpectError("Multiple accessible and overridable generic execute methods found [executeVoid1(), executeVoid2()]. Remove all but one or mark all but one as final.")
     abstract static class ExecuteThisVoid3 extends Node {
 
         // allow only one execute void
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java	Tue Apr 14 15:12:48 2015 +0200
@@ -32,6 +32,7 @@
 import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback3Factory;
 import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback4Factory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.*;
+import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 
 public class FallbackTest {
@@ -53,6 +54,9 @@
     @NodeChild("a")
     abstract static class Fallback1 extends ValueNode {
 
+        @Override
+        public abstract String executeString(VirtualFrame frame);
+
         @Specialization
         String f1(int a) {
             return "(int)";
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NoTypeSystemTest.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NoTypeSystemTest.java	Tue Apr 14 15:12:48 2015 +0200
@@ -37,6 +37,10 @@
 
 public class NoTypeSystemTest {
 
+    abstract static class DummyChild extends Node {
+        abstract Object execute();
+    }
+
     abstract static class NoParameterTestNode extends Node {
         abstract void execute();
 
@@ -138,9 +142,9 @@
 
         abstract int[] executeIntArray(Object primitive) throws UnexpectedResultException;
 
-        abstract void executeVoid(Object primitive) throws UnexpectedResultException;
+        abstract void executeVoid(Object primitive);
 
-        abstract void executeChar(Object primitive) throws UnexpectedResultException;
+        abstract void executeChar(Object primitive);
 
         abstract int executeInt(int primitive) throws UnexpectedResultException;
 
@@ -150,7 +154,7 @@
 
         abstract int[] executeIntArray(int[] primitive) throws UnexpectedResultException;
 
-        abstract void executeChar(char primitive) throws UnexpectedResultException;
+        abstract void executeChar(char primitive);
 
         @Specialization
         int s1(int primitive) {
@@ -178,19 +182,6 @@
 
     }
 
-    // make nodes replacable
-    private static <T extends Node> T createRoot(final T node) {
-        new RootNode() {
-            @Child T child = node;
-
-            @Override
-            public Object execute(VirtualFrame frame) {
-                return null;
-            }
-        }.adoptChildren();
-        return node;
-    }
-
     @Test
     public void testTypesNotInTypeSystem() throws UnexpectedResultException {
         int[] someArray = {1, 2, 3};
@@ -232,7 +223,7 @@
     }
 
     private static TypesNotInTypeSystemTest createTypesNotInTypeSystem() {
-        return createRoot(TypesNotInTypeSystemTestNodeGen.create());
+        return TestHelper.createRoot(TypesNotInTypeSystemTestNodeGen.create());
     }
 
     abstract static class ErrorImpossibleTypes1 extends Node {
@@ -281,7 +272,7 @@
 
     @ExpectError("Not enough child node declarations found. Please annotate the node class with addtional @NodeChild annotations or remove all execute methods that do not provide all evaluated values. "
                     + "The following execute methods do not provide all evaluated values for the expected signature size 2: [execute(int)].")
-    @NodeChild
+    @NodeChild(type = DummyChild.class)
     abstract static class ErrorMissingNodeChild2 extends Node {
 
         abstract int execute(int arg0);
@@ -308,7 +299,7 @@
     }
 
     @ExpectError("Unnecessary @NodeChild declaration. All evaluated child values are provided as parameters in execute methods.")
-    @NodeChild
+    @NodeChild(type = DummyChild.class)
     abstract static class ErrorAdditionalNodeChild1 extends Node {
 
         abstract int execute(int arg0);
@@ -319,7 +310,7 @@
         }
     }
 
-    @NodeChild
+    @NodeChild(type = DummyChild.class)
     @ExpectError("Not enough child node declarations found. Please annotate the node class with addtional @NodeChild annotations or remove all execute methods that do not provide all evaluated values. "
                     + "The following execute methods do not provide all evaluated values for the expected signature size 2: [execute(int)].")
     abstract static class ErrorAdditionalNodeChild2 extends Node {
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java	Tue Apr 14 15:12:48 2015 +0200
@@ -123,7 +123,7 @@
         }
 
         @Specialization(contains = "alwaysRewrite")
-        String returnField() {
+        Object returnField() {
             return getField();
         }
     }
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestHelper.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestHelper.java	Tue Apr 14 15:12:48 2015 +0200
@@ -32,12 +32,27 @@
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ChildrenNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
 
 /**
  * Utility class to provide some test helper functions.
  */
 class TestHelper {
 
+    // make nodes replacable
+    public static <T extends Node> T createRoot(final T node) {
+        new RootNode() {
+            @Child T child = node;
+
+            @Override
+            public Object execute(VirtualFrame frame) {
+                return null;
+            }
+        }.adoptChildren();
+        return node;
+    }
+
     private static ArgumentNode[] arguments(int count) {
         ArgumentNode[] nodes = new ArgumentNode[count];
         for (int i = 0; i < nodes.length; i++) {
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java	Tue Apr 14 15:12:48 2015 +0200
@@ -96,12 +96,12 @@
 
     public static class RubyHeadNode extends ExampleNode {
 
-        @Child private RubyLookupNode lookup = RubyLookupNodeGen.create(null);
-        @Child private RubyDispatchNode dispatch = RubyDispatchNodeGen.create(null);
+        @Child private RubyLookupNode lookup = RubyLookupNodeGen.create();
+        @Child private RubyDispatchNode dispatch = RubyDispatchNodeGen.create();
 
         @Specialization
-        public Object doCall(VirtualFrame frame, Object receiverObject, Object methodName, Object blockObject, Object... argumentsObjects) {
-            InternalMethod method = lookup.executeLookup(frame, receiverObject, methodName);
+        public Object doCall(VirtualFrame frame, RubyObject receiverObject, Object methodName, Object blockObject, Object... argumentsObjects) {
+            InternalMethod method = lookup.executeLookup(receiverObject, methodName);
 
             Object[] packedArguments = new Object[argumentsObjects.length + 3];
             packedArguments[0] = method;
@@ -113,9 +113,9 @@
         }
     }
 
-    public abstract static class RubyLookupNode extends ExampleNode {
+    public abstract static class RubyLookupNode extends Node {
 
-        public abstract InternalMethod executeLookup(VirtualFrame frame, Object receiverObject, Object methodName);
+        public abstract InternalMethod executeLookup(RubyObject receiver, Object method);
 
         @Specialization(guards = "receiver.getRubyClass() == cachedClass", assumptions = "cachedClass.getDependentAssumptions()")
         protected static InternalMethod cachedLookup(RubyObject receiver, Object name, //
@@ -132,7 +132,7 @@
     }
 
     @ImportStatic(InternalMethod.class)
-    public abstract static class RubyDispatchNode extends ExampleNode {
+    public abstract static class RubyDispatchNode extends Node {
 
         public abstract Object executeDispatch(VirtualFrame frame, InternalMethod function, Object[] packedArguments);
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java	Tue Apr 14 15:12:48 2015 +0200
@@ -62,18 +62,17 @@
     private final NodeData node;
     private final TypeSystemData typeSystem;
     private final TypeMirror genericType;
-    private final TypeMirror voidType;
     private final DSLOptions options;
     private final boolean singleSpecializable;
     private final int varArgsThreshold;
     private final Set<TypeMirror> expectedTypes = new HashSet<>();
+    private boolean nextUsed;
 
     public NodeGenFactory(ProcessorContext context, NodeData node) {
         this.context = context;
         this.node = node;
         this.typeSystem = node.getTypeSystem();
         this.genericType = context.getType(Object.class);
-        this.voidType = context.getType(void.class);
         this.options = typeSystem.getOptions();
         this.singleSpecializable = isSingleSpecializableImpl();
         this.varArgsThreshold = calculateVarArgsThreshold();
@@ -206,16 +205,14 @@
             }
         }
 
-        Collection<TypeMirror> specializedTypes = node.findSpecializedReturnTypes();
-        List<ExecutableTypeData> implementedExecutables = new ArrayList<>();
-        for (ExecutableTypeData execType : node.getExecutableTypes()) {
-            if (shouldImplementExecutableType(specializedTypes, execType)) {
-                implementedExecutables.add(execType);
+        List<ExecutableTypeData> usedTypes = filterBaseExecutableTypes(node.getExecutableTypes(), getReachableSpecializations());
+        for (ExecutableTypeData execType : usedTypes) {
+            if (execType.getMethod() == null) {
+                continue;
             }
+            clazz.add(createExecutableTypeOverride(usedTypes, execType));
         }
-        for (ExecutableTypeData execType : implementedExecutables) {
-            clazz.add(createExecutableTypeOverride(implementedExecutables, execType));
-        }
+
         clazz.add(createGetCostMethod());
 
         avoidFindbugsProblems(clazz);
@@ -377,7 +374,9 @@
             generated.put(specialization, clazz.add(createSpecialization(specialization, baseSpecializationType)));
         }
 
-        baseSpecialization.addOptional(createCreateNext(generated));
+        if (nextUsed) {
+            baseSpecialization.addOptional(createCreateNext(generated));
+        }
         baseSpecialization.addOptional(createCreateFallback(generated));
         baseSpecialization.addOptional(createCreatePolymorphic(generated));
 
@@ -414,13 +413,12 @@
 
         clazz.addOptional(createUnsupported());
         clazz.add(createGetSuppliedChildrenMethod());
-
-        int signatureSize = node.getSignatureSize();
-        Set<Integer> evaluatedCount = getEvaluatedCounts();
-        for (int evaluated : evaluatedCount) {
-            if (signatureSize != evaluated || signatureSize == 0) {
-                clazz.add(createFastPathExecuteMethod(null, evaluated > 0 ? null : genericType, evaluated));
-            }
+        clazz.add(createGetNext(clazz));
+        clazz.add(createAcceptAndExecute());
+
+        List<ExecutableTypeData> usedTypes = filterBaseExecutableTypes(node.getExecutableTypes(), getReachableSpecializations());
+        for (ExecutableTypeData type : usedTypes) {
+            clazz.add(createFastPathExecuteMethod(null, type, usedTypes));
         }
 
         for (NodeExecutionData execution : node.getChildExecutions()) {
@@ -436,6 +434,126 @@
         return clazz;
     }
 
+    private Element createAcceptAndExecute() {
+
+        TypeMirror[] parameters = new TypeMirror[node.getSignatureSize()];
+        Arrays.fill(parameters, genericType);
+
+        ExecutableTypeData executableElement = new ExecutableTypeData(genericType, "acceptAndExecute", context.getType(Frame.class), Arrays.asList(parameters));
+
+        LocalContext currentLocals = LocalContext.load(this, node.getSignatureSize(), varArgsThreshold);
+        CodeExecutableElement executable = createExecuteMethod(null, executableElement, currentLocals, false);
+
+        executable.getModifiers().add(FINAL);
+        CodeTreeBuilder builder = executable.createBuilder();
+
+        CodeTree receiver = CodeTreeBuilder.singleString("this");
+
+        builder.tree(createCallDelegateExecute(builder, receiver, currentLocals, executableElement, node.getGenericExecutableType(null)));
+
+        return executable;
+    }
+
+    private boolean shouldImplementExecutableType(SpecializationData specialization, ExecutableTypeData executableType) {
+        // always implement the root execute method. they are declared abstract in the base node.
+        if (executableType.getDelegatedTo() == null) {
+            return true;
+        }
+
+        if (!isSubtypeBoxed(context, specialization.getReturnType().getType(), executableType.getReturnType())) {
+            return false;
+        }
+
+        // specializations with more parameters are just ignored
+        if (executableType.getEvaluatedCount() > node.getSignatureSize()) {
+            return false;
+        }
+
+        // the evaluated signature might be compatible to the specialization
+        boolean specializationCompatible = true;
+        for (int i = 0; i < executableType.getEvaluatedCount(); i++) {
+            TypeMirror evaluatedType = executableType.getEvaluatedParameters().get(i);
+            TypeMirror specializedType = specialization.findParameterOrDie(node.getChildExecutions().get(i)).getType();
+
+            if (!isSubtypeBoxed(context, evaluatedType, specializedType) && !isSubtypeBoxed(context, specializedType, evaluatedType)) {
+                specializationCompatible = false;
+                break;
+            }
+        }
+        if (!specializationCompatible) {
+            return false;
+        }
+
+        // possibly trigger void optimization for a specialization if it is enabled
+        if (isVoid(executableType.getReturnType())) {
+            if (isTypeBoxingOptimized(options.voidBoxingOptimization(), specialization.getReturnType().getType())) {
+                return true;
+            }
+        }
+
+        // trigger type boxing elimination for unevaluated arguments
+        for (int i = executableType.getEvaluatedCount(); i < node.getSignatureSize(); i++) {
+            NodeExecutionData execution = node.getChildExecutions().get(i);
+            TypeMirror specializedType = specialization.findParameterOrDie(execution).getType();
+            if (isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), specializedType)) {
+                // it does not make sense to do type boxing elimination for children with
+                // no type specialized execute method
+                if (execution.getChild() != null) {
+                    ExecutableTypeData executedType = execution.getChild().findExecutableType(specializedType);
+                    if (executedType != null) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        // trigger type boxing elimination for return types
+        if (typeEquals(executableType.getReturnType(), specialization.getReturnType().getType())) {
+            if (isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), executableType.getReturnType())) {
+                return true;
+            }
+        }
+
+        // trigger generation for evaluated assignable type matches other than generic
+        for (int i = 0; i < executableType.getEvaluatedCount(); i++) {
+            TypeMirror evaluatedType = executableType.getEvaluatedParameters().get(i);
+            NodeExecutionData execution = node.getChildExecutions().get(i);
+            TypeMirror specializedType = specialization.findParameterOrDie(execution).getType();
+
+            if (isSubtypeBoxed(context, evaluatedType, specializedType) && !isObject(specializedType)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private List<ExecutableTypeData> filterBaseExecutableTypes(List<ExecutableTypeData> executableTypes, List<SpecializationData> specializations) {
+        Set<ExecutableTypeData> usedTypes = new HashSet<>();
+        type: for (ExecutableTypeData type : executableTypes) {
+            for (SpecializationData specialization : specializations) {
+                if (shouldImplementExecutableType(specialization, type) || type.isAbstract() || !(type.hasUnexpectedValue(context) && type.getMethod() != null)) {
+                    usedTypes.add(type);
+                    continue type;
+                }
+            }
+        }
+        Set<ExecutableTypeData> delegatesToAdd = new HashSet<>();
+        do {
+            delegatesToAdd.clear();
+            for (ExecutableTypeData type : usedTypes) {
+                ExecutableTypeData delegate = type.getDelegatedTo();
+                if (delegate != null && !usedTypes.contains(delegate)) {
+                    delegatesToAdd.add(delegate);
+                }
+            }
+            usedTypes.addAll(delegatesToAdd);
+        } while (!delegatesToAdd.isEmpty());
+        List<ExecutableTypeData> newUsedTypes = new ArrayList<>(usedTypes);
+        Collections.sort(newUsedTypes);
+        return newUsedTypes;
+    }
+
     private CodeTypeElement createSpecialization(SpecializationData specialization, TypeMirror baseType) {
         CodeTypeElement clazz = createClass(node, specialization, modifiers(PRIVATE, STATIC, FINAL), specializationTypeName(specialization), baseType);
 
@@ -463,27 +581,35 @@
         clazz.addOptional(createIsSameMethod(specialization));
         clazz.addOptional(createIsIdenticalMethod(specialization));
 
-        TypeMirror returnType = specialization.getReturnType().getType();
-        int signatureSize = specialization.getSignatureSize();
-
-        clazz.add(createFastPathExecuteMethod(specialization, null, signatureSize));
-
-        if (isTypeBoxingEliminated(specialization)) {
-            if (node.getMinimalEvaluatedParameters() == 0 || signatureSize == 0) {
-                clazz.add(createFastPathExecuteMethod(specialization, returnType, 0));
-                if (signatureSize > 0 && !isObject(returnType)) {
-                    clazz.add(createFastPathWrapExecuteMethod(genericType, returnType));
-                }
-                ExecutableTypeData voidExecutableType = node.findExecutableType(voidType, 0);
-                if (voidExecutableType != null && isTypeBoxingOptimized(options.voidBoxingOptimization(), returnType)) {
-                    clazz.add(createFastPathWrapVoidMethod(returnType));
-                }
+        // get types that should get implemented
+        List<ExecutableTypeData> types = new ArrayList<>();
+        for (ExecutableTypeData type : node.getExecutableTypes()) {
+            if (shouldImplementExecutableType(specialization, type)) {
+                types.add(type);
             }
         }
+        for (ExecutableTypeData type : types) {
+            clazz.add(createFastPathExecuteMethod(specialization, type, types));
+        }
 
         return clazz;
     }
 
+    public static List<Parameter> getDynamicParameters(TemplateMethod method) {
+        List<Parameter> parameters = new ArrayList<>();
+        for (Parameter param : method.getReturnTypeAndParameters()) {
+            if (param.getSpecification().isLocal()) {
+                // ignore parameters passed by locals
+                continue;
+            } else if (param.getVariableElement() != null && param.getVariableElement().getAnnotation(Cached.class) != null) {
+                // ignore cached parameters
+                continue;
+            }
+            parameters.add(param);
+        }
+        return parameters;
+    }
+
     private Element createDeepCopyMethod() {
         if (singleSpecializable) {
             return null;
@@ -506,6 +632,7 @@
             builder.startReturn().startCall(specializationStartFieldName(), "getNodeCost").end().end();
         }
         return executable;
+
     }
 
     private Element createIsIdenticalMethod(SpecializationData specialization) {
@@ -619,43 +746,6 @@
         return executable;
     }
 
-    private Element createFastPathWrapVoidMethod(TypeMirror wrap) {
-
-        CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), voidType, TypeSystemNodeFactory.executeName(voidType));
-        executable.addParameter(new CodeVariableElement(getType(Frame.class), FRAME_VALUE));
-        executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
-        CodeTreeBuilder builder = executable.createBuilder();
-        builder.startStatement();
-        builder.startCall(TypeSystemNodeFactory.voidBoxingExecuteName(wrap));
-        builder.string(FRAME_VALUE);
-        builder.end();
-        builder.end();
-
-        return executable;
-    }
-
-    private Element createFastPathWrapExecuteMethod(TypeMirror override, TypeMirror wrap) {
-        CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), override, TypeSystemNodeFactory.executeName(override));
-        executable.addParameter(new CodeVariableElement(getType(Frame.class), FRAME_VALUE));
-        executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
-        CodeTreeBuilder builder = executable.createBuilder();
-        if (wrap != null) {
-            builder.startTryBlock();
-        }
-        builder.startReturn();
-        builder.startCall(TypeSystemNodeFactory.executeName(wrap));
-        builder.string(FRAME_VALUE);
-        builder.end();
-        builder.end();
-        if (wrap != null) {
-            builder.end().startCatchBlock(getType(UnexpectedResultException.class), "ex");
-            builder.statement("return ex.getResult()");
-            builder.end();
-        }
-
-        return executable;
-    }
-
     private Element createCreateFallback(Map<SpecializationData, CodeTypeElement> generatedSpecializationClasses) {
         SpecializationData fallback = node.getGenericSpecialization();
         if (fallback == null) {
@@ -767,35 +857,6 @@
         return null;
     }
 
-    private boolean isTypeBoxingEliminated(SpecializationData specialization) {
-        if (specialization.getMethod() == null) {
-            return false;
-        }
-
-        TypeBoxingOptimization optimization = options.monomorphicTypeBoxingOptimization();
-        if (isTypeBoxingOptimized(optimization, specialization.getReturnType().getType())) {
-            return true;
-        }
-        for (Parameter p : specialization.getSignatureParameters()) {
-            if (isTypeBoxingOptimized(optimization, p.getType())) {
-                return true;
-            }
-        }
-        return false;
-
-    }
-
-    private Set<Integer> getEvaluatedCounts() {
-        Set<Integer> evaluatedCount = new TreeSet<>();
-        Collection<TypeMirror> returnSpecializedTypes = node.findSpecializedReturnTypes();
-        for (ExecutableTypeData execType : node.getExecutableTypes()) {
-            if (shouldImplementExecutableType(returnSpecializedTypes, execType)) {
-                evaluatedCount.add(execType.getEvaluatedCount());
-            }
-        }
-        return evaluatedCount;
-    }
-
     private Element createUnsupported() {
         SpecializationData fallback = node.getGenericSpecialization();
         if (fallback == null || optimizeFallback(fallback) || fallback.getMethod() == null) {
@@ -866,166 +927,23 @@
         }
     }
 
-    private CodeExecutableElement createExecutableTypeOverride(List<ExecutableTypeData> implementedExecutables, ExecutableTypeData execType) {
-        final String varArgsName = "args";
-        final TypeMirror returnType = execType.getReturnType();
-        final TypeMirror executedType = execType.getEvaluatedCount() > 0 ? null : returnType;
-
+    private CodeExecutableElement createExecutableTypeOverride(List<ExecutableTypeData> usedExecutables, ExecutableTypeData execType) {
         LocalContext locals = LocalContext.load(this, execType.getEvaluatedCount(), Integer.MAX_VALUE);
-        CodeExecutableElement method = cloneExecutableTypeOverride(locals, execType, varArgsName);
-
-        // rename varargs parameter
-        int signatureIndex = 0;
-        for (TypeMirror parameter : execType.getEvaluatedParameters()) {
-            LocalVariable var = locals.getValue(signatureIndex);
-            if (var != null) {
-                int varArgsIndex = execType.getVarArgsIndex(execType.getParameterIndex(signatureIndex));
-                if (varArgsIndex >= 0) {
-                    var = var.accessWith(CodeTreeBuilder.singleString(varArgsName + "[" + varArgsIndex + "]"));
-                }
-                if (!isObject(parameter)) {
-                    var = var.newType(parameter);
-                }
-                locals.setValue(node.getChildExecutions().get(signatureIndex), var);
-            }
-
-            signatureIndex++;
-        }
-
-        TypeMirror frame = execType.getFrameParameter();
+        CodeExecutableElement method = createExecuteMethod(null, execType, locals, true);
+
         CodeTreeBuilder builder = method.createBuilder();
         if (singleSpecializable) {
-            LocalVariable frameVar = null;
-            if (frame != null) {
-                frameVar = locals.get(FRAME_VALUE).newType(frame);
-            }
-            method.getThrownTypes().clear();
-            locals.set(FRAME_VALUE, frameVar);
-
             SpecializationData specialization = getReachableSpecializations().iterator().next();
-            ExecutableTypeData wrappedExecutableType = findWrappedExecutable(specialization, implementedExecutables, execType);
-            if (wrappedExecutableType != null) {
-                builder.startReturn().tree(callExecuteMethod(null, wrappedExecutableType, locals)).end();
-            } else {
-                builder.tree(createFastPath(builder, specialization, execType.getReturnType(), locals));
-            }
+            builder.tree(createFastPath(builder, specialization, execType, usedExecutables, locals));
         } else {
             // create acceptAndExecute
-            CodeTreeBuilder executeBuilder = builder.create();
-            executeBuilder.startCall(specializationStartFieldName(), TypeSystemNodeFactory.executeName(executedType));
-            if (frame == null) {
-                executeBuilder.nullLiteral();
-            } else {
-                executeBuilder.string(locals.get(FRAME_VALUE).getName());
-            }
-            locals.addReferencesTo(executeBuilder);
-            executeBuilder.end();
-
-            boolean hasExecutedUnexpected = executedType != null && !isObject(executedType) && !isVoid(executedType);
-
-            CodeTreeBuilder contentBuilder = builder.create();
-            contentBuilder.startReturn();
-            if (!hasExecutedUnexpected && !execType.hasUnexpectedValue(context)) {
-                if (executedType == null || needsCastTo(executedType, returnType)) {
-                    contentBuilder.cast(returnType, executeBuilder.build());
-                } else {
-                    contentBuilder.tree(executeBuilder.build());
-                }
-            } else {
-                contentBuilder.tree(expect(executedType, returnType, executeBuilder.build()));
-            }
-            contentBuilder.end();
-            // try catch assert if unexpected value is not expected
-            CodeTree content = contentBuilder.build();
-            if (!execType.hasUnexpectedValue(context) && hasExecutedUnexpected) {
-                content = wrapTryCatchUnexpected(content);
-            }
-            builder.tree(content);
+            ExecutableTypeData delegate = execType;
+            CodeTree receiver = CodeTreeBuilder.singleString(specializationStartFieldName());
+            builder.tree(createCallDelegateExecute(builder, receiver, locals, execType, delegate));
         }
         return method;
     }
 
-    private CodeTree wrapTryCatchUnexpected(CodeTree content) {
-        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
-        builder.startTryBlock();
-        builder.tree(content);
-        builder.end().startCatchBlock(getType(UnexpectedResultException.class), "ex");
-        builder.startThrow().startNew(getType(AssertionError.class)).end().end();
-        builder.end();
-        return builder.build();
-    }
-
-    private static ExecutableTypeData findWrappedExecutable(SpecializationData specialization, List<ExecutableTypeData> implementedExecutables, ExecutableTypeData executedType) {
-        if (specialization.getReturnType().getType() == executedType.getReturnType()) {
-            return null;
-        }
-        for (ExecutableTypeData otherType : implementedExecutables) {
-            if (otherType != executedType && //
-                            otherType.getReturnType() == specialization.getReturnType().getType() && //
-                            otherType.getEvaluatedCount() == executedType.getEvaluatedCount()) {
-                return otherType;
-            }
-        }
-        return null;
-    }
-
-    private CodeExecutableElement cloneExecutableTypeOverride(LocalContext locals, ExecutableTypeData execType, final String varArgsName) throws AssertionError {
-        CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), execType.getMethod());
-
-        method.getAnnotationMirrors().clear();
-        method.getModifiers().remove(Modifier.ABSTRACT);
-
-        // align argument names
-        int parameterIndex = 0;
-        if (execType.getFrameParameter() != null) {
-            CodeVariableElement frameParameter = (CodeVariableElement) method.getParameters().get(0);
-            frameParameter.setName(FRAME_VALUE);
-            frameParameter.getAnnotationMirrors().clear();
-            parameterIndex++;
-        }
-
-        for (int signatureIndex = 0; signatureIndex < execType.getEvaluatedCount(); signatureIndex++) {
-            CodeVariableElement var = (CodeVariableElement) method.getParameters().get(parameterIndex);
-            if (signatureIndex < node.getSignatureSize()) {
-                if (execType.getVarArgsIndex(parameterIndex) >= 0) {
-                    var.getAnnotationMirrors().clear();
-                    var.setName(varArgsName);
-                    break;
-                }
-                var.setName(locals.getValue(signatureIndex).getName());
-                var.getAnnotationMirrors().clear();
-            } else {
-                var.setName("other" + signatureIndex);
-            }
-            parameterIndex++;
-        }
-        return method;
-    }
-
-    private boolean shouldImplementExecutableType(Collection<TypeMirror> specializedTypes, ExecutableTypeData execType) {
-        TypeMirror type = execType.getReturnType();
-        Set<Modifier> modifiers = execType.getMethod().getModifiers();
-        if (modifiers.contains(FINAL) || modifiers.contains(STATIC) || modifiers.contains(PRIVATE)) {
-            return false;
-        } else if (execType.isAbstract()) {
-            return true;
-        } else if (ElementUtils.isObject(type)) {
-            return true;
-        } else if (ElementUtils.isVoid(type)) {
-            for (TypeMirror specializedType : specializedTypes) {
-                if (isTypeBoxingOptimized(options.voidBoxingOptimization(), specializedType)) {
-                    return true;
-                }
-            }
-            return false;
-        } else if (!specializedTypes.contains(type)) {
-            return false;
-        } else if (!isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), type)) {
-            return false;
-        }
-        return true;
-    }
-
     private Element createMethodGetSpecializationNode() {
         TypeMirror returntype = getType(SpecializationNode.class);
         CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returntype, "getSpecializationNode");
@@ -1091,8 +1009,7 @@
         return builder.build();
     }
 
-    private CodeTree callExecuteMethod(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) {
-        CodeTree receiver = execution != null ? accessParent(nodeFieldName(execution)) : null;
+    private CodeTree[] bindExecuteMethodParameters(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) {
         List<NodeExecutionData> executeWith = execution != null ? execution.getChild().getExecuteWith() : null;
 
         List<CodeTree> values = new ArrayList<>();
@@ -1114,7 +1031,13 @@
             }
             values.add(createTypeSafeReference(variable, targetParameter));
         }
-        return callMethod(receiver, method.getMethod(), values.toArray(new CodeTree[values.size()]));
+
+        return values.toArray(new CodeTree[values.size()]);
+    }
+
+    private CodeTree callExecuteMethod(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) {
+        CodeTree receiver = execution != null ? accessParent(nodeFieldName(execution)) : null;
+        return callMethod(receiver, method.getMethod(), bindExecuteMethodParameters(execution, method, currentValues));
     }
 
     private CodeTree callTemplateMethod(CodeTree receiver, TemplateMethod method, LocalContext currentValues) {
@@ -1148,8 +1071,6 @@
         }
         if (needsCastTo(sourceType, targetType)) {
             valueReference = TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference);
-        } else if (ElementUtils.needsCastTo(sourceType, targetType)) {
-            valueReference = CodeTreeBuilder.createBuilder().cast(targetType, valueReference).build();
         }
         return valueReference;
     }
@@ -1304,6 +1225,13 @@
         return false;
     }
 
+    private static Element createGetNext(CodeTypeElement type) {
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), type.asType(), "getNext");
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startReturn().cast(type.asType(), CodeTreeBuilder.singleString("this.next")).end();
+        return method;
+    }
+
     private Element createGetSuppliedChildrenMethod() {
         ArrayType nodeArray = context.getEnvironment().getTypeUtils().getArrayType(getType(Node.class));
 
@@ -1507,18 +1435,17 @@
         return builder.build();
     }
 
-    private CodeTree createCallNext(TypeMirror forType, LocalContext currentValues) {
+    private CodeTree createCallNext(CodeTreeBuilder parent, ExecutableTypeData currentType, ExecutableTypeData callType, LocalContext currentValues) {
         if (singleSpecializable) {
             return createThrowUnsupported(currentValues);
         }
-        CodeTreeBuilder callBuilder = CodeTreeBuilder.createBuilder();
-        callBuilder.startCall("next", TypeSystemNodeFactory.executeName(null));
-        currentValues.addReferencesTo(callBuilder, FRAME_VALUE);
-        callBuilder.end();
-        return CodeTreeBuilder.createBuilder().startReturn().tree(expect(genericType, forType, callBuilder.build())).end().build();
+        CodeTreeBuilder callBuilder = parent.create();
+        callBuilder.tree(createCallDelegateExecute(callBuilder, CodeTreeBuilder.singleString("getNext()"), currentValues, currentType, callType));
+        nextUsed = true;
+        return callBuilder.build();
     }
 
-    private CodeTree createCallRemove(String reason, TypeMirror forType, LocalContext currentValues) {
+    private CodeTree createCallRemove(String reason, ExecutableTypeData forType, LocalContext currentValues) {
         if (singleSpecializable) {
             return createThrowUnsupported(currentValues);
         }
@@ -1531,12 +1458,12 @@
 
         builder = builder.create();
         builder.startReturn();
-        builder.tree(expect(genericType, forType, call));
+        builder.tree(expectOrCast(genericType, forType, call));
         builder.end();
         return builder.build();
     }
 
-    private CodeTree createCallDelegate(String methodName, String reason, TypeMirror forType, LocalContext currentValues) {
+    private CodeTree createCallDelegate(String methodName, String reason, ExecutableTypeData forType, LocalContext currentValues) {
         CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
         builder.startCall(methodName);
         if (reason != null) {
@@ -1545,7 +1472,24 @@
         currentValues.addReferencesTo(builder, FRAME_VALUE);
         builder.end();
 
-        return expect(genericType, forType, builder.build());
+        CodeTree expectOrCast = expectOrCast(genericType, forType, builder.build());
+        return expectOrCast;
+    }
+
+    private CodeTree expectOrCast(TypeMirror sourceType, ExecutableTypeData targetType, CodeTree content) {
+        if (targetType.hasUnexpectedValue(context)) {
+            return expect(sourceType, targetType.getReturnType(), content);
+        } else {
+            return cast(sourceType, targetType.getReturnType(), content);
+        }
+    }
+
+    private CodeTree cast(TypeMirror sourceType, TypeMirror targetType, CodeTree content) {
+        if (ElementUtils.needsCastTo(sourceType, targetType) && !isVoid(sourceType)) {
+            return TypeSystemCodeGenerator.cast(typeSystem, targetType, content);
+        } else {
+            return content;
+        }
     }
 
     private CodeTree expect(TypeMirror sourceType, TypeMirror forType, CodeTree tree) {
@@ -1584,70 +1528,236 @@
         return false;
     }
 
-    private Element createFastPathExecuteMethod(SpecializationData specialization, final TypeMirror forType, int evaluatedArguments) {
-        TypeMirror type = forType == null ? genericType : forType;
-        LocalContext currentLocals = LocalContext.load(this, evaluatedArguments, varArgsThreshold);
+    private Element createFastPathExecuteMethod(SpecializationData specialization, ExecutableTypeData executedType, List<ExecutableTypeData> allTypes) {
+        LocalContext currentLocals = LocalContext.load(this, executedType.getEvaluatedCount(), varArgsThreshold);
+        CodeExecutableElement executable = createExecuteMethod(specialization, executedType, currentLocals, false);
+        CodeTreeBuilder builder = executable.createBuilder();
+        if (specialization == null) {
+            if (executedType.getDelegatedTo() == null) {
+                executable.getModifiers().add(ABSTRACT);
+            }
+        } else {
+            executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        }
+        builder.tree(createFastPath(builder, specialization, executedType, allTypes, currentLocals));
+
+        return executable;
+    }
+
+    private CodeExecutableElement createExecuteMethod(SpecializationData specialization, ExecutableTypeData executedType, LocalContext currentLocals, boolean originalOverride) {
+        TypeMirror returnType = executedType.getReturnType();
+        TypeMirror frame = executedType.getFrameParameter();
+        List<TypeMirror> evaluatedParameters = executedType.getEvaluatedParameters();
 
         if (specialization != null) {
             currentLocals.loadFastPathState(specialization);
         }
 
-        CodeExecutableElement executable = currentLocals.createMethod(modifiers(PUBLIC), type, TypeSystemNodeFactory.executeName(forType), FRAME_VALUE);
-        executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
-
-        if (!isObject(type)) {
-            executable.getThrownTypes().add(getType(UnexpectedResultException.class));
+        if (frame == null) {
+            currentLocals.removeValue(FRAME_VALUE);
+        } else {
+            currentLocals.set(FRAME_VALUE, currentLocals.get(FRAME_VALUE).newType(frame));
+        }
+
+        for (int i = 0; i < Math.min(node.getChildExecutions().size(), evaluatedParameters.size()); i++) {
+            NodeExecutionData execution = node.getChildExecutions().get(i);
+            currentLocals.setValue(execution, currentLocals.getValue(execution).newType(evaluatedParameters.get(i)));
+        }
+
+        String methodName;
+        if (originalOverride) {
+            methodName = executedType.getMethod().getSimpleName().toString();
+        } else {
+            methodName = executedType.getUniqueName();
         }
 
-        CodeTreeBuilder builder = executable.createBuilder();
-        builder.tree(createFastPath(builder, specialization, type, currentLocals));
+        CodeExecutableElement executable;
+        if (originalOverride && executedType.getMethod() != null) {
+            executable = CodeExecutableElement.clone(context.getEnvironment(), executedType.getMethod());
+            executable.getAnnotationMirrors().clear();
+            executable.getModifiers().remove(ABSTRACT);
+            for (VariableElement var : executable.getParameters()) {
+                ((CodeVariableElement) var).getAnnotationMirrors().clear();
+            }
+            if (executedType.getFrameParameter() != null) {
+                ((CodeVariableElement) executable.getParameters().get(0)).setName(FRAME_VALUE);
+            }
+
+            final String varArgsName = "args";
+            if (executable.isVarArgs()) {
+                ((CodeVariableElement) executable.getParameters().get(executable.getParameters().size() - 1)).setName(varArgsName);
+            }
+
+            // rename varargs parameter
+            int signatureIndex = 0;
+            for (TypeMirror parameter : executedType.getEvaluatedParameters()) {
+                LocalVariable var = currentLocals.getValue(signatureIndex);
+                if (var != null) {
+                    int varArgsIndex = executedType.getVarArgsIndex(executedType.getParameterIndex(signatureIndex));
+                    if (varArgsIndex >= 0) {
+                        var = var.accessWith(CodeTreeBuilder.singleString(varArgsName + "[" + varArgsIndex + "]"));
+                    } else {
+                        ((CodeVariableElement) executable.getParameters().get(executedType.getParameterIndex(signatureIndex))).setName(var.getName());
+                    }
+                    if (!isObject(parameter)) {
+                        var = var.newType(parameter);
+                    }
+                    currentLocals.setValue(node.getChildExecutions().get(signatureIndex), var);
+                }
+
+                signatureIndex++;
+            }
+        } else {
+            executable = currentLocals.createMethod(modifiers(PUBLIC), returnType, methodName, FRAME_VALUE);
+            if (executedType.hasUnexpectedValue(context)) {
+                executable.getThrownTypes().add(context.getDeclaredType(UnexpectedResultException.class));
+            }
+        }
 
         return executable;
     }
 
-    private CodeTree createFastPath(CodeTreeBuilder parent, SpecializationData specialization, TypeMirror type, LocalContext currentLocals) {
+    private CodeTree createFastPath(CodeTreeBuilder parent, SpecializationData specialization, final ExecutableTypeData executableType, List<ExecutableTypeData> allTypes, LocalContext currentLocals) {
         final CodeTreeBuilder builder = parent.create();
+        TypeMirror returnType = executableType.getReturnType();
+
+        ExecutableTypeData delegate = null;
+        if (specialization == null) {
+            delegate = executableType.getDelegatedTo();
+        }
+
+        if (delegate == null) {
+            delegate = findFastPathDelegate((specialization != null ? specialization.getReturnType().getType() : genericType), executableType, allTypes);
+        }
 
         for (NodeExecutionData execution : node.getChildExecutions()) {
+            if (specialization == null && delegate != null && execution.getIndex() >= delegate.getEvaluatedCount()) {
+                // we just evaluate children for the next delegate
+                continue;
+            } else if (specialization != null && delegate != null) {
+                // skip if already delegated
+                break;
+            }
+
             LocalVariable var = currentLocals.getValue(execution);
             if (var == null) {
                 TypeMirror targetType;
                 if (specialization == null) {
-                    targetType = genericType;
+                    List<TypeMirror> genericTypes = node.getGenericTypes(execution);
+                    if (genericTypes.isEmpty()) {
+                        targetType = genericType;
+                    } else {
+                        targetType = genericTypes.get(0);
+                    }
                 } else {
                     targetType = specialization.findParameterOrDie(execution).getType();
                 }
                 LocalVariable shortCircuit = resolveShortCircuit(specialization, execution, currentLocals);
-                LocalVariable value = currentLocals.createValue(execution, targetType).nextName();
-                builder.tree(createAssignExecuteChild(execution, type, value, shortCircuit, currentLocals));
-                currentLocals.setValue(execution, value);
+                var = currentLocals.createValue(execution, targetType).nextName();
+                builder.tree(createAssignExecuteChild(builder, execution, executableType, var, shortCircuit, currentLocals));
+                currentLocals.setValue(execution, var);
+
             }
         }
 
         LocalContext originalValues = currentLocals.copy();
-        if (specialization == null) {
-            builder.startReturn().tree(createCallDelegate("acceptAndExecute", null, type, currentLocals)).end();
+        if (delegate != null) {
+            builder.tree(createCallDelegateExecute(builder, null, currentLocals, executableType, delegate));
+        } else if (specialization == null) {
+            // nothing to do. abstract anyway
         } else if (specialization.isPolymorphic()) {
-            builder.tree(createCallNext(type, currentLocals));
+            builder.tree(createCallNext(builder, executableType, node.getGenericExecutableType(executableType), currentLocals));
         } else if (specialization.isUninitialized()) {
-            builder.startReturn().tree(createCallDelegate("uninitialized", null, type, currentLocals)).end();
+            builder.startReturn().tree(createCallDelegate("uninitialized", null, executableType, currentLocals)).end();
         } else {
-            final TypeMirror finalType = type;
             SpecializationGroup group = SpecializationGroup.create(specialization);
             SpecializationBody executionFactory = new SpecializationBody(true, true) {
                 @Override
                 public CodeTree createBody(SpecializationData s, LocalContext values) {
-                    return createFastPathExecute(builder, finalType, s, values);
+                    return createFastPathExecute(builder, executableType, s, values);
                 }
             };
-            builder.tree(createGuardAndCast(group, type, currentLocals, executionFactory));
-            if (hasFallthrough(group, type, originalValues, true, null) || group.getSpecialization().isFallback()) {
-                builder.tree(createCallNext(type, originalValues));
+            builder.tree(createGuardAndCast(group, returnType, currentLocals, executionFactory));
+            if (hasFallthrough(group, returnType, originalValues, true, null) || group.getSpecialization().isFallback()) {
+                builder.tree(createCallNext(builder, executableType, executableType, originalValues));
             }
         }
         return builder.build();
     }
 
+    private CodeTree createCallDelegateExecute(final CodeTreeBuilder parent, CodeTree receiver, LocalContext currentLocals, ExecutableTypeData source, ExecutableTypeData delegate) {
+        CodeTreeBuilder callBuilder = parent.create();
+
+        if (singleSpecializable) {
+            callBuilder.startCall(receiver, delegate.getMethod().getSimpleName().toString());
+        } else {
+            callBuilder.startCall(receiver, delegate.getUniqueName());
+        }
+        callBuilder.trees(bindExecuteMethodParameters(null, delegate, currentLocals));
+        callBuilder.end();
+        CodeTree call = expectOrCast(delegate.getReturnType(), source, callBuilder.build());
+
+        CodeTreeBuilder returnBuilder = parent.create();
+        if (isVoid(source.getReturnType())) {
+            returnBuilder.statement(call);
+            returnBuilder.returnStatement();
+        } else if (isVoid(delegate.getReturnType())) {
+            returnBuilder.statement(call);
+            returnBuilder.returnDefault();
+        } else {
+            returnBuilder.startReturn().tree(call).end();
+        }
+
+        CodeTreeBuilder builder = parent.create();
+
+        if (!source.hasUnexpectedValue(context) && delegate.hasUnexpectedValue(context)) {
+            builder.startTryBlock();
+            builder.tree(returnBuilder.build());
+            builder.end().startCatchBlock(context.getType(UnexpectedResultException.class), "ex");
+            if (!isVoid(source.getReturnType())) {
+                builder.startReturn().tree(cast(context.getType(Object.class), source.getReturnType(), CodeTreeBuilder.singleString("ex.getResult()"))).end();
+            }
+            builder.end();
+        } else {
+            builder.tree(returnBuilder.build());
+        }
+        return builder.build();
+    }
+
+    private ExecutableTypeData findFastPathDelegate(TypeMirror targetType, ExecutableTypeData executableType, List<ExecutableTypeData> allTypes) {
+        if (typeEquals(executableType.getReturnType(), targetType)) {
+            // type matches look for even better delegates
+            for (ExecutableTypeData type : allTypes) {
+                if (typeEquals(type.getReturnType(), targetType) && executableType.sameParameters(type)) {
+                    if (type != executableType) {
+                        return type;
+                    }
+                }
+            }
+            return null;
+        } else {
+            for (ExecutableTypeData type : allTypes) {
+                if (typeEquals(type.getReturnType(), targetType) && executableType.sameParameters(type)) {
+                    return type;
+                }
+            }
+            int executableIndex = allTypes.indexOf(executableType);
+            int compareIndex = 0;
+            for (ExecutableTypeData type : allTypes) {
+                if (executableIndex != compareIndex && executableType.sameParameters(type)) {
+                    int result = ExecutableTypeData.compareType(context, type.getReturnType(), executableType.getReturnType());
+                    if (result < 0) {
+                        return type;
+                    } else if (result == 0 && executableIndex < compareIndex) {
+                        return type;
+                    }
+                }
+                compareIndex++;
+            }
+            return null;
+        }
+    }
+
     private LocalVariable resolveShortCircuit(SpecializationData specialization, NodeExecutionData execution, LocalContext currentLocals) {
         LocalVariable shortCircuit = null;
         SpecializationData resolvedSpecialization = specialization;
@@ -1676,7 +1786,7 @@
         return shortCircuitIndex;
     }
 
-    private CodeTree createFastPathExecute(CodeTreeBuilder parent, final TypeMirror forType, SpecializationData specialization, LocalContext currentValues) {
+    private CodeTree createFastPathExecute(CodeTreeBuilder parent, final ExecutableTypeData forType, SpecializationData specialization, LocalContext currentValues) {
         CodeTreeBuilder builder = parent.create();
         int ifCount = 0;
         if (specialization.isFallback()) {
@@ -1729,11 +1839,11 @@
             execute.tree(callTemplateMethod(accessParent(null), specialization, currentValues));
             execute.end();
             if (!doReturn) {
-                if (isVoid(forType)) {
+                if (isVoid(forType.getReturnType())) {
                     execute.returnStatement();
                 } else {
                     execute.startReturn();
-                    execute.defaultValue(forType);
+                    execute.defaultValue(forType.getReturnType());
                     execute.end();
                 }
             }
@@ -1941,8 +2051,9 @@
         }
     }
 
-    private CodeTree createAssignExecuteChild(NodeExecutionData execution, TypeMirror returnType, LocalVariable targetValue, LocalVariable shortCircuit, LocalContext currentValues) {
-        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+    private CodeTree createAssignExecuteChild(CodeTreeBuilder parent, NodeExecutionData execution, ExecutableTypeData type, LocalVariable targetValue, LocalVariable shortCircuit,
+                    LocalContext currentValues) {
+        CodeTreeBuilder builder = parent.create();
         boolean hasUnexpected = hasUnexpectedResult(execution, targetValue.getTypeMirror());
 
         CodeTree executeChild;
@@ -1962,21 +2073,22 @@
         if (hasUnexpected) {
             builder.startCatchBlock(getType(UnexpectedResultException.class), "ex");
             LocalContext slowPathValues = currentValues.copy();
-
             slowPathValues.setValue(execution, targetValue.makeGeneric(context).accessWith(CodeTreeBuilder.singleString("ex.getResult()")));
+
+            ExecutableTypeData delegateType = node.getGenericExecutableType(type);
             boolean found = false;
             for (NodeExecutionData otherExecution : node.getChildExecutions()) {
                 if (found) {
                     LocalVariable childEvaluatedValue = slowPathValues.createValue(otherExecution, genericType);
                     LocalVariable genericShortCircuit = resolveShortCircuit(null, otherExecution, slowPathValues);
-                    builder.tree(createAssignExecuteChild(otherExecution, genericType, childEvaluatedValue, genericShortCircuit, slowPathValues));
+                    builder.tree(createAssignExecuteChild(builder, otherExecution, delegateType, childEvaluatedValue, genericShortCircuit, slowPathValues));
                     slowPathValues.setValue(otherExecution, childEvaluatedValue);
                 } else {
                     // skip forward already evaluated
                     found = execution == otherExecution;
                 }
             }
-            builder.tree(createCallNext(returnType, slowPathValues));
+            builder.tree(createCallNext(builder, type, delegateType, slowPathValues));
             builder.end();
         }
 
@@ -2223,7 +2335,7 @@
         }
 
         LocalVariable genericValue = target.makeGeneric(context).nextName();
-        builder.tree(createAssignExecuteChild(execution, genericValue.getTypeMirror(), genericValue, null, currentValues));
+        builder.tree(createAssignExecuteChild(builder, execution, node.getGenericExecutableType(null), genericValue, null, currentValues));
         if (executableTypes.size() == sourceTypes.size()) {
             builder.startThrow().startNew(getType(UnexpectedResultException.class)).tree(genericValue.createReference()).end().end();
         } else {
@@ -2238,7 +2350,7 @@
         return builder.build();
     }
 
-    private CodeTree createFastPathTryCatchRewriteException(SpecializationData specialization, TypeMirror forType, LocalContext currentValues, CodeTree execution) {
+    private CodeTree createFastPathTryCatchRewriteException(SpecializationData specialization, ExecutableTypeData forType, LocalContext currentValues, CodeTree execution) {
         if (specialization.getExceptions().isEmpty()) {
             return execution;
         }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemNodeFactory.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemNodeFactory.java	Tue Apr 14 15:12:48 2015 +0200
@@ -50,12 +50,7 @@
     }
 
     public static TypeMirror nodeType(TypeSystemData typeSystem) {
-        if (typeSystem.isDefault()) {
-            return typeSystem.getContext().getType(SpecializationNode.class);
-        } else {
-            TypeMirror parentType = TypeSystemCodeGenerator.createTypeSystemGen(typeSystem);
-            return new GeneratedTypeMirror(getQualifiedName(parentType), typeName(typeSystem));
-        }
+        return typeSystem.getContext().getType(SpecializationNode.class);
     }
 
     public static String typeName(TypeSystemData typeSystem) {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/ElementUtils.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/ElementUtils.java	Tue Apr 14 15:12:48 2015 +0200
@@ -120,6 +120,9 @@
     }
 
     public static TypeMirror boxType(ProcessorContext context, TypeMirror primitiveType) {
+        if (primitiveType == null) {
+            return null;
+        }
         TypeMirror boxedType = primitiveType;
         if (boxedType.getKind().isPrimitive()) {
             boxedType = context.getEnvironment().getTypeUtils().boxedClass((PrimitiveType) boxedType).asType();
@@ -217,6 +220,10 @@
         }
     }
 
+    public static boolean isSubtypeBoxed(ProcessorContext context, TypeMirror from, TypeMirror to) {
+        return isSubtype(boxType(context, from), boxType(context, to));
+    }
+
     public static boolean isSubtype(TypeMirror type1, TypeMirror type2) {
         if (type1 instanceof CodeTypeMirror || type2 instanceof CodeTypeMirror) {
             throw new UnsupportedOperationException();
@@ -1178,7 +1185,7 @@
 
     public static List<TypeMirror> uniqueSortedTypes(Collection<TypeMirror> types) {
         if (types.isEmpty()) {
-            return Collections.emptyList();
+            return new ArrayList<>(0);
         } else if (types.size() <= 1) {
             if (types instanceof List) {
                 return (List<TypeMirror>) types;
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTreeBuilder.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTreeBuilder.java	Tue Apr 14 15:12:48 2015 +0200
@@ -316,6 +316,13 @@
         }
     }
 
+    public CodeTreeBuilder trees(CodeTree... trees) {
+        for (CodeTree tree : trees) {
+            tree(tree);
+        }
+        return this;
+    }
+
     public CodeTreeBuilder string(String chunk1, String chunk2, String chunk3, String chunk4, String... chunks) {
         push(GROUP).string(chunk1).string(chunk2).string(chunk3).string(chunk4);
         for (int i = 0; i < chunks.length; i++) {
@@ -861,4 +868,15 @@
         }
     }
 
+    public CodeTreeBuilder returnDefault() {
+        ExecutableElement method = findMethod();
+        if (ElementUtils.isVoid(method.getReturnType())) {
+            returnStatement();
+        } else {
+            startReturn().defaultValue(method.getReturnType()).end();
+        }
+        return this;
+
+    }
+
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ExecutableTypeData.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ExecutableTypeData.java	Tue Apr 14 15:12:48 2015 +0200
@@ -24,6 +24,8 @@
 
 import java.util.*;
 
+import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
+
 import javax.lang.model.element.*;
 import javax.lang.model.type.*;
 
@@ -34,11 +36,25 @@
 public class ExecutableTypeData extends MessageContainer implements Comparable<ExecutableTypeData> {
 
     private final ExecutableElement method;
+    private final TypeMirror returnType;
     private final TypeMirror frameParameter;
     private final List<TypeMirror> evaluatedParameters;
+    private ExecutableTypeData delegatedTo;
+    private final List<ExecutableTypeData> delegatedFrom = new ArrayList<>();
+
+    private String uniqueName;
+
+    public ExecutableTypeData(TypeMirror returnType, String uniqueName, TypeMirror frameParameter, List<TypeMirror> evaluatedParameters) {
+        this.returnType = returnType;
+        this.frameParameter = frameParameter;
+        this.evaluatedParameters = evaluatedParameters;
+        this.uniqueName = uniqueName;
+        this.method = null;
+    }
 
     public ExecutableTypeData(ExecutableElement method, int signatureSize, List<TypeMirror> frameTypes) {
         this.method = method;
+        this.returnType = method.getReturnType();
         TypeMirror foundFrameParameter = null;
         List<? extends VariableElement> parameters = method.getParameters();
 
@@ -70,14 +86,32 @@
             evaluatedParameters.add(parameter);
         }
         this.frameParameter = foundFrameParameter;
+        this.uniqueName = "execute" + (ElementUtils.isObject(getReturnType()) ? "" : ElementUtils.getTypeId(getReturnType()));
+    }
+
+    public void addDelegatedFrom(ExecutableTypeData child) {
+        this.delegatedFrom.add(child);
+        child.delegatedTo = this;
+    }
+
+    public List<ExecutableTypeData> getDelegatedFrom() {
+        return delegatedFrom;
+    }
+
+    public ExecutableTypeData getDelegatedTo() {
+        return delegatedTo;
     }
 
     public ExecutableElement getMethod() {
         return method;
     }
 
-    public String getName() {
-        return method.getSimpleName().toString();
+    public String getUniqueName() {
+        return uniqueName;
+    }
+
+    public void setUniqueName(String name) {
+        this.uniqueName = name;
     }
 
     @Override
@@ -106,27 +140,198 @@
     }
 
     public TypeMirror getReturnType() {
-        return method.getReturnType();
+        return returnType;
     }
 
     public boolean hasUnexpectedValue(ProcessorContext context) {
-        return ElementUtils.canThrowType(method.getThrownTypes(), context.getType(UnexpectedResultException.class));
+        return method == null ? false : ElementUtils.canThrowType(method.getThrownTypes(), context.getType(UnexpectedResultException.class));
     }
 
     public boolean isFinal() {
-        return method.getModifiers().contains(Modifier.FINAL);
+        return method == null ? false : method.getModifiers().contains(Modifier.FINAL);
     }
 
     public boolean isAbstract() {
-        return method.getModifiers().contains(Modifier.ABSTRACT);
+        return method == null ? false : method.getModifiers().contains(Modifier.ABSTRACT);
     }
 
     public int getEvaluatedCount() {
         return evaluatedParameters.size();
     }
 
-    public int compareTo(ExecutableTypeData o) {
-        return ElementUtils.compareMethod(method, o.getMethod());
+    public boolean canDelegateTo(NodeData node, ExecutableTypeData to) {
+        ExecutableTypeData from = this;
+        if (to.getEvaluatedCount() < from.getEvaluatedCount()) {
+            return false;
+        }
+
+        ProcessorContext context = node.getContext();
+
+        // we cannot delegate from generic to unexpected
+        if (!from.hasUnexpectedValue(context) && to.hasUnexpectedValue(context)) {
+            return false;
+        }
+
+        // we can skip the return type check for void. everything is assignable to void.
+        if (!isVoid(from.getReturnType())) {
+            if (!isSubtypeBoxed(context, from.getReturnType(), to.getReturnType()) && !isSubtypeBoxed(context, to.getReturnType(), from.getReturnType())) {
+                return false;
+            }
+        }
+        if (from.getFrameParameter() != to.getFrameParameter() && from.getFrameParameter() != null && to.getFrameParameter() != null &&
+                        !isSubtypeBoxed(context, from.getFrameParameter(), to.getFrameParameter())) {
+            return false;
+        }
+
+        for (int i = 0; i < from.getEvaluatedCount(); i++) {
+            if (!isSubtypeBoxed(context, from.getEvaluatedParameters().get(i), to.getEvaluatedParameters().get(i))) {
+                return false;
+            }
+        }
+
+        for (int i = from.getEvaluatedCount(); i < to.getEvaluatedCount(); i++) {
+            TypeMirror delegateToParameter = to.getEvaluatedParameters().get(i);
+            if (i < node.getChildExecutions().size()) {
+                List<TypeMirror> genericTypes = node.getGenericTypes(node.getChildExecutions().get(i));
+
+                boolean typeFound = false;
+                for (TypeMirror generic : genericTypes) {
+                    if (isSubtypeBoxed(context, generic, delegateToParameter)) {
+                        typeFound = true;
+                    }
+                }
+                if (!typeFound) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
     }
 
+    public int compareTo(ExecutableTypeData o2) {
+        ExecutableTypeData o1 = this;
+        ProcessorContext context = ProcessorContext.getInstance();
+
+        int result = Integer.compare(o2.getEvaluatedCount(), o1.getEvaluatedCount());
+        if (result != 0) {
+            return result;
+        }
+
+        result = Boolean.compare(o1.hasUnexpectedValue(context), o2.hasUnexpectedValue(context));
+        if (result != 0) {
+            return result;
+        }
+
+        result = compareType(context, o1.getReturnType(), o2.getReturnType());
+        if (result != 0) {
+            return result;
+        }
+        result = compareType(context, o1.getFrameParameter(), o2.getFrameParameter());
+        if (result != 0) {
+            return result;
+        }
+
+        for (int i = 0; i < o1.getEvaluatedCount(); i++) {
+            result = compareType(context, o1.getEvaluatedParameters().get(i), o2.getEvaluatedParameters().get(i));
+            if (result != 0) {
+                return result;
+            }
+        }
+
+        result = o1.getUniqueName().compareTo(o2.getUniqueName());
+        if (result != 0) {
+            return result;
+        }
+
+        if (o1.getMethod() != null && o2.getMethod() != null) {
+            result = ElementUtils.compareMethod(o1.getMethod(), o2.getMethod());
+            if (result != 0) {
+                return result;
+            }
+        }
+        return 0;
+    }
+
+    public static int compareType(ProcessorContext context, TypeMirror signature1, TypeMirror signature2) {
+        if (signature1 == null) {
+            if (signature2 == null) {
+                return 0;
+            }
+            return -1;
+        } else if (signature2 == null) {
+            return 1;
+        }
+        if (ElementUtils.typeEquals(signature1, signature2)) {
+            return 0;
+        }
+        if (isVoid(signature1)) {
+            if (isVoid(signature2)) {
+                return 0;
+            }
+            return 1;
+        } else if (isVoid(signature2)) {
+            return -1;
+        }
+
+        TypeMirror boxedType1 = ElementUtils.boxType(context, signature1);
+        TypeMirror boxedType2 = ElementUtils.boxType(context, signature2);
+
+        if (ElementUtils.isSubtype(boxedType1, boxedType2)) {
+            if (ElementUtils.isSubtype(boxedType2, boxedType1)) {
+                return 0;
+            }
+            return 1;
+        } else if (ElementUtils.isSubtype(boxedType2, boxedType1)) {
+            return -1;
+        } else {
+            return ElementUtils.getSimpleName(signature1).compareTo(ElementUtils.getSimpleName(signature2));
+        }
+    }
+
+    @Override
+    public String toString() {
+        return method != null ? ElementUtils.createReferenceName(method) : getUniqueName() + evaluatedParameters.toString();
+    }
+
+    public boolean sameParameters(ExecutableTypeData other) {
+        if (!typeEquals(other.getFrameParameter(), getFrameParameter())) {
+            return false;
+        }
+
+        if (getEvaluatedCount() != other.getEvaluatedCount()) {
+            return false;
+        }
+
+        for (int i = 0; i < getEvaluatedCount(); i++) {
+            if (!typeEquals(getEvaluatedParameters().get(i), other.getEvaluatedParameters().get(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public boolean sameSignature(ExecutableTypeData other) {
+        if (!typeEquals(other.getReturnType(), getReturnType())) {
+            return false;
+        }
+
+        if (other.getFrameParameter() != null) {
+            if (!typeEquals(getFrameParameter(), other.getFrameParameter())) {
+                return false;
+            }
+        }
+
+        if (getEvaluatedCount() != other.getEvaluatedCount()) {
+            return false;
+        }
+
+        for (int i = 0; i < getEvaluatedCount(); i++) {
+            if (!typeEquals(getEvaluatedParameters().get(i), other.getEvaluatedParameters().get(i))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/MessageContainer.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/MessageContainer.java	Tue Apr 14 15:12:48 2015 +0200
@@ -106,7 +106,7 @@
         visitedSinks.add(this);
 
         List<Message> foundMessages = new ArrayList<>();
-        if (ElementUtils.typeEquals(getMessageElement().asType(), e.asType())) {
+        if (getMessageElement() != null && ElementUtils.typeEquals(getMessageElement().asType(), e.asType())) {
             foundMessages.addAll(getMessages());
         }
         for (MessageContainer sink : findChildContainers()) {
@@ -119,14 +119,16 @@
         TypeElement expectError = context.getTruffleTypes().getExpectError();
         if (expectError != null) {
             Element element = getMessageElement();
-            AnnotationMirror mirror = ElementUtils.findAnnotationMirror(element.getAnnotationMirrors(), expectError.asType());
-            if (mirror != null) {
-                List<String> values = ElementUtils.getAnnotationValueList(String.class, mirror, "value");
-                if (values == null) {
-                    values = Collections.emptyList();
-                }
-                if (values.size() != msgs.size()) {
-                    log.message(Kind.ERROR, element, mirror, ElementUtils.getAnnotationValue(mirror, "value"), String.format("Error count expected %s but was %s.", values.size(), msgs.size()));
+            if (element != null) {
+                AnnotationMirror mirror = ElementUtils.findAnnotationMirror(element.getAnnotationMirrors(), expectError.asType());
+                if (mirror != null) {
+                    List<String> values = ElementUtils.getAnnotationValueList(String.class, mirror, "value");
+                    if (values == null) {
+                        values = Collections.emptyList();
+                    }
+                    if (values.size() != msgs.size()) {
+                        log.message(Kind.ERROR, element, mirror, ElementUtils.getAnnotationValue(mirror, "value"), String.format("Error count expected %s but was %s.", values.size(), msgs.size()));
+                    }
                 }
             }
         }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeData.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeData.java	Tue Apr 14 15:12:48 2015 +0200
@@ -48,7 +48,7 @@
     private final List<SpecializationData> specializations = new ArrayList<>();
     private final List<ShortCircuitData> shortCircuits = new ArrayList<>();
     private final List<CreateCastData> casts = new ArrayList<>();
-    private Map<Integer, List<ExecutableTypeData>> executableTypes;
+    private final List<ExecutableTypeData> executableTypes = new ArrayList<>();
 
     private final NodeExecutionData thisExecution;
     private final boolean generateFactory;
@@ -135,10 +135,7 @@
     }
 
     public int getSignatureSize() {
-        if (getSpecializations() != null && !getSpecializations().isEmpty()) {
-            return getSpecializations().get(0).getSignatureSize();
-        }
-        return 0;
+        return getChildExecutions().size();
     }
 
     public boolean isFrameUsedByAnyGuard() {
@@ -292,9 +289,10 @@
         }
 
         for (ExecutableTypeData execType : getExecutableTypes()) {
-            methods.add(execType.getMethod());
+            if (execType.getMethod() != null) {
+                methods.add(execType.getMethod());
+            }
         }
-
         for (ShortCircuitData shortcircuit : getShortCircuits()) {
             methods.add(shortcircuit.getMethod());
         }
@@ -329,21 +327,16 @@
     }
 
     public List<ExecutableTypeData> getExecutableTypes(int evaluatedCount) {
-        if (executableTypes == null) {
-            return Collections.emptyList();
-        }
         if (evaluatedCount == -1) {
-            List<ExecutableTypeData> typeData = new ArrayList<>();
-            for (int currentEvaluationCount : executableTypes.keySet()) {
-                typeData.addAll(executableTypes.get(currentEvaluationCount));
+            return executableTypes;
+        } else {
+            List<ExecutableTypeData> filteredTypes = new ArrayList<>();
+            for (ExecutableTypeData type : executableTypes) {
+                if (type.getEvaluatedCount() == evaluatedCount) {
+                    filteredTypes.add(type);
+                }
             }
-            return typeData;
-        } else {
-            List<ExecutableTypeData> types = executableTypes.get(evaluatedCount);
-            if (types == null) {
-                return Collections.emptyList();
-            }
-            return types;
+            return filteredTypes;
         }
     }
 
@@ -496,6 +489,25 @@
         return specializations;
     }
 
+    public ExecutableTypeData getGenericExecutableType(ExecutableTypeData typeHint) {
+        ExecutableTypeData polymorphicDelegate = null;
+        if (typeHint != null) {
+            polymorphicDelegate = typeHint;
+            while (polymorphicDelegate.getDelegatedTo() != null && polymorphicDelegate.getEvaluatedCount() != getSignatureSize()) {
+                polymorphicDelegate = polymorphicDelegate.getDelegatedTo();
+            }
+        }
+        if (polymorphicDelegate == null) {
+            for (ExecutableTypeData type : getExecutableTypes()) {
+                if (type.getDelegatedTo() == null && type.getEvaluatedCount() == getSignatureSize()) {
+                    polymorphicDelegate = type;
+                    break;
+                }
+            }
+        }
+        return polymorphicDelegate;
+    }
+
     public List<ExecutableTypeData> getExecutableTypes() {
         return getExecutableTypes(-1);
     }
@@ -512,10 +524,6 @@
         return minimalEvaluatedParameters;
     }
 
-    public void setExecutableTypes(Map<Integer, List<ExecutableTypeData>> executableTypes) {
-        this.executableTypes = executableTypes;
-    }
-
     @Override
     public String toString() {
         return getClass().getSimpleName() + "[" + getNodeId() + "]";
@@ -536,7 +544,7 @@
         return getNodeId().compareTo(o.getNodeId());
     }
 
-    public List<TypeMirror> getPossibleTypes(NodeExecutionData execution) {
+    public List<TypeMirror> getGenericTypes(NodeExecutionData execution) {
         List<TypeMirror> types = new ArrayList<>();
 
         // add types possible through return types and evaluated parameters in execute methods
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ParameterSpec.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ParameterSpec.java	Tue Apr 14 15:12:48 2015 +0200
@@ -27,6 +27,7 @@
 import javax.lang.model.element.*;
 import javax.lang.model.type.*;
 
+import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.java.*;
 import com.oracle.truffle.dsl.processor.model.MethodSpec.TypeDef;
 
@@ -127,7 +128,7 @@
             }
             if (allowSubclasses) {
                 for (TypeMirror type : allowedTypes) {
-                    if (ElementUtils.isAssignable(variable.asType(), type)) {
+                    if (ElementUtils.isSubtypeBoxed(ProcessorContext.getInstance(), variable.asType(), type)) {
                         return true;
                     }
                 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java	Tue Apr 14 15:12:48 2015 +0200
@@ -41,7 +41,7 @@
     }
 
     private final NodeData node;
-    private final SpecializationKind kind;
+    private SpecializationKind kind;
     private final List<SpecializationThrowsData> exceptions;
     private List<GuardExpression> guards = Collections.emptyList();
     private List<CacheExpression> caches = Collections.emptyList();
@@ -92,6 +92,10 @@
         return false;
     }
 
+    public void setKind(SpecializationKind kind) {
+        this.kind = kind;
+    }
+
     public boolean isDynamicParameterBound(DSLExpression expression) {
         Set<VariableElement> boundVariables = expression.findBoundVariableElements();
         for (Parameter parameter : getDynamicParameters()) {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TemplateMethod.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TemplateMethod.java	Tue Apr 14 15:12:48 2015 +0200
@@ -73,6 +73,18 @@
         return findParameter(FRAME_NAME);
     }
 
+    public void removeParameter(Parameter p) {
+        this.parameters.remove(p);
+        this.parameterCache.remove(p.getLocalName());
+        p.setMethod(this);
+    }
+
+    public void addParameter(int index, Parameter p) {
+        this.parameters.add(index, p);
+        this.parameterCache.put(p.getLocalName(), p);
+        p.setMethod(this);
+    }
+
     public String createReferenceName() {
         if (getMethod() == null) {
             return "-";
@@ -132,12 +144,13 @@
     public void replaceParameter(String localName, Parameter newParameter) {
         if (returnType.getLocalName().equals(localName)) {
             returnType = newParameter;
-            returnType.setMethod(this);
         } else {
             Parameter local = findParameter(localName);
             int index = parameters.indexOf(local);
             parameters.set(index, newParameter);
         }
+        parameterCache.put(newParameter.getLocalName(), newParameter);
+        newParameter.setMethod(this);
     }
 
     public Iterable<Parameter> getSignatureParameters() {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/GenericParser.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/GenericParser.java	Tue Apr 14 15:12:48 2015 +0200
@@ -23,11 +23,14 @@
 package com.oracle.truffle.dsl.processor.parser;
 
 import java.lang.annotation.*;
+import java.util.*;
 
 import javax.lang.model.element.*;
+import javax.lang.model.type.*;
 
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.dsl.processor.*;
+import com.oracle.truffle.dsl.processor.java.*;
 import com.oracle.truffle.dsl.processor.model.*;
 import com.oracle.truffle.dsl.processor.model.SpecializationData.SpecializationKind;
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/MethodSpecParser.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/MethodSpecParser.java	Tue Apr 14 15:12:48 2015 +0200
@@ -81,7 +81,7 @@
         ParameterSpec returnTypeSpec = methodSpecification.getReturnType();
         Parameter returnTypeMirror = matchParameter(returnTypeSpec, new CodeVariableElement(returnType, "returnType"), -1, -1);
         if (returnTypeMirror == null) {
-            if (emitErrors) {
+            if (isEmitErrors() && method != null) {
                 TemplateMethod invalidMethod = new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, Collections.<Parameter> emptyList());
                 String expectedReturnType = returnTypeSpec.toSignatureString(true);
                 String actualReturnType = ElementUtils.getSimpleName(returnType);
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeMethodParser.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeMethodParser.java	Tue Apr 14 15:12:48 2015 +0200
@@ -48,7 +48,7 @@
     }
 
     protected Collection<TypeMirror> getPossibleParameterTypes(NodeExecutionData execution) {
-        return getNode().getPossibleTypes(execution);
+        return getNode().getGenericTypes(execution);
     }
 
     protected ParameterSpec createReturnParameterSpec() {
@@ -58,7 +58,12 @@
     }
 
     protected Collection<TypeMirror> getPossibleReturnTypes() {
-        return getNode().getPossibleTypes(getNode().getThisExecution());
+        List<TypeMirror> possibleTypes = getNode().getGenericTypes(getNode().getThisExecution());
+        if (possibleTypes.size() > 1) {
+            return Arrays.asList(ElementUtils.getCommonSuperType(getContext(), possibleTypes.toArray(new TypeMirror[0])));
+        } else {
+            return possibleTypes;
+        }
     }
 
     @Override
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Tue Apr 14 15:12:48 2015 +0200
@@ -33,6 +33,7 @@
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.dsl.internal.*;
+import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.expression.*;
@@ -43,7 +44,6 @@
 import com.oracle.truffle.dsl.processor.model.*;
 import com.oracle.truffle.dsl.processor.model.NodeChildData.Cardinality;
 import com.oracle.truffle.dsl.processor.model.SpecializationData.SpecializationKind;
-import com.oracle.truffle.dsl.processor.model.TemplateMethod.TypeSignature;
 
 @DSLOptions
 public class NodeParser extends AbstractParser<NodeData> {
@@ -104,8 +104,9 @@
         } catch (CompileErrorException e) {
             throw e;
         } catch (Throwable e) {
-            e.addSuppressed(new RuntimeException(String.format("Parsing of Node %s failed.", ElementUtils.getQualifiedName(rootType)), e));
-            throw e;
+            RuntimeException e2 = new RuntimeException(String.format("Parsing of Node %s failed.", ElementUtils.getQualifiedName(rootType)));
+            e2.addSuppressed(e);
+            throw e2;
         }
         if (node == null && !enclosedNodes.isEmpty()) {
             node = new NodeData(context, rootType);
@@ -147,17 +148,16 @@
         node.getFields().addAll(parseFields(lookupTypes, members));
         node.getChildren().addAll(parseChildren(lookupTypes, members));
         node.getChildExecutions().addAll(parseExecutions(node.getFields(), node.getChildren(), members));
-        node.setExecutableTypes(groupExecutableTypes(parseExecutableTypeData(members, node.getChildExecutions().size(), context.getFrameTypes())));
+        node.getExecutableTypes().addAll(parseExecutableTypeData(members, node.getChildExecutions().size(), context.getFrameTypes(), false));
 
         initializeExecutableTypes(node);
         initializeImportGuards(node, lookupTypes, members);
+        initializeChildren(node);
 
         if (node.hasErrors()) {
             return node; // error sync point
         }
 
-        initializeChildren(node);
-
         if (node.hasErrors()) {
             return node; // error sync point
         }
@@ -170,9 +170,10 @@
         if (node.hasErrors()) {
             return node;  // error sync point
         }
+        initializeSpecializations(members, node);
+        initializeExecutableTypeHierarchy(node);
 
         verifySpecializationSameLength(node);
-        initializeSpecializations(members, node);
         initializeShortCircuits(node); // requires specializations and polymorphic specializations
 
         verifyVisibilities(node);
@@ -183,6 +184,67 @@
         return node;
     }
 
+    private static void initializeExecutableTypeHierarchy(NodeData node) {
+        SpecializationData polymorphic = node.getPolymorphicSpecialization();
+        if (polymorphic != null) {
+            boolean polymorphicSignatureFound = false;
+            TypeMirror frame = polymorphic.getFrame() != null ? polymorphic.getFrame().getType() : null;
+            ExecutableTypeData polymorphicType = new ExecutableTypeData(polymorphic.getReturnType().getType(), "execute", frame, TemplateMethod.getSignatureTypes(polymorphic));
+            for (ExecutableTypeData type : node.getExecutableTypes()) {
+                if (polymorphicType.sameSignature(type)) {
+                    polymorphicSignatureFound = true;
+                    break;
+                }
+            }
+
+            if (!polymorphicSignatureFound) {
+                node.getExecutableTypes().add(polymorphicType);
+            }
+        }
+
+        List<ExecutableTypeData> rootTypes = buildExecutableHierarchy(node);
+        List<ExecutableTypeData> additionalAbstractRootTypes = new ArrayList<>();
+        for (int i = 1; i < rootTypes.size(); i++) {
+            ExecutableTypeData rootType = rootTypes.get(i);
+            if (rootType.isAbstract()) {
+                // cannot implemement root
+                additionalAbstractRootTypes.add(rootType);
+            } else {
+                node.getExecutableTypes().remove(rootType);
+            }
+        }
+        if (!additionalAbstractRootTypes.isEmpty()) {
+            node.addError("Incompatible abstract execute methods found %s.", rootTypes);
+        }
+
+    }
+
+    private static List<ExecutableTypeData> buildExecutableHierarchy(NodeData node) {
+        List<ExecutableTypeData> executes = node.getExecutableTypes();
+        if (executes.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<ExecutableTypeData> hierarchyExecutes = new ArrayList<>(executes);
+        Collections.sort(hierarchyExecutes);
+        ExecutableTypeData parent = hierarchyExecutes.get(0);
+        ListIterator<ExecutableTypeData> executesIterator = hierarchyExecutes.listIterator(1);
+        buildExecutableHierarchy(node, parent, executesIterator);
+        return hierarchyExecutes;
+    }
+
+    private static void buildExecutableHierarchy(NodeData node, ExecutableTypeData parent, ListIterator<ExecutableTypeData> executesIterator) {
+        while (executesIterator.hasNext()) {
+            ExecutableTypeData other = executesIterator.next();
+            if (other.canDelegateTo(node, parent)) {
+                parent.addDelegatedFrom(other);
+                executesIterator.remove();
+            }
+        }
+        for (int i = 1; i < parent.getDelegatedFrom().size(); i++) {
+            buildExecutableHierarchy(node, parent.getDelegatedFrom().get(i - 1), parent.getDelegatedFrom().listIterator(i));
+        }
+    }
+
     private List<Element> loadMembers(TypeElement templateType) {
         List<Element> members = new ArrayList<>(CompilerFactory.getCompiler(templateType).getAllMembersInDeclarationOrder(context.getEnvironment(), templateType));
 
@@ -512,13 +574,17 @@
         return executions;
     }
 
-    private List<ExecutableTypeData> parseExecutableTypeData(List<? extends Element> elements, int signatureSize, List<TypeMirror> frameTypes) {
+    private List<ExecutableTypeData> parseExecutableTypeData(List<? extends Element> elements, int signatureSize, List<TypeMirror> frameTypes, boolean includeFinals) {
         List<ExecutableTypeData> typeData = new ArrayList<>();
         for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
             Set<Modifier> modifiers = method.getModifiers();
             if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.STATIC)) {
                 continue;
             }
+            if (!includeFinals && modifiers.contains(Modifier.FINAL)) {
+                continue;
+            }
+
             if (!method.getSimpleName().toString().startsWith("execute")) {
                 continue;
             }
@@ -545,6 +611,19 @@
         }
 
         Collections.sort(typeData);
+
+        List<String> names = new ArrayList<>();
+        for (ExecutableTypeData type : typeData) {
+            names.add(type.getUniqueName());
+        }
+        while (renameDuplicateIds(names)) {
+            // fix point
+        }
+
+        for (int i = 0; i < typeData.size(); i++) {
+            typeData.get(i).setUniqueName(names.get(i));
+        }
+
         return typeData;
     }
 
@@ -582,47 +661,16 @@
 
         node.setFrameType(frameType);
 
-        int totalGenericCount = 0;
-        int totalVoidCount = 0;
-        for (Integer evaluatedCount : evaluatedCounts) {
-            List<ExecutableTypeData> genericExecutes = node.findGenericExecutableTypes(context, evaluatedCount);
-            int genericCount = 0;
-            int voidCount = 0;
-            for (ExecutableTypeData executableTypeData : genericExecutes) {
-                if (!executableTypeData.getMethod().getModifiers().contains(Modifier.FINAL)) {
-                    if (ElementUtils.isVoid(executableTypeData.getReturnType())) {
-                        voidCount++;
-                    } else {
-                        genericCount++;
-                    }
-                }
+        boolean genericFound = false;
+        for (ExecutableTypeData type : node.getExecutableTypes()) {
+            if (!type.hasUnexpectedValue(context)) {
+                genericFound = true;
+                break;
             }
-            // multiple generic execute
-            if (evaluatedCount == 0) {
-                if (voidCount > 1) {
-                    List<String> methodSignatures = new ArrayList<>();
-                    for (ExecutableTypeData type : genericExecutes) {
-                        if (context.isType(type.getReturnType(), void.class)) {
-                            methodSignatures.add(ElementUtils.createReferenceName(type.getMethod()));
-                        }
-                    }
-                    node.addWarning("Multiple accessible and overridable generic execute methods found %s. Remove all but one or mark all but one as final.", methodSignatures);
-                } else if (genericCount > 1) {
-                    List<String> methodSignatures = new ArrayList<>();
-                    for (ExecutableTypeData type : genericExecutes) {
-                        if (!context.isType(type.getReturnType(), void.class)) {
-                            methodSignatures.add(ElementUtils.createReferenceName(type.getMethod()));
-                        }
-                    }
-                    node.addWarning("Multiple accessible and overridable generic execute methods found %s. Remove all but one or mark all but one as final.", methodSignatures);
-                }
-            }
-            totalGenericCount += genericCount;
-            totalVoidCount += voidCount;
         }
 
         // no generic executes
-        if (totalGenericCount + totalVoidCount == 0) {
+        if (!genericFound) {
             node.addError("No accessible and overridable generic execute method found. Generic execute methods usually have the "
                             + "signature 'public abstract {Type} execute(VirtualFrame)' and must not throw any checked exceptions.");
         }
@@ -658,25 +706,6 @@
 
     }
 
-    private static Map<Integer, List<ExecutableTypeData>> groupExecutableTypes(List<ExecutableTypeData> executableTypes) {
-        Map<Integer, List<ExecutableTypeData>> groupedTypes = new TreeMap<>();
-        for (ExecutableTypeData type : executableTypes) {
-            int evaluatedCount = type.getEvaluatedCount();
-
-            List<ExecutableTypeData> types = groupedTypes.get(evaluatedCount);
-            if (types == null) {
-                types = new ArrayList<>();
-                groupedTypes.put(evaluatedCount, types);
-            }
-            types.add(type);
-        }
-
-        for (List<ExecutableTypeData> types : groupedTypes.values()) {
-            Collections.sort(types);
-        }
-        return groupedTypes;
-    }
-
     private void initializeChildren(NodeData node) {
         initializeExecuteWith(node);
 
@@ -759,7 +788,7 @@
         if (parentNode.getFrameType() != null) {
             frameTypes = Arrays.asList(parentNode.getFrameType());
         }
-        node.setExecutableTypes(groupExecutableTypes(parseExecutableTypeData(members, child.getExecuteWith().size(), frameTypes)));
+        node.getExecutableTypes().addAll(parseExecutableTypeData(members, child.getExecuteWith().size(), frameTypes, true));
         node.setFrameType(parentNode.getFrameType());
         return node;
     }
@@ -1259,7 +1288,7 @@
         if (execution == null) {
             allowedTypes = spec.getAllowedTypes();
         } else {
-            allowedTypes = node.getPossibleTypes(execution);
+            allowedTypes = node.getGenericTypes(execution);
         }
         if (allowedTypes.size() == 1) {
             return allowedTypes.iterator().next();
@@ -1301,36 +1330,68 @@
 
         SpecializationData generic = node.getGenericSpecialization();
 
-        List<TypeMirror> polymorphicSignature = new ArrayList<>();
-        List<Parameter> updatePolymorphic = Arrays.asList();
-        for (Parameter genericParameter : updatePolymorphic) {
-            if (!genericParameter.getSpecification().isSignature()) {
-                continue;
-            }
+        List<VariableElement> types = new ArrayList<>();
 
-            Set<TypeMirror> usedTypes = new HashSet<>();
-            for (SpecializationData specialization : node.getSpecializations()) {
-                if (!specialization.isSpecialized()) {
-                    continue;
-                }
-                Parameter parameter = specialization.findParameter(genericParameter.getLocalName());
-                if (parameter == null) {
-                    throw new AssertionError("Parameter existed in generic specialization but not in specialized. param = " + genericParameter.getLocalName());
-                }
-                usedTypes.add(parameter.getType());
+        Set<TypeMirror> frameTypes = new HashSet<>();
+        for (SpecializationData specialization : node.getSpecializations()) {
+            if (specialization.getFrame() != null) {
+                frameTypes.add(specialization.getFrame().getType());
             }
-
-            TypeMirror polymorphicType;
-            if (usedTypes.size() == 1) {
-                polymorphicType = usedTypes.iterator().next();
+        }
+        if (!frameTypes.isEmpty()) {
+            TypeMirror frameType;
+            if (frameTypes.size() == 1) {
+                frameType = frameTypes.iterator().next();
             } else {
-                polymorphicType = context.getType(Object.class);
+                frameType = context.getType(Frame.class);
             }
-            polymorphicSignature.add(polymorphicType);
+            types.add(new CodeVariableElement(frameType, "frameValue"));
         }
 
-        SpecializationData polymorphic = new SpecializationData(node, generic, SpecializationKind.POLYMORPHIC);
-        polymorphic.updateSignature(new TypeSignature(polymorphicSignature));
+        TypeMirror returnType = null;
+        int index = 0;
+        for (Parameter genericParameter : generic.getReturnTypeAndParameters()) {
+            TypeMirror polymorphicType;
+            if (!genericParameter.getSpecification().isSignature()) {
+                polymorphicType = genericParameter.getType();
+            } else {
+                Set<TypeMirror> usedTypes = new HashSet<>();
+                for (SpecializationData specialization : node.getSpecializations()) {
+                    if (specialization.isUninitialized()) {
+                        continue;
+                    }
+                    Parameter parameter = specialization.findParameter(genericParameter.getLocalName());
+                    if (parameter == specialization.getReturnType() && specialization.isFallback() && specialization.getMethod() == null) {
+                        continue;
+                    }
+                    if (parameter == null) {
+                        throw new AssertionError("Parameter existed in generic specialization but not in specialized. param = " + genericParameter.getLocalName());
+                    }
+                    usedTypes.add(parameter.getType());
+                }
+
+                if (usedTypes.size() == 1) {
+                    polymorphicType = usedTypes.iterator().next();
+
+                    if (node.getTypeSystem().hasImplicitSourceTypes(polymorphicType)) {
+                        polymorphicType = context.getType(Object.class);
+                    }
+                } else {
+                    polymorphicType = context.getType(Object.class);
+                }
+            }
+            if (genericParameter == generic.getReturnType()) {
+                returnType = polymorphicType;
+            } else {
+                types.add(new CodeVariableElement(polymorphicType, "param" + index));
+            }
+            index++;
+        }
+
+        SpecializationMethodParser parser = new SpecializationMethodParser(context, node);
+
+        SpecializationData polymorphic = parser.create("Polymorphic", TemplateMethod.NO_NATURAL_ORDER, null, null, returnType, types);
+        polymorphic.setKind(SpecializationKind.POLYMORPHIC);
         node.getSpecializations().add(polymorphic);
     }
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/SpecializationMethodParser.java	Tue Apr 14 15:12:48 2015 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/SpecializationMethodParser.java	Tue Apr 14 15:12:48 2015 +0200
@@ -58,55 +58,59 @@
     }
 
     private SpecializationData parseSpecialization(TemplateMethod method) {
-        AnnotationValue rewriteValue = ElementUtils.getAnnotationValue(method.getMarkerAnnotation(), "rewriteOn");
-        List<TypeMirror> exceptionTypes = ElementUtils.getAnnotationValueList(TypeMirror.class, method.getMarkerAnnotation(), "rewriteOn");
         List<SpecializationThrowsData> exceptionData = new ArrayList<>();
-        List<TypeMirror> rewriteOnTypes = new ArrayList<>();
-        for (TypeMirror exceptionType : exceptionTypes) {
-            SpecializationThrowsData throwsData = new SpecializationThrowsData(method.getMarkerAnnotation(), rewriteValue, exceptionType);
-            if (!ElementUtils.canThrowType(method.getMethod().getThrownTypes(), exceptionType)) {
-                method.addError("A rewriteOn checked exception was specified but not thrown in the method's throws clause. The @%s method must specify a throws clause with the exception type '%s'.",
-                                Specialization.class.getSimpleName(), ElementUtils.getQualifiedName(exceptionType));
+        if (method.getMethod() != null) {
+            AnnotationValue rewriteValue = ElementUtils.getAnnotationValue(method.getMarkerAnnotation(), "rewriteOn");
+            List<TypeMirror> exceptionTypes = ElementUtils.getAnnotationValueList(TypeMirror.class, method.getMarkerAnnotation(), "rewriteOn");
+            List<TypeMirror> rewriteOnTypes = new ArrayList<>();
+            for (TypeMirror exceptionType : exceptionTypes) {
+                SpecializationThrowsData throwsData = new SpecializationThrowsData(method.getMarkerAnnotation(), rewriteValue, exceptionType);
+                if (!ElementUtils.canThrowType(method.getMethod().getThrownTypes(), exceptionType)) {
+                    method.addError("A rewriteOn checked exception was specified but not thrown in the method's throws clause. The @%s method must specify a throws clause with the exception type '%s'.",
+                                    Specialization.class.getSimpleName(), ElementUtils.getQualifiedName(exceptionType));
+                }
+                rewriteOnTypes.add(throwsData.getJavaClass());
+                exceptionData.add(throwsData);
             }
-            rewriteOnTypes.add(throwsData.getJavaClass());
-            exceptionData.add(throwsData);
-        }
-
-        for (TypeMirror typeMirror : method.getMethod().getThrownTypes()) {
-            if (!ElementUtils.canThrowType(rewriteOnTypes, typeMirror)) {
-                method.addError(rewriteValue,
-                                "A checked exception '%s' is thrown but is not specified using the rewriteOn property. Checked exceptions that are not used for rewriting are not handled by the DSL. Use RuntimeExceptions for this purpose instead.",
-                                ElementUtils.getQualifiedName(typeMirror));
-            }
-        }
 
-        Collections.sort(exceptionData, new Comparator<SpecializationThrowsData>() {
-
-            @Override
-            public int compare(SpecializationThrowsData o1, SpecializationThrowsData o2) {
-                return ElementUtils.compareByTypeHierarchy(o1.getJavaClass(), o2.getJavaClass());
-            }
-        });
-        SpecializationData specialization = new SpecializationData(getNode(), method, SpecializationKind.SPECIALIZED, exceptionData);
-
-        String insertBeforeName = ElementUtils.getAnnotationValue(String.class, method.getMarkerAnnotation(), "insertBefore");
-        if (!insertBeforeName.equals("")) {
-            specialization.setInsertBeforeName(insertBeforeName);
-        }
-
-        List<String> containsDefs = ElementUtils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "contains");
-        Set<String> containsNames = specialization.getContainsNames();
-        containsNames.clear();
-        if (containsDefs != null) {
-            for (String include : containsDefs) {
-                if (!containsNames.contains(include)) {
-                    specialization.getContainsNames().add(include);
-                } else {
-                    AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains");
-                    specialization.addError(value, "Duplicate contains declaration '%s'.", include);
+            for (TypeMirror typeMirror : method.getMethod().getThrownTypes()) {
+                if (!ElementUtils.canThrowType(rewriteOnTypes, typeMirror)) {
+                    method.addError(rewriteValue, "A checked exception '%s' is thrown but is not specified using the rewriteOn property. "
+                                    + "Checked exceptions that are not used for rewriting are not handled by the DSL. Use RuntimeExceptions for this purpose instead.",
+                                    ElementUtils.getQualifiedName(typeMirror));
                 }
             }
 
+            Collections.sort(exceptionData, new Comparator<SpecializationThrowsData>() {
+
+                @Override
+                public int compare(SpecializationThrowsData o1, SpecializationThrowsData o2) {
+                    return ElementUtils.compareByTypeHierarchy(o1.getJavaClass(), o2.getJavaClass());
+                }
+            });
+        }
+        SpecializationData specialization = new SpecializationData(getNode(), method, SpecializationKind.SPECIALIZED, exceptionData);
+
+        if (method.getMethod() != null) {
+            String insertBeforeName = ElementUtils.getAnnotationValue(String.class, method.getMarkerAnnotation(), "insertBefore");
+            if (!insertBeforeName.equals("")) {
+                specialization.setInsertBeforeName(insertBeforeName);
+            }
+
+            List<String> containsDefs = ElementUtils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "contains");
+            Set<String> containsNames = specialization.getContainsNames();
+            containsNames.clear();
+            if (containsDefs != null) {
+                for (String include : containsDefs) {
+                    if (!containsNames.contains(include)) {
+                        specialization.getContainsNames().add(include);
+                    } else {
+                        AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains");
+                        specialization.addError(value, "Duplicate contains declaration '%s'.", include);
+                    }
+                }
+
+            }
         }
 
         return specialization;