changeset 18761:a665483c3881

Truffle-DSL: new node layout implementation.
author Christian Humer <christian.humer@gmail.com>
date Mon, 29 Dec 2014 23:38:54 +0100
parents 6fa3999631d8
children 0ef23ff7d5a1
files graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ContainsTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteEvaluatedTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest2.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationGroupingTest.java graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestSerialization.java graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/GeneratedBy.java graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/internal/DSLOptions.java graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/internal/RewriteEvent.java graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/internal/SpecializationNode.java graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/internal/SpecializedNode.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ProcessorContext.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleProcessor.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/GeneratorUtils.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/ImplicitCastNodeFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeBaseFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeCodeGenerator.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeFactoryFactory.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/PolymorphicNodeFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/SpecializedNodeFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemCodeGenerator.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/CodeElement.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeElementScanner.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeExecutableElement.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTree.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/java/transform/AbstractCodeWriter.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/NodeData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ShortCircuitData.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/Template.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/model/TypeData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TypeSystemData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/ExecutableTypeMethodParser.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/ImplicitCastParser.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/SpecializationGroup.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/TypeSystemParser.java
diffstat 46 files changed, 4051 insertions(+), 224 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ContainsTest.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ContainsTest.java	Mon Dec 29 23:38:54 2014 +0100
@@ -54,8 +54,12 @@
                         new ExecutionListener() {
                             public void afterExecution(TestRootNode<? extends ValueNode> node, int index, Object value, Object expectedResult, Object actualResult, boolean last) {
                                 if (value instanceof String) {
-                                    // assert that the final specialization is always Object
-                                    Assert.assertEquals(Object.class, ((DSLNode) node.getNode()).getMetadata0().getSpecializedTypes()[0]);
+                                    if (node.getNode() instanceof DSLNode) {
+                                        // assert that the final specialization is always Object
+                                        Assert.assertEquals(Object.class, ((DSLNode) node.getNode()).getMetadata0().getSpecializedTypes()[0]);
+                                    } else {
+                                        Assert.assertTrue(((SpecializedNode) node.getNode()).getSpecializationNode().toString().startsWith("ObjectNode"));
+                                    }
                                 }
                             }
                         });
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteEvaluatedTest.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteEvaluatedTest.java	Mon Dec 29 23:38:54 2014 +0100
@@ -161,10 +161,10 @@
         Assert.assertEquals(42, root.getNode().execute1(null, 42));
     }
 
-    @Test(expected = AssertionError.class)
+    @Test(expected = Throwable.class)
     public void test1VarArgs2() {
         TestRootNode<TestEvaluatedVarArgs2> root = TestHelper.createRoot(TestEvaluatedVarArgs2Factory.getInstance());
-        Assert.assertEquals(-1, root.getNode().execute1(null));
+        root.getNode().execute1(null);
     }
 
     abstract static class TestEvaluatedVarArgs1 extends ChildrenNode {
@@ -183,16 +183,16 @@
         Assert.assertEquals(42, root.getNode().execute1(null, 21, 21));
     }
 
-    @Test(expected = AssertionError.class)
+    @Test(expected = Throwable.class)
     public void test2VarArgs2() {
         TestRootNode<TestEvaluatedVarArgs2> root = TestHelper.createRoot(TestEvaluatedVarArgs2Factory.getInstance());
-        Assert.assertEquals(-1, root.getNode().execute1(null, 42));
+        root.getNode().execute1(null, 42);
     }
 
-    @Test(expected = AssertionError.class)
+    @Test(expected = Throwable.class)
     public void test2VarArgs3() {
         TestRootNode<TestEvaluatedVarArgs2> root = TestHelper.createRoot(TestEvaluatedVarArgs2Factory.getInstance());
-        Assert.assertEquals(-1, root.getNode().execute1(null));
+        root.getNode().execute1(null);
     }
 
     abstract static class TestEvaluatedVarArgs2 extends ChildrenNode {
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java	Mon Dec 29 23:38:54 2014 +0100
@@ -28,28 +28,45 @@
 
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.dsl.test.LazyClassLoadingTestFactory.TestNodeFactory;
-import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.*;
 
 public class LazyClassLoadingTest {
     @Test
     public void test() {
-        String testClassName = getClass().getName();
-        String factoryClassName = testClassName + "Factory";
-        String nodeFactoryClassName = factoryClassName + "$TestNodeFactory";
-
-        Assert.assertFalse(isLoaded(factoryClassName + "$TestNode"));
-        Assert.assertFalse(isLoaded(nodeFactoryClassName));
+        String factoryName = TestNodeFactory.class.getName();
+        String nodeName = factoryName + "$" + TestNode.class.getSimpleName() + "Gen";
+        Assert.assertTrue(isLoaded(factoryName));
+        Assert.assertFalse(isLoaded(nodeName));
 
         NodeFactory<TestNode> factory = TestNodeFactory.getInstance();
+        Assert.assertTrue(isLoaded(factoryName));
+        Assert.assertTrue(isLoaded(nodeName));
 
-        Assert.assertTrue(isLoaded(nodeFactoryClassName));
-        Assert.assertFalse(isLoaded(nodeFactoryClassName + "$TestBaseNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$UninitializedNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$BaseNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$IntNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$BooleanNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$PolymorphicNode"));
+
+        TestRootNode<TestNode> root = TestHelper.createRoot(factory);
 
-        TestHelper.createRoot(factory);
+        Assert.assertTrue(isLoaded(nodeName + "$BaseNode"));
+        Assert.assertTrue(isLoaded(nodeName + "$UninitializedNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$IntNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$BooleanNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$PolymorphicNode"));
+
+        Assert.assertEquals(42, TestHelper.executeWith(root, 42));
 
-        Assert.assertTrue(isLoaded(nodeFactoryClassName + "$TestBaseNode"));
-        Assert.assertTrue(isLoaded(nodeFactoryClassName + "$TestUninitializedNode"));
-        Assert.assertFalse(isLoaded(nodeFactoryClassName + "$TestGenericNode"));
+        Assert.assertTrue(isLoaded(nodeName + "$IntNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$BooleanNode"));
+        Assert.assertFalse(isLoaded(nodeName + "$PolymorphicNode"));
+
+        Assert.assertEquals(true, TestHelper.executeWith(root, true));
+
+        Assert.assertTrue(isLoaded(nodeName + "$IntNode"));
+        Assert.assertTrue(isLoaded(nodeName + "$BooleanNode"));
+        Assert.assertTrue(isLoaded(nodeName + "$PolymorphicNode"));
     }
 
     private boolean isLoaded(String className) {
@@ -64,22 +81,18 @@
         }
     }
 
-    @SuppressWarnings("unused")
-    @NodeChildren({@NodeChild("left"), @NodeChild("right")})
+    @NodeChild("a")
     abstract static class TestNode extends ValueNode {
-        @Specialization(order = 1)
-        int add(int left, int right) {
-            return 42;
+
+        @Specialization
+        int s(int a) {
+            return a;
         }
 
-        @Specialization(order = 2)
-        int add(boolean left, boolean right) {
-            return 21;
+        @Specialization
+        boolean s(boolean a) {
+            return a;
         }
 
-        @Specialization(order = 4)
-        String add(boolean left, int right) {
-            return "(boolean,int)";
-        }
     }
 }
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest2.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest2.java	Mon Dec 29 23:38:54 2014 +0100
@@ -48,17 +48,17 @@
     @SuppressWarnings("unused")
     abstract static class Polymorphic1 extends BinaryNode {
 
-        @Specialization(order = 1)
+        @Specialization
         int add(int left, int right) {
             return 42;
         }
 
-        @Specialization(order = 2)
+        @Specialization
         int add(boolean left, boolean right) {
             return 21;
         }
 
-        @Specialization(order = 4)
+        @Specialization
         String add(boolean left, int right) {
             return "(boolean,int)";
         }
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java	Mon Dec 29 23:38:54 2014 +0100
@@ -31,6 +31,7 @@
 import org.junit.runner.*;
 
 import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.internal.*;
 import com.oracle.truffle.api.dsl.test.SourceSectionTestFactory.SourceSection0Factory;
 import com.oracle.truffle.api.dsl.test.SourceSectionTestFactory.SourceSection1Factory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ArgumentNode;
@@ -61,7 +62,7 @@
     private static void expectSourceSection(Node root, SourceSection section) {
         assertThat(root.getSourceSection(), is(sameInstance(section)));
         for (Node child : root.getChildren()) {
-            if (child instanceof ArgumentNode) {
+            if (child instanceof ArgumentNode || child instanceof SpecializationNode) {
                 continue;
             }
             if (child != null) {
--- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationGroupingTest.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationGroupingTest.java	Mon Dec 29 23:38:54 2014 +0100
@@ -45,10 +45,9 @@
     @Test
     public void testGrouping() {
         MockAssumption a1 = new MockAssumption(true);
-        MockAssumption a2 = new MockAssumption(false);
         MockAssumption a3 = new MockAssumption(true);
 
-        TestRootNode<TestGrouping> root = TestHelper.createRoot(TestGroupingFactory.getInstance(), a1, a2, a3);
+        TestRootNode<TestGrouping> root = TestHelper.createRoot(TestGroupingFactory.getInstance(), a1, a3);
 
         SimpleTypes.intCast = 0;
         SimpleTypes.intCheck = 0;
@@ -64,10 +63,9 @@
         Assert.assertEquals(4, TestGrouping.true2);
         Assert.assertEquals(5, TestGrouping.false2);
         Assert.assertEquals(5, TestGrouping.true3);
-        Assert.assertEquals(8, SimpleTypes.intCheck);
+        Assert.assertEquals(10, SimpleTypes.intCheck);
         Assert.assertEquals(8, SimpleTypes.intCast);
         Assert.assertEquals(4, a1.checked);
-        Assert.assertEquals(0, a2.checked);
         Assert.assertEquals(4, a3.checked);
 
         Assert.assertEquals(42, TestHelper.executeWith(root, 21, 21));
@@ -78,16 +76,15 @@
         Assert.assertEquals(6, TestGrouping.true3);
 
         Assert.assertEquals(5, a1.checked);
-        Assert.assertEquals(0, a2.checked);
         Assert.assertEquals(5, a3.checked);
-        Assert.assertEquals(8, SimpleTypes.intCheck);
+        Assert.assertEquals(10, SimpleTypes.intCheck);
         Assert.assertEquals(8, SimpleTypes.intCast);
 
     }
 
     @SuppressWarnings("unused")
     @NodeChildren({@NodeChild, @NodeChild})
-    @NodeAssumptions({"a1", "a2", "a3"})
+    @NodeAssumptions({"a1", "a3"})
     public abstract static class TestGrouping extends ValueNode {
 
         private static int true1;
--- /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/TestSerialization.java	Mon Dec 29 23:38:54 2014 +0100
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014, 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 static com.oracle.truffle.api.dsl.test.TestHelper.*;
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.test.TestSerializationFactory.SerializedNodeFactory;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
+import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.nodes.*;
+
+public class TestSerialization {
+
+    @Test
+    public void testUpdateRoot() {
+        /* Tests the unexpected polymorphic case. */
+        TestRootNode<SerializedNode> node = TestHelper.createRoot(SerializedNodeFactory.getInstance());
+        assertEquals(true, executeWith(node, true));
+        assertEquals(21, executeWith(node, 21));
+        assertEquals("s", executeWith(node, "s"));
+        assertEquals(3, node.getNode().invocations);
+        assertEquals(NodeCost.POLYMORPHIC, node.getNode().getCost());
+
+        @SuppressWarnings("unchecked")
+        TestRootNode<SerializedNode> copiedNode = (TestRootNode<SerializedNode>) node.deepCopy();
+        copiedNode.adoptChildren();
+        assertTrue(copiedNode != node);
+        assertEquals(true, executeWith(copiedNode, true));
+        assertEquals(21, executeWith(copiedNode, 21));
+        assertEquals("s", executeWith(copiedNode, "s"));
+        assertEquals(6, copiedNode.getNode().invocations);
+    }
+
+    @NodeChild
+    abstract static class SerializedNode extends ValueNode {
+
+        int invocations;
+
+        @Specialization
+        int add(int left) {
+            invocations++;
+            return left;
+        }
+
+        @Specialization
+        boolean add(boolean left) {
+            invocations++;
+            return left;
+        }
+
+        @Specialization
+        String add(String left) {
+            invocations++;
+            return left;
+        }
+
+    }
+
+}
--- a/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/GeneratedBy.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/GeneratedBy.java	Mon Dec 29 23:38:54 2014 +0100
@@ -27,7 +27,7 @@
 import java.lang.annotation.*;
 
 /**
- * Marks a type as being generated based on another class or method.
+ * Marks a type as being generated based on another class or method of a class.
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.TYPE})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/internal/DSLOptions.java	Mon Dec 29 23:38:54 2014 +0100
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2014, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.internal;
+
+import java.lang.annotation.*;
+
+/**
+ * Internal DSL options to tune the generated code. These are expert options and not intended to be
+ * changed used for guest language implementations.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface DSLOptions {
+
+    /** Enable the new DSL generation layout. */
+    boolean useNewLayout() default false;
+
+    /**
+     * Lazy class loading ensures that all generated specialization classes are loaded lazily.
+     * Disabling this feature will eagerly load all classes but will also reduce the generated code
+     * size.
+     */
+    boolean useLazyClassLoading() default true;
+
+    /**
+     * Sets the optimization strategy for implicit casts.
+     */
+    ImplicitCastOptimization implicitCastOptimization() default ImplicitCastOptimization.DUPLICATE_TAIL;
+
+    /** Not yet implemented. */
+    boolean useDisjunctiveMethodGuardOptimization() default true;
+
+    public enum ImplicitCastOptimization {
+
+        /** Perform no informed optimization for implicit casts. */
+        NONE,
+
+        /** Duplicate specializations for each used implicit cast combination */
+        DUPLICATE_TAIL,
+
+        /**
+         * Use the same specialization for multiple combinations of implicit casts and specialize
+         * them independently. Not yet fully implemented.
+         */
+        MERGE_CASTS;
+
+        public boolean isNone() {
+            return this == NONE;
+        }
+
+        public boolean isDuplicateTail() {
+            return this == DUPLICATE_TAIL;
+        }
+
+        public boolean isMergeCasts() {
+            return this == MERGE_CASTS;
+        }
+    }
+
+    public enum TypeBoxingOptimization {
+        /** Perform the optimization for all types. */
+        ALWAYS,
+        /** Perform the optimization just for primitive types. */
+        PRIMITIVE,
+        /** Perform the optimization for no types. */
+        NONE;
+    }
+
+    /**
+     * Defines the range of the generation of type specialized execute methods for return types and
+     * for specialized parameter types. A type specialized execute method is generated as soon as
+     * one declared type is either returned or used a specialized parameter.
+     */
+    TypeBoxingOptimization monomorphicTypeBoxingOptimization() default TypeBoxingOptimization.PRIMITIVE;
+
+    /**
+     * Defines the range of types for which type specialized execute methods should be used for
+     * polymorphic operations.
+     */
+    TypeBoxingOptimization polymorphicTypeBoxingElimination() default TypeBoxingOptimization.PRIMITIVE;
+
+    /**
+     * Defines the range of types for which type specialized execute methods for implicit cast
+     * optimizations are used. This option only has an effect if
+     * {@link ImplicitCastOptimization#DUPLICATE_TAIL} or
+     * {@link ImplicitCastOptimization#MERGE_CASTS} is set in {@link #implicitCastOptimization()}.
+     */
+    TypeBoxingOptimization implicitTypeBoxingOptimization() default TypeBoxingOptimization.PRIMITIVE;
+
+    /**
+     * Defines range of specialization return types in which the void boxing optimization is used.
+     * Void boxing generates an extra execute method with {@link Void} return type in order to avoid
+     * boxing and type checking of the return type in case the return type is not needed. For this
+     * to work the operation class needs to provide an overridable execute method returning
+     * {@link Void}.
+     */
+    TypeBoxingOptimization voidBoxingOptimization() default TypeBoxingOptimization.PRIMITIVE;
+
+    public enum FallbackOptimization {
+        /** Always generate an optimized fallback specialization. */
+        ALWAYS,
+
+        /**
+         * Only generate an optimized fallback specialization if a method annotated with @Fallback
+         * is used in the operation.
+         */
+        DECLARED,
+
+        /**
+         * Never generate an optimized fallback specialization. Please be aware that triggering a @Fallback
+         * case without optimization will also invalidate your compiled code.
+         */
+        NEVER;
+    }
+
+    /** Defines the optimization strategy that is used to optimize @Fallback annotated methods. */
+    FallbackOptimization optimizeFallback() default FallbackOptimization.DECLARED;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/internal/RewriteEvent.java	Mon Dec 29 23:38:54 2014 +0100
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2014, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.internal;
+
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Lazy rewrite event that implements {@link CharSequence} to be provided as message in
+ * {@link Node#replace(Node, CharSequence)}.
+ */
+abstract class RewriteEvent implements CharSequence {
+
+    private final Node source;
+    private final String reason;
+    private String message;
+
+    private RewriteEvent(Node source, String reason) {
+        this.source = source;
+        this.reason = reason;
+    }
+
+    public int length() {
+        return getMessage().length();
+    }
+
+    public char charAt(int index) {
+        return getMessage().charAt(index);
+    }
+
+    public CharSequence subSequence(int start, int end) {
+        return getMessage().subSequence(start, end);
+    }
+
+    @Override
+    public String toString() {
+        return getMessage();
+    }
+
+    private String getMessage() {
+        if (message == null) {
+            message = createMessage();
+        }
+        return message;
+    }
+
+    private String createMessage() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(source);
+        builder.append(" ");
+        builder.append(reason);
+        Object[] values = getValues();
+        if (values.length > 0) {
+            builder.append(" with parameters (");
+            String sep = "";
+            for (Object value : values) {
+                builder.append(sep);
+                if (value == null) {
+                    builder.append("null");
+                } else {
+                    builder.append(value).append(" (").append(value.getClass().getSimpleName()).append(")");
+                }
+
+                sep = ", ";
+            }
+            builder.append(")");
+        }
+        return builder.toString();
+    }
+
+    public abstract Object[] getValues();
+
+    static final class RewriteEvent0 extends RewriteEvent {
+
+        private static final Object[] EMPTY = new Object[0];
+
+        public RewriteEvent0(Node source, String reason) {
+            super(source, reason);
+        }
+
+        @Override
+        public Object[] getValues() {
+            return EMPTY;
+        }
+
+    }
+
+    static final class RewriteEvent1 extends RewriteEvent {
+
+        private final Object o1;
+
+        public RewriteEvent1(Node source, String reason, Object o1) {
+            super(source, reason);
+            this.o1 = o1;
+        }
+
+        @Override
+        public Object[] getValues() {
+            return new Object[]{o1};
+        }
+
+    }
+
+    static final class RewriteEvent2 extends RewriteEvent {
+
+        private final Object o1;
+        private final Object o2;
+
+        public RewriteEvent2(Node source, String reason, Object o1, Object o2) {
+            super(source, reason);
+            this.o1 = o1;
+            this.o2 = o2;
+        }
+
+        @Override
+        public Object[] getValues() {
+            return new Object[]{o1, o2};
+        }
+
+    }
+
+    static final class RewriteEvent3 extends RewriteEvent {
+
+        private final Object o1;
+        private final Object o2;
+        private final Object o3;
+
+        public RewriteEvent3(Node source, String reason, Object o1, Object o2, Object o3) {
+            super(source, reason);
+            this.o1 = o1;
+            this.o2 = o2;
+            this.o3 = o3;
+        }
+
+        @Override
+        public Object[] getValues() {
+            return new Object[]{o1, o2, o3};
+        }
+
+    }
+
+    static final class RewriteEvent4 extends RewriteEvent {
+
+        private final Object o1;
+        private final Object o2;
+        private final Object o3;
+        private final Object o4;
+
+        public RewriteEvent4(Node source, String reason, Object o1, Object o2, Object o3, Object o4) {
+            super(source, reason);
+            this.o1 = o1;
+            this.o2 = o2;
+            this.o3 = o3;
+            this.o4 = o4;
+        }
+
+        @Override
+        public Object[] getValues() {
+            return new Object[]{o1, o2, o3, o4};
+        }
+
+    }
+
+    static final class RewriteEventN extends RewriteEvent {
+
+        private final Object[] args;
+
+        public RewriteEventN(Node source, String reason, Object[] args) {
+            super(source, reason);
+            this.args = args;
+        }
+
+        @Override
+        public Object[] getValues() {
+            return args;
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/internal/SpecializationNode.java	Mon Dec 29 23:38:54 2014 +0100
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 2014, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.internal;
+
+import java.lang.reflect.*;
+import java.util.concurrent.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.internal.RewriteEvent.RewriteEvent0;
+import com.oracle.truffle.api.dsl.internal.RewriteEvent.RewriteEvent1;
+import com.oracle.truffle.api.dsl.internal.RewriteEvent.RewriteEvent2;
+import com.oracle.truffle.api.dsl.internal.RewriteEvent.RewriteEvent3;
+import com.oracle.truffle.api.dsl.internal.RewriteEvent.RewriteEvent4;
+import com.oracle.truffle.api.dsl.internal.RewriteEvent.RewriteEventN;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.nodes.NodeUtil.NodeClass;
+import com.oracle.truffle.api.nodes.NodeUtil.NodeField;
+
+/**
+ * Internal implementation dependent base class for generated specialized nodes.
+ */
+@SuppressWarnings("unused")
+@NodeInfo(cost = NodeCost.NONE)
+public abstract class SpecializationNode extends Node {
+
+    @Child protected SpecializationNode next;
+
+    private final int index;
+
+    public SpecializationNode() {
+        this(-1);
+    }
+
+    public SpecializationNode(int index) {
+        this.index = index;
+    }
+
+    @Override
+    public final NodeCost getCost() {
+        return NodeCost.NONE;
+    }
+
+    public static Node updateRoot(Node node) {
+        updateRootImpl(((SpecializedNode) node).getSpecializationNode(), node);
+        return node;
+    }
+
+    private static void updateRootImpl(SpecializationNode start, Node node) {
+        NodeField[] fields = NodeClass.get(start.getClass()).getFields();
+        for (int i = fields.length - 1; i >= 0; i--) {
+            NodeField f = fields[i];
+            if (f.getName().equals("root")) {
+                f.putObject(start, node);
+                break;
+            }
+        }
+        if (start.next != null) {
+            updateRootImpl(start.next, node);
+        }
+    }
+
+    protected final SpecializationNode polymorphicMerge(SpecializationNode newNode) {
+        SpecializationNode merged = next.merge(newNode);
+        if (merged == newNode && !isSame(newNode) && count() <= 2) {
+            return removeSame(new RewriteEvent0(findParentNode(), "merged polymorphic to monomorphic"));
+        }
+        return merged;
+    }
+
+    public final NodeCost getNodeCost() {
+        switch (count()) {
+            case 0:
+            case 1:
+                return NodeCost.UNINITIALIZED;
+            case 2:
+                return NodeCost.MONOMORPHIC;
+            default:
+                return NodeCost.POLYMORPHIC;
+        }
+    }
+
+    protected abstract Node[] getSuppliedChildren();
+
+    protected SpecializationNode merge(SpecializationNode newNode) {
+        if (this.isSame(newNode)) {
+            return this;
+        }
+        return next != null ? next.merge(newNode) : newNode;
+    }
+
+    @Override
+    public final boolean equals(Object obj) {
+        if (obj instanceof SpecializationNode) {
+            return ((SpecializationNode) obj).isSame(this);
+        }
+        return super.equals(obj);
+    }
+
+    @Override
+    public final int hashCode() {
+        return index;
+    }
+
+    protected boolean isSame(SpecializationNode other) {
+        return getClass() == other.getClass();
+    }
+
+    private final int count() {
+        return next != null ? next.count() + 1 : 1;
+    }
+
+    protected final SpecializationNode removeSame(final CharSequence reason) {
+        return atomic(new Callable<SpecializationNode>() {
+            public SpecializationNode call() throws Exception {
+                return removeImpl(SpecializationNode.this, reason);
+            }
+        });
+    }
+
+    /** Find the topmost of the specialization chain. */
+    private final SpecializationNode findStart() {
+        SpecializationNode node = this;
+        Node parent = this.getParent();
+        while (parent instanceof SpecializationNode) {
+            SpecializationNode parentCast = ((SpecializationNode) parent);
+            if (parentCast.next != node) {
+                break;
+            }
+            node = parentCast;
+            parent = node.getParent();
+        }
+        return node;
+    }
+
+    private final Node findParentNode() {
+        return findStart().getParent();
+    }
+
+    private SpecializationNode removeImpl(SpecializationNode toRemove, CharSequence reason) {
+        SpecializationNode start = findStart();
+        SpecializationNode current = start;
+        while (current != null) {
+            if (current.isSame(toRemove)) {
+                current.replace(current.next, reason);
+                if (current == start) {
+                    start = start.next;
+                }
+            }
+            current = current.next;
+        }
+        return start;
+    }
+
+    public Object acceptAndExecute(VirtualFrame frame) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object acceptAndExecute(VirtualFrame frame, Object o1) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object acceptAndExecute(VirtualFrame frame, Object o1, Object o2) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object acceptAndExecute(VirtualFrame frame, Object o1, Object o2, Object o3) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object acceptAndExecute(VirtualFrame frame, Object o1, Object o2, Object o3, Object o4) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object acceptAndExecute(VirtualFrame frame, Object... args) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected SpecializationNode createFallback() {
+        return null;
+    }
+
+    protected SpecializationNode createPolymorphic() {
+        return null;
+    }
+
+    protected SpecializationNode createNext(VirtualFrame frame) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected SpecializationNode createNext(VirtualFrame frame, Object o1) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected SpecializationNode createNext(VirtualFrame frame, Object o1, Object o2) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected SpecializationNode createNext(VirtualFrame frame, Object o1, Object o2, Object o3) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected SpecializationNode createNext(VirtualFrame frame, Object o1, Object o2, Object o3, Object o4) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected SpecializationNode createNext(VirtualFrame frame, Object... args) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected final Object uninitialized(VirtualFrame frame) {
+        CompilerDirectives.transferToInterpreterAndInvalidate();
+        SpecializationNode nextSpecialization = createNext(frame);
+        if (nextSpecialization == null) {
+            nextSpecialization = createFallback();
+        }
+        if (nextSpecialization == null) {
+            return unsupported(frame);
+        }
+        return insertSpecialization(nextSpecialization, new RewriteEvent0(findParentNode(), "inserted new specialization")).acceptAndExecute(frame);
+    }
+
+    protected final Object uninitialized(VirtualFrame frame, Object o1) {
+        CompilerDirectives.transferToInterpreterAndInvalidate();
+        SpecializationNode nextSpecialization = createNext(frame, o1);
+        if (nextSpecialization == null) {
+            nextSpecialization = createFallback();
+        }
+        if (nextSpecialization == null) {
+            return unsupported(frame, o1);
+        }
+        return insertSpecialization(nextSpecialization, new RewriteEvent1(findParentNode(), "inserted new specialization", o1)).acceptAndExecute(frame, o1);
+    }
+
+    protected final Object uninitialized(VirtualFrame frame, Object o1, Object o2) {
+        CompilerDirectives.transferToInterpreterAndInvalidate();
+        SpecializationNode nextSpecialization = createNext(frame, o1, o2);
+        if (nextSpecialization == null) {
+            nextSpecialization = createFallback();
+        }
+        if (nextSpecialization == null) {
+            return unsupported(frame, o1, o2);
+        }
+        return insertSpecialization(nextSpecialization, new RewriteEvent2(findParentNode(), "inserted new specialization", o1, o2)).acceptAndExecute(frame, o1, o2);
+    }
+
+    protected final Object uninitialized(VirtualFrame frame, Object o1, Object o2, Object o3) {
+        CompilerDirectives.transferToInterpreterAndInvalidate();
+        SpecializationNode nextSpecialization = createNext(frame, o1, o2, o3);
+        if (nextSpecialization == null) {
+            nextSpecialization = createFallback();
+        }
+        if (nextSpecialization == null) {
+            return unsupported(frame, o1, o2, o3);
+        }
+        return insertSpecialization(nextSpecialization, new RewriteEvent3(findParentNode(), "inserted new specialization", o1, o2, o3)).acceptAndExecute(frame, o1, o2, o3);
+    }
+
+    protected final Object uninitialized(VirtualFrame frame, Object o1, Object o2, Object o3, Object o4) {
+        CompilerDirectives.transferToInterpreterAndInvalidate();
+        SpecializationNode nextSpecialization = createNext(frame, o1, o2, o3, o4);
+        if (nextSpecialization == null) {
+            nextSpecialization = createFallback();
+        }
+        if (nextSpecialization == null) {
+            return unsupported(frame, o1, o2, o3, o4);
+        }
+        return insertSpecialization(nextSpecialization, new RewriteEvent4(findParentNode(), "inserts new specialization", o1, o2, o3, o4)).acceptAndExecute(frame, o1, o2, o3, o4);
+    }
+
+    protected final Object uninitialized(VirtualFrame frame, Object... args) {
+        CompilerDirectives.transferToInterpreterAndInvalidate();
+        SpecializationNode nextSpecialization = createNext(frame, args);
+        if (nextSpecialization == null) {
+            nextSpecialization = createFallback();
+        }
+        if (nextSpecialization == null) {
+            unsupported(frame, args);
+        }
+        return insertSpecialization(nextSpecialization, new RewriteEventN(findParentNode(), "inserts new specialization", args)).acceptAndExecute(frame, args);
+    }
+
+    private boolean needsPolymorphic() {
+        return findStart().count() == 2;
+    }
+
+    protected final Object remove(String reason, VirtualFrame frame) {
+        return removeSame(new RewriteEvent0(findParentNode(), reason)).acceptAndExecute(frame);
+    }
+
+    protected final Object remove(String reason, VirtualFrame frame, Object o1) {
+        return removeSame(new RewriteEvent1(findParentNode(), reason, o1)).acceptAndExecute(frame, o1);
+    }
+
+    protected final Object remove(String reason, VirtualFrame frame, Object o1, Object o2) {
+        return removeSame(new RewriteEvent2(findParentNode(), reason, o1, o2)).acceptAndExecute(frame, o1, o2);
+    }
+
+    protected final Object remove(String reason, VirtualFrame frame, Object o1, Object o2, Object o3) {
+        return removeSame(new RewriteEvent3(findParentNode(), reason, o1, o2, o3)).acceptAndExecute(frame, o1, o2, o3);
+    }
+
+    protected final Object remove(String reason, VirtualFrame frame, Object o1, Object o2, Object o3, Object o4) {
+        return removeSame(new RewriteEvent4(findParentNode(), reason, o1, o2, o3, o4)).acceptAndExecute(frame, o1, o2, o3, o4);
+    }
+
+    protected final Object remove(String reason, VirtualFrame frame, Object... args) {
+        return removeSame(new RewriteEventN(findParentNode(), reason, args)).acceptAndExecute(frame, args);
+    }
+
+    protected Object unsupported(VirtualFrame frame) {
+        throw new UnsupportedSpecializationException(findParentNode(), getSuppliedChildren());
+    }
+
+    protected Object unsupported(VirtualFrame frame, Object o1) {
+        throw new UnsupportedSpecializationException(findParentNode(), getSuppliedChildren(), o1);
+    }
+
+    protected Object unsupported(VirtualFrame frame, Object o1, Object o2) {
+        throw new UnsupportedSpecializationException(findParentNode(), getSuppliedChildren(), o1, o2);
+    }
+
+    protected Object unsupported(VirtualFrame frame, Object o1, Object o2, Object o3) {
+        throw new UnsupportedSpecializationException(findParentNode(), getSuppliedChildren(), o1, o2, o3);
+    }
+
+    protected Object unsupported(VirtualFrame frame, Object o1, Object o2, Object o3, Object o4) {
+        throw new UnsupportedSpecializationException(findParentNode(), getSuppliedChildren(), o1, o2, o3, o4);
+    }
+
+    protected Object unsupported(VirtualFrame frame, Object... args) {
+        throw new UnsupportedSpecializationException(findParentNode(), getSuppliedChildren(), args);
+    }
+
+    private SpecializationNode insertSpecialization(final SpecializationNode generated, final CharSequence message) {
+        return atomic(new Callable<SpecializationNode>() {
+            public SpecializationNode call() {
+                return insert(generated, message);
+            }
+        });
+    }
+
+    private final SpecializationNode insert(final SpecializationNode generated, CharSequence message) {
+        SpecializationNode start = findStart();
+        if (start == this) {
+            // fast path for first insert
+            return insertBefore(this, generated, message);
+        } else {
+            return slowSortedInsert(start, generated, message);
+        }
+    }
+
+    private static <T> SpecializationNode slowSortedInsert(SpecializationNode start, final SpecializationNode generated, final CharSequence message) {
+        final SpecializationNode merged = start.merge(generated);
+        if (merged == generated) {
+            // new node
+            if (start.count() == 2) {
+                insertBefore(start, start.createPolymorphic(), "insert polymorphic");
+            }
+            SpecializationNode insertBefore = findInsertBeforeNode(generated.index, start);
+            return insertBefore(insertBefore, generated, message);
+        } else {
+            // existing node
+            merged.replace(merged, new RewriteEvent0(merged.findParentNode(), "merged specialization"));
+            return merged;
+        }
+    }
+
+    private static SpecializationNode findInsertBeforeNode(int generatedIndex, SpecializationNode start) {
+        SpecializationNode current = start;
+        while (current != null && current.index < generatedIndex) {
+            current = current.next;
+        }
+        return current;
+    }
+
+    private static <T> SpecializationNode insertBefore(SpecializationNode node, SpecializationNode insertBefore, CharSequence message) {
+        insertBefore.next = node;
+        return node.replace(insertBefore, message);
+    }
+
+    @Override
+    public final String toString() {
+        Class<?> clazz = getClass();
+        StringBuilder b = new StringBuilder();
+        b.append(clazz.getSimpleName());
+
+        appendFields(b, clazz);
+        if (next != null) {
+            b.append(" -> ").append(next.toString());
+        }
+        return b.toString();
+    }
+
+    private void appendFields(StringBuilder b, Class<?> clazz) {
+        Field[] fields = clazz.getDeclaredFields();
+        if (fields.length == 0) {
+            return;
+        }
+        b.append("(");
+        for (Field field : fields) {
+            if (Modifier.isStatic(field.getModifiers())) {
+                continue;
+            }
+            String name = field.getName();
+            if (name.equals("root")) {
+                continue;
+            }
+            b.append(field.getName());
+            try {
+                field.setAccessible(true);
+                b.append(field.get(this));
+            } catch (IllegalArgumentException e) {
+                b.append(e.toString());
+            } catch (IllegalAccessException e) {
+                b.append(e.toString());
+            }
+        }
+        b.append(")");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/internal/SpecializedNode.java	Mon Dec 29 23:38:54 2014 +0100
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.internal;
+
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Implemented by DSL generated operation classes. This is internal implementation dependent API.
+ */
+public interface SpecializedNode extends NodeInterface {
+
+    /** Returns the root {@link SpecializationNode} of the DSL operation. */
+    SpecializationNode getSpecializationNode();
+
+}
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ProcessorContext.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ProcessorContext.java	Mon Dec 29 23:38:54 2014 +0100
@@ -83,6 +83,10 @@
         return model;
     }
 
+    public DeclaredType getDeclaredType(Class<?> element) {
+        return (DeclaredType) ElementUtils.getType(environment, element);
+    }
+
     public TypeMirror getType(Class<?> element) {
         return ElementUtils.getType(environment, element);
     }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleProcessor.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleProcessor.java	Mon Dec 29 23:38:54 2014 +0100
@@ -93,7 +93,7 @@
     }
 
     private static void handleThrowable(AnnotationProcessor<?> generator, Throwable t, Element e) {
-        String message = "Uncaught error in " + generator.getClass().getSimpleName() + " while processing " + e;
+        String message = "Uncaught error in " + generator.getClass().getSimpleName() + " while processing " + e + " ";
         ProcessorContext.getInstance().getEnvironment().getMessager().printMessage(Kind.ERROR, message + ": " + ElementUtils.printException(t), e);
     }
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/GeneratorUtils.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/GeneratorUtils.java	Mon Dec 29 23:38:54 2014 +0100
@@ -31,18 +31,31 @@
 import javax.lang.model.type.*;
 import javax.lang.model.util.*;
 
+import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.internal.DSLOptions.*;
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.java.model.*;
 import com.oracle.truffle.dsl.processor.model.*;
 
 public class GeneratorUtils {
 
-    public static CodeExecutableElement createConstructorUsingFields(ProcessorContext context, Set<Modifier> modifiers, CodeTypeElement clazz) {
+    public static CodeTree createTransferToInterpreterAndInvalidate() {
+        ProcessorContext context = ProcessorContext.getInstance();
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.startStatement().startStaticCall(context.getType(CompilerDirectives.class), "transferToInterpreterAndInvalidate").end().end();
+        return builder.build();
+    }
+
+    public static CodeExecutableElement createConstructorUsingFields(Set<Modifier> modifiers, CodeTypeElement clazz) {
+        TypeElement superClass = fromTypeMirror(clazz.getSuperclass());
+        ExecutableElement constructor = findConstructor(superClass);
+        return createConstructorUsingFields(modifiers, clazz, constructor);
+    }
+
+    public static CodeExecutableElement createConstructorUsingFields(Set<Modifier> modifiers, CodeTypeElement clazz, ExecutableElement constructor) {
         CodeExecutableElement method = new CodeExecutableElement(modifiers, null, clazz.getSimpleName().toString());
         CodeTreeBuilder builder = method.createBuilder();
-        TypeElement superClass = fromTypeMirror(clazz.getSuperclass());
-        ExecutableElement constructor = findConstructor(superClass);
         if (constructor != null && constructor.getParameters().size() > 0) {
             builder.startStatement();
             builder.startSuperCall();
@@ -64,17 +77,26 @@
             builder.string("this.");
             builder.string(fieldName);
             builder.string(" = ");
-            if (isAssignable(field.asType(), context.getTruffleTypes().getNode())) {
-                builder.string("adoptChild(").string(fieldName).string(")");
-            } else {
-                builder.string(fieldName);
-            }
+            builder.string(fieldName);
             builder.end(); // statement
         }
 
         return method;
     }
 
+    public static boolean isTypeBoxingOptimized(TypeBoxingOptimization boxing, TypeData type) {
+        switch (boxing) {
+            case NONE:
+                return false;
+            case ALWAYS:
+                return !type.isGeneric() && !type.isVoid();
+            case PRIMITIVE:
+                return type.isPrimitive();
+            default:
+                throw new AssertionError();
+        }
+    }
+
     private static ExecutableElement findConstructor(TypeElement clazz) {
         List<ExecutableElement> constructors = ElementFilter.constructorsIn(clazz.getEnclosedElements());
         if (constructors.isEmpty()) {
@@ -103,13 +125,13 @@
         return executable;
     }
 
-    public static CodeTypeElement createClass(Template model, Set<Modifier> modifiers, String simpleName, TypeMirror superType, boolean enumType) {
-        TypeElement templateType = model.getTemplateType();
+    public static CodeTypeElement createClass(Template sourceModel, TemplateMethod sourceMethod, Set<Modifier> modifiers, String simpleName, TypeMirror superType) {
+        TypeElement templateType = sourceModel.getTemplateType();
 
         ProcessorContext context = ProcessorContext.getInstance();
 
         PackageElement pack = context.getEnvironment().getElementUtils().getPackageOf(templateType);
-        CodeTypeElement clazz = new CodeTypeElement(modifiers, enumType ? ElementKind.ENUM : ElementKind.CLASS, pack, simpleName);
+        CodeTypeElement clazz = new CodeTypeElement(modifiers, ElementKind.CLASS, pack, simpleName);
         TypeMirror resolvedSuperType = superType;
         if (resolvedSuperType == null) {
             resolvedSuperType = context.getType(Object.class);
@@ -118,8 +140,8 @@
 
         CodeAnnotationMirror generatedByAnnotation = new CodeAnnotationMirror((DeclaredType) context.getType(GeneratedBy.class));
         generatedByAnnotation.setElementValue(generatedByAnnotation.findExecutableElement("value"), new CodeAnnotationValue(templateType.asType()));
-        if (model.getTemplateMethodName() != null) {
-            generatedByAnnotation.setElementValue(generatedByAnnotation.findExecutableElement("methodName"), new CodeAnnotationValue(model.getTemplateMethodName()));
+        if (sourceMethod != null && sourceMethod.getMethod() != null) {
+            generatedByAnnotation.setElementValue(generatedByAnnotation.findExecutableElement("methodName"), new CodeAnnotationValue(sourceMethod.createReferenceName()));
         }
 
         clazz.addAnnotationMirror(generatedByAnnotation);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/ImplicitCastNodeFactory.java	Mon Dec 29 23:38:54 2014 +0100
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2014, 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.dsl.processor.generator;
+
+import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
+import static javax.lang.model.element.Modifier.*;
+
+import java.util.*;
+
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.*;
+import com.oracle.truffle.api.dsl.internal.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.dsl.processor.*;
+import com.oracle.truffle.dsl.processor.java.*;
+import com.oracle.truffle.dsl.processor.java.model.*;
+import com.oracle.truffle.dsl.processor.model.*;
+
+import static com.oracle.truffle.dsl.processor.generator.GeneratorUtils.*;
+
+public class ImplicitCastNodeFactory {
+
+    private final ProcessorContext context;
+    private final TypeData forType;
+    private final TypeSystemData typeSystem;
+    private final DSLOptions options;
+    private final List<TypeData> sourceTypes;
+
+    public ImplicitCastNodeFactory(ProcessorContext context, TypeData forType) {
+        this.context = context;
+        this.forType = forType;
+        this.typeSystem = forType.getTypeSystem();
+        this.options = typeSystem.getOptions();
+        this.sourceTypes = typeSystem.lookupSourceTypes(forType);
+    }
+
+    public static String typeName(TypeData type) {
+        return "Implicit" + getTypeId(type.getBoxedType()) + "Cast";
+    }
+
+    public static TypeMirror type(TypeData type) {
+        TypeSystemData typeSystem = type.getTypeSystem();
+        String typeSystemName = TypeSystemCodeGenerator.typeName(typeSystem);
+        return new GeneratedTypeMirror(ElementUtils.getPackageName(typeSystem.getTemplateType()) + "." + typeSystemName, typeName(type));
+    }
+
+    public static CodeTree create(TypeData type, CodeTree value) {
+        return CodeTreeBuilder.createBuilder().startStaticCall(type(type), "create").tree(value).end().build();
+    }
+
+    public static CodeTree cast(String nodeName, CodeTree value) {
+        return CodeTreeBuilder.createBuilder().startCall(nodeName, "cast").tree(value).end().build();
+    }
+
+    public static CodeTree check(String nodeName, CodeTree value) {
+        return CodeTreeBuilder.createBuilder().startCall(nodeName, "check").tree(value).end().build();
+    }
+
+    private static String seenFieldName(TypeData type) {
+        return "seen" + getTypeId(type.getBoxedType());
+    }
+
+    public CodeTypeElement create() {
+        String typeName = typeName(forType);
+        TypeMirror baseType = context.getType(Object.class);
+        CodeTypeElement clazz = GeneratorUtils.createClass(typeSystem, null, modifiers(PUBLIC, FINAL, STATIC), typeName, baseType);
+
+        for (TypeData sourceType : sourceTypes) {
+            CodeVariableElement hasSeen = new CodeVariableElement(modifiers(PUBLIC), context.getType(boolean.class), seenFieldName(sourceType));
+            hasSeen.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(CompilationFinal.class)));
+            clazz.add(hasSeen);
+        }
+
+        clazz.add(createConstructor(clazz));
+        if (isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), forType)) {
+            clazz.add(createIsMonomorphic());
+        }
+        clazz.add(createCast(false));
+        clazz.add(createCast(true));
+        clazz.add(createCheck());
+        clazz.add(createMerge(clazz));
+        clazz.add(createCreate(clazz));
+
+        return clazz;
+    }
+
+    private Element createIsMonomorphic() {
+        String methodName = "isMonomorphic";
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), context.getType(boolean.class), methodName);
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startReturn();
+        String operator = "";
+        for (TypeData sourceType : sourceTypes) {
+            builder.string(operator);
+            builder.string(seenFieldName(sourceType));
+            operator = " ^ ";
+        }
+        builder.end();
+        return method;
+    }
+
+    private static Element createConstructor(CodeTypeElement clazz) {
+        return new CodeExecutableElement(modifiers(PRIVATE), null, clazz.getSimpleName().toString());
+    }
+
+    private Element createCreate(CodeTypeElement clazz) {
+        String methodName = "create";
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, STATIC), clazz.asType(), methodName);
+        method.addParameter(new CodeVariableElement(typeSystem.getGenericType(), "value"));
+        CodeTreeBuilder builder = method.createBuilder();
+
+        builder.declaration(clazz.asType(), "newCast", builder.create().startNew(clazz.asType()).end());
+
+        for (TypeData sourceType : sourceTypes) {
+            String seenField = seenFieldName(sourceType);
+            builder.startStatement();
+            builder.string("newCast.").string(seenField).string(" = ").tree(TypeSystemCodeGenerator.check(sourceType, "value"));
+            builder.end();
+        }
+        builder.startReturn().string("newCast").end();
+        return method;
+    }
+
+    private Element createMerge(CodeTypeElement clazz) {
+        String methodName = "merge";
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), context.getType(void.class), methodName);
+        method.addParameter(new CodeVariableElement(clazz.asType(), "otherCast"));
+        CodeTreeBuilder builder = method.createBuilder();
+
+        for (TypeData sourceType : sourceTypes) {
+            String seenField = seenFieldName(sourceType);
+            builder.startStatement();
+            builder.string("this.").string(seenField).string(" |= ").string("otherCast.").string(seenField);
+            builder.end();
+        }
+        return method;
+    }
+
+    private Element createCheck() {
+        String methodName = "check";
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), context.getType(boolean.class), methodName);
+        method.addParameter(new CodeVariableElement(typeSystem.getGenericType(), "value"));
+        CodeTreeBuilder builder = method.createBuilder();
+
+        boolean elseIf = false;
+        for (TypeData sourceType : sourceTypes) {
+            elseIf = builder.startIf(elseIf);
+            builder.string(seenFieldName(sourceType)).string(" && ").tree(TypeSystemCodeGenerator.check(sourceType, "value"));
+            builder.end();
+            builder.startBlock().returnTrue().end();
+        }
+        builder.returnFalse();
+        return method;
+    }
+
+    private Element createCast(boolean expect) {
+        String methodName = expect ? "expect" : "cast";
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), forType.getPrimitiveType(), methodName);
+        method.addParameter(new CodeVariableElement(typeSystem.getGenericType(), "value"));
+        if (expect) {
+            method.getThrownTypes().add(context.getType(UnexpectedResultException.class));
+        }
+
+        CodeTreeBuilder builder = method.createBuilder();
+
+        boolean elseIf = false;
+        for (TypeData sourceType : sourceTypes) {
+            elseIf = builder.startIf(elseIf);
+            builder.string(seenFieldName(sourceType)).string(" && ").tree(TypeSystemCodeGenerator.check(sourceType, "value"));
+            builder.end();
+            builder.startBlock();
+            builder.startReturn();
+            CodeTree castTree = TypeSystemCodeGenerator.cast(sourceType, "value");
+            ImplicitCastData cast = typeSystem.lookupCast(sourceType, forType);
+            if (cast != null) {
+                builder.tree(TypeSystemCodeGenerator.invokeImplicitCast(cast, castTree));
+            } else {
+                builder.tree(castTree);
+            }
+            builder.end();
+            builder.end();
+        }
+        if (expect) {
+            builder.startThrow().startNew(context.getType(UnexpectedResultException.class)).string("value").end().end();
+        } else {
+            builder.startStatement().startStaticCall(context.getType(CompilerDirectives.class), "transferToInterpreter").end().end();
+            builder.startThrow().startNew(context.getType(AssertionError.class)).end().end();
+        }
+        return method;
+    }
+
+}
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeBaseFactory.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeBaseFactory.java	Mon Dec 29 23:38:54 2014 +0100
@@ -71,7 +71,7 @@
     }
 
     public CodeTypeElement create() {
-        CodeTypeElement clazz = GeneratorUtils.createClass(node, modifiers(PRIVATE, ABSTRACT), baseClassName(node), node.getNodeType(), false);
+        CodeTypeElement clazz = GeneratorUtils.createClass(node, null, modifiers(PRIVATE, ABSTRACT), baseClassName(node), node.getNodeType());
         clazz.getImplements().add(context.getTruffleTypes().getDslNode());
 
         for (NodeChildData child : node.getChildren()) {
@@ -321,7 +321,7 @@
     private CodeTree truffleBooleanOption(CodeTreeBuilder parent, String name) {
         CodeTreeBuilder builder = parent.create();
         builder.staticReference(context.getTruffleTypes().getTruffleOptions(), name);
-        return builder.getRoot();
+        return builder.build();
     }
 
     private final void addInternalValueParameters(CodeExecutableElement executableMethod, TemplateMethod method, boolean forceFrame, boolean disableFrame, boolean evaluated) {
@@ -507,7 +507,7 @@
             final String varName = var.getSimpleName().toString();
             final TypeMirror varType = var.asType();
             if (ElementUtils.isAssignable(varType, context.getTruffleTypes().getNodeArray())) {
-                CodeTree size = builder.create().string("copy.", varName, ".length").getRoot();
+                CodeTree size = builder.create().string("copy.", varName, ".length").build();
                 builder.startStatement().string("this.").string(varName).string(" = ").startNewArray((ArrayType) varType, size).end().end();
             } else {
                 builder.startStatement().string("this.", varName, " = copy.", varName).end();
@@ -598,7 +598,7 @@
         builder.startCall(CREATE_INFO).string(reason);
         addInternalValueParameterNames(builder, specialization, specialization, null, false, false, null);
         builder.end();
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeExecutableElement createMonomorphicRewrite() {
@@ -633,7 +633,7 @@
         builder.end();
 
         builder.declaration(baseClassName, "returnNode",
-                        builder.create().startStaticCall(context.getTruffleTypes().getDslShare(), DSLSHARE_REWRITE).string("this").string("newNode").string("message").end().getRoot());
+                        builder.create().startStaticCall(context.getTruffleTypes().getDslShare(), DSLSHARE_REWRITE).string("this").string("newNode").string("message").end().build());
         builder.startIf().string("returnNode == null").end().startBlock();
         builder.tree(createRewritePolymorphic(builder, "this"));
         builder.end();
@@ -654,15 +654,15 @@
         builder.startStatement().string("returnNode = ");
         builder.startStaticCall(context.getTruffleTypes().getDslShare(), DSLSHARE_REWRITE_TO_POLYMORHPIC);
         builder.string("this");
-        builder.tree(builder.create().startNew(nodeSpecializationClassName(node.getUninitializedSpecialization())).string(currentNode).end().getRoot());
-        builder.tree(builder.create().startNew(polyClassName).string(currentNode).end().getRoot());
+        builder.tree(builder.create().startNew(nodeSpecializationClassName(node.getUninitializedSpecialization())).string(currentNode).end().build());
+        builder.tree(builder.create().startNew(polyClassName).string(currentNode).end().build());
         builder.startGroup().cast(baseClassName(node)).startCall("copy").end().end();
         builder.string("newNode");
         builder.string("message");
         builder.end();
         builder.end();
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeExecutableElement createCreateSpecializationMethod(SpecializationGroup group) {
@@ -691,7 +691,7 @@
 
     private CodeTree createCreateSpecializationMethodBody0(CodeTreeBuilder parent, SpecializationData current, boolean useDeoptimize) {
         CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-        if (current.isGeneric()) {
+        if (current.isFallback()) {
             builder.startReturn().nullLiteral().end();
         } else {
             String className = nodeSpecializationClassName(current);
@@ -721,7 +721,7 @@
                 builder.end();
             }
         }
-        return builder.getRoot();
+        return builder.build();
 
     }
 
@@ -752,7 +752,7 @@
                     }
                 }
 
-                return builder.getRoot();
+                return builder.build();
             }
         }, elseBlock, forceElse, emitAssumptions, typedCasts, castForGuardsOnly);
     }
@@ -775,7 +775,7 @@
             }
         }
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private static boolean isReachableGroup(SpecializationGroup group, int ifCount) {
@@ -877,22 +877,22 @@
             }
         }
 
-        int ifCount = startGuardIf(builder, guardsBuilder, 0, elseGuards);
-        builder.tree(castBuilder.getRoot());
-        ifCount = startGuardIf(builder, guardsCastBuilder, ifCount, elseGuards);
+        int ifCount = startGuardIf(builder, guardsBuilder.build(), 0, elseGuards);
+        builder.tree(castBuilder.build());
+        ifCount = startGuardIf(builder, guardsCastBuilder.build(), ifCount, elseGuards);
         return ifCount;
     }
 
-    private static int startGuardIf(CodeTreeBuilder builder, CodeTreeBuilder conditionBuilder, int ifCount, List<GuardExpression> elseGuard) {
+    private static int startGuardIf(CodeTreeBuilder builder, CodeTree condition, int ifCount, List<GuardExpression> elseGuard) {
         int newIfCount = ifCount;
 
-        if (!conditionBuilder.isEmpty()) {
+        if (!condition.isEmpty()) {
             if (ifCount == 0 && !elseGuard.isEmpty()) {
                 builder.startElseIf();
             } else {
                 builder.startIf();
             }
-            builder.tree(conditionBuilder.getRoot());
+            builder.tree(condition);
             builder.end().startBlock();
             newIfCount++;
         } else if (ifCount == 0 && !elseGuard.isEmpty()) {
@@ -957,13 +957,13 @@
             }
             CodeTree check;
             if (castTypeName == null) {
-                check = TypeSystemCodeGenerator.implicitCheck(targetType, valueName(source), null);
+                check = TypeSystemCodeGenerator.implicitCheck(targetType, CodeTreeBuilder.singleString(valueName(source)), null);
             } else {
-                check = TypeSystemCodeGenerator.implicitCheck(targetType, valueName(source), castTypeName);
+                check = TypeSystemCodeGenerator.implicitCheck(targetType, CodeTreeBuilder.singleString(valueName(source)), castTypeName);
             }
             builder.tree(check);
         } else {
-            builder.tree(TypeSystemCodeGenerator.check(targetType, valueName(source)));
+            builder.tree(TypeSystemCodeGenerator.check(targetType, CodeTreeBuilder.singleString(valueName(source))));
         }
 
         if (execution.isShortCircuit()) {
@@ -972,7 +972,7 @@
 
         builder.end(); // group
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     // TODO merge redundancies with #createTypeGuard
@@ -997,7 +997,7 @@
             if (typedCasts) {
                 castTypeName = implicitTypeName(source);
             }
-            cast = TypeSystemCodeGenerator.implicitCast(targetType, valueName(source), castTypeName);
+            cast = TypeSystemCodeGenerator.implicitCast(targetType, CodeTreeBuilder.singleString(valueName(source)), castTypeName);
         } else {
             cast = TypeSystemCodeGenerator.cast(targetType, valueName(source));
         }
@@ -1005,7 +1005,7 @@
         CodeTreeBuilder builder = parent.create();
         builder.tree(createLazyAssignment(parent, castValueName(source), targetType.getPrimitiveType(), condition, cast));
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeTree createGetImplicitType(CodeTreeBuilder parent, NodeExecutionData execution, Parameter source, TypeData targetType) {
@@ -1019,10 +1019,10 @@
         CodeTreeBuilder builder = parent.create();
         List<TypeData> types = getSpecialization().getNode().getTypeSystem().lookupSourceTypes(targetType);
         if (types.size() > 1) {
-            CodeTree castType = TypeSystemCodeGenerator.implicitType(targetType, valueName(source));
+            CodeTree castType = TypeSystemCodeGenerator.implicitType(targetType, CodeTreeBuilder.singleString(valueName(source)));
             builder.tree(createLazyAssignment(builder, implicitTypeName(source), context.getType(Class.class), condition, castType));
         }
-        return builder.getRoot();
+        return builder.build();
     }
 
     private static CodeTree createMethodGuard(CodeTreeBuilder parent, String prefix, SpecializationData source, GuardExpression guard) {
@@ -1032,7 +1032,7 @@
             builder.string("!");
         }
         builder.tree(createTemplateMethodCall(builder, null, source, guard.getResolvedGuard(), null));
-        return builder.getRoot();
+        return builder.build();
     }
 
     protected CodeTree createGenericInvoke(CodeTreeBuilder parent, SpecializationData source, SpecializationData current) {
@@ -1044,7 +1044,7 @@
             builder.startReturn().tree(createTemplateMethodCall(builder, null, source, current, null)).end();
         }
 
-        return encloseThrowsWithFallThrough(parent, current, builder.getRoot());
+        return encloseThrowsWithFallThrough(parent, current, builder.build());
     }
 
     private CodeTree encloseThrowsWithFallThrough(CodeTreeBuilder parent, SpecializationData current, CodeTree tree) {
@@ -1063,7 +1063,7 @@
         }
         builder.end();
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     protected CodeTree createCastingExecute(CodeTreeBuilder parent, ExecutableTypeData executable, ExecutableTypeData castExecutable) {
@@ -1134,7 +1134,7 @@
             }
         }
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private static CodeTree createExpectExecutableType(TypeData sourceType, boolean hasUnexpected, TypeData exepctedType, CodeTree value) {
@@ -1166,7 +1166,7 @@
             }
 
         }
-        return builder.getRoot();
+        return builder.build();
     }
 
     private ExecutableTypeData resolveExecutableType(NodeExecutionData execution, TypeData type) {
@@ -1204,7 +1204,7 @@
                 builder.startStatement().tree(createExecuteChildImplicit(parent, execution, sourceExecutable, targetParameter, unexpectedParameter)).end();
                 builder.end();
 
-                return builder.getRoot();
+                return builder.build();
             }
         }
         return createExecuteChildImplicit(parent, execution, sourceExecutable, targetParameter, unexpectedParameter);
@@ -1270,7 +1270,7 @@
                 builder.tree(createExecuteChildExpression(parent, execution, expectType, param, unexpectedParameter, null));
             }
         }
-        return builder.getRoot();
+        return builder.build();
     }
 
     private static String createExecuteChildMethodName(Parameter param, boolean expect) {
@@ -1359,7 +1359,7 @@
             builder.end();
             index++;
         }
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeTree createExecuteChildExpression(CodeTreeBuilder parent, NodeExecutionData execution, TypeData sourceParameterType, Parameter targetParameter, Parameter unexpectedParameter,
@@ -1393,7 +1393,7 @@
         builder.string(valueName(targetParameter));
         builder.string(" = ");
         builder.tree(expression);
-        return builder.getRoot();
+        return builder.build();
     }
 
     private static CodeTree createImplicitCast(ImplicitCastData cast, CodeTree expression) {
@@ -1503,7 +1503,7 @@
         }
         builder.end(); // catch block
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeTree createReturnOptimizeTypes(CodeTreeBuilder parent, ExecutableTypeData currentExecutable, SpecializationData currentSpecialization, Parameter param) {
@@ -1521,10 +1521,10 @@
 
         TypeData sourceType = polymorphic.getReturnType().getTypeSystemType();
 
-        builder.tree(createExpectExecutableType(sourceType, currentExecutable.hasUnexpectedValue(context), currentExecutable.getType(), execute.getRoot()));
+        builder.tree(createExpectExecutableType(sourceType, currentExecutable.hasUnexpectedValue(context), currentExecutable.getType(), execute.build()));
 
         builder.end();
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeTree createExecuteChildExpression(CodeTreeBuilder parent, NodeExecutionData targetExecution, ExecutableTypeData targetExecutable, Parameter unexpectedParameter) {
@@ -1581,7 +1581,7 @@
 
         builder.end();
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeTree createShortCircuitTree(CodeTreeBuilder parent, CodeTree body, SpecializationData currentSpecialization, Parameter parameter, Parameter exceptionParam) {
@@ -1604,7 +1604,7 @@
         }
         builder.end();
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private static CodeTree createShortCircuitValue(CodeTreeBuilder parent, SpecializationData specialization, NodeExecutionData execution, Parameter shortCircuitParam, Parameter exceptionParam) {
@@ -1624,7 +1624,7 @@
         builder.tree(createTemplateMethodCall(builder, null, specialization, shortCircuitData, exceptionParam != null ? exceptionParam.getLocalName() : null));
         builder.end(); // statement
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     protected CodeTree createCallRewriteMonomorphic(CodeTreeBuilder parent, boolean hasUnexpected, TypeData returnType, Parameter exceptionParam, String reason) {
@@ -1638,10 +1638,10 @@
         CodeTreeBuilder builder = new CodeTreeBuilder(parent);
 
         builder.startReturn();
-        builder.tree(createExpectExecutableType(generic.getReturnType().getTypeSystemType(), hasUnexpected, returnType, specializeCall.getRoot()));
+        builder.tree(createExpectExecutableType(generic.getReturnType().getTypeSystemType(), hasUnexpected, returnType, specializeCall.build()));
         builder.end();
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     static String valueNameEvaluated(Parameter targetParameter) {
@@ -1677,7 +1677,7 @@
         if (targetExecution.isIndexed()) {
             builder.string("[" + targetExecution.getIndex() + "]");
         }
-        return builder.getRoot();
+        return builder.build();
     }
 
     private static String castValueName(Parameter parameter) {
@@ -1825,7 +1825,7 @@
 
         builder.end().end();
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     public static String baseClassName(NodeData node) {
@@ -1855,7 +1855,7 @@
         if (condition == null) {
             builder.declaration(type, name, value);
         } else {
-            builder.declaration(type, name, new CodeTreeBuilder(parent).defaultValue(type).getRoot());
+            builder.declaration(type, name, new CodeTreeBuilder(parent).defaultValue(type).build());
 
             builder.startIf().tree(condition).end();
             builder.startBlock();
@@ -1866,7 +1866,7 @@
             builder.end(); // statement
             builder.end(); // block
         }
-        return builder.getRoot();
+        return builder.build();
     }
 
     void emitEncounteredSynthetic(CodeTreeBuilder builder, NodeData model, TemplateMethod current) {
@@ -1895,10 +1895,10 @@
         builder.startThrow().startNew(context.getType(UnsupportedSpecializationException.class));
         builder.string("rootNode");
         builder.startNewArray(context.getTruffleTypes().getNodeArray(), null);
-        builder.tree(nodes.getRoot());
+        builder.tree(nodes.build());
         builder.end();
         if (!empty) {
-            builder.tree(arguments.getRoot());
+            builder.tree(arguments.build());
         }
         builder.end().end();
     }
@@ -1959,7 +1959,7 @@
         builder.startStatement();
         builder.startStaticCall(ProcessorContext.getInstance().getTruffleTypes().getCompilerDirectives(), "transferToInterpreterAndInvalidate").end();
         builder.end();
-        return builder.getRoot();
+        return builder.build();
     }
 
     private TypeMirror getUnexpectedValueException() {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeCodeGenerator.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeCodeGenerator.java	Mon Dec 29 23:38:54 2014 +0100
@@ -95,19 +95,21 @@
         } else {
             // wrap all types into the first node
             CodeTypeElement first = generatedNodes.get(0);
-            CodeTypeElement last = generatedNodes.get(1);
-
-            for (CodeTypeElement generatedNode : generatedNodes) {
-                if (first != generatedNode) {
-                    first.add(makeInnerClass(generatedNode));
+            CodeTypeElement second = first;
+            if (generatedNodes.size() > 1) {
+                second = generatedNodes.get(1);
+                for (CodeTypeElement generatedNode : generatedNodes) {
+                    if (first != generatedNode) {
+                        first.add(makeInnerClass(generatedNode));
+                    }
                 }
             }
+            new NodeFactoryFactory(context, node, second).createFactoryMethods(first, ElementUtils.getVisibility(node.getTemplateType().getModifiers()));
             ElementUtils.setVisibility(first.getModifiers(), ElementUtils.getVisibility(node.getTemplateType().getModifiers()));
             for (ExecutableElement constructor : ElementFilter.constructorsIn(first.getEnclosedElements())) {
                 ElementUtils.setVisibility(((CodeExecutableElement) constructor).getModifiers(), Modifier.PRIVATE);
             }
 
-            NodeFactoryFactory.createFactoryMethods(context, node, first, last, ElementUtils.getVisibility(node.getTemplateType().getModifiers()));
             return first;
         }
     }
@@ -116,7 +118,7 @@
         CodeTypeElement container;
         Modifier visibility = ElementUtils.getVisibility(node.getTemplateType().getModifiers());
         String containerName = NodeFactoryFactory.factoryClassName(node);
-        container = GeneratorUtils.createClass(node, modifiers(), containerName, null, false);
+        container = GeneratorUtils.createClass(node, null, modifiers(), containerName, null);
         if (visibility != null) {
             container.getModifiers().add(visibility);
         }
@@ -133,13 +135,21 @@
         if (!node.needsFactory()) {
             return Collections.emptyList();
         }
+        if (node.getTypeSystem().getOptions().useNewLayout()) {
+            return Arrays.asList(new NodeGenFactory(context, node).create());
+        } else {
+            return generateNodesOld(context, node);
+        }
+    }
+
+    private static List<CodeTypeElement> generateNodesOld(ProcessorContext context, NodeData node) {
         List<CodeTypeElement> nodeTypes = new ArrayList<>();
         SpecializationData generic = node.getGenericSpecialization() == null ? node.getSpecializations().get(0) : node.getGenericSpecialization();
         CodeTypeElement baseNode = new NodeBaseFactory(context, node, generic).create();
         nodeTypes.add(baseNode);
 
         for (SpecializationData specialization : node.getSpecializations()) {
-            if (!specialization.isReachable() || specialization.isGeneric()) {
+            if (!specialization.isReachable() || specialization.isFallback()) {
                 continue;
             }
             if (specialization.isPolymorphic() && node.isPolymorphic(context)) {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeFactoryFactory.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeFactoryFactory.java	Mon Dec 29 23:38:54 2014 +0100
@@ -31,6 +31,7 @@
 import javax.lang.model.type.*;
 
 import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.internal.*;
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.java.*;
 import com.oracle.truffle.dsl.processor.java.model.*;
@@ -43,12 +44,16 @@
 
     private final ProcessorContext context;
     private final NodeData node;
+    private final TypeSystemData typeSystem;
+    private final DSLOptions options;
     private final CodeTypeElement createdFactoryElement;
 
     public NodeFactoryFactory(ProcessorContext context, NodeData node, CodeTypeElement createdClass) {
         this.context = context;
         this.node = node;
         this.createdFactoryElement = createdClass;
+        this.typeSystem = node.getTypeSystem();
+        this.options = typeSystem.getOptions();
     }
 
     public static String factoryClassName(NodeData node) {
@@ -59,14 +64,14 @@
         Modifier visibility = ElementUtils.getVisibility(node.getTemplateType().getModifiers());
         TypeMirror nodeFactory = ElementUtils.getDeclaredType(ElementUtils.fromTypeMirror(context.getTruffleTypes().getNodeFactoryBase()), node.getNodeType());
 
-        CodeTypeElement clazz = GeneratorUtils.createClass(node, modifiers(), factoryClassName(node), null, false);
+        CodeTypeElement clazz = GeneratorUtils.createClass(node, null, modifiers(), factoryClassName(node), null);
         if (visibility != null) {
             clazz.getModifiers().add(visibility);
         }
         clazz.getModifiers().add(Modifier.FINAL);
 
         if (createdFactoryElement != null) {
-            createFactoryMethods(context, node, clazz, createdFactoryElement, visibility);
+            createFactoryMethods(clazz, visibility);
             clazz.setSuperClass(nodeFactory);
             clazz.add(createNodeFactoryConstructor());
             clazz.add(createCreateNodeMethod());
@@ -224,14 +229,14 @@
         return var;
     }
 
-    public static void createFactoryMethods(ProcessorContext context, NodeData node, CodeTypeElement clazz, CodeTypeElement createdFactoryElement, Modifier createVisibility) {
+    public void createFactoryMethods(CodeTypeElement clazz, Modifier createVisibility) {
         List<ExecutableElement> constructors = NodeBaseFactory.findUserConstructors(createdFactoryElement.asType());
         for (ExecutableElement constructor : constructors) {
-            clazz.add(createCreateMethod(context, node, createVisibility, constructor));
+            clazz.add(createCreateMethod(createVisibility, constructor));
         }
     }
 
-    private static CodeExecutableElement createCreateMethod(ProcessorContext context, NodeData node, Modifier visibility, ExecutableElement constructor) {
+    private CodeExecutableElement createCreateMethod(Modifier visibility, ExecutableElement constructor) {
         CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), constructor);
         method.setSimpleName(CodeNames.of("create"));
         method.getModifiers().clear();
@@ -246,14 +251,18 @@
         if (node.getSpecializations().isEmpty()) {
             body.nullLiteral();
         } else {
-            body.startCall(NodeBaseFactory.nodeSpecializationClassName(node.getSpecializations().get(0)), FACTORY_METHOD_NAME);
+            if (options.useNewLayout()) {
+                body.startNew(NodeGenFactory.nodeType(node));
+            } else {
+                body.startCall(NodeBaseFactory.nodeSpecializationClassName(node.getSpecializations().get(0)), FACTORY_METHOD_NAME);
+            }
             for (VariableElement var : method.getParameters()) {
                 body.string(var.getSimpleName().toString());
             }
             body.end();
+
         }
         body.end();
         return method;
     }
-
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java	Mon Dec 29 23:38:54 2014 +0100
@@ -0,0 +1,2230 @@
+/*
+ * Copyright (c) 2014, 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.dsl.processor.generator;
+
+import static com.oracle.truffle.dsl.processor.generator.GeneratorUtils.*;
+import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
+import static javax.lang.model.element.Modifier.*;
+
+import java.util.*;
+
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.lang.model.util.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.internal.*;
+import com.oracle.truffle.api.dsl.internal.DSLOptions.ImplicitCastOptimization;
+import com.oracle.truffle.api.dsl.internal.DSLOptions.TypeBoxingOptimization;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.nodes.Node.Child;
+import com.oracle.truffle.dsl.processor.*;
+import com.oracle.truffle.dsl.processor.java.*;
+import com.oracle.truffle.dsl.processor.java.model.*;
+import com.oracle.truffle.dsl.processor.model.*;
+import com.oracle.truffle.dsl.processor.parser.*;
+import com.oracle.truffle.dsl.processor.parser.SpecializationGroup.TypeGuard;
+
+public class NodeGenFactory {
+
+    private static final String FRAME_VALUE = "frameValue";
+
+    private final ProcessorContext context;
+    private final NodeData node;
+    private final TypeSystemData typeSystem;
+    private final TypeData genericType;
+    private final DSLOptions options;
+
+    public NodeGenFactory(ProcessorContext context, NodeData node) {
+        this.context = context;
+        this.node = node;
+        this.typeSystem = node.getTypeSystem();
+        this.genericType = typeSystem.getGenericTypeData();
+        this.options = typeSystem.getOptions();
+    }
+
+    public static String nodeTypeName(NodeData node) {
+        return resolveNodeId(node) + "NodeGen";
+    }
+
+    private static String resolveNodeId(NodeData node) {
+        String nodeid = node.getNodeId();
+        if (nodeid.endsWith("Node") && !nodeid.equals("Node")) {
+            nodeid = nodeid.substring(0, nodeid.length() - 4);
+        }
+        return nodeid;
+    }
+
+    public static TypeMirror nodeType(NodeData node) {
+        return new GeneratedTypeMirror(ElementUtils.getPackageName(node.getTemplateType()), nodeTypeName(node));
+    }
+
+    private static String specializationTypeName(SpecializationData specialization) {
+        return specialization.getId() + "Node";
+    }
+
+    private static TypeMirror specializationType(SpecializationData specialization) {
+        return new GeneratedTypeMirror(ElementUtils.getPackageName(specialization.getNode().getTemplateType()) + "." + nodeTypeName(specialization.getNode()), specializationTypeName(specialization));
+    }
+
+    private static String polymorphicTypeProfileFieldName(NodeExecutionData execution) {
+        return execution.getName() + "Type_";
+    }
+
+    private static String nodeFieldName(NodeExecutionData execution) {
+        return execution.getName() + "_";
+    }
+
+    private static String specializationStartFieldName() {
+        return "specialization_";
+    }
+
+    private static String excludedFieldName(SpecializationData specialization) {
+        return "exclude" + specialization.getId() + "_";
+    }
+
+    private static String executeChildMethodName(NodeExecutionData execution, TypeData type) {
+        return "execute" + ElementUtils.firstLetterUpperCase(execution.getName()) + (type.isGeneric() ? "" : getTypeId(type.getBoxedType())) + "_";
+    }
+
+    private static CodeTree accessParent(String name) {
+        if (name == null) {
+            return CodeTreeBuilder.singleString("root");
+        } else {
+            return CodeTreeBuilder.createBuilder().string("root.").string(name).build();
+        }
+    }
+
+    private static String assumptionName(String assumption) {
+        return assumption + "_";
+    }
+
+    public CodeTypeElement create() {
+        String typeName = nodeTypeName(node);
+        TypeMirror baseType = node.getTemplateType().asType();
+        CodeTypeElement clazz = GeneratorUtils.createClass(node, null, modifiers(PUBLIC, FINAL), typeName, baseType);
+
+        clazz.getImplements().add(getType(SpecializedNode.class));
+
+        for (String assumption : node.getAssumptions()) {
+            clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), getType(Assumption.class), assumptionName(assumption)));
+        }
+
+        for (NodeChildData child : node.getChildren()) {
+            clazz.addOptional(createAccessChildMethod(child));
+        }
+
+        for (NodeFieldData field : node.getFields()) {
+            if (!field.isGenerated()) {
+                continue;
+            }
+
+            clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), field.getType(), field.getName()));
+            if (field.getGetter() != null && field.getGetter().getModifiers().contains(Modifier.ABSTRACT)) {
+                CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), field.getGetter());
+                method.getModifiers().remove(Modifier.ABSTRACT);
+                method.createBuilder().startReturn().string("this.").string(field.getName()).end();
+                clazz.add(method);
+            }
+        }
+
+        List<? extends ExecutableElement> superConstructors = ElementFilter.constructorsIn(node.getTemplateType().getEnclosedElements());
+        for (ExecutableElement superConstructor : superConstructors) {
+            if (getVisibility(superConstructor.getModifiers()) == PRIVATE) {
+                continue;
+            }
+            if (superConstructors.size() > 1 && superConstructor.getParameters().size() > 0 &&
+                            ElementUtils.typeEquals(superConstructor.getEnclosingElement().asType(), superConstructor.getParameters().get(0).asType())) {
+                // constructor is copy constructor
+                continue;
+            }
+            clazz.add(createNodeConstructor(clazz, superConstructor));
+        }
+
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            clazz.add(createNodeField(PRIVATE, execution.getNodeType(), nodeFieldName(execution), Child.class));
+        }
+
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            if (findSpecializedExecutables(execution, node.findSpecializedTypes(execution), options.polymorphicTypeBoxingElimination()).isEmpty()) {
+                continue;
+            }
+            clazz.add(createNodeField(PRIVATE, getType(Class.class), polymorphicTypeProfileFieldName(execution), CompilationFinal.class));
+        }
+
+        for (SpecializationData specialization : node.getSpecializations()) {
+            if (mayBeExcluded(specialization)) {
+                clazz.add(createNodeField(PRIVATE, getType(boolean.class), excludedFieldName(specialization), CompilationFinal.class));
+            }
+        }
+
+        clazz.add(createNodeField(PRIVATE, TypeSystemNodeFactory.nodeType(node.getTypeSystem()), specializationStartFieldName(), Child.class));
+        clazz.add(createMethodGetSpecializationNode());
+        clazz.add(createDeepCopyMethod());
+        clazz.add(createGetCostMethod());
+
+        Collection<TypeData> specializedTypes = node.findSpecializedReturnTypes();
+        for (ExecutableTypeData execType : node.getExecutableTypes()) {
+            if (shouldImplementExecutableType(specializedTypes, execType)) {
+                clazz.add(createExecutableTypeOverride(execType));
+            }
+        }
+
+        SpecializationData initialSpecialization = createSpecializations(clazz);
+
+        for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
+            CodeTreeBuilder builder = ((CodeExecutableElement) constructor).appendBuilder();
+            builder.startStatement();
+            builder.string("this.").string(specializationStartFieldName());
+            builder.string(" = ").tree(createCallCreateMethod(initialSpecialization, "this", null));
+            builder.end();
+        }
+
+        return clazz;
+    }
+
+    private CodeExecutableElement createNodeConstructor(CodeTypeElement clazz, ExecutableElement superConstructor) {
+        CodeExecutableElement constructor = GeneratorUtils.createConstructorUsingFields(modifiers(PUBLIC), clazz, superConstructor);
+
+        List<CodeVariableElement> childParameters = new ArrayList<>();
+        for (NodeChildData child : node.getChildren()) {
+            childParameters.add(new CodeVariableElement(child.getOriginalType(), child.getName()));
+        }
+        constructor.getParameters().addAll(superConstructor.getParameters().size(), childParameters);
+
+        CodeTreeBuilder builder = constructor.appendBuilder();
+        List<String> childValues = new ArrayList<>(node.getChildren().size());
+        for (NodeChildData child : node.getChildren()) {
+            String name = child.getName();
+            if (child.getCardinality().isMany()) {
+                CreateCastData createCast = node.findCast(child.getName());
+                if (createCast != null) {
+                    CodeTree nameTree = CodeTreeBuilder.singleString(name);
+                    CodeTreeBuilder callBuilder = builder.create();
+                    callBuilder.string(name).string(" != null ? ");
+                    callBuilder.tree(callTemplateMethod(builder, null, createCast, nameTree));
+                    callBuilder.string(" : null");
+                    name += "_";
+                    builder.declaration(child.getNodeType(), name, callBuilder.build());
+                }
+            }
+            childValues.add(name);
+        }
+
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            CreateCastData createCast = node.findCast(execution.getChild().getName());
+
+            builder.startStatement();
+            builder.string("this.").string(nodeFieldName(execution)).string(" = ");
+
+            String name = childValues.get(node.getChildren().indexOf(execution.getChild()));
+            CodeTreeBuilder accessorBuilder = builder.create();
+            accessorBuilder.string(name);
+
+            if (execution.isIndexed()) {
+                accessorBuilder.string("[").string(String.valueOf(execution.getIndex())).string("]");
+            }
+
+            CodeTree accessor = accessorBuilder.build();
+
+            if (createCast != null && execution.getChild().getCardinality().isOne()) {
+                accessor = callTemplateMethod(builder, null, createCast, accessor);
+            }
+
+            if (execution.isIndexed()) {
+                CodeTreeBuilder nullCheck = builder.create();
+                nullCheck.string(name).string(" != null ? ");
+                nullCheck.tree(accessor);
+                nullCheck.string(" : null");
+                accessor = nullCheck.build();
+            }
+
+            builder.tree(accessor);
+
+            builder.end();
+        }
+
+        return constructor;
+    }
+
+    private static boolean mayBeExcluded(SpecializationData specialization) {
+        return !specialization.getExceptions().isEmpty() || !specialization.getExcludedBy().isEmpty();
+    }
+
+    private SpecializationData createSpecializations(CodeTypeElement clazz) {
+        List<SpecializationData> reachableSpecializations = getReachableSpecializations();
+
+        if (isSingleSpecializable(reachableSpecializations)) {
+            SpecializationData single = reachableSpecializations.get(0);
+            clazz.add(createSingleSpecialization(single));
+            return single;
+        } else {
+            CodeTypeElement baseSpecialization = clazz.add(createBaseSpecialization(clazz));
+            TypeMirror baseSpecializationType = baseSpecialization.asType();
+
+            Map<SpecializationData, CodeTypeElement> generated = new LinkedHashMap<>();
+
+            List<SpecializationData> generateSpecializations = new ArrayList<>();
+            generateSpecializations.add(node.getUninitializedSpecialization());
+            if (needsPolymorphic(reachableSpecializations)) {
+                generateSpecializations.add(node.getPolymorphicSpecialization());
+            }
+            generateSpecializations.addAll(reachableSpecializations);
+
+            for (SpecializationData specialization : generateSpecializations) {
+                generated.put(specialization, clazz.add(createSpecialization(specialization, baseSpecializationType)));
+            }
+
+            baseSpecialization.addOptional(createCreateNext(generated));
+            baseSpecialization.addOptional(createCreateFallback(generated));
+            baseSpecialization.addOptional(createCreatePolymorphic(generated));
+
+            return node.getUninitializedSpecialization();
+        }
+    }
+
+    // create specialization
+
+    private CodeTypeElement createBaseSpecialization(CodeTypeElement parentClass) {
+        CodeTypeElement clazz = createClass(node, null, modifiers(PRIVATE, STATIC, ABSTRACT), "BaseNode", TypeSystemNodeFactory.nodeType(typeSystem));
+
+        clazz.addOptional(createSpecializationConstructor(clazz, null, null));
+        clazz.add(new CodeVariableElement(modifiers(PROTECTED, FINAL), nodeType(node), "root"));
+
+        clazz.addOptional(createUnsupported());
+        clazz.add(createGetSuppliedChildren());
+
+        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));
+            }
+        }
+
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            Collection<TypeData> specializedTypes = node.findSpecializedTypes(execution);
+            specializedTypes.add(genericType);
+            for (TypeData specializedType : specializedTypes) {
+                if (isExecuteChildShared(execution, specializedType)) {
+                    if (specializedType.isGeneric()) {
+                        parentClass.add(createExecuteChildMethod(execution, specializedType));
+                    } else {
+                        clazz.add(createExecuteChildMethod(execution, specializedType));
+                    }
+                }
+            }
+        }
+
+        return clazz;
+    }
+
+    private CodeTypeElement createSingleSpecialization(SpecializationData specialization) {
+        CodeTypeElement clazz = createClass(node, specialization, modifiers(PRIVATE, STATIC, FINAL), specializationTypeName(specialization), TypeSystemNodeFactory.nodeType(typeSystem));
+        CodeExecutableElement constructor = clazz.addOptional(createSpecializationConstructor(clazz, null, "0"));
+        clazz.add(new CodeVariableElement(modifiers(PROTECTED, FINAL), nodeType(node), "root"));
+        TypeData returnType = specialization.getReturnType().getTypeSystemType();
+        Set<Integer> evaluatedCount = getEvaluatedCounts();
+        for (int evaluated : evaluatedCount) {
+            clazz.add(createFastPathExecuteMethod(specialization, null, evaluated));
+        }
+        if (isTypeBoxingEliminated(specialization)) {
+            clazz.add(createFastPathExecuteMethod(specialization, returnType, 0));
+        }
+        clazz.add(createFastPathWrapExecuteMethod(genericType, null));
+
+        clazz.addOptional(createUnsupported());
+        clazz.addOptional(createSpecializationCreateMethod(specialization, constructor));
+        clazz.add(createGetSuppliedChildren());
+
+        return clazz;
+    }
+
+    private CodeTypeElement createSpecialization(SpecializationData specialization, TypeMirror baseType) {
+        CodeTypeElement clazz = createClass(node, specialization, modifiers(PRIVATE, STATIC, FINAL), specializationTypeName(specialization), baseType);
+
+        CodeExecutableElement constructor = clazz.addOptional(createSpecializationConstructor(clazz, specialization, null));
+
+        for (Parameter p : specialization.getSignatureParameters()) {
+            TypeData targetType = p.getTypeSystemType();
+            if (targetType.hasImplicitSourceTypes()) {
+                NodeExecutionData execution = p.getSpecification().getExecution();
+                CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, p.getTypeSystemType());
+                if (implicitProfile != null) {
+                    implicitProfile.getModifiers().add(PRIVATE);
+                    implicitProfile.getModifiers().add(FINAL);
+                    clazz.add(implicitProfile);
+                }
+            }
+        }
+
+        if (specialization.isFallback()) {
+            clazz.add(createFallbackGuardMethod());
+        }
+
+        clazz.addOptional(createSpecializationCreateMethod(specialization, constructor));
+        clazz.addOptional(createMergeMethod(specialization));
+        clazz.addOptional(createIsSameMethod(specialization));
+
+        TypeData returnType = specialization.getReturnType().getTypeSystemType();
+        int signatureSize = specialization.getSignatureSize();
+
+        clazz.add(createFastPathExecuteMethod(specialization, null, signatureSize));
+
+        if (isTypeBoxingEliminated(specialization)) {
+            clazz.add(createFastPathExecuteMethod(specialization, returnType, 0));
+
+            if (signatureSize > 0 && !returnType.isGeneric()) {
+                clazz.add(createFastPathWrapExecuteMethod(genericType, returnType));
+            }
+
+            ExecutableTypeData voidExecutableType = node.findExecutableType(typeSystem.getVoidType(), 0);
+            if (voidExecutableType != null && isTypeBoxingOptimized(options.voidBoxingOptimization(), returnType)) {
+                clazz.add(createFastPathWrapVoidMethod(returnType));
+            }
+        }
+
+        return clazz;
+    }
+
+    private Element createDeepCopyMethod() {
+        CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), getType(Node.class), "deepCopy");
+        executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        CodeTreeBuilder builder = executable.createBuilder();
+        builder.startReturn().startStaticCall(getType(SpecializationNode.class), "updateRoot").string("super.deepCopy()").end().end();
+        return executable;
+    }
+
+    private Element createGetCostMethod() {
+        TypeMirror returnType = getType(NodeCost.class);
+        CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getCost");
+        executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        CodeTreeBuilder builder = executable.createBuilder();
+        builder.startReturn().startCall(specializationStartFieldName(), "getNodeCost").end().end();
+        return executable;
+    }
+
+    private CodeExecutableElement createIsSameMethod(SpecializationData specialization) {
+        if (!specialization.isSpecialized() || !options.implicitCastOptimization().isDuplicateTail()) {
+            return null;
+        }
+
+        List<CodeVariableElement> profiles = new ArrayList<>();
+        for (Parameter parameter : specialization.getSignatureParameters()) {
+            NodeExecutionData execution = parameter.getSpecification().getExecution();
+            if (execution == null) {
+                continue;
+            }
+            CodeVariableElement var = createImplicitProfileParameter(execution, parameter.getTypeSystemType());
+            if (var != null) {
+                profiles.add(var);
+            }
+        }
+
+        if (profiles.isEmpty()) {
+            return null;
+        }
+
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getType(boolean.class), "isSame");
+        method.addParameter(new CodeVariableElement(getType(SpecializationNode.class), "other"));
+        method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        CodeTreeBuilder builder = method.createBuilder();
+
+        builder.startReturn();
+        builder.string("super.isSame(other)");
+
+        for (CodeVariableElement profile : profiles) {
+            builder.string(" && ");
+            builder.string("this.").string(profile.getName()).string(" == ").string("(").cast(specializationType(specialization)).string("other).").string(profile.getName());
+        }
+
+        builder.end();
+        return method;
+    }
+
+    private Element createMergeMethod(SpecializationData specialization) {
+        if (specialization.getExcludedBy().isEmpty() && !specialization.isPolymorphic()) {
+            return null;
+        }
+        TypeMirror specializationNodeType = getType(SpecializationNode.class);
+        CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), specializationNodeType, "merge");
+        executable.addParameter(new CodeVariableElement(specializationNodeType, "newNode"));
+        executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        CodeTreeBuilder builder = executable.createBuilder();
+
+        if (specialization.isPolymorphic()) {
+            builder.statement("return polymorphicMerge(newNode)");
+        } else {
+            boolean elseIf = false;
+            for (SpecializationData containedSpecialization : specialization.getExcludedBy()) {
+                elseIf = builder.startIf(elseIf);
+                builder.string("newNode.getClass() == ").typeLiteral(specializationType(containedSpecialization));
+                builder.end();
+                builder.startBlock();
+                builder.statement("removeSame(\"Contained by " + containedSpecialization.createReferenceName() + "\")");
+                builder.end();
+            }
+            builder.statement("return super.merge(newNode)");
+        }
+
+        return executable;
+    }
+
+    private Element createFastPathWrapVoidMethod(TypeData wrap) {
+        CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), typeSystem.getVoidType().getPrimitiveType(), TypeSystemNodeFactory.executeName(typeSystem.getVoidType()));
+        executable.addParameter(new CodeVariableElement(getType(VirtualFrame.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(TypeData override, TypeData wrap) {
+        CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), override.getPrimitiveType(), TypeSystemNodeFactory.executeName(override));
+        executable.addParameter(new CodeVariableElement(getType(VirtualFrame.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 boolean needsPolymorphic(List<SpecializationData> reachableSpecializations) {
+        if (reachableSpecializations.size() > 1) {
+            return true;
+        }
+        if (options.implicitCastOptimization().isDuplicateTail()) {
+            SpecializationData specialization = reachableSpecializations.get(0);
+            for (Parameter parameter : specialization.getSignatureParameters()) {
+                if (parameter.getTypeSystemType().hasImplicitSourceTypes()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private Element createCreateFallback(Map<SpecializationData, CodeTypeElement> generatedSpecializationClasses) {
+        SpecializationData fallback = node.getGenericSpecialization();
+        if (fallback == null) {
+            return null;
+        }
+        CodeTypeElement generatedType = generatedSpecializationClasses.get(fallback);
+        if (generatedType == null) {
+            return null;
+        }
+
+        TypeMirror returnType = getType(SpecializationNode.class);
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), returnType, "createFallback");
+        method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        method.createBuilder().startReturn().tree(createCallCreateMethod(fallback, null, null)).end();
+        return method;
+    }
+
+    private Element createCreatePolymorphic(Map<SpecializationData, CodeTypeElement> generatedSpecializationClasses) {
+        SpecializationData polymorphic = node.getPolymorphicSpecialization();
+        CodeTypeElement generatedPolymorphic = generatedSpecializationClasses.get(polymorphic);
+        if (generatedPolymorphic == null) {
+            return null;
+        }
+        TypeMirror returnType = getType(SpecializationNode.class);
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), returnType, "createPolymorphic");
+        method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        method.createBuilder().startReturn().tree(createCallCreateMethod(polymorphic, null, null)).end();
+        return method;
+    }
+
+    private CodeExecutableElement createCreateNext(final Map<SpecializationData, CodeTypeElement> specializationClasses) {
+        final LocalContext locals = LocalContext.load(this);
+
+        CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), getType(SpecializationNode.class), "createNext", FRAME_VALUE);
+        method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+
+        CodeTreeBuilder builder = method.createBuilder();
+        SpecializationGroup group = createSpecializationGroups();
+        CodeTree execution = createGuardAndCast(group, genericType, locals, new SpecializationExecution() {
+            public CodeTree createExecute(SpecializationData specialization, LocalContext values) {
+                CodeTypeElement generatedType = specializationClasses.get(specialization);
+                if (generatedType == null) {
+                    throw new AssertionError("No generated type for " + specialization);
+                }
+                return createSlowPathExecute(specialization, locals);
+            }
+
+            public boolean isFastPath() {
+                return false;
+            }
+        });
+
+        builder.tree(execution);
+
+        if (hasFallthrough(group, genericType, locals, false)) {
+            builder.returnNull();
+        }
+        return method;
+    }
+
+    private CodeExecutableElement createFallbackGuardMethod() {
+        boolean frameUsed = node.isFrameUsedByAnyGuard(context);
+        LocalContext locals = LocalContext.load(this);
+
+        if (!frameUsed) {
+            locals.removeValue(FRAME_VALUE);
+        }
+
+        CodeExecutableElement boundaryMethod = locals.createMethod(modifiers(PRIVATE), getType(boolean.class), "guardFallback", FRAME_VALUE);
+        if (!frameUsed) {
+            boundaryMethod.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(TruffleBoundary.class)));
+        }
+
+        CodeTreeBuilder builder = boundaryMethod.createBuilder();
+        builder.startReturn();
+        builder.startCall("createNext");
+        locals.addReferencesTo(builder, FRAME_VALUE);
+        builder.end();
+        builder.string(" == null");
+        builder.end();
+        return boundaryMethod;
+    }
+
+    private ExecutableElement createAccessChildMethod(NodeChildData child) {
+        if (child.getAccessElement() != null && child.getAccessElement().getModifiers().contains(Modifier.ABSTRACT)) {
+            ExecutableElement getter = (ExecutableElement) child.getAccessElement();
+            CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), getter);
+            method.getModifiers().remove(Modifier.ABSTRACT);
+
+            List<NodeExecutionData> executions = new ArrayList<>();
+            for (NodeExecutionData execution : node.getChildExecutions()) {
+                if (execution.getChild() == child) {
+                    executions.add(execution);
+                }
+            }
+
+            CodeTreeBuilder builder = method.createBuilder();
+            if (child.getCardinality().isMany()) {
+                builder.startReturn().startNewArray((ArrayType) child.getOriginalType(), null);
+                for (NodeExecutionData execution : executions) {
+                    builder.string(nodeFieldName(execution));
+                }
+                builder.end().end();
+            } else {
+                for (NodeExecutionData execution : executions) {
+                    builder.startReturn().string("this.").string(nodeFieldName(execution)).end();
+                    break;
+                }
+            }
+            return method;
+        }
+        return null;
+    }
+
+    private boolean isTypeBoxingEliminated(SpecializationData specialization) {
+        if (specialization.getMethod() == null) {
+            return false;
+        }
+
+        TypeBoxingOptimization optimization = options.monomorphicTypeBoxingOptimization();
+        if (isTypeBoxingOptimized(optimization, specialization.getReturnType().getTypeSystemType())) {
+            return true;
+        }
+        for (Parameter p : specialization.getSignatureParameters()) {
+            if (isTypeBoxingOptimized(optimization, p.getTypeSystemType())) {
+                return true;
+            }
+        }
+        return false;
+
+    }
+
+    private Set<Integer> getEvaluatedCounts() {
+        Set<Integer> evaluatedCount = new TreeSet<>();
+        Collection<TypeData> returnSpecializedTypes = node.findSpecializedReturnTypes();
+        for (ExecutableTypeData execType : node.getExecutableTypes()) {
+            if (shouldImplementExecutableType(returnSpecializedTypes, execType)) {
+                evaluatedCount.add(execType.getEvaluatedCount());
+            }
+        }
+        return evaluatedCount;
+    }
+
+    // create specialization
+
+    private Element createUnsupported() {
+        SpecializationData fallback = node.getGenericSpecialization();
+        if (fallback == null || optimizeFallback(fallback) || fallback.getMethod() == null) {
+            return null;
+        }
+        LocalContext locals = LocalContext.load(this);
+
+        CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), genericType.getPrimitiveType(), "unsupported", FRAME_VALUE);
+        method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startReturn();
+        builder.tree(callTemplateMethod(builder, accessParent(null), fallback, locals));
+        builder.end();
+
+        return method;
+    }
+
+    private boolean isSingleSpecializable(List<SpecializationData> reachableSpecializations) {
+        if (reachableSpecializations.size() != 1) {
+            return false;
+        }
+        return !reachableSpecializations.get(0).hasRewrite(context);
+    }
+
+    private List<SpecializationData> getReachableSpecializations() {
+        List<SpecializationData> specializations = new ArrayList<>();
+        for (SpecializationData specialization : node.getSpecializations()) {
+            if (specialization.isReachable() && //
+                            (specialization.isSpecialized() //
+                            || (specialization.isFallback() && optimizeFallback(specialization)))) {
+                specializations.add(specialization);
+            }
+        }
+        return specializations;
+    }
+
+    private boolean optimizeFallback(SpecializationData specialization) {
+        switch (options.optimizeFallback()) {
+            case NEVER:
+                return false;
+            case DECLARED:
+                return specialization.getMethod() != null;
+            case ALWAYS:
+                return true;
+            default:
+                throw new AssertionError();
+        }
+    }
+
+    private CodeExecutableElement createExecutableTypeOverride(ExecutableTypeData execType) {
+        final String varArgsName = "args";
+        final TypeData returnType = execType.getType();
+        final TypeData executedType = execType.getEvaluatedCount() > 0 ? null : returnType;
+
+        CodeExecutableElement method = cloneExecutableTypeOverride(execType, varArgsName);
+
+        LocalContext locals = LocalContext.load(this, execType.getSignatureSize());
+
+        // rename varargs parameter
+        int signatureIndex = 0;
+        for (Parameter parameter : execType.getSignatureParameters()) {
+            if (parameter.isTypeVarArgs()) {
+                String newName = varArgsName + "[" + parameter.getTypeVarArgsIndex() + "]";
+                NodeExecutionData execution = node.getChildExecutions().get(signatureIndex);
+                locals.setValue(execution, locals.getValue(execution).accessWith(CodeTreeBuilder.singleString(newName)));
+            }
+            signatureIndex++;
+        }
+
+        CodeTreeBuilder builder = method.createBuilder();
+
+        // create acceptAndExecute
+        CodeTreeBuilder executeBuilder = builder.create();
+        executeBuilder.startCall(specializationStartFieldName(), TypeSystemNodeFactory.executeName(executedType));
+        Parameter frame = execType.getFrame();
+        if (frame == null) {
+            executeBuilder.nullLiteral();
+        } else {
+            executeBuilder.string(frame.getLocalName());
+        }
+        locals.addReferencesTo(executeBuilder);
+        executeBuilder.end();
+
+        CodeTreeBuilder contentBuilder = builder.create();
+        contentBuilder.startReturn();
+        contentBuilder.tree(TypeSystemCodeGenerator.expect(executedType, returnType, executeBuilder.build()));
+        contentBuilder.end();
+
+        // try catch assert if unexpected value is not expected
+        if (!execType.hasUnexpectedValue(context) && !returnType.isGeneric() && !returnType.isVoid()) {
+            builder.startTryBlock();
+            builder.tree(contentBuilder.build());
+            builder.end().startCatchBlock(getType(UnexpectedResultException.class), "ex");
+            builder.startThrow().startNew(getType(AssertionError.class)).end().end();
+            builder.end();
+        } else {
+            builder.tree(contentBuilder.build());
+        }
+
+        return method;
+    }
+
+    private CodeExecutableElement cloneExecutableTypeOverride(ExecutableTypeData execType, final String varArgsName) throws AssertionError {
+        CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), execType.getMethod());
+
+        method.getAnnotationMirrors().clear();
+        method.getModifiers().remove(Modifier.ABSTRACT);
+
+        if (!execType.getMethod().isVarArgs() && execType.getParameters().size() != method.getParameters().size()) {
+            throw new AssertionError("Should be verified in the parser");
+        }
+
+        // align argument names
+        int index = 0;
+        for (Parameter parameter : execType.getParameters()) {
+            CodeVariableElement var = (CodeVariableElement) method.getParameters().get(index);
+            if (parameter.isTypeVarArgs()) {
+                var.getAnnotationMirrors().clear();
+                var.setName(varArgsName);
+                break;
+            }
+            var.setName(LocalVariable.fromParameter(parameter).createParameter().getName());
+            var.getAnnotationMirrors().clear();
+            index++;
+        }
+        return method;
+    }
+
+    private boolean shouldImplementExecutableType(Collection<TypeData> specializedTypes, ExecutableTypeData execType) {
+        TypeData type = execType.getType();
+        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 (type.isGeneric()) {
+            return true;
+        } else if (type.isVoid()) {
+            for (TypeData 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");
+        method.createBuilder().startReturn().string(specializationStartFieldName()).end();
+        return method;
+    }
+
+    private TypeMirror getType(Class<?> clazz) {
+        return context.getType(clazz);
+    }
+
+    private CodeVariableElement createNodeField(Modifier visibility, TypeMirror type, String name, Class<?> annotationType) {
+        CodeVariableElement childField = new CodeVariableElement(modifiers(), type, name);
+        childField.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(annotationType)));
+        setVisibility(childField.getModifiers(), visibility);
+        return childField;
+    }
+
+    private static List<ExecutableTypeData> findSpecializedExecutables(NodeExecutionData execution, Collection<TypeData> types, TypeBoxingOptimization optimization) {
+        if (optimization == TypeBoxingOptimization.NONE) {
+            return Collections.emptyList();
+        }
+
+        List<ExecutableTypeData> executables = new ArrayList<>();
+        for (TypeData type : types) {
+            if (!isTypeBoxingOptimized(optimization, type)) {
+                continue;
+            }
+            ExecutableTypeData foundType = execution.getChild().getNodeData().findExecutableType(type, execution.getChild().getExecuteWith().size());
+            if (foundType != null) {
+                executables.add(foundType);
+            }
+        }
+        return executables;
+    }
+
+    private static CodeTree callTemplateMethod(CodeTreeBuilder parent, CodeTree receiver, TemplateMethod method, CodeTree... boundValues) {
+        CodeTreeBuilder builder = parent.create();
+        if (method.getMethod().getModifiers().contains(STATIC)) {
+            builder.startStaticCall(method.getMethod().getEnclosingElement().asType(), method.getMethodName());
+        } else {
+            builder.startCall(receiver, method.getMethodName());
+        }
+        int index = -1;
+        for (Parameter parameter : method.getParameters()) {
+            index++;
+            if (index < boundValues.length) {
+                CodeTree tree = boundValues[index];
+                if (tree != null) {
+                    builder.tree(tree);
+                    continue;
+                }
+            }
+            builder.string(parameter.getLocalName());
+        }
+        builder.end();
+        return builder.build();
+    }
+
+    private static CodeTree callTemplateMethod(CodeTreeBuilder parent, CodeTree receiver, TemplateMethod method, LocalContext currentValues) {
+        CodeTree[] bindings = new CodeTree[method.getParameters().size()];
+
+        int signatureIndex = 0;
+        for (int i = 0; i < bindings.length; i++) {
+            Parameter parameter = method.getParameters().get(i);
+            LocalVariable var = currentValues.get(parameter, signatureIndex);
+            if (var != null) {
+                CodeTree valueReference = bindings[i] = var.createReference();
+                if (parameter.getTypeSystemType() != null && var.getType() != null && var.getType().needsCastTo(parameter.getTypeSystemType())) {
+                    valueReference = TypeSystemCodeGenerator.cast(parameter.getTypeSystemType(), valueReference);
+                }
+                bindings[i] = valueReference;
+            }
+            if (parameter.getSpecification().isSignature()) {
+                signatureIndex++;
+            }
+        }
+        return callTemplateMethod(parent, receiver, method, bindings);
+    }
+
+    private SpecializationGroup createSpecializationGroups() {
+        return SpecializationGroup.create(getReachableSpecializations());
+    }
+
+    private CodeTree createSlowPathExecute(SpecializationData specialization, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        if (specialization.isFallback()) {
+            return builder.returnNull().build();
+        }
+        if (node.isFrameUsedByAnyGuard(context)) {
+            builder.tree(createTransferToInterpreterAndInvalidate());
+        }
+        for (SpecializationData otherSpeciailzation : node.getSpecializations()) {
+            if (otherSpeciailzation == specialization) {
+                continue;
+            }
+            if (otherSpeciailzation.getExcludedBy().contains(specialization)) {
+                builder.startStatement();
+                builder.tree(accessParent(excludedFieldName(otherSpeciailzation)));
+                builder.string(" = true");
+                builder.end();
+            }
+        }
+
+        builder.startReturn().tree(createCallCreateMethod(specialization, null, currentValues)).end();
+
+        if (mayBeExcluded(specialization)) {
+            CodeTreeBuilder checkHasSeenBuilder = builder.create();
+            checkHasSeenBuilder.startIf().string("!").tree(accessParent(excludedFieldName(specialization))).end().startBlock();
+            checkHasSeenBuilder.tree(builder.build());
+            checkHasSeenBuilder.end();
+            return checkHasSeenBuilder.build();
+        }
+        return builder.build();
+    }
+
+    private static boolean hasFallthrough(SpecializationGroup group, TypeData forType, LocalContext currentValues, boolean fastPath) {
+        for (TypeGuard guard : group.getTypeGuards()) {
+            if (currentValues.getValue(guard.getSignatureIndex()) == null) {
+                // not evaluated
+                return true;
+            }
+            LocalVariable value = currentValues.getValue(guard.getSignatureIndex());
+            if (value.getType().needsCastTo(guard.getType())) {
+                return true;
+            }
+        }
+
+        List<GuardExpression> expressions = new ArrayList<>(group.getGuards());
+        expressions.removeAll(group.findElseConnectableGuards());
+        if (!expressions.isEmpty()) {
+            return true;
+        }
+
+        if ((!fastPath || forType.isGeneric()) && !group.getAssumptions().isEmpty()) {
+            return true;
+        }
+
+        if (!fastPath && group.getSpecialization() != null && !group.getSpecialization().getExceptions().isEmpty()) {
+            return true;
+        }
+
+        if (!group.getChildren().isEmpty()) {
+            return hasFallthrough(group.getChildren().get(group.getChildren().size() - 1), forType, currentValues, fastPath);
+        }
+
+        return false;
+    }
+
+    private Element createGetSuppliedChildren() {
+        ArrayType nodeArray = context.getEnvironment().getTypeUtils().getArrayType(getType(Node.class));
+
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), nodeArray, "getSuppliedChildren");
+        method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+
+        CodeTreeBuilder builder = method.createBuilder();
+
+        builder.startReturn().startNewArray(nodeArray, null);
+        for (int i = 0; i < node.getChildExecutions().size(); i++) {
+            NodeExecutionData execution = node.getChildExecutions().get(i);
+            if (execution.isShortCircuit()) {
+                builder.nullLiteral();
+            }
+            builder.tree(accessParent(nodeFieldName(execution)));
+        }
+        builder.end().end();
+
+        return method;
+    }
+
+    // create specialization
+
+    private CodeTree createCallCreateMethod(SpecializationData specialization, String rootName, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+
+        TypeMirror specializationType = specializationType(specialization);
+        if (options.useLazyClassLoading()) {
+            builder.startStaticCall(specializationType(specialization), "create");
+        } else {
+            builder.startNew(specializationType);
+        }
+        if (rootName != null) {
+            builder.string(rootName);
+        } else {
+            builder.string("root");
+        }
+        if (currentValues != null) {
+            for (Parameter p : specialization.getSignatureParameters()) {
+                LocalVariable local = currentValues.get(p.getLocalName());
+                CodeVariableElement var = createImplicitProfileParameter(p.getSpecification().getExecution(), p.getTypeSystemType());
+                if (var != null) {
+                    builder.tree(local.createReference());
+                }
+            }
+        }
+        builder.end();
+
+        return builder.build();
+    }
+
+    private Element createSpecializationCreateMethod(SpecializationData specialization, CodeExecutableElement constructor) {
+        if (!options.useLazyClassLoading()) {
+            return null;
+        }
+
+        CodeExecutableElement executable = CodeExecutableElement.clone(context.getEnvironment(), constructor);
+
+        TypeMirror specializationType = specializationType(specialization);
+
+        executable.setReturnType(TypeSystemNodeFactory.nodeType(typeSystem));
+        executable.setSimpleName(CodeNames.of("create"));
+        executable.getModifiers().add(STATIC);
+
+        CodeTreeBuilder builder = executable.createBuilder();
+        builder.startReturn().startNew(specializationType);
+        for (VariableElement parameter : executable.getParameters()) {
+            builder.string(parameter.getSimpleName().toString());
+        }
+        builder.end().end();
+        return executable;
+    }
+
+    private static String implicitClassFieldName(NodeExecutionData execution) {
+        return execution.getName() + "ImplicitType";
+    }
+
+    private static String implicitNodeFieldName(NodeExecutionData execution) {
+        return execution.getName() + "Cast";
+    }
+
+    private CodeExecutableElement createSpecializationConstructor(CodeTypeElement clazz, SpecializationData specialization, String constantIndex) {
+        CodeExecutableElement constructor = new CodeExecutableElement(modifiers(), null, clazz.getSimpleName().toString());
+
+        constructor.addParameter(new CodeVariableElement(nodeType(node), "root"));
+        CodeTreeBuilder builder = constructor.createBuilder();
+
+        if (specialization == null) {
+            if (constantIndex == null) {
+                builder.statement("super(index)");
+                constructor.addParameter(new CodeVariableElement(getType(int.class), "index"));
+            } else {
+                builder.startStatement().startSuperCall().string(constantIndex).end().end();
+            }
+            builder.statement("this.root = root");
+        } else {
+            int index = resolveSpecializationIndex(specialization);
+            builder.startStatement().startSuperCall().string("root").string(String.valueOf(index)).end().end();
+
+            for (Parameter p : specialization.getSignatureParameters()) {
+                NodeExecutionData execution = p.getSpecification().getExecution();
+
+                CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, p.getTypeSystemType());
+                if (implicitProfile != null) {
+                    LocalVariable var = LocalVariable.fromParameter(p).makeGeneric();
+
+                    String implicitFieldName = implicitProfile.getName();
+                    if (options.implicitCastOptimization().isDuplicateTail()) {
+                        constructor.addParameter(var.createParameter());
+                        CodeTree implicitType = TypeSystemCodeGenerator.implicitType(p.getTypeSystemType(), var.createReference());
+                        builder.startStatement().string("this.").string(implicitFieldName).string(" = ").tree(implicitType).end();
+                    } else if (options.implicitCastOptimization().isMergeCasts()) {
+                        // use node that supports polymorphism
+                        constructor.addParameter(var.createParameter());
+                        builder.startStatement().string("this.").string(implicitFieldName).string(" = ").tree(ImplicitCastNodeFactory.create(p.getTypeSystemType(), var.createReference())).end();
+                    } else {
+                        throw new AssertionError();
+                    }
+                }
+            }
+        }
+
+        if (constructor.getParameters().isEmpty()) {
+            // do not generate default constructor
+            return null;
+        }
+        return constructor;
+    }
+
+    // TODO this logic can be inlined to the parser as soon as the old NodeGen layout is gone
+    private static int resolveSpecializationIndex(SpecializationData specialization) {
+        if (specialization.isFallback()) {
+            return Integer.MAX_VALUE - 1;
+        } else if (specialization.isUninitialized()) {
+            return Integer.MAX_VALUE;
+        } else if (specialization.isPolymorphic()) {
+            return 0;
+        } else {
+            return specialization.getIndex();
+        }
+    }
+
+    private CodeTree createCallNext(TypeData forType, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.startCall("next", TypeSystemNodeFactory.executeName(null));
+        currentValues.addReferencesTo(builder, FRAME_VALUE);
+        builder.end();
+        return TypeSystemCodeGenerator.expect(genericType, forType, builder.build());
+    }
+
+    private static CodeTree createCallDelegate(String methodName, String reason, TypeData forType, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.startCall(methodName);
+        if (reason != null) {
+            builder.doubleQuote(reason);
+        }
+        currentValues.addReferencesTo(builder, FRAME_VALUE);
+        builder.end();
+
+        TypeData executedType = forType.getTypeSystem().getGenericTypeData();
+        return TypeSystemCodeGenerator.expect(executedType, forType, builder.build());
+    }
+
+    private static ExecutableTypeData findSpecializedExecutableType(NodeExecutionData execution, TypeData type) {
+        NodeChildData child = execution.getChild();
+        int executeWithCount = child.getExecuteWith().size();
+        return child.getNodeData().findExecutableType(type, executeWithCount);
+    }
+
+    private boolean hasUnexpectedResult(NodeExecutionData execution, TypeData type) {
+        if (type.isGeneric() || type.isVoid()) {
+            return false;
+        }
+        List<ExecutableTypeData> executableTypes = new ArrayList<>();
+        executableTypes.add(findSpecializedExecutableType(execution, type));
+
+        if (!options.implicitCastOptimization().isNone()) {
+            executableTypes.addAll(findSpecializedExecutables(execution, type.getImplicitSourceTypes(), options.implicitTypeBoxingOptimization()));
+        }
+
+        for (ExecutableTypeData executableType : executableTypes) {
+            if (executableType != null && executableType.hasUnexpectedValue(context)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private Element createFastPathExecuteMethod(SpecializationData specialization, final TypeData forType, int evaluatedArguments) {
+        TypeData type = forType == null ? genericType : forType;
+        LocalContext currentLocals = LocalContext.load(this, evaluatedArguments);
+
+        CodeExecutableElement executable = currentLocals.createMethod(modifiers(PUBLIC), type.getPrimitiveType(), TypeSystemNodeFactory.executeName(forType), FRAME_VALUE);
+        executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+
+        if (!type.isGeneric()) {
+            executable.getThrownTypes().add(getType(UnexpectedResultException.class));
+        }
+
+        CodeTreeBuilder builder = executable.createBuilder();
+
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            LocalVariable var = currentLocals.getValue(execution);
+            if (var == null) {
+                TypeData targetType;
+                if (specialization == null) {
+                    targetType = genericType;
+                } else {
+                    targetType = specialization.findParameterOrDie(execution).getTypeSystemType();
+                }
+                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);
+            }
+        }
+
+        LocalContext originalValues = currentLocals.copy();
+        if (specialization == null) {
+            builder.startReturn().tree(createCallDelegate("acceptAndExecute", null, type, currentLocals)).end();
+        } else if (specialization.isPolymorphic()) {
+            builder.startReturn().tree(createCallNext(type, currentLocals)).end();
+        } else if (specialization.isUninitialized()) {
+            builder.startReturn().tree(createCallDelegate("uninitialized", null, type, currentLocals)).end();
+        } else {
+            final TypeData type_ = type;
+            SpecializationGroup group = SpecializationGroup.create(specialization);
+            SpecializationExecution executionFactory = new SpecializationExecution() {
+                public CodeTree createExecute(SpecializationData s, LocalContext values) {
+                    return createFastPathExecute(type_, s, values);
+                }
+
+                public boolean isFastPath() {
+                    return true;
+                }
+            };
+            builder.tree(createGuardAndCast(group, type, currentLocals, executionFactory));
+            if (hasFallthrough(group, type, originalValues, true) || group.getSpecialization().isFallback()) {
+                builder.startReturn().tree(createCallNext(type, originalValues)).end();
+            }
+        }
+
+        return executable;
+    }
+
+    private LocalVariable resolveShortCircuit(SpecializationData specialization, NodeExecutionData execution, LocalContext currentLocals) {
+        LocalVariable shortCircuit = null;
+        SpecializationData resolvedSpecialization = specialization;
+        if (specialization == null) {
+            resolvedSpecialization = node.getGenericSpecialization();
+        }
+
+        if (execution.isShortCircuit()) {
+            ShortCircuitData shortCircuitData = resolvedSpecialization.getShortCircuits().get(calculateShortCircuitIndex(execution));
+            CodeTree access = callTemplateMethod(CodeTreeBuilder.createBuilder(), accessParent(null), shortCircuitData, currentLocals);
+            shortCircuit = currentLocals.createShortCircuitValue(execution).accessWith(access);
+        }
+        return shortCircuit;
+    }
+
+    private int calculateShortCircuitIndex(NodeExecutionData execution) {
+        int shortCircuitIndex = 0;
+        for (NodeExecutionData otherExectuion : node.getChildExecutions()) {
+            if (otherExectuion.isShortCircuit()) {
+                if (otherExectuion == execution) {
+                    break;
+                }
+                shortCircuitIndex++;
+            }
+        }
+        return shortCircuitIndex;
+    }
+
+    private CodeTree createFastPathExecute(final TypeData forType, SpecializationData specialization, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        int ifCount = 0;
+        if (specialization.isFallback()) {
+            builder.startIf().startCall("guardFallback");
+            if (node.isFrameUsedByAnyGuard(context)) {
+                builder.string(FRAME_VALUE);
+            }
+            currentValues.addReferencesTo(builder);
+
+            builder.end();
+            builder.end();
+            builder.startBlock();
+            ifCount++;
+        }
+        CodeTreeBuilder execute = builder.create();
+        execute.startReturn();
+        if (specialization.getMethod() == null) {
+            execute.startCall("unsupported");
+            currentValues.addReferencesTo(execute, FRAME_VALUE);
+            execute.end();
+        } else {
+            execute.tree(callTemplateMethod(execute, accessParent(null), specialization, currentValues));
+        }
+        execute.end();
+        builder.tree(createFastPathTryCatchRewriteException(specialization, forType, currentValues, execute.build()));
+
+        builder.end(ifCount);
+        return builder.build();
+    }
+
+    private CodeTree createGuardAndCast(SpecializationGroup group, TypeData forType, LocalContext currentValues, SpecializationExecution execution) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+
+        Set<TypeGuard> castGuards;
+        if (execution.isFastPath()) {
+            castGuards = null; // cast all
+        } else {
+            castGuards = new HashSet<>();
+            for (TypeGuard castGuard : group.getTypeGuards()) {
+                if (isTypeGuardUsedInAnyGuardBelow(group, currentValues, castGuard)) {
+                    castGuards.add(castGuard);
+                }
+            }
+        }
+        CodeTree[] checkAndCast = createTypeCheckAndCast(group.getTypeGuards(), castGuards, currentValues, execution);
+        CodeTree check = checkAndCast[0];
+        CodeTree cast = checkAndCast[1];
+
+        List<GuardExpression> elseGuardExpressions = group.findElseConnectableGuards();
+        List<GuardExpression> guardExpressions = new ArrayList<>(group.getGuards());
+        guardExpressions.removeAll(elseGuardExpressions);
+        CodeTree methodGuards = createMethodGuardCheck(guardExpressions, currentValues);
+
+        if (!group.getAssumptions().isEmpty()) {
+            if (execution.isFastPath() && !forType.isGeneric()) {
+                cast = appendAssumptionFastPath(cast, group.getAssumptions(), forType, currentValues);
+            } else {
+                methodGuards = appendAssumptionSlowPath(methodGuards, group.getAssumptions());
+            }
+        }
+
+        int ifCount = 0;
+        if (!check.isEmpty()) {
+            builder.startIf();
+            builder.tree(check).end();
+            builder.startBlock();
+            ifCount++;
+        }
+        if (!cast.isEmpty()) {
+            builder.tree(cast);
+        }
+        boolean elseIf = !elseGuardExpressions.isEmpty();
+        if (!methodGuards.isEmpty()) {
+            builder.startIf(elseIf);
+            builder.tree(methodGuards).end();
+            builder.startBlock();
+            ifCount++;
+        } else if (elseIf) {
+            builder.startElseBlock();
+            ifCount++;
+        }
+
+        boolean reachable = isReachableGroup(group, ifCount);
+        if (reachable) {
+            for (SpecializationGroup child : group.getChildren()) {
+                builder.tree(createGuardAndCast(child, forType, currentValues.copy(), execution));
+            }
+            SpecializationData specialization = group.getSpecialization();
+            if (specialization != null) {
+                builder.tree(execution.createExecute(specialization, currentValues));
+            }
+        }
+        builder.end(ifCount);
+
+        return builder.build();
+    }
+
+    private static CodeTree appendAssumptionSlowPath(CodeTree methodGuards, List<String> assumptions) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+
+        builder.tree(methodGuards);
+        String connect = methodGuards.isEmpty() ? "" : " && ";
+        for (String assumption : assumptions) {
+            builder.string(connect);
+            builder.startCall(accessParent(assumptionName(assumption)), "isValid").end();
+            connect = " && ";
+        }
+
+        return builder.build();
+    }
+
+    private CodeTree appendAssumptionFastPath(CodeTree casts, List<String> assumptions, TypeData forType, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.tree(casts);
+        builder.startTryBlock();
+        for (String assumption : assumptions) {
+            builder.startStatement().startCall(accessParent(assumptionName(assumption)), "check").end().end();
+        }
+        builder.end().startCatchBlock(getType(InvalidAssumptionException.class), "ae");
+        builder.startReturn().tree(createCallNext(forType, currentValues)).end();
+        builder.end();
+        return builder.build();
+    }
+
+    private static boolean isReachableGroup(SpecializationGroup group, int ifCount) {
+        if (ifCount != 0) {
+            return true;
+        }
+        SpecializationGroup previous = group.getPreviousGroup();
+        if (previous == null || previous.findElseConnectableGuards().isEmpty()) {
+            return true;
+        }
+
+        /*
+         * Hacky else case. In this case the specialization is not reachable due to previous else
+         * branch. This is only true if the minimum state is not checked.
+         */
+        if (previous.getGuards().size() == 1 && previous.getTypeGuards().isEmpty() && previous.getAssumptions().isEmpty() &&
+                        (previous.getParent() == null || previous.getMaxSpecializationIndex() != previous.getParent().getMaxSpecializationIndex())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean isTypeGuardUsedInAnyGuardBelow(SpecializationGroup group, LocalContext currentValues, TypeGuard typeGuard) {
+        NodeExecutionData execution = node.getChildExecutions().get(typeGuard.getSignatureIndex());
+
+        for (GuardExpression guard : group.getGuards()) {
+            List<Parameter> guardParameters = guard.getResolvedGuard().findByExecutionData(execution);
+            TypeData sourceType = currentValues.getValue(typeGuard.getSignatureIndex()).getType();
+
+            for (Parameter guardParameter : guardParameters) {
+                if (sourceType.needsCastTo(guardParameter.getType())) {
+                    return true;
+                }
+            }
+        }
+
+        for (SpecializationGroup child : group.getChildren()) {
+            if (isTypeGuardUsedInAnyGuardBelow(child, currentValues, typeGuard)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private CodeExecutableElement createExecuteChildMethod(NodeExecutionData execution, TypeData targetType) {
+        LocalContext locals = LocalContext.load(this, 0);
+
+        CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), targetType.getPrimitiveType(), executeChildMethodName(execution, targetType), FRAME_VALUE);
+        if (hasUnexpectedResult(execution, targetType)) {
+            method.getThrownTypes().add(getType(UnexpectedResultException.class));
+        }
+
+        CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, targetType);
+        if (implicitProfile != null) {
+            method.addParameter(implicitProfile);
+        }
+
+        for (int i = 0; i < execution.getChild().getExecuteWith().size(); i++) {
+            NodeExecutionData executeWith = node.getChildExecutions().get(i);
+            LocalVariable var = locals.createValue(executeWith, genericType);
+            method.addParameter(var.createParameter());
+            locals.setValue(executeWith, var);
+        }
+
+        CodeTreeBuilder builder = method.createBuilder();
+        CodeTree executeChild = createExecuteChild(execution, targetType, locals.createValue(execution, targetType), locals, true);
+        if (executeChild.isSingleLine()) {
+            builder.statement(executeChild);
+        } else {
+            builder.tree(executeChild);
+        }
+        return method;
+    }
+
+    private CodeVariableElement createImplicitProfileParameter(NodeExecutionData execution, TypeData targetType) {
+        if (targetType.hasImplicitSourceTypes()) {
+            switch (options.implicitCastOptimization()) {
+                case NONE:
+                    return null;
+                case DUPLICATE_TAIL:
+                    return new CodeVariableElement(getType(Class.class), implicitClassFieldName(execution));
+                case MERGE_CASTS:
+                    return new CodeVariableElement(ImplicitCastNodeFactory.type(targetType), implicitNodeFieldName(execution));
+            }
+        }
+        return null;
+    }
+
+    private boolean isExecuteChildShared(NodeExecutionData execution, TypeData targetType) {
+        if (targetType.isVoid()) {
+            return false;
+        } else if (targetType.isGeneric()) {
+            if (isSingleSpecializable(getReachableSpecializations())) {
+                return false;
+            }
+            return findSpecializedExecutables(execution, node.findSpecializedTypes(execution), options.polymorphicTypeBoxingElimination()).size() >= 1;
+        } else {
+            if (!isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), targetType)) {
+                return false;
+            }
+            if (!targetType.hasImplicitSourceTypes()) {
+                return false;
+            }
+
+            int uses = 0;
+            for (SpecializationData specialization : node.getSpecializations()) {
+                List<Parameter> parameters = specialization.findByExecutionData(execution);
+                for (Parameter parameter : parameters) {
+                    if (targetType.equals(parameter.getTypeSystemType())) {
+                        uses++;
+                    }
+                }
+            }
+            if (uses > 1) {
+                return findSpecializedExecutables(execution, targetType.getImplicitSourceTypes(), options.implicitTypeBoxingOptimization()).size() > 1;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private CodeTree createAssignExecuteChild(NodeExecutionData execution, TypeData returnType, LocalVariable targetValue, LocalVariable shortCircuit, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        boolean hasUnexpected = hasUnexpectedResult(execution, targetValue.getType());
+
+        CodeTree executeChild;
+        if (isExecuteChildShared(execution, targetValue.getType())) {
+            executeChild = createCallSharedExecuteChild(execution, targetValue, currentValues);
+        } else {
+            executeChild = createExecuteChild(execution, targetValue.getType(), targetValue, currentValues, false);
+        }
+
+        builder.tree(createTryExecuteChild(targetValue, executeChild, shortCircuit == null, hasUnexpected));
+        builder.end();
+        if (hasUnexpected) {
+            builder.startCatchBlock(getType(UnexpectedResultException.class), "ex");
+
+            LocalContext slowPathValues = currentValues.copy();
+            slowPathValues.setValue(execution, targetValue.makeGeneric().accessWith(CodeTreeBuilder.singleString("ex.getResult()")));
+            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));
+                    slowPathValues.setValue(otherExecution, childEvaluatedValue);
+                } else {
+                    // skip forward already evaluated
+                    found = execution == otherExecution;
+                }
+            }
+            builder.startReturn().tree(createCallNext(returnType, slowPathValues)).end();
+            builder.end();
+        }
+
+        if (shortCircuit != null) {
+            currentValues.setShortCircuitValue(execution, shortCircuit.accessWith(null));
+        }
+        return createShortCircuit(targetValue, shortCircuit, builder.build());
+    }
+
+    private static CodeTree createShortCircuit(LocalVariable targetValue, LocalVariable shortCircuitValue, CodeTree tryExecute) {
+        if (shortCircuitValue == null) {
+            return tryExecute;
+        }
+
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+
+        builder.tree(shortCircuitValue.createDeclaration(shortCircuitValue.createReference()));
+        builder.tree(targetValue.createDeclaration(builder.create().defaultValue(targetValue.getTypeMirror()).build()));
+
+        builder.startIf().string(shortCircuitValue.getName()).end().startBlock();
+        builder.tree(tryExecute);
+        builder.end();
+
+        return builder.build();
+    }
+
+    private static CodeTree createTryExecuteChild(LocalVariable value, CodeTree executeChild, boolean needDeclaration, boolean hasTry) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        boolean hasDeclaration = false;
+        if ((hasTry || !executeChild.isSingleLine()) && needDeclaration) {
+            builder.tree(value.createDeclaration(null));
+            hasDeclaration = true;
+        }
+
+        if (hasTry) {
+            builder.startTryBlock();
+        } else {
+            builder.startGroup();
+        }
+
+        if (executeChild.isSingleLine()) {
+            builder.startStatement();
+            if (hasDeclaration || !needDeclaration) {
+                builder.tree(executeChild);
+            } else {
+                builder.type(value.getTypeMirror()).string(" ").tree(executeChild);
+            }
+            builder.end();
+        } else {
+            builder.tree(executeChild);
+        }
+
+        builder.end();
+
+        return builder.build();
+    }
+
+    private CodeTree createCallSharedExecuteChild(NodeExecutionData execution, LocalVariable targetValue, LocalContext currentValues) {
+        if (!isExecuteChildShared(execution, targetValue.getType())) {
+            throw new AssertionError("Execute child not shared with method but called.");
+        }
+
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.tree(targetValue.createReference()).string(" = ");
+        if (targetValue.getType().isGeneric()) {
+            builder.startCall("root", executeChildMethodName(execution, targetValue.getType()));
+        } else {
+            builder.startCall(executeChildMethodName(execution, targetValue.getType()));
+        }
+        builder.string(FRAME_VALUE);
+
+        CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, targetValue.getType());
+        if (implicitProfile != null) {
+            builder.string(implicitProfile.getName());
+        }
+        for (int i = 0; i < execution.getChild().getExecuteWith().size(); i++) {
+            builder.tree(currentValues.getValue(i).createReference());
+        }
+        builder.end();
+        return builder.build();
+    }
+
+    private CodeTree createExecuteChild(NodeExecutionData execution, TypeData returnType, LocalVariable target, LocalContext currentValues, boolean shared) {
+        final CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        final ExecutableTypeData executableType = findSpecializedExecutableType(execution, target.getType());
+
+        CodeTree assignment = createAssignmentStart(target, shared, false);
+
+        if (executableType == null) {
+            if (target.getType().isGeneric()) {
+                throw new AssertionError("Should be caught by the parser.");
+            }
+            CodeTree genericExecute = createExecuteChild(execution, returnType, target.makeGeneric(), currentValues, shared);
+            builder.tree(genericExecute);
+        } else {
+            if (target.getType().isGeneric() && executableType.getEvaluatedCount() == 0) {
+                return createPolymorphicExecuteChild(execution, target, currentValues, shared);
+            } else if (target.getType().hasImplicitSourceTypes()) {
+                if (options.implicitCastOptimization().isNone()) {
+                    CodeTree execute = createCallSharedExecuteChild(execution, target.makeGeneric(), currentValues);
+                    return TypeSystemCodeGenerator.implicitExpect(target.getType(), execute, null);
+                } else if (options.implicitCastOptimization().isDuplicateTail()) {
+                    builder.tree(createExecuteChildDuplicateTail(builder, execution, assignment, target, currentValues));
+                } else if (options.implicitCastOptimization().isMergeCasts()) {
+                    // TODO
+                } else {
+                    throw new AssertionError();
+                }
+            } else {
+                builder.tree(assignment);
+
+                CodeTree accessChild;
+                if (shared && target.getType().isGeneric()) {
+                    accessChild = CodeTreeBuilder.singleString(nodeFieldName(execution));
+                } else {
+                    accessChild = accessParent(nodeFieldName(execution));
+                }
+
+                CodeTree execute = callTemplateMethod(builder, accessChild, executableType, currentValues);
+                CodeTree expect = TypeSystemCodeGenerator.expect(executableType.getType(), returnType, execute);
+                builder.tree(expect);
+            }
+        }
+        return builder.build();
+    }
+
+    private CodeTree createPolymorphicExecuteChild(NodeExecutionData execution, LocalVariable target, LocalContext currentValues, boolean shared) throws AssertionError {
+        ExecutableTypeData genericExecutableType = execution.getChild().getNodeData().findAnyGenericExecutableType(context, execution.getChild().getExecuteWith().size());
+        if (genericExecutableType == null) {
+            throw new AssertionError("error should be caught by the parser");
+        }
+
+        CodeTree assignment = createAssignmentStart(target, shared, true);
+
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        CodeTreeBuilder polyChainBuilder = builder.create();
+        boolean hasUnexpectedResult = false;
+
+        Set<TypeData> specializedTypes = new HashSet<>();
+        for (TypeData type : node.findSpecializedTypes(execution)) {
+            specializedTypes.addAll(type.getImplicitSourceTypes());
+        }
+
+        List<ExecutableTypeData> specializedExecutables = findSpecializedExecutables(execution, specializedTypes, options.polymorphicTypeBoxingElimination());
+
+        Collections.sort(specializedExecutables, new Comparator<ExecutableTypeData>() {
+            public int compare(ExecutableTypeData o1, ExecutableTypeData o2) {
+                return o1.getType().compareTo(o2.getType());
+            }
+        });
+
+        if (isSingleSpecializable(getReachableSpecializations())) {
+            specializedExecutables = Collections.emptyList();
+        }
+
+        boolean hasSpecializedTypes = false;
+        for (ExecutableTypeData executableType : specializedExecutables) {
+            hasSpecializedTypes = polyChainBuilder.startIf(hasSpecializedTypes);
+            polyChainBuilder.tree(createAccessPolymorphicField(execution, shared));
+            polyChainBuilder.string(" == ").typeLiteral(executableType.getType().getPrimitiveType());
+            polyChainBuilder.end();
+            polyChainBuilder.startBlock();
+            polyChainBuilder.startStatement();
+            polyChainBuilder.tree(assignment);
+            polyChainBuilder.tree(callTemplateMethod(polyChainBuilder, CodeTreeBuilder.singleString(nodeFieldName(execution)), executableType, currentValues)).end();
+            polyChainBuilder.end();
+            hasUnexpectedResult |= executableType.hasUnexpectedValue(context);
+        }
+
+        CodeTree executeGeneric = callTemplateMethod(polyChainBuilder, CodeTreeBuilder.singleString(nodeFieldName(execution)), genericExecutableType, currentValues);
+
+        if (specializedExecutables.isEmpty()) {
+            builder.tree(assignment);
+            builder.tree(executeGeneric);
+        } else {
+            CodeTree accessPolymorphicProfile = createAccessPolymorphicField(execution, shared);
+            polyChainBuilder.startElseIf().tree(accessPolymorphicProfile).string(" == null").end();
+            polyChainBuilder.startBlock();
+            polyChainBuilder.tree(createTransferToInterpreterAndInvalidate());
+            polyChainBuilder.declaration(genericExecutableType.getType().getPrimitiveType(), "value_", executeGeneric);
+
+            hasSpecializedTypes = false;
+            for (ExecutableTypeData executableType : specializedExecutables) {
+                hasSpecializedTypes = polyChainBuilder.startIf(hasSpecializedTypes);
+                polyChainBuilder.tree(TypeSystemCodeGenerator.check(executableType.getType(), CodeTreeBuilder.singleString("value_")));
+                polyChainBuilder.end();
+                polyChainBuilder.startBlock();
+                polyChainBuilder.startStatement().tree(accessPolymorphicProfile).string(" = ").typeLiteral(executableType.getType().getPrimitiveType()).end();
+                polyChainBuilder.end();
+            }
+
+            polyChainBuilder.startElseBlock();
+            polyChainBuilder.startStatement().tree(accessPolymorphicProfile).string(" = ").typeLiteral(genericType.getPrimitiveType()).end();
+            polyChainBuilder.end();
+
+            polyChainBuilder.startReturn().string("value_").end();
+
+            polyChainBuilder.end();
+            polyChainBuilder.startElseBlock();
+            polyChainBuilder.startStatement().tree(assignment).tree(executeGeneric).end();
+            polyChainBuilder.end();
+
+            if (hasUnexpectedResult) {
+                builder.startTryBlock();
+            }
+
+            builder.tree(polyChainBuilder.build());
+
+            if (hasUnexpectedResult) {
+                builder.end();
+                builder.startCatchBlock(getType(UnexpectedResultException.class), "ex");
+                builder.startStatement().tree(accessPolymorphicProfile).string(" = ").typeLiteral(genericType.getPrimitiveType()).end();
+                builder.startReturn().string("ex.getResult()").end();
+                builder.end();
+            }
+        }
+        return builder.build();
+    }
+
+    private static CodeTree createAssignmentStart(LocalVariable target, boolean shared, boolean accessParent) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        if (shared) {
+            builder.string("return ");
+        } else {
+            builder.string(target.getName()).string(" = ");
+            if (accessParent) {
+                builder.tree(accessParent(null)).string(".");
+            }
+        }
+        return builder.build();
+    }
+
+    private static CodeTree createAccessPolymorphicField(NodeExecutionData execution, boolean shared) {
+        String name = polymorphicTypeProfileFieldName(execution);
+        if (shared) {
+            return CodeTreeBuilder.singleString(name);
+        } else {
+            return accessParent(name);
+        }
+    }
+
+    private CodeTree createExecuteChildDuplicateTail(CodeTreeBuilder parent, NodeExecutionData execution, CodeTree assignment, LocalVariable target, LocalContext currentValues) {
+        CodeTreeBuilder builder = parent.create();
+        List<TypeData> sourceTypes = target.getType().getImplicitSourceTypes();
+        String implicitClassFieldName = implicitClassFieldName(execution);
+        String nodeFieldName = nodeFieldName(execution);
+        List<ExecutableTypeData> executableTypes = findSpecializedExecutables(execution, sourceTypes, options.implicitTypeBoxingOptimization());
+
+        boolean elseIf = false;
+        for (ExecutableTypeData executableType : executableTypes) {
+            elseIf = builder.startIf(elseIf);
+            builder.string(implicitClassFieldName).string(" == ").typeLiteral(executableType.getType().getBoxedType());
+            builder.end();
+            builder.startBlock();
+            builder.startStatement().tree(assignment);
+
+            CodeTree execute = callTemplateMethod(builder, accessParent(nodeFieldName), executableType, currentValues);
+            ImplicitCastData cast = typeSystem.lookupCast(executableType.getType(), target.getType());
+            if (cast != null) {
+                execute = callTemplateMethod(builder, null, cast, execute);
+            }
+            builder.tree(execute);
+            builder.end();
+            builder.end();
+        }
+
+        if (!executableTypes.isEmpty()) {
+            builder.startElseBlock();
+        }
+
+        LocalVariable genericValue = target.makeGeneric().nextName();
+        LocalVariable genericShortCircuit = resolveShortCircuit(null, execution, currentValues);
+
+        builder.tree(createAssignExecuteChild(execution, genericValue.getType(), genericValue, genericShortCircuit, currentValues));
+        if (executableTypes.size() == sourceTypes.size()) {
+            builder.startThrow().startNew(getType(UnexpectedResultException.class)).tree(genericValue.createReference()).end().end();
+        } else {
+            builder.startStatement().tree(assignment);
+            builder.tree(TypeSystemCodeGenerator.implicitExpect(target.getType(), genericValue.createReference(), implicitClassFieldName));
+            builder.end();
+        }
+
+        if (!executableTypes.isEmpty()) {
+            builder.end();
+        }
+        return builder.build();
+    }
+
+    private static CodeTree createFastPathTryCatchRewriteException(SpecializationData specialization, TypeData forType, LocalContext currentValues, CodeTree execution) {
+        if (specialization.getExceptions().isEmpty()) {
+            return execution;
+        }
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.startTryBlock();
+        builder.tree(execution);
+        TypeMirror[] exceptionTypes = new TypeMirror[specialization.getExceptions().size()];
+        for (int i = 0; i < exceptionTypes.length; i++) {
+            exceptionTypes[i] = specialization.getExceptions().get(i).getJavaClass();
+        }
+        builder.end().startCatchBlock(exceptionTypes, "ex");
+        builder.startStatement().tree(accessParent(excludedFieldName(specialization))).string(" = true").end();
+        builder.startReturn();
+        builder.tree(createCallDelegate("remove", "threw rewrite exception", forType, currentValues));
+        builder.end();
+        builder.end();
+        return builder.build();
+    }
+
+    private static CodeTree createMethodGuardCheck(List<GuardExpression> guardExpressions, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        String and = "";
+        for (GuardExpression guard : guardExpressions) {
+            builder.string(and);
+            if (guard.isNegated()) {
+                builder.string("!");
+            }
+            builder.tree(callTemplateMethod(builder, accessParent(null), guard.getResolvedGuard(), currentValues));
+            and = " && ";
+        }
+        return builder.build();
+    }
+
+    private CodeTree[] createTypeCheckAndCast(List<TypeGuard> typeGuards, Set<TypeGuard> castGuards, LocalContext currentValues, SpecializationExecution specializationExecution) {
+        CodeTreeBuilder checksBuilder = CodeTreeBuilder.createBuilder();
+        CodeTreeBuilder castsBuilder = CodeTreeBuilder.createBuilder();
+        for (TypeGuard typeGuard : typeGuards) {
+            int signatureIndex = typeGuard.getSignatureIndex();
+            LocalVariable value = currentValues.getValue(signatureIndex);
+            TypeData targetType = typeGuard.getType();
+            if (!value.getType().needsCastTo(targetType)) {
+                continue;
+            }
+            NodeExecutionData execution = node.getChildExecutions().get(signatureIndex);
+            if (!checksBuilder.isEmpty()) {
+                checksBuilder.string(" && ");
+            }
+
+            CodeTreeBuilder checkBuilder = checksBuilder.create();
+            CodeTreeBuilder castBuilder = checksBuilder.create();
+
+            LocalVariable shortCircuit = currentValues.getShortCircuit(execution);
+            if (shortCircuit != null) {
+                checkBuilder.string("(");
+                CodeTreeBuilder referenceBuilder = checkBuilder.create();
+                if (!shortCircuit.getType().isPrimitive()) {
+                    referenceBuilder.string("(boolean) ");
+                }
+                referenceBuilder.tree(shortCircuit.createReference());
+                checkBuilder.string("!").tree(referenceBuilder.build());
+                checkBuilder.string(" || ");
+                castBuilder.tree(referenceBuilder.build()).string(" ? ");
+            }
+
+            List<ImplicitCastData> sourceTypes = typeSystem.lookupByTargetType(targetType);
+            CodeTree valueReference = value.createReference();
+            if (sourceTypes.isEmpty()) {
+                checkBuilder.tree(TypeSystemCodeGenerator.check(targetType, value.createReference()));
+                castBuilder.tree(TypeSystemCodeGenerator.cast(targetType, valueReference));
+            } else {
+                ImplicitCastOptimization opt = options.implicitCastOptimization();
+                if (specializationExecution.isFastPath() && !opt.isNone()) {
+                    if (opt.isDuplicateTail()) {
+                        String typeHintField = implicitClassFieldName(execution);
+                        checkBuilder.tree(TypeSystemCodeGenerator.implicitCheck(targetType, valueReference, typeHintField));
+                        castBuilder.tree(TypeSystemCodeGenerator.implicitCast(targetType, valueReference, typeHintField));
+                    } else if (opt.isMergeCasts()) {
+                        checkBuilder.tree(ImplicitCastNodeFactory.check(implicitNodeFieldName(execution), valueReference));
+                        castBuilder.tree(ImplicitCastNodeFactory.cast(implicitNodeFieldName(execution), valueReference));
+                    } else {
+                        throw new AssertionError("implicit cast opt");
+                    }
+                } else {
+                    checkBuilder.tree(TypeSystemCodeGenerator.implicitCheck(targetType, valueReference, null));
+                    castBuilder.tree(TypeSystemCodeGenerator.implicitCast(targetType, valueReference, null));
+                }
+            }
+
+            if (shortCircuit != null) {
+                checkBuilder.string(")");
+                castBuilder.string(" : ").defaultValue(targetType.getPrimitiveType());
+            }
+
+            if (castGuards == null || castGuards.contains(typeGuard)) {
+                LocalVariable castVariable = currentValues.getValue(execution).nextName().newType(typeGuard.getType()).accessWith(null);
+                currentValues.setValue(execution, castVariable);
+                castsBuilder.tree(castVariable.createDeclaration(castBuilder.build()));
+            }
+
+            checksBuilder.tree(checkBuilder.build());
+        }
+        return new CodeTree[]{checksBuilder.build(), castsBuilder.build()};
+    }
+
+    public static final class LocalContext {
+
+        private final NodeGenFactory factory;
+        private final Map<String, LocalVariable> values = new HashMap<>();
+
+        private LocalContext(NodeGenFactory factory) {
+            this.factory = factory;
+        }
+
+        public CodeExecutableElement createMethod(Set<Modifier> modifiers, TypeMirror returnType, String name, String... optionalArguments) {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers, returnType, name);
+            addParametersTo(method, optionalArguments);
+            return method;
+        }
+
+        public static LocalContext load(NodeGenFactory factory, int signatureSize) {
+            LocalContext context = new LocalContext(factory);
+            context.loadValues(signatureSize);
+            return context;
+        }
+
+        public static LocalContext load(NodeGenFactory factory) {
+            return load(factory, factory.node.getSignatureSize());
+        }
+
+        public LocalContext copy() {
+            LocalContext copy = new LocalContext(factory);
+            copy.values.putAll(values);
+            return copy;
+        }
+
+        private static String fieldValueName(NodeFieldData field) {
+            return field.getName() + "Value";
+        }
+
+        @SuppressWarnings("static-method")
+        public LocalVariable createValue(NodeExecutionData execution, TypeData type) {
+            return new LocalVariable(type, type.getPrimitiveType(), valueName(execution), null);
+        }
+
+        public LocalVariable createShortCircuitValue(NodeExecutionData execution) {
+            return new LocalVariable(factory.typeSystem.getBooleanType(), factory.getType(boolean.class), shortCircuitName(execution), null);
+        }
+
+        private static String valueName(NodeExecutionData execution) {
+            return execution.getName() + "Value";
+        }
+
+        private static String shortCircuitName(NodeExecutionData execution) {
+            return "has" + ElementUtils.firstLetterUpperCase(valueName(execution));
+        }
+
+        public LocalVariable get(String id) {
+            return values.get(id);
+        }
+
+        public LocalVariable get(Parameter parameter, int signatureIndex) {
+            LocalVariable var = get(parameter.getLocalName());
+            if (var == null && parameter.getSpecification().isSignature()) {
+                // lookup by signature index for executeWith
+                NodeExecutionData execution = factory.node.getChildExecutions().get(signatureIndex);
+                var = getValue(execution);
+            }
+            return var;
+        }
+
+        public LocalVariable getValue(NodeExecutionData execution) {
+            return get(valueName(execution));
+        }
+
+        public LocalVariable getValue(int signatureIndex) {
+            return getValue(factory.node.getChildExecutions().get(signatureIndex));
+        }
+
+        public void removeValue(String id) {
+            values.remove(id);
+        }
+
+        public void setValue(NodeExecutionData execution, LocalVariable var) {
+            values.put(valueName(execution), var);
+        }
+
+        public void setShortCircuitValue(NodeExecutionData execution, LocalVariable var) {
+            if (var == null) {
+                return;
+            }
+            values.put(shortCircuitName(execution), var);
+        }
+
+        private boolean needsVarargs(boolean requireLoaded) {
+            int size = 0;
+            for (NodeExecutionData execution : factory.node.getChildExecutions()) {
+                if (requireLoaded && getValue(execution) == null) {
+                    continue;
+                }
+                if (execution.isShortCircuit()) {
+                    size += 2;
+                } else {
+                    size++;
+                }
+            }
+            return size > 4;
+        }
+
+        private void loadValues(int evaluatedArguments) {
+            values.put(FRAME_VALUE, new LocalVariable(null, factory.getType(VirtualFrame.class), FRAME_VALUE, null));
+
+            for (NodeFieldData field : factory.node.getFields()) {
+                String fieldName = fieldValueName(field);
+                values.put(fieldName, new LocalVariable(null, field.getType(), fieldName, NodeGenFactory.accessParent(field.getName())));
+            }
+
+            boolean varargs = needsVarargs(false);
+            for (int i = 0; i < evaluatedArguments; i++) {
+                NodeExecutionData execution = factory.node.getChildExecutions().get(i);
+                if (execution.isShortCircuit()) {
+                    LocalVariable shortCircuit = createShortCircuitValue(execution).makeGeneric();
+                    if (varargs) {
+                        shortCircuit = shortCircuit.accessWith(createReadVarargs(i));
+                    }
+                    values.put(shortCircuit.getName(), shortCircuit);
+                }
+                LocalVariable value = createValue(execution, factory.genericType);
+                if (varargs) {
+                    value = value.accessWith(createReadVarargs(i));
+                }
+                values.put(value.getName(), value);
+            }
+        }
+
+        private static CodeTree createReadVarargs(int i) {
+            return CodeTreeBuilder.createBuilder().string("args_[").string(String.valueOf(i)).string("]").build();
+        }
+
+        public void addReferencesTo(CodeTreeBuilder builder, String... optionalNames) {
+            for (String var : optionalNames) {
+                LocalVariable local = values.get(var);
+                if (local == null) {
+                    builder.nullLiteral();
+                } else {
+                    builder.tree(local.createReference());
+                }
+            }
+
+            List<NodeExecutionData> executions = factory.node.getChildExecutions();
+            for (NodeExecutionData execution : executions) {
+                if (execution.isShortCircuit()) {
+                    LocalVariable shortCircuitVar = getShortCircuit(execution);
+                    if (shortCircuitVar != null) {
+                        builder.tree(shortCircuitVar.createReference());
+                    }
+                }
+                LocalVariable var = getValue(execution);
+                if (var != null) {
+                    builder.startGroup();
+                    if (executions.size() == 1 && ElementUtils.typeEquals(var.getTypeMirror(), factory.getType(Object[].class))) {
+                        // if the current type is Object[] do not use varargs for a single argument
+                        builder.string("(Object) ");
+                    }
+                    builder.tree(var.createReference());
+                    builder.end();
+                }
+            }
+        }
+
+        public void addParametersTo(CodeExecutableElement method, String... optionalNames) {
+            for (String var : optionalNames) {
+                LocalVariable local = values.get(var);
+                if (local != null) {
+                    method.addParameter(local.createParameter());
+                }
+            }
+            if (needsVarargs(true)) {
+                method.addParameter(new CodeVariableElement(factory.getType(Object[].class), "args_"));
+                method.setVarArgs(true);
+            } else {
+                for (NodeExecutionData execution : factory.node.getChildExecutions()) {
+                    if (execution.isShortCircuit()) {
+                        LocalVariable shortCircuitVar = getShortCircuit(execution);
+                        if (shortCircuitVar != null) {
+                            method.addParameter(shortCircuitVar.createParameter());
+                        }
+                    }
+
+                    LocalVariable var = getValue(execution);
+                    if (var != null) {
+                        method.addParameter(var.createParameter());
+                    }
+                }
+            }
+        }
+
+        private LocalVariable getShortCircuit(NodeExecutionData execution) {
+            return values.get(shortCircuitName(execution));
+        }
+
+    }
+
+    public final static class LocalVariable {
+
+        private final TypeData type;
+        private final TypeMirror typeMirror;
+        private final CodeTree accessorTree;
+        private final String name;
+
+        public static LocalVariable fromParameter(Parameter parameter) {
+            NodeExecutionData execution = parameter.getSpecification().getExecution();
+            String name = null;
+            if (execution == null) {
+                name = parameter.getLocalName();
+            } else {
+                name = createName(execution);
+            }
+            return new LocalVariable(parameter.getTypeSystemType(), parameter.getType(), name, null);
+        }
+
+        private LocalVariable(TypeData type, TypeMirror typeMirror, String name, CodeTree accessorTree) {
+            Objects.requireNonNull(typeMirror);
+            this.typeMirror = typeMirror;
+            this.accessorTree = accessorTree;
+            this.type = type;
+            this.name = name;
+        }
+
+        public TypeData getType() {
+            return type;
+        }
+
+        public String getShortCircuitName() {
+            return "has" + ElementUtils.firstLetterUpperCase(getName());
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        private static String createNextName(String name) {
+            return name + "_";
+        }
+
+        private static String createName(NodeExecutionData execution) {
+            if (execution == null) {
+                return "<error>";
+            }
+            return execution.getName() + "Value";
+        }
+
+        public TypeMirror getTypeMirror() {
+            return typeMirror;
+        }
+
+        public CodeVariableElement createParameter() {
+            return new CodeVariableElement(getTypeMirror(), getName());
+        }
+
+        public CodeTree createDeclaration(CodeTree init) {
+            return CodeTreeBuilder.createBuilder().declaration(getTypeMirror(), getName(), init).build();
+        }
+
+        public CodeTree createReference() {
+            if (accessorTree != null) {
+                return accessorTree;
+            } else {
+                return CodeTreeBuilder.singleString(getName());
+            }
+        }
+
+        public LocalVariable newType(TypeData newType) {
+            return new LocalVariable(newType, newType.getPrimitiveType(), name, accessorTree);
+        }
+
+        public final LocalVariable accessWith(CodeTree tree) {
+            return new LocalVariable(type, typeMirror, name, tree);
+        }
+
+        public final LocalVariable nextName() {
+            return new LocalVariable(type, typeMirror, createNextName(name), accessorTree);
+        }
+
+        public final LocalVariable makeGeneric() {
+            return newType(type.getTypeSystem().getGenericTypeData());
+        }
+
+    }
+
+    private interface SpecializationExecution {
+
+        boolean isFastPath();
+
+        CodeTree createExecute(SpecializationData specialization, LocalContext currentValues);
+
+    }
+
+}
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/PolymorphicNodeFactory.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/PolymorphicNodeFactory.java	Mon Dec 29 23:38:54 2014 +0100
@@ -46,7 +46,7 @@
         if (nodeGen != null) {
             baseType = nodeGen.asType();
         }
-        CodeTypeElement clazz = GeneratorUtils.createClass(node, modifiers(PRIVATE, FINAL), nodePolymorphicClassName(node), baseType, false);
+        CodeTypeElement clazz = GeneratorUtils.createClass(node, null, modifiers(PRIVATE, FINAL), nodePolymorphicClassName(node), baseType);
 
         clazz.getAnnotationMirrors().add(createNodeInfo(NodeCost.POLYMORPHIC));
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/SpecializedNodeFactory.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/SpecializedNodeFactory.java	Mon Dec 29 23:38:54 2014 +0100
@@ -54,7 +54,7 @@
         if (nodeGen != null) {
             baseType = nodeGen.asType();
         }
-        CodeTypeElement clazz = GeneratorUtils.createClass(node, modifiers(PRIVATE, FINAL), nodeSpecializationClassName(specialization), baseType, false);
+        CodeTypeElement clazz = GeneratorUtils.createClass(node, null, modifiers(PRIVATE, FINAL), nodeSpecializationClassName(specialization), baseType);
 
         if (specialization.isSpecialized() || specialization.isUninitialized()) {
             clazz.add(createGetMetadata0(false));
@@ -62,7 +62,7 @@
         }
 
         NodeCost cost;
-        if (specialization.isGeneric()) {
+        if (specialization.isFallback()) {
             cost = NodeCost.MEGAMORPHIC;
         } else if (specialization.isUninitialized()) {
             cost = NodeCost.UNINITIALIZED;
@@ -162,7 +162,7 @@
             builder.end();
         }
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeTree createSpecializationListLiteral(CodeTreeBuilder parent, Set<SpecializationData> list) {
@@ -175,7 +175,7 @@
             builder.startNewArray(classArray, null);
             for (SpecializationData current : list) {
                 SpecializationData s = current;
-                if (s.isGeneric() || s.isPolymorphic()) {
+                if (s.isFallback() || s.isPolymorphic()) {
                     s = getSpecialization().getNode().getUninitializedSpecialization();
                 }
                 builder.startGroup().string(nodeSpecializationClassName(s)).string(".class").end();
@@ -183,7 +183,7 @@
             builder.end();
         }
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     protected CodeAnnotationMirror createNodeInfo(NodeCost cost) {
@@ -295,7 +295,7 @@
                 public CodeTree create(CodeTreeBuilder b, SpecializationData current) {
                     return createGenericInvoke(b, polymorphic, current);
                 }
-            }, elseBuilder.getRoot(), false, true, true, false));
+            }, elseBuilder.build(), false, true, true, false));
         }
         clazz.add(executeMethod);
     }
@@ -309,7 +309,7 @@
         } else {
             builder.tree(createDeoptimize(builder));
         }
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeTree createExecuteBody(CodeTreeBuilder parent, ExecutableTypeData execType, List<ExecutableTypeData> primaryExecutes) {
@@ -324,7 +324,7 @@
             return null;
         }
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeExecutableElement createExecutableTypeOverride(ExecutableTypeData execType, boolean evaluated) {
@@ -444,7 +444,7 @@
             CodeTreeBuilder returnBuilder = new CodeTreeBuilder(builder);
             returnBuilder.tree(createDeoptimize(builder));
             returnBuilder.tree(createCallRewriteMonomorphic(builder, executable.hasUnexpectedValue(context), executable.getType(), null, "One of guards " + specialization.getGuards() + " failed"));
-            returnSpecialized = returnBuilder.getRoot();
+            returnSpecialized = returnBuilder.build();
         }
 
         builder.tree(createExecuteTree(builder, specialization, SpecializationGroup.create(specialization), new CodeBlock<SpecializationData>() {
@@ -454,7 +454,7 @@
             }
         }, returnSpecialized, false, false, false, false));
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeTree createExecute(CodeTreeBuilder parent, ExecutableTypeData executable) {
@@ -490,17 +490,17 @@
 
             builder.startReturn();
             if (targetType == null || sourceType == null) {
-                builder.tree(returnBuilder.getRoot());
+                builder.tree(returnBuilder.build());
             } else if (sourceType.needsCastTo(targetType)) {
                 CodeTree cast;
                 if (executable.hasUnexpectedValue(context)) {
-                    cast = TypeSystemCodeGenerator.expect(targetType, returnBuilder.getRoot());
+                    cast = TypeSystemCodeGenerator.expect(targetType, returnBuilder.build());
                 } else {
-                    cast = TypeSystemCodeGenerator.cast(targetType, returnBuilder.getRoot());
+                    cast = TypeSystemCodeGenerator.cast(targetType, returnBuilder.build());
                 }
                 builder.tree(cast);
             } else {
-                builder.tree(returnBuilder.getRoot());
+                builder.tree(returnBuilder.build());
             }
             builder.end();
         }
@@ -519,7 +519,7 @@
             builder.end();
         }
 
-        return builder.getRoot();
+        return builder.build();
     }
 
     private CodeExecutableElement createCopyConstructorFactoryMethod(CodeTypeElement clazz, TypeMirror baseType) {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemCodeGenerator.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemCodeGenerator.java	Mon Dec 29 23:38:54 2014 +0100
@@ -23,12 +23,14 @@
 package com.oracle.truffle.dsl.processor.generator;
 
 import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
+import static com.oracle.truffle.dsl.processor.generator.GeneratorUtils.*;
 import static javax.lang.model.element.Modifier.*;
 
 import java.util.*;
 
 import javax.lang.model.type.*;
 
+import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.java.*;
 import com.oracle.truffle.dsl.processor.java.model.*;
@@ -40,15 +42,15 @@
         return cast(type, CodeTreeBuilder.singleString(content));
     }
 
-    public static CodeTree implicitType(TypeData type, String valueName) {
+    public static CodeTree implicitType(TypeData type, CodeTree value) {
         if (type.isGeneric()) {
-            return CodeTreeBuilder.singleString(valueName);
+            return value;
         }
         CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
         TypeSystemData typeSystem = type.getTypeSystem();
-        builder.startStaticCall(createTypeSystemGen(typeSystem), getImplicitClass(type)).string(valueName);
+        builder.startStaticCall(createTypeSystemGen(typeSystem), getImplicitClass(type)).tree(value);
         builder.end();
-        return builder.getRoot();
+        return builder.build();
     }
 
     public static CodeTree invokeImplicitCast(ImplicitCastData cast, CodeTree expression) {
@@ -56,35 +58,49 @@
         TypeSystemData typeSystem = cast.getTargetType().getTypeSystem();
         builder.startStaticCall(createTypeSystemGen(typeSystem), cast.getMethodName()).tree(expression);
         builder.end();
-        return builder.getRoot();
+        return builder.build();
     }
 
-    public static CodeTree implicitCheck(TypeData type, String valueName, String typeHint) {
+    public static CodeTree implicitCheck(TypeData type, CodeTree value, String typeHint) {
         if (type.isGeneric()) {
-            return CodeTreeBuilder.singleString(valueName);
+            return value;
         }
         CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
         TypeSystemData typeSystem = type.getTypeSystem();
-        builder.startStaticCall(createTypeSystemGen(typeSystem), isImplicitTypeMethodName(type)).string(valueName);
+        builder.startStaticCall(createTypeSystemGen(typeSystem), isImplicitTypeMethodName(type)).tree(value);
         if (typeHint != null) {
             builder.string(typeHint);
         }
         builder.end();
-        return builder.getRoot();
+        return builder.build();
     }
 
-    public static CodeTree implicitCast(TypeData type, String valueName, String typeHint) {
+    public static CodeTree implicitExpect(TypeData type, CodeTree value, String typeHint) {
         if (type.isGeneric()) {
-            return CodeTreeBuilder.singleString(valueName);
+            return value;
         }
         CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
         TypeSystemData typeSystem = type.getTypeSystem();
-        builder.startStaticCall(createTypeSystemGen(typeSystem), asImplicitTypeMethodName(type)).string(valueName);
+        builder.startStaticCall(createTypeSystemGen(typeSystem), expectImplicitTypeMethodName(type)).tree(value);
         if (typeHint != null) {
             builder.string(typeHint);
         }
         builder.end();
-        return builder.getRoot();
+        return builder.build();
+    }
+
+    public static CodeTree implicitCast(TypeData type, CodeTree value, String typeHint) {
+        if (type.isGeneric()) {
+            return value;
+        }
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        TypeSystemData typeSystem = type.getTypeSystem();
+        builder.startStaticCall(createTypeSystemGen(typeSystem), asImplicitTypeMethodName(type)).tree(value);
+        if (typeHint != null) {
+            builder.string(typeHint);
+        }
+        builder.end();
+        return builder.build();
     }
 
     public static CodeTree cast(TypeData type, CodeTree content) {
@@ -99,20 +115,28 @@
         } else {
             builder.startStaticCall(typeSystem.getTemplateType().asType(), type.getTypeCasts().get(0).getMethodName()).tree(content).end();
         }
-        return builder.getRoot();
+        return builder.build();
     }
 
     public static CodeTree expect(TypeData type, CodeTree content) {
-        if (type.isGeneric()) {
+        if (type.isGeneric() || type.isVoid()) {
             return content;
         }
         CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
         TypeSystemData typeSystem = type.getTypeSystem();
         builder.startStaticCall(createTypeSystemGen(typeSystem), expectTypeMethodName(type)).tree(content).end();
-        return builder.getRoot();
+        return builder.build();
     }
 
-    private static CodeTypeMirror createTypeSystemGen(TypeSystemData typeSystem) {
+    public static CodeTree expect(TypeData sourceType, TypeData targetType, CodeTree content) {
+        if (sourceType != null && !sourceType.needsCastTo(targetType)) {
+            return content;
+        } else {
+            return expect(targetType, content);
+        }
+    }
+
+    public static CodeTypeMirror createTypeSystemGen(TypeSystemData typeSystem) {
         return new GeneratedTypeMirror(ElementUtils.getPackageName(typeSystem.getTemplateType()), typeName(typeSystem));
     }
 
@@ -132,7 +156,7 @@
         } else {
             builder.startStaticCall(typeSystem.getTemplateType().asType(), type.getTypeChecks().get(0).getMethodName()).tree(content).end();
         }
-        return builder.getRoot();
+        return builder.build();
     }
 
     public static String isTypeMethodName(TypeData type) {
@@ -151,6 +175,10 @@
         return "asImplicit" + ElementUtils.getTypeId(type.getBoxedType());
     }
 
+    static String expectImplicitTypeMethodName(TypeData type) {
+        return "expectImplicit" + ElementUtils.getTypeId(type.getBoxedType());
+    }
+
     static String getImplicitClass(TypeData type) {
         return "getImplicit" + ElementUtils.getTypeId(type.getBoxedType()) + "Class";
     }
@@ -170,7 +198,21 @@
 
     @Override
     public CodeTypeElement create(ProcessorContext context, TypeSystemData typeSystem) {
-        return new TypeClassFactory(context, typeSystem).create();
+        CodeTypeElement clazz = new TypeClassFactory(context, typeSystem).create();
+
+        if (typeSystem.getOptions().useNewLayout()) {
+            clazz.add(new TypeSystemNodeFactory(context, typeSystem).create());
+
+            if (typeSystem.getOptions().implicitCastOptimization().isMergeCasts()) {
+                for (TypeData type : typeSystem.getTypes()) {
+                    List<TypeData> sourceTypes = typeSystem.lookupSourceTypes(type);
+                    if (sourceTypes.size() > 1) {
+                        clazz.add(new ImplicitCastNodeFactory(context, type).create());
+                    }
+                }
+            }
+        }
+        return clazz;
     }
 
     private static class TypeClassFactory {
@@ -187,16 +229,17 @@
 
         public CodeTypeElement create() {
             String name = typeName(typeSystem);
-            CodeTypeElement clazz = GeneratorUtils.createClass(typeSystem, modifiers(PUBLIC, FINAL), name, typeSystem.getTemplateType().asType(), false);
+            CodeTypeElement clazz = GeneratorUtils.createClass(typeSystem, null, modifiers(PUBLIC, FINAL), name, typeSystem.getTemplateType().asType());
 
-            clazz.add(GeneratorUtils.createConstructorUsingFields(context, modifiers(PROTECTED), clazz));
+            clazz.add(GeneratorUtils.createConstructorUsingFields(modifiers(PROTECTED), clazz));
             CodeVariableElement singleton = createSingleton(clazz);
             clazz.add(singleton);
 
             for (TypeData type : typeSystem.getTypes()) {
-                if (type.isGeneric() || type.isVoid()) {
+                if (type.isVoid() || type.isGeneric()) {
                     continue;
                 }
+
                 clazz.addOptional(createIsTypeMethod(type));
                 clazz.addOptional(createAsTypeMethod(type));
 
@@ -204,11 +247,20 @@
                     clazz.addOptional(createExpectTypeMethod(type, sourceType));
                 }
 
-                clazz.addOptional(createAsImplicitTypeMethod(type, true));
-                clazz.addOptional(createAsImplicitTypeMethod(type, false));
-                clazz.addOptional(createIsImplicitTypeMethod(type, true));
-                clazz.addOptional(createIsImplicitTypeMethod(type, false));
-                clazz.addOptional(createGetTypeIndex(type));
+                if (type.hasImplicitSourceTypes()) {
+                    clazz.add(createAsImplicitTypeMethod(type, false));
+                    if (typeSystem.getOptions().implicitCastOptimization().isNone()) {
+                        clazz.add(createExpectImplicitTypeMethod(type, false));
+                    }
+                    clazz.add(createIsImplicitTypeMethod(type, false));
+
+                    if (typeSystem.getOptions().implicitCastOptimization().isDuplicateTail()) {
+                        clazz.add(createAsImplicitTypeMethod(type, true));
+                        clazz.add(createExpectImplicitTypeMethod(type, true));
+                        clazz.add(createIsImplicitTypeMethod(type, true));
+                        clazz.add(createGetImplicitClass(type));
+                    }
+                }
             }
 
             return clazz;
@@ -238,10 +290,6 @@
         }
 
         private CodeExecutableElement createIsImplicitTypeMethod(TypeData type, boolean typed) {
-            List<ImplicitCastData> casts = typeSystem.lookupByTargetType(type);
-            if (casts.isEmpty()) {
-                return null;
-            }
             CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, STATIC), context.getType(boolean.class), TypeSystemCodeGenerator.isImplicitTypeMethodName(type));
             method.addParameter(new CodeVariableElement(context.getType(Object.class), LOCAL_VALUE));
             if (typed) {
@@ -275,14 +323,11 @@
             return method;
         }
 
-        private CodeExecutableElement createAsImplicitTypeMethod(TypeData type, boolean typed) {
-            List<ImplicitCastData> casts = typeSystem.lookupByTargetType(type);
-            if (casts.isEmpty()) {
-                return null;
-            }
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, STATIC), type.getPrimitiveType(), TypeSystemCodeGenerator.asImplicitTypeMethodName(type));
+        private CodeExecutableElement createAsImplicitTypeMethod(TypeData type, boolean useTypeHint) {
+            String name = asImplicitTypeMethodName(type);
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, STATIC), type.getPrimitiveType(), name);
             method.addParameter(new CodeVariableElement(context.getType(Object.class), LOCAL_VALUE));
-            if (typed) {
+            if (useTypeHint) {
                 method.addParameter(new CodeVariableElement(context.getType(Class.class), "typeHint"));
             }
 
@@ -292,7 +337,7 @@
             boolean elseIf = false;
             for (TypeData sourceType : sourceTypes) {
                 elseIf = builder.startIf(elseIf);
-                if (typed) {
+                if (useTypeHint) {
                     builder.string("typeHint == ").typeLiteral(sourceType.getPrimitiveType());
                 } else {
                     builder.tree(check(sourceType, LOCAL_VALUE));
@@ -314,17 +359,55 @@
             }
 
             builder.startElseBlock();
-            builder.startStatement().startStaticCall(context.getTruffleTypes().getCompilerDirectives(), "transferToInterpreterAndInvalidate").end().end();
+            builder.tree(createTransferToInterpreterAndInvalidate());
             builder.startThrow().startNew(context.getType(IllegalArgumentException.class)).doubleQuote("Illegal type ").end().end();
             builder.end();
             return method;
         }
 
-        private CodeExecutableElement createGetTypeIndex(TypeData type) {
-            List<ImplicitCastData> casts = typeSystem.lookupByTargetType(type);
-            if (casts.isEmpty()) {
-                return null;
+        private CodeExecutableElement createExpectImplicitTypeMethod(TypeData type, boolean useTypeHint) {
+            String name = expectImplicitTypeMethodName(type);
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, STATIC), type.getPrimitiveType(), name);
+            method.addParameter(new CodeVariableElement(context.getType(Object.class), LOCAL_VALUE));
+            if (useTypeHint) {
+                method.addParameter(new CodeVariableElement(context.getType(Class.class), "typeHint"));
             }
+            method.getThrownTypes().add(context.getType(UnexpectedResultException.class));
+
+            List<TypeData> sourceTypes = typeSystem.lookupSourceTypes(type);
+
+            CodeTreeBuilder builder = method.createBuilder();
+            boolean elseIf = false;
+            for (TypeData sourceType : sourceTypes) {
+                elseIf = builder.startIf(elseIf);
+                if (useTypeHint) {
+                    builder.string("typeHint == ").typeLiteral(sourceType.getPrimitiveType());
+                    builder.string(" && ");
+                }
+                builder.tree(check(sourceType, LOCAL_VALUE));
+
+                builder.end().startBlock();
+
+                builder.startReturn();
+                ImplicitCastData cast = typeSystem.lookupCast(sourceType, type);
+                if (cast != null) {
+                    builder.startCall(cast.getMethodName());
+                }
+                builder.tree(cast(sourceType, LOCAL_VALUE)).end();
+                if (cast != null) {
+                    builder.end();
+                }
+                builder.end();
+                builder.end();
+            }
+
+            builder.startElseBlock();
+            builder.startThrow().startNew(context.getType(UnexpectedResultException.class)).string(LOCAL_VALUE).end().end();
+            builder.end();
+            return method;
+        }
+
+        private CodeExecutableElement createGetImplicitClass(TypeData type) {
             CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, STATIC), context.getType(Class.class), TypeSystemCodeGenerator.getImplicitClass(type));
             method.addParameter(new CodeVariableElement(context.getType(Object.class), LOCAL_VALUE));
 
@@ -340,7 +423,7 @@
             }
 
             builder.startElseBlock();
-            builder.startStatement().startStaticCall(context.getTruffleTypes().getCompilerDirectives(), "transferToInterpreterAndInvalidate").end().end();
+            builder.tree(createTransferToInterpreterAndInvalidate());
             builder.startThrow().startNew(context.getType(IllegalArgumentException.class)).doubleQuote("Illegal type ").end().end();
             builder.end();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemNodeFactory.java	Mon Dec 29 23:38:54 2014 +0100
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2014, 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.dsl.processor.generator;
+
+import static com.oracle.truffle.dsl.processor.java.ElementUtils.*;
+import static javax.lang.model.element.Modifier.*;
+
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.lang.model.util.*;
+
+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.java.*;
+import com.oracle.truffle.dsl.processor.java.model.*;
+import com.oracle.truffle.dsl.processor.model.*;
+
+public class TypeSystemNodeFactory {
+
+    private final ProcessorContext context;
+    private final TypeSystemData typeSystem;
+    private final DSLOptions options;
+
+    public TypeSystemNodeFactory(ProcessorContext context, TypeSystemData typeSystem) {
+        this.context = context;
+        this.typeSystem = typeSystem;
+        this.options = typeSystem.getOptions();
+    }
+
+    public static TypeMirror nodeType(TypeSystemData typeSystem) {
+        TypeMirror parentType = TypeSystemCodeGenerator.createTypeSystemGen(typeSystem);
+        return new GeneratedTypeMirror(getQualifiedName(parentType), typeName(typeSystem));
+    }
+
+    public static String typeName(TypeSystemData typeSystem) {
+        return getTypeId(typeSystem.getTemplateType().asType()) + "Node";
+    }
+
+    public static String acceptAndExecuteName() {
+        return "acceptAndExecute";
+    }
+
+    public static String executeName(TypeData type) {
+        if (type == null) {
+            return acceptAndExecuteName();
+        } else if (type.isGeneric()) {
+            return "executeGeneric";
+        } else {
+            return "execute" + getTypeId(type.getBoxedType());
+        }
+    }
+
+    public static String voidBoxingExecuteName(TypeData type) {
+        return executeName(type) + "Void";
+    }
+
+    public CodeTypeElement create() {
+        String typeName = typeName(typeSystem);
+        TypeMirror baseType = context.getType(SpecializationNode.class);
+        CodeTypeElement clazz = GeneratorUtils.createClass(typeSystem, null, modifiers(PUBLIC, ABSTRACT, STATIC), typeName, baseType);
+
+        for (ExecutableElement constructor : ElementFilter.constructorsIn(ElementUtils.fromTypeMirror(baseType).getEnclosedElements())) {
+            clazz.add(GeneratorUtils.createSuperConstructor(context, clazz, constructor));
+        }
+
+        for (TypeData type : typeSystem.getTypes()) {
+            clazz.add(createExecuteMethod(type));
+            if (GeneratorUtils.isTypeBoxingOptimized(options.voidBoxingOptimization(), type)) {
+                clazz.add(createVoidBoxingExecuteMethod(type));
+            }
+        }
+        return clazz;
+    }
+
+    private Element createVoidBoxingExecuteMethod(TypeData type) {
+        TypeData voidType = typeSystem.getVoidType();
+        String methodName = voidBoxingExecuteName(type);
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), voidType.getPrimitiveType(), methodName);
+        method.addParameter(new CodeVariableElement(context.getType(VirtualFrame.class), "frame"));
+
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startTryBlock();
+        builder.startStatement().startCall(executeName(type)).string("frame").end().end();
+        builder.end();
+        builder.startCatchBlock(context.getType(UnexpectedResultException.class), "e");
+        builder.end();
+
+        return method;
+    }
+
+    private Element createExecuteMethod(TypeData type) {
+        TypeData genericType = typeSystem.getGenericTypeData();
+        String methodName = executeName(type);
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), type.getPrimitiveType(), methodName);
+        method.addParameter(new CodeVariableElement(context.getType(VirtualFrame.class), "frame"));
+
+        if (type.isGeneric()) {
+            method.getModifiers().add(ABSTRACT);
+        } else {
+            CodeTreeBuilder builder = method.createBuilder();
+            CodeTree executeGeneric = builder.create().startCall(executeName(genericType)).string("frame").end().build();
+            if (!type.isVoid()) {
+                method.getThrownTypes().add(context.getType(UnexpectedResultException.class));
+            }
+            builder.startReturn().tree(TypeSystemCodeGenerator.expect(type, executeGeneric)).end();
+        }
+
+        return method;
+    }
+}
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/ElementUtils.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/ElementUtils.java	Mon Dec 29 23:38:54 2014 +0100
@@ -844,6 +844,7 @@
         PrintWriter writer = new PrintWriter(string);
         e.printStackTrace(writer);
         writer.flush();
+        string.flush();
         return e.getMessage() + "\r\n" + string.toString();
     }
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeElement.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeElement.java	Mon Dec 29 23:38:54 2014 +0100
@@ -65,7 +65,7 @@
         return generatorElement;
     }
 
-    public E add(E element) {
+    public <T extends E> T add(T element) {
         if (element == null) {
             throw new NullPointerException();
         }
@@ -73,7 +73,7 @@
         return element;
     }
 
-    public E addOptional(E element) {
+    public <T extends E> T addOptional(T element) {
         if (element != null) {
             add(element);
         }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeElementScanner.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeElementScanner.java	Mon Dec 29 23:38:54 2014 +0100
@@ -31,6 +31,9 @@
 
     @Override
     public final R visitExecutable(ExecutableElement e, P p) {
+        if (!(e instanceof CodeExecutableElement)) {
+            throw new ClassCastException(e.toString());
+        }
         return visitExecutable(cast(e, CodeExecutableElement.class), p);
     }
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeExecutableElement.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeExecutableElement.java	Mon Dec 29 23:38:54 2014 +0100
@@ -123,6 +123,17 @@
         return builder;
     }
 
+    public CodeTreeBuilder appendBuilder() {
+        CodeTreeBuilder builder = new CodeTreeBuilder(null);
+        builder.setEnclosingElement(this);
+        if (bodyTree != null) {
+            builder.tree(bodyTree);
+        }
+        this.bodyTree = builder.getTree();
+        this.body = null;
+        return builder;
+    }
+
     public void setBodyTree(CodeTree body) {
         this.bodyTree = body;
     }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTree.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTree.java	Mon Dec 29 23:38:54 2014 +0100
@@ -80,4 +80,26 @@
         this.type = type;
     }
 
+    public boolean isEmpty() {
+        return children == null || children.isEmpty();
+    }
+
+    public boolean containsKind(CodeTreeKind k) {
+        if (this.kind == k) {
+            return true;
+        }
+        if (children != null) {
+            for (CodeTree child : children) {
+                if (child.containsKind(k)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public boolean isSingleLine() {
+        return !containsKind(CodeTreeKind.NEW_LINE);
+    }
+
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTreeBuilder.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTreeBuilder.java	Mon Dec 29 23:38:54 2014 +0100
@@ -174,7 +174,11 @@
     }
 
     public CodeTreeBuilder startCall(String receiver, String callSite) {
-        return startCall(singleString(receiver), callSite);
+        if (receiver != null) {
+            return startCall(singleString(receiver), callSite);
+        } else {
+            return startCall(callSite);
+        }
     }
 
     public CodeTreeBuilder startCall(CodeTree receiver, String callSite) {
@@ -614,7 +618,7 @@
         return root;
     }
 
-    public CodeTree getRoot() {
+    public CodeTree build() {
         return root;
     }
 
@@ -623,6 +627,11 @@
         return this;
     }
 
+    public CodeTreeBuilder cast(TypeMirror type) {
+        string("(").type(type).string(") ");
+        return this;
+    }
+
     public CodeTreeBuilder cast(TypeMirror type, CodeTree content) {
         if (ElementUtils.isVoid(type)) {
             tree(content);
@@ -738,6 +747,21 @@
         return startBlock();
     }
 
+    public CodeTreeBuilder startCatchBlock(TypeMirror[] exceptionTypes, String localVarName) {
+        clearLast(CodeTreeKind.NEW_LINE);
+        string(" catch (");
+
+        for (int i = 0; i < exceptionTypes.length; i++) {
+            if (i != 0) {
+                string(" | ");
+            }
+            type(exceptionTypes[i]);
+        }
+
+        string(" ").string(localVarName).string(") ");
+        return startBlock();
+    }
+
     public CodeTreeBuilder startFinallyBlock() {
         clearLast(CodeTreeKind.NEW_LINE);
         string(" finally ");
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/transform/AbstractCodeWriter.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/transform/AbstractCodeWriter.java	Mon Dec 29 23:38:54 2014 +0100
@@ -525,9 +525,6 @@
         return null;
     }
 
-    public void foo() {
-    }
-
     @Override
     public void visitTree(CodeTree e, Void p, Element enclosingElement) {
         CodeTreeKind kind = e.getCodeKind();
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ExecutableTypeData.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ExecutableTypeData.java	Mon Dec 29 23:38:54 2014 +0100
@@ -84,4 +84,12 @@
         return super.equals(obj);
     }
 
+    public boolean hasFrame() {
+        return getFrame() != null;
+    }
+
+    public Parameter getFrame() {
+        return findParameter("frameValue");
+    }
+
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeData.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeData.java	Mon Dec 29 23:38:54 2014 +0100
@@ -56,7 +56,7 @@
 
     public NodeData(ProcessorContext context, TypeElement type, String shortName, TypeSystemData typeSystem, List<NodeChildData> children, List<NodeExecutionData> executions,
                     List<NodeFieldData> fields, List<String> assumptions, boolean generateFactory) {
-        super(context, type, null, null);
+        super(context, type, null);
         this.nodeId = type.getSimpleName().toString();
         this.shortName = shortName;
         this.typeSystem = typeSystem;
@@ -98,6 +98,35 @@
         return childExecutions;
     }
 
+    public Set<TypeData> findSpecializedTypes(NodeExecutionData execution) {
+        Set<TypeData> types = new HashSet<>();
+        for (SpecializationData specialization : getSpecializations()) {
+            if (!specialization.isSpecialized()) {
+                continue;
+            }
+            List<Parameter> parameters = specialization.findByExecutionData(execution);
+            for (Parameter parameter : parameters) {
+                TypeData type = parameter.getTypeSystemType();
+                if (type == null) {
+                    throw new AssertionError();
+                }
+                types.add(type);
+            }
+        }
+        return types;
+    }
+
+    public Collection<TypeData> findSpecializedReturnTypes() {
+        Set<TypeData> types = new HashSet<>();
+        for (SpecializationData specialization : getSpecializations()) {
+            if (!specialization.isSpecialized()) {
+                continue;
+            }
+            types.add(specialization.getReturnType().getTypeSystemType());
+        }
+        return types;
+    }
+
     public int getSignatureSize() {
         if (getSpecializations() != null && !getSpecializations().isEmpty()) {
             return getSpecializations().get(0).getSignatureSize();
@@ -353,7 +382,7 @@
 
     public SpecializationData getGenericSpecialization() {
         for (SpecializationData specialization : specializations) {
-            if (specialization.isGeneric()) {
+            if (specialization.isFallback()) {
                 return specialization;
             }
         }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ShortCircuitData.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ShortCircuitData.java	Mon Dec 29 23:38:54 2014 +0100
@@ -55,7 +55,7 @@
     }
 
     public boolean isCompatibleTo(SpecializationData specialization) {
-        if (isGeneric() && specialization.isGeneric()) {
+        if (isGeneric() && specialization.isFallback()) {
             return true;
         }
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java	Mon Dec 29 23:38:54 2014 +0100
@@ -33,7 +33,7 @@
         UNINITIALIZED,
         SPECIALIZED,
         POLYMORPHIC,
-        GENERIC
+        FALLBACK
     }
 
     private final NodeData node;
@@ -221,13 +221,6 @@
         return true;
     }
 
-    public String createReferenceName() {
-        if (getMethod() == null) {
-            return "-";
-        }
-        return ElementUtils.createReferenceName(getMethod());
-    }
-
     public NodeData getNode() {
         return node;
     }
@@ -240,8 +233,8 @@
         return kind == SpecializationKind.SPECIALIZED;
     }
 
-    public boolean isGeneric() {
-        return kind == SpecializationKind.GENERIC;
+    public boolean isFallback() {
+        return kind == SpecializationKind.FALLBACK;
     }
 
     public boolean isUninitialized() {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/Template.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/Template.java	Mon Dec 29 23:38:54 2014 +0100
@@ -33,13 +33,11 @@
 
     private final ProcessorContext context;
     private final TypeElement templateType;
-    private final String templateMethodName;
     private final AnnotationMirror annotation;
 
-    public Template(ProcessorContext context, TypeElement templateType, String templateMethodName, AnnotationMirror annotation) {
+    public Template(ProcessorContext context, TypeElement templateType, AnnotationMirror annotation) {
         this.context = context;
         this.templateType = templateType;
-        this.templateMethodName = templateMethodName;
         this.annotation = annotation;
     }
 
@@ -64,10 +62,6 @@
         return Collections.emptyList();
     }
 
-    public String getTemplateMethodName() {
-        return templateMethodName;
-    }
-
     public TypeElement getTemplateType() {
         return templateType;
     }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TemplateMethod.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TemplateMethod.java	Mon Dec 29 23:38:54 2014 +0100
@@ -68,6 +68,13 @@
         this.id = id;
     }
 
+    public String createReferenceName() {
+        if (getMethod() == null) {
+            return "-";
+        }
+        return ElementUtils.createReferenceName(getMethod());
+    }
+
     public int getNaturalOrder() {
         return naturalOrder;
     }
@@ -160,6 +167,15 @@
         return foundParameters;
     }
 
+    public Parameter findParameterOrDie(NodeExecutionData execution) {
+        for (Parameter parameter : parameters) {
+            if (parameter.getSpecification().isSignature() && parameter.getSpecification().getExecution() == execution) {
+                return parameter;
+            }
+        }
+        throw new AssertionError("Could not find parameter for execution");
+    }
+
     public List<Parameter> findByExecutionData(NodeExecutionData execution) {
         List<Parameter> foundParameters = new ArrayList<>();
         for (Parameter parameter : getParameters()) {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TypeData.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TypeData.java	Mon Dec 29 23:38:54 2014 +0100
@@ -156,6 +156,14 @@
         return ElementUtils.isPrimitive(getPrimitiveType());
     }
 
+    public List<TypeData> getImplicitSourceTypes() {
+        return getTypeSystem().lookupSourceTypes(this);
+    }
+
+    public boolean hasImplicitSourceTypes() {
+        return getTypeSystem().hasImplicitSourceTypes(this);
+    }
+
     public boolean isImplicitSubtypeOf(TypeData other) {
         List<ImplicitCastData> casts = other.getTypeSystem().lookupByTargetType(other);
         for (ImplicitCastData cast : casts) {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TypeSystemData.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TypeSystemData.java	Mon Dec 29 23:38:54 2014 +0100
@@ -27,6 +27,7 @@
 import javax.lang.model.element.*;
 import javax.lang.model.type.*;
 
+import com.oracle.truffle.api.dsl.internal.*;
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.java.*;
 
@@ -42,10 +43,18 @@
     private List<TypeCheckData> checks;
 
     private TypeMirror genericType;
+    private TypeData booleanType;
     private TypeData voidType;
 
-    public TypeSystemData(ProcessorContext context, TypeElement templateType, AnnotationMirror annotation) {
-        super(context, templateType, null, annotation);
+    private DSLOptions options;
+
+    public TypeSystemData(ProcessorContext context, TypeElement templateType, AnnotationMirror annotation, DSLOptions options) {
+        super(context, templateType, annotation);
+        this.options = options;
+    }
+
+    public DSLOptions getOptions() {
+        return options;
     }
 
     @Override
@@ -196,6 +205,18 @@
         return null;
     }
 
+    public boolean hasImplicitSourceTypes(TypeData targetType) {
+        if (getImplicitCasts() == null) {
+            return false;
+        }
+        for (ImplicitCastData cast : getImplicitCasts()) {
+            if (cast.getTargetType() == targetType) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public List<TypeData> lookupSourceTypes(TypeData type) {
         List<TypeData> sourceTypes = new ArrayList<>();
         sourceTypes.add(type);
@@ -210,4 +231,12 @@
         return sourceTypes;
     }
 
+    public TypeData getBooleanType() {
+        return booleanType;
+    }
+
+    public void setBooleanType(TypeData booleanType) {
+        this.booleanType = booleanType;
+    }
+
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/ExecutableTypeMethodParser.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/ExecutableTypeMethodParser.java	Mon Dec 29 23:38:54 2014 +0100
@@ -28,7 +28,9 @@
 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.*;
 
 public class ExecutableTypeMethodParser extends NodeMethodParser<ExecutableTypeData> {
@@ -77,6 +79,8 @@
             return false;
         } else if (method.getModifiers().contains(Modifier.NATIVE)) {
             return false;
+        } else if (ElementUtils.findAnnotationMirror(getContext().getEnvironment(), method, Specialization.class) != null) {
+            return false;
         }
         return method.getSimpleName().toString().startsWith("execute");
     }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/GenericParser.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/GenericParser.java	Mon Dec 29 23:38:54 2014 +0100
@@ -62,7 +62,7 @@
 
     @Override
     public SpecializationData create(TemplateMethod method, boolean invalid) {
-        return new SpecializationData(getNode(), method, SpecializationKind.GENERIC);
+        return new SpecializationData(getNode(), method, SpecializationKind.FALLBACK);
     }
 
     @Override
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/ImplicitCastParser.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/ImplicitCastParser.java	Mon Dec 29 23:38:54 2014 +0100
@@ -48,7 +48,7 @@
         List<TypeMirror> types = getTypeSystem().getPrimitiveTypeMirrors();
         Set<String> identifiers = getTypeSystem().getTypeIdentifiers();
         MethodSpec spec = new MethodSpec(new ParameterSpec("target", types, identifiers));
-        spec.addRequired(new ParameterSpec("source", types, identifiers));
+        spec.addRequired(new ParameterSpec("source", types, identifiers)).setSignature(true);
         return spec;
     }
 
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeMethodParser.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeMethodParser.java	Mon Dec 29 23:38:54 2014 +0100
@@ -42,7 +42,8 @@
     }
 
     protected ParameterSpec createValueParameterSpec(NodeExecutionData execution) {
-        ParameterSpec spec = new ParameterSpec(execution.getName(), nodeTypeMirrors(execution.getChild().getNodeData()), nodeTypeIdentifiers(execution.getChild().getNodeData()));
+        NodeData childNode = execution.getChild().getNodeData();
+        ParameterSpec spec = new ParameterSpec(execution.getName(), nodeTypeMirrors(childNode), nodeTypeIdentifiers(childNode));
         spec.setExecution(execution);
         return spec;
     }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java	Mon Dec 29 23:38:54 2014 +0100
@@ -151,6 +151,22 @@
         node.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, node).parse(elements)));
         initializeChildren(node);
 
+        // ensure the processed element has at least one @Specialization annotation.
+        boolean foundSpecialization = false;
+        for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
+            if (ElementUtils.findAnnotationMirror(processingEnv, method, Specialization.class) != null) {
+                foundSpecialization = true;
+                break;
+            }
+        }
+        if (!foundSpecialization) {
+            return node;
+        }
+
+        if (node.hasErrors()) {
+            return node; // error sync point
+        }
+
         node.getSpecializations().addAll(new SpecializationMethodParser(context, node).parse(elements));
         node.getSpecializations().addAll(new GenericParser(context, node).parse(elements));
         node.getCasts().addAll(new CreateCastParser(context, node).parse(elements));
@@ -224,7 +240,7 @@
         final TypeSystemData typeSystem = (TypeSystemData) context.getTemplate(typeSystemType, true);
         if (typeSystem == null) {
             NodeData nodeData = new NodeData(context, templateType);
-            nodeData.addError("The used type system '%s' is invalid or not a Node.", ElementUtils.getQualifiedName(typeSystemType));
+            nodeData.addError("The used type system '%s' is invalid. Fix errors in the type system first.", ElementUtils.getQualifiedName(typeSystemType));
             return nodeData;
         }
 
@@ -565,14 +581,25 @@
         initializeReachability(node);
         initializeContains(node);
 
-        if (!node.hasErrors()) {
+        if (!node.hasErrors() && !node.getTypeSystem().getOptions().useNewLayout()) {
             initializeExceptions(node);
         }
         resolveContains(node);
 
+        if (node.getTypeSystem().getOptions().useNewLayout()) {
+            List<SpecializationData> specializations = node.getSpecializations();
+            for (SpecializationData cur : specializations) {
+                for (SpecializationData child : specializations) {
+                    if (child != null && child != cur && child.getContains().contains(cur)) {
+                        cur.getExcludedBy().add(child);
+                    }
+                }
+            }
+        }
+
         List<SpecializationData> needsId = new ArrayList<>();
         for (SpecializationData specialization : node.getSpecializations()) {
-            if (specialization.isGeneric()) {
+            if (specialization.isFallback()) {
                 specialization.setId("Generic");
             } else if (specialization.isUninitialized()) {
                 specialization.setId("Uninitialized");
@@ -643,6 +670,7 @@
 
     private static void initializeExceptions(NodeData node) {
         List<SpecializationData> specializations = node.getSpecializations();
+
         for (int i = 0; i < specializations.size(); i++) {
             SpecializationData cur = specializations.get(i);
             if (cur.getExceptions().isEmpty()) {
@@ -671,6 +699,7 @@
                 }
             }
         }
+
     }
 
     private static void initializeContains(NodeData node) {
@@ -771,7 +800,7 @@
                     name.append(shadowSpecialization.createReferenceName());
                     sep = ", ";
                 }
-                current.addError("%s is not reachable. It is shadowed by %s.", current.isGeneric() ? "Generic" : "Specialization", name);
+                current.addError("%s is not reachable. It is shadowed by %s.", current.isFallback() ? "Generic" : "Specialization", name);
             }
             current.setReachable(shadowedBy == null);
         }
@@ -966,7 +995,7 @@
 
         List<SpecializationData> generics = new ArrayList<>();
         for (SpecializationData spec : node.getSpecializations()) {
-            if (spec.isGeneric()) {
+            if (spec.isFallback()) {
                 generics.add(spec);
             }
         }
@@ -1313,6 +1342,24 @@
     }
 
     private void verifyConstructors(NodeData nodeData) {
+        if (nodeData.getTypeSystem().getOptions().useNewLayout()) {
+            List<ExecutableElement> constructors = ElementFilter.constructorsIn(nodeData.getTemplateType().getEnclosedElements());
+            if (constructors.isEmpty()) {
+                return;
+            }
+
+            boolean oneNonPrivate = false;
+            for (ExecutableElement constructor : constructors) {
+                if (ElementUtils.getVisibility(constructor.getModifiers()) != Modifier.PRIVATE) {
+                    oneNonPrivate = true;
+                    break;
+                }
+            }
+            if (!oneNonPrivate && !nodeData.getTemplateType().getModifiers().contains(Modifier.PRIVATE)) {
+                nodeData.addError("At least one constructor must be non-private.");
+            }
+            return;
+        }
         if (!nodeData.needsRewrites(context)) {
             // no specialization constructor is needed if the node never rewrites.
             return;
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/SpecializationGroup.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/SpecializationGroup.java	Mon Dec 29 23:38:54 2014 +0100
@@ -444,4 +444,5 @@
 
         return false;
     }
+
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/TypeSystemParser.java	Mon Dec 29 23:38:50 2014 +0100
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/TypeSystemParser.java	Mon Dec 29 23:38:54 2014 +0100
@@ -32,10 +32,12 @@
 import javax.lang.model.util.*;
 
 import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.internal.*;
 import com.oracle.truffle.dsl.processor.generator.*;
 import com.oracle.truffle.dsl.processor.java.*;
 import com.oracle.truffle.dsl.processor.model.*;
 
+@DSLOptions
 public class TypeSystemParser extends AbstractParser<TypeSystemData> {
 
     public static final List<Class<? extends Annotation>> ANNOTATIONS = Arrays.asList(TypeSystem.class, ExpectError.class);
@@ -49,7 +51,13 @@
     protected TypeSystemData parse(Element element, AnnotationMirror mirror) {
         TypeElement templateType = (TypeElement) element;
         AnnotationMirror templateTypeAnnotation = mirror;
-        TypeSystemData typeSystem = new TypeSystemData(context, templateType, templateTypeAnnotation);
+        DSLOptions options = element.getAnnotation(DSLOptions.class);
+        if (options == null) {
+            options = TypeSystemParser.class.getAnnotation(DSLOptions.class);
+        }
+        assert options != null;
+
+        TypeSystemData typeSystem = new TypeSystemData(context, templateType, templateTypeAnnotation, options);
 
         // annotation type on class path!?
         TypeElement annotationTypeElement = processingEnv.getElementUtils().getTypeElement(getAnnotationType().getCanonicalName());
@@ -77,10 +85,15 @@
         if (typeSystem.hasErrors()) {
             return typeSystem;
         }
-
         typeSystem.setGenericType(genericType);
         typeSystem.setVoidType(voidType);
 
+        TypeData booleanType = typeSystem.findTypeData(context.getType(boolean.class));
+        if (booleanType == null) {
+            booleanType = new TypeData(typeSystem, types.size(), null, context.getType(boolean.class), context.getType(Boolean.class));
+        }
+        typeSystem.setBooleanType(booleanType);
+
         verifyExclusiveMethodAnnotation(typeSystem, TypeCast.class, TypeCheck.class);
 
         List<Element> elements = new ArrayList<>(context.getEnvironment().getElementUtils().getAllMembers(templateType));