diff truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java @ 21951:9c8c0937da41

Moving all sources into truffle subdirectory
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Wed, 17 Jun 2015 10:58:08 +0200
parents graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java@90fd69243227
children 844d6d053d1b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java	Wed Jun 17 10:58:08 2015 +0200
@@ -0,0 +1,3044 @@
+/*
+ * 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.*;
+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.api.nodes.Node.Children;
+import com.oracle.truffle.dsl.processor.*;
+import com.oracle.truffle.dsl.processor.expression.*;
+import com.oracle.truffle.dsl.processor.expression.DSLExpression.Variable;
+import com.oracle.truffle.dsl.processor.java.*;
+import com.oracle.truffle.dsl.processor.java.model.*;
+import com.oracle.truffle.dsl.processor.java.model.CodeTypeMirror.ArrayCodeTypeMirror;
+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 = TemplateMethod.FRAME_NAME;
+    private static final String NAME_SUFFIX = "_";
+    private static final String NODE_SUFFIX = "NodeGen";
+
+    private final ProcessorContext context;
+    private final NodeData node;
+    private final TypeSystemData typeSystem;
+    private final TypeMirror genericType;
+    private final DSLOptions options;
+    private final boolean singleSpecializable;
+    private final int varArgsThreshold;
+    private final Set<TypeMirror> expectedTypes = new HashSet<>();
+    private final Set<NodeExecutionData> usedExecuteChildMethods = new HashSet<>();
+    private boolean nextUsed;
+    private boolean singleSpecializableUnsupportedUsed;
+
+    private List<ExecutableTypeData> usedTypes;
+    private List<SpecializationData> reachableSpecializations;
+
+    public NodeGenFactory(ProcessorContext context, NodeData node) {
+        this.context = context;
+        this.node = node;
+        this.typeSystem = node.getTypeSystem();
+        this.genericType = context.getType(Object.class);
+        this.options = typeSystem.getOptions();
+        this.varArgsThreshold = calculateVarArgsThreshold();
+        this.reachableSpecializations = calculateReachableSpecializations();
+        this.singleSpecializable = isSingleSpecializableImpl();
+        this.usedTypes = filterBaseExecutableTypes(node.getExecutableTypes(), reachableSpecializations);
+    }
+
+    private int calculateVarArgsThreshold() {
+        TypeMirror specialization = context.getType(SpecializationNode.class);
+        TypeElement specializationType = fromTypeMirror(specialization);
+
+        int maxParameters = 0;
+        for (ExecutableElement element : ElementFilter.methodsIn(specializationType.getEnclosedElements())) {
+            if (element.getSimpleName().contentEquals("acceptAndExecute")) {
+                maxParameters = Math.max(maxParameters, element.getParameters().size());
+            }
+        }
+        return maxParameters;
+    }
+
+    public static String nodeTypeName(NodeData node) {
+        return resolveNodeId(node) + NODE_SUFFIX;
+    }
+
+    private static String assumptionName(AssumptionExpression assumption) {
+        return assumption.getId() + NAME_SUFFIX;
+    }
+
+    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) {
+        String id;
+        if (specialization == null) {
+            id = "Base";
+        } else {
+            id = specialization.getId();
+        }
+        return id + "Node_";
+    }
+
+    private TypeMirror specializationType(SpecializationData specialization) {
+        return new GeneratedTypeMirror(ElementUtils.getPackageName(node.getTemplateType()) + "." + nodeTypeName(node), specializationTypeName(specialization));
+    }
+
+    private static String polymorphicTypeProfileFieldName(NodeExecutionData execution) {
+        return execution.getName() + "Type" + NAME_SUFFIX;
+    }
+
+    private static String nodeFieldName(NodeExecutionData execution) {
+        return execution.getName() + NAME_SUFFIX;
+    }
+
+    private static String specializationStartFieldName() {
+        return "specialization" + NAME_SUFFIX;
+    }
+
+    private static String excludedFieldName(SpecializationData specialization) {
+        return "exclude" + specialization.getId() + NAME_SUFFIX;
+    }
+
+    private static String executeChildMethodName(NodeExecutionData execution, TypeMirror type) {
+        return "execute" + ElementUtils.firstLetterUpperCase(execution.getName()) + (ElementUtils.isObject(type) ? "" : getTypeId(type)) + NAME_SUFFIX;
+    }
+
+    private CodeTree accessParent(String name) {
+        if (singleSpecializable) {
+            if (name == null) {
+                return CodeTreeBuilder.singleString("this");
+            } else {
+                return CodeTreeBuilder.singleString(name);
+            }
+        } else {
+            if (name == null) {
+                return CodeTreeBuilder.singleString("root");
+            } else {
+                return CodeTreeBuilder.createBuilder().string("root.").string(name).build();
+            }
+        }
+    }
+
+    public CodeTypeElement create() {
+        CodeTypeElement clazz = GeneratorUtils.createClass(node, null, modifiers(FINAL), nodeTypeName(node), node.getTemplateType().asType());
+        ElementUtils.setVisibility(clazz.getModifiers(), ElementUtils.getVisibility(node.getTemplateType().getModifiers()));
+
+        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);
+            }
+        }
+
+        for (ExecutableElement superConstructor : GeneratorUtils.findUserConstructors(node.getTemplateType().asType())) {
+            clazz.add(createNodeConstructor(clazz, superConstructor));
+        }
+
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            if (execution.getChild() != null) {
+                clazz.add(createNodeField(PRIVATE, execution.getNodeType(), nodeFieldName(execution), Child.class));
+            }
+        }
+
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            if (!resolvePolymorphicExecutables(execution).isEmpty()) {
+                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(createGetCostMethod());
+
+        avoidFindbugsProblems(clazz);
+
+        if (singleSpecializable) {
+            SpecializationData specialization = reachableSpecializations.iterator().next();
+
+            for (ExecutableTypeData execType : usedTypes) {
+                if (execType.getMethod() == null) {
+                    boolean foundDelegate = false;
+                    for (ExecutableTypeData type : usedTypes) {
+                        if (type == execType) {
+                            continue;
+                        }
+                        if (findFastPathDelegate(specialization.getReturnType().getType(), type, usedTypes) == execType) {
+                            foundDelegate = true;
+                            break;
+                        }
+                    }
+                    // just exclude synthetic execute methods that were not delegated to
+                    if (!foundDelegate) {
+                        continue;
+                    }
+                }
+
+                clazz.add(createExecutableTypeOverride(usedTypes, execType));
+            }
+
+            if (singleSpecializableUnsupportedUsed) {
+                addUnsupportedMethod(clazz);
+            }
+        } else {
+
+            for (ExecutableTypeData execType : usedTypes) {
+                if (execType.getMethod() == null) {
+                    continue;
+                }
+                clazz.add(createExecutableTypeOverride(usedTypes, execType));
+            }
+
+            clazz.getImplements().add(getType(SpecializedNode.class));
+            clazz.add(createMethodGetSpecializationNode());
+            clazz.add(createDeepCopyMethod());
+            SpecializationData specializationStart = createSpecializations(clazz);
+            clazz.add(createNodeField(PRIVATE, specializationType(null), specializationStartFieldName(), Child.class));
+
+            for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
+                CodeTreeBuilder builder = ((CodeExecutableElement) constructor).appendBuilder();
+                builder.startStatement();
+                builder.string("this.").string(specializationStartFieldName());
+                builder.string(" = ").tree(createCallCreateMethod(specializationStart, "this", null));
+                builder.end();
+            }
+        }
+
+        for (TypeMirror type : ElementUtils.uniqueSortedTypes(expectedTypes, false)) {
+            if (!typeSystem.hasType(type)) {
+                clazz.addOptional(TypeSystemCodeGenerator.createExpectMethod(PRIVATE, typeSystem, context.getType(Object.class), type));
+            }
+        }
+
+        return clazz;
+    }
+
+    private void avoidFindbugsProblems(CodeTypeElement clazz) {
+        TypeElement type = context.getEnvironment().getElementUtils().getTypeElement("edu.umd.cs.findbugs.annotations.SuppressFBWarnings");
+        if (type == null) {
+            return;
+        }
+        boolean foundComparison = false;
+        outer: for (SpecializationData specialization : node.getSpecializations()) {
+            for (GuardExpression guard : specialization.getGuards()) {
+                if (guard.getExpression().containsComparisons()) {
+                    foundComparison = true;
+                    break outer;
+                }
+            }
+        }
+
+        if (foundComparison) {
+            CodeAnnotationMirror annotation = new CodeAnnotationMirror((DeclaredType) type.asType());
+            annotation.setElementValue(annotation.findExecutableElement("value"), new CodeAnnotationValue("SA_LOCAL_SELF_COMPARISON"));
+            clazz.addAnnotationMirror(annotation);
+        }
+    }
+
+    private void addUnsupportedMethod(CodeTypeElement clazz) {
+        CodeVariableElement seenUnsupportedField = new CodeVariableElement(modifiers(PRIVATE), getType(boolean.class), "seenUnsupported0");
+        seenUnsupportedField.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(CompilationFinal.class)));
+        clazz.add(seenUnsupportedField);
+        LocalContext locals = LocalContext.load(this);
+        CodeExecutableElement method = locals.createMethod(modifiers(PRIVATE), getType(UnsupportedSpecializationException.class), "unsupported", varArgsThreshold);
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startIf().string("!").string(seenUnsupportedField.getName()).end().startBlock();
+        builder.startStatement().startStaticCall(getType(CompilerDirectives.class), "transferToInterpreterAndInvalidate").end().end();
+        builder.startStatement().string(seenUnsupportedField.getName()).string(" = true").end();
+        builder.end();
+
+        builder.startReturn();
+        builder.startNew(getType(UnsupportedSpecializationException.class));
+        builder.string("this");
+        builder.tree(createGetSuppliedChildren());
+        locals.addReferencesTo(builder);
+        builder.end();
+        builder.end();
+        clazz.add(method);
+    }
+
+    private CodeExecutableElement createNodeConstructor(CodeTypeElement clazz, ExecutableElement superConstructor) {
+        CodeExecutableElement constructor = GeneratorUtils.createConstructorUsingFields(modifiers(), clazz, superConstructor);
+        ElementUtils.setVisibility(constructor.getModifiers(), ElementUtils.getVisibility(superConstructor.getModifiers()));
+
+        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(callMethod(null, createCast.getMethod(), nameTree));
+                    callBuilder.string(" : null");
+                    name += "_";
+                    builder.declaration(child.getNodeType(), name, callBuilder.build());
+                }
+            }
+            childValues.add(name);
+        }
+
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            if (execution.getChild() == null) {
+                continue;
+            }
+            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.getChildIndex())).string("]");
+            }
+
+            CodeTree accessor = accessorBuilder.build();
+
+            if (createCast != null && execution.getChild().getCardinality().isOne()) {
+                accessor = callMethod(null, createCast.getMethod(), accessor);
+            }
+
+            if (execution.isIndexed()) {
+                CodeTreeBuilder nullCheck = builder.create();
+                nullCheck.string(name).string(" != null && ").string(String.valueOf(execution.getChildIndex())).string(" < ").string(name).string(".length").string(" ? ");
+                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) {
+        CodeTypeElement baseSpecialization = clazz.add(createBaseSpecialization());
+        TypeMirror baseSpecializationType = baseSpecialization.asType();
+
+        Map<SpecializationData, CodeTypeElement> generated = new LinkedHashMap<>();
+
+        List<SpecializationData> generateSpecializations = new ArrayList<>();
+        generateSpecializations.add(node.getUninitializedSpecialization());
+        if (needsPolymorphic()) {
+            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));
+        baseSpecialization.addOptional(createGetNext(baseSpecialization));
+
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            Collection<TypeMirror> specializedTypes = node.findSpecializedTypes(execution);
+            specializedTypes.add(genericType);
+            for (TypeMirror specializedType : specializedTypes) {
+                if (isExecuteChildShared(execution, specializedType)) {
+                    baseSpecialization.addOptional(createExecuteChildMethod(execution, specializedType));
+                }
+            }
+        }
+
+        return node.getUninitializedSpecialization();
+    }
+
+    private boolean needsPolymorphic() {
+        int signatureSize = node.getSignatureSize();
+        boolean allEvaluated = true;
+        for (ExecutableTypeData type : usedTypes) {
+            if (type.getEvaluatedCount() != signatureSize) {
+                allEvaluated = false;
+            }
+        }
+        if (allEvaluated) {
+            return false;
+        }
+
+        if (reachableSpecializations.size() != 1) {
+            return true;
+        }
+
+        SpecializationData specialization = reachableSpecializations.get(0);
+        for (Parameter parameter : specialization.getSignatureParameters()) {
+            TypeMirror type = parameter.getType();
+            if (type != null && typeSystem.hasImplicitSourceTypes(type)) {
+                return true;
+            }
+        }
+        if (specialization.hasMultipleInstances()) {
+            return true;
+        }
+        return false;
+
+    }
+
+    // create specialization
+
+    private CodeTypeElement createBaseSpecialization() {
+        CodeTypeElement clazz = createClass(node, null, modifiers(PRIVATE, ABSTRACT, STATIC), specializationTypeName(null), typeSystem.getContext().getType(SpecializationNode.class));
+
+        clazz.addOptional(createSpecializationConstructor(clazz, null, null));
+        clazz.add(new CodeVariableElement(modifiers(PROTECTED, FINAL), nodeType(node), "root"));
+
+        clazz.addOptional(createUnsupported());
+        clazz.add(createGetSuppliedChildrenMethod());
+        clazz.add(createAcceptAndExecute());
+
+        for (ExecutableTypeData type : usedTypes) {
+            clazz.add(createFastPathExecuteMethod(null, type, usedTypes));
+        }
+
+        return clazz;
+    }
+
+    private Element createAcceptAndExecute() {
+        ExecutableTypeData executableElement = createSpecializationNodeSignature(node.getSignatureSize());
+        LocalContext currentLocals = LocalContext.load(this, executableElement, varArgsThreshold);
+        CodeExecutableElement executable = createExecuteMethod(null, executableElement, currentLocals, false, varArgsThreshold);
+
+        executable.getModifiers().add(FINAL);
+        CodeTreeBuilder builder = executable.createBuilder();
+
+        CodeTree receiver = CodeTreeBuilder.singleString("this");
+
+        builder.tree(createCallDelegateExecute(builder, receiver, currentLocals, executableElement, node.getGenericExecutableType(null)));
+
+        return executable;
+    }
+
+    private ExecutableTypeData createSpecializationNodeSignature(int argumentCount) {
+        TypeMirror[] parameters = new TypeMirror[argumentCount];
+        Arrays.fill(parameters, genericType);
+        return new ExecutableTypeData(node, genericType, "acceptAndExecute", context.getType(Frame.class), Arrays.asList(parameters));
+    }
+
+    private boolean shouldImplementExecutableType(SpecializationData specialization, ExecutableTypeData executableType) {
+        // always implement the root execute method. they are declared abstract in the base node.
+        if (executableType.getDelegatedTo() == null) {
+            return true;
+        }
+
+        // specializations with more parameters are just ignored
+        if (executableType.getEvaluatedCount() > node.getExecutionCount()) {
+            return false;
+        }
+
+        if (!isSubtypeBoxed(context, specialization.getReturnType().getType(), executableType.getReturnType())) {
+            return false;
+        }
+
+        // the evaluated signature might be compatible to the specialization
+        boolean specializationCompatible = true;
+        List<TypeMirror> signatureParameters = executableType.getSignatureParameters();
+        for (int i = 0; i < signatureParameters.size(); i++) {
+            TypeMirror evaluatedType = signatureParameters.get(i);
+            TypeMirror specializedType = specialization.findParameterOrDie(node.getChildExecutions().get(i)).getType();
+
+            if (!isSubtypeBoxed(context, evaluatedType, specializedType) && !isSubtypeBoxed(context, specializedType, evaluatedType)) {
+                specializationCompatible = false;
+                break;
+            }
+        }
+        if (!specializationCompatible) {
+            return false;
+        }
+
+        // possibly trigger void optimization for a specialization if it is enabled
+        if (isVoid(executableType.getReturnType())) {
+            if (isTypeBoxingOptimized(options.voidBoxingOptimization(), specialization.getReturnType().getType())) {
+                return true;
+            }
+        }
+
+        // trigger type boxing elimination for unevaluated arguments
+        for (int i = executableType.getEvaluatedCount(); i < node.getExecutionCount(); i++) {
+            NodeExecutionData execution = node.getChildExecutions().get(i);
+            TypeMirror specializedType = specialization.findParameterOrDie(execution).getType();
+            if (isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), specializedType)) {
+                // it does not make sense to do type boxing elimination for children with
+                // no type specialized execute method
+                if (execution.getChild() != null) {
+                    ExecutableTypeData executedType = execution.getChild().findExecutableType(specializedType);
+                    if (executedType != null) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        // trigger type boxing elimination for return types
+        if (typeEquals(executableType.getReturnType(), specialization.getReturnType().getType())) {
+            if (isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), executableType.getReturnType())) {
+                return true;
+            }
+        }
+
+        // trigger generation for evaluated assignable type matches other than generic
+        for (int i = 0; i < signatureParameters.size(); i++) {
+            TypeMirror evaluatedType = signatureParameters.get(i);
+            NodeExecutionData execution = node.getChildExecutions().get(i);
+            TypeMirror specializedType = specialization.findParameterOrDie(execution).getType();
+
+            if (isSubtypeBoxed(context, evaluatedType, specializedType) && !isObject(specializedType)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private List<ExecutableTypeData> filterBaseExecutableTypes(List<ExecutableTypeData> executableTypes, List<SpecializationData> specializations) {
+        Set<TypeMirror> returnTypes = new HashSet<>();
+        for (SpecializationData specialization : node.getSpecializations()) {
+            returnTypes.add(specialization.getReturnType().getType());
+        }
+
+        List<ExecutableTypeData> prefilteredTypes = new ArrayList<>();
+        for (ExecutableTypeData type : executableTypes) {
+            if (type.getDelegatedTo() == null || shouldAlwaysImplementExecutableType(type)) {
+                prefilteredTypes.add(type);
+            } else {
+                boolean foundSubtype = false;
+                for (TypeMirror returnType : returnTypes) {
+                    if (isSubtypeBoxed(context, returnType, type.getReturnType())) {
+                        foundSubtype = true;
+                    }
+                }
+                if (foundSubtype) {
+                    prefilteredTypes.add(type);
+                }
+            }
+        }
+
+        Set<ExecutableTypeData> types = new HashSet<>();
+        type: for (ExecutableTypeData type : prefilteredTypes) {
+            for (SpecializationData specialization : specializations) {
+                if (shouldImplementExecutableType(specialization, type) || shouldAlwaysImplementExecutableType(type)) {
+                    types.add(type);
+                    continue type;
+                }
+            }
+        }
+        Set<ExecutableTypeData> delegatesToAdd = new HashSet<>();
+        do {
+            delegatesToAdd.clear();
+            for (ExecutableTypeData type : types) {
+                ExecutableTypeData delegate = type.getDelegatedTo();
+                if (delegate != null && !types.contains(delegate)) {
+                    delegatesToAdd.add(delegate);
+                }
+            }
+            types.addAll(delegatesToAdd);
+        } while (!delegatesToAdd.isEmpty());
+        List<ExecutableTypeData> newUsedTypes = new ArrayList<>(types);
+        Collections.sort(newUsedTypes);
+        return newUsedTypes;
+    }
+
+    private boolean shouldAlwaysImplementExecutableType(ExecutableTypeData type) {
+        return type.isAbstract() || !(type.hasUnexpectedValue(context) && type.getMethod() != null);
+    }
+
+    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()) {
+            TypeMirror targetType = p.getType();
+            if (typeSystem.hasImplicitSourceTypes(targetType)) {
+                NodeExecutionData execution = p.getSpecification().getExecution();
+                CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, p.getType());
+                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));
+        clazz.addOptional(createIsIdenticalMethod(specialization));
+
+        // get types that should get implemented
+        List<ExecutableTypeData> types = new ArrayList<>();
+        for (ExecutableTypeData type : node.getExecutableTypes()) {
+            if (shouldImplementExecutableType(specialization, type)) {
+                types.add(type);
+            }
+        }
+        for (ExecutableTypeData type : types) {
+            clazz.add(createFastPathExecuteMethod(specialization, type, types));
+        }
+
+        return clazz;
+    }
+
+    public static List<Parameter> getDynamicParameters(TemplateMethod method) {
+        List<Parameter> parameters = new ArrayList<>();
+        for (Parameter param : method.getReturnTypeAndParameters()) {
+            if (param.getSpecification().isLocal()) {
+                // ignore parameters passed by locals
+                continue;
+            } else if (param.getVariableElement() != null && param.getVariableElement().getAnnotation(Cached.class) != null) {
+                // ignore cached parameters
+                continue;
+            }
+            parameters.add(param);
+        }
+        return parameters;
+    }
+
+    private Element createDeepCopyMethod() {
+        if (singleSpecializable) {
+            return null;
+        }
+        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();
+        if (singleSpecializable) {
+            builder.startReturn().staticReference(getType(NodeCost.class), "MONOMORPHIC").end().end();
+        } else {
+            builder.startReturn().startCall(specializationStartFieldName(), "getNodeCost").end().end();
+        }
+        return executable;
+
+    }
+
+    private Element createIsIdenticalMethod(SpecializationData specialization) {
+        boolean cacheBoundGuard = specialization.hasMultipleInstances();
+        if (!cacheBoundGuard) {
+            return null;
+        }
+
+        LocalContext currentLocals = LocalContext.load(this, createSpecializationNodeSignature(node.getSignatureSize()), varArgsThreshold);
+        currentLocals.loadFastPathState(specialization);
+
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getType(boolean.class), "isIdentical");
+        method.addParameter(new CodeVariableElement(getType(SpecializationNode.class), "other"));
+        currentLocals.addParametersTo(method, varArgsThreshold, FRAME_VALUE);
+        method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        final CodeTreeBuilder builder = method.createBuilder();
+
+        SpecializationGroup group = SpecializationGroup.create(specialization);
+        SpecializationBody executionFactory = new SpecializationBody(true, false) {
+            @Override
+            public CodeTree createBody(SpecializationData s, LocalContext values) {
+                return builder.create().returnTrue().build();
+            }
+        };
+
+        builder.tree(createGuardAndCast(group, genericType, currentLocals, executionFactory));
+        builder.returnFalse();
+        return method;
+    }
+
+    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.getType());
+            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);
+        LocalContext currentLocals = LocalContext.load(this, createSpecializationNodeSignature(node.getSignatureSize()), varArgsThreshold);
+
+        CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), specializationNodeType, "merge");
+        executable.addParameter(new CodeVariableElement(specializationNodeType, "newNode"));
+        currentLocals.addParametersTo(executable, varArgsThreshold, FRAME_VALUE);
+        executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        CodeTreeBuilder builder = executable.createBuilder();
+
+        if (specialization.isPolymorphic()) {
+            builder.startReturn();
+            builder.startCall("polymorphicMerge");
+            builder.string("newNode");
+            builder.startCall("super", "merge");
+            builder.string("newNode");
+            currentLocals.addReferencesTo(builder, FRAME_VALUE);
+            builder.end();
+            builder.end();
+            builder.end();
+
+        } 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.startReturn();
+            builder.startCall("super", "merge");
+            builder.string("newNode");
+            currentLocals.addReferencesTo(builder, FRAME_VALUE);
+            builder.end();
+            builder.end();
+        }
+
+        return executable;
+    }
+
+    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", varArgsThreshold, 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 SpecializationBody(false, false) {
+            @Override
+            public CodeTree createBody(SpecializationData specialization, LocalContext values) {
+                CodeTypeElement generatedType = specializationClasses.get(specialization);
+                if (generatedType == null) {
+                    throw new AssertionError("No generated type for " + specialization);
+                }
+                return createSlowPathExecute(specialization, values);
+            }
+        });
+
+        builder.tree(execution);
+
+        if (hasFallthrough(group, genericType, locals, false, null)) {
+            builder.returnNull();
+        }
+        return method;
+    }
+
+    private CodeExecutableElement createFallbackGuardMethod() {
+        boolean frameUsed = node.isFrameUsedByAnyGuard();
+        LocalContext locals = LocalContext.load(this);
+
+        if (!frameUsed) {
+            locals.removeValue(FRAME_VALUE);
+        }
+
+        CodeExecutableElement boundaryMethod = locals.createMethod(modifiers(PRIVATE), getType(boolean.class), "guardFallback", varArgsThreshold, 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 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, "unsupported", varArgsThreshold, FRAME_VALUE);
+        method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startReturn();
+        builder.tree(callTemplateMethod(accessParent(null), fallback, locals));
+        builder.end();
+
+        return method;
+    }
+
+    private boolean isSingleSpecializableImpl() {
+        if (reachableSpecializations.size() != 1) {
+            return false;
+        }
+
+        SpecializationData specialization = reachableSpecializations.get(0);
+
+        for (Parameter parameter : specialization.getSignatureParameters()) {
+            TypeMirror type = parameter.getType();
+            if (type != null && typeSystem.hasImplicitSourceTypes(type)) {
+                return false;
+            }
+        }
+
+        if (!specialization.getAssumptionExpressions().isEmpty()) {
+            return false;
+        }
+
+        if (specialization.getCaches().size() > 0) {
+            // TODO chumer: caches do not yet support single specialization.
+            // it could be worthwhile to explore if this is possible
+            return false;
+        }
+        return true;
+    }
+
+    private List<SpecializationData> calculateReachableSpecializations() {
+        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(List<ExecutableTypeData> usedExecutables, ExecutableTypeData execType) {
+        LocalContext locals = LocalContext.load(this, execType, Integer.MAX_VALUE);
+        CodeExecutableElement method = createExecuteMethod(null, execType, locals, true, Integer.MAX_VALUE);
+
+        CodeTreeBuilder builder = method.createBuilder();
+        if (singleSpecializable) {
+            SpecializationData specialization = reachableSpecializations.iterator().next();
+            builder.tree(createFastPath(builder, specialization, execType, usedExecutables, locals));
+        } else {
+            // create acceptAndExecute
+            ExecutableTypeData delegate = execType;
+            CodeTree receiver = CodeTreeBuilder.singleString(specializationStartFieldName());
+            builder.tree(createCallDelegateExecute(builder, receiver, locals, execType, delegate));
+        }
+        return method;
+    }
+
+    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> resolveSpecializedExecutables(NodeExecutionData execution, Collection<TypeMirror> types, TypeBoxingOptimization optimization) {
+        if (optimization == TypeBoxingOptimization.NONE) {
+            return Collections.emptyList();
+        } else if (types.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        List<ExecutableTypeData> executables = new ArrayList<>();
+        for (TypeMirror type : types) {
+            if (!isTypeBoxingOptimized(optimization, type)) {
+                continue;
+            }
+            if (execution.getChild() == null) {
+                continue;
+            }
+            ExecutableTypeData foundType = execution.getChild().getNodeData().findExecutableType(type, execution.getChild().getExecuteWith().size());
+            if (foundType != null) {
+                executables.add(foundType);
+            }
+        }
+        return executables;
+    }
+
+    private static CodeTree callMethod(CodeTree receiver, ExecutableElement method, CodeTree... boundValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        if (method.getModifiers().contains(STATIC)) {
+            builder.startStaticCall(method.getEnclosingElement().asType(), method.getSimpleName().toString());
+        } else {
+            builder.startCall(receiver, method.getSimpleName().toString());
+        }
+        int index = -1;
+        for (VariableElement parameter : method.getParameters()) {
+            index++;
+            if (index < boundValues.length) {
+                CodeTree tree = boundValues[index];
+                if (tree != null) {
+                    builder.tree(tree);
+                    continue;
+                }
+            }
+
+            builder.defaultValue(parameter.asType());
+        }
+        builder.end();
+        return builder.build();
+    }
+
+    private CodeTree[] bindExecuteMethodParameters(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) {
+        List<NodeExecutionData> executeWith = execution != null ? execution.getChild().getExecuteWith() : null;
+
+        List<CodeTree> values = new ArrayList<>();
+        if (method.getFrameParameter() != null) {
+            LocalVariable frameLocal = currentValues.get(FRAME_VALUE);
+            if (frameLocal == null) {
+                values.add(CodeTreeBuilder.singleString("null"));
+            } else {
+                values.add(createTypeSafeReference(frameLocal, method.getFrameParameter()));
+            }
+        }
+
+        int evaluatedIndex = 0;
+        for (int executionIndex = 0; executionIndex < node.getExecutionCount(); executionIndex++) {
+            NodeExecutionData parameterExecution;
+            if (executeWith != null && executionIndex < executeWith.size()) {
+                parameterExecution = executeWith.get(executionIndex);
+            } else {
+                parameterExecution = node.getChildExecutions().get(executionIndex);
+            }
+            if (parameterExecution.isShortCircuit()) {
+                if (evaluatedIndex < method.getEvaluatedCount()) {
+                    TypeMirror targetType = method.getEvaluatedParameters().get(evaluatedIndex);
+                    LocalVariable shortCircuit = currentValues.getShortCircuit(parameterExecution);
+                    if (shortCircuit != null) {
+                        values.add(createTypeSafeReference(shortCircuit, targetType));
+                    } else {
+                        values.add(CodeTreeBuilder.createBuilder().defaultValue(targetType).build());
+                    }
+                    evaluatedIndex++;
+                }
+            }
+            if (evaluatedIndex < method.getEvaluatedCount()) {
+                TypeMirror targetType = method.getEvaluatedParameters().get(evaluatedIndex);
+                LocalVariable value = currentValues.getValue(parameterExecution);
+                if (value != null) {
+                    values.add(createTypeSafeReference(value, targetType));
+                } else {
+                    values.add(CodeTreeBuilder.createBuilder().defaultValue(targetType).build());
+                }
+                evaluatedIndex++;
+            }
+        }
+        return values.toArray(new CodeTree[values.size()]);
+    }
+
+    private CodeTree callExecuteMethod(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) {
+        CodeTree receiver = execution != null ? accessParent(nodeFieldName(execution)) : null;
+        return callMethod(receiver, method.getMethod(), bindExecuteMethodParameters(execution, method, currentValues));
+    }
+
+    private CodeTree callTemplateMethod(CodeTree receiver, TemplateMethod method, LocalContext currentValues) {
+        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) {
+                var = currentValues.get(parameter.getLocalName());
+            }
+
+            if (var != null) {
+                bindings[i] = createTypeSafeReference(var, parameter.getType());
+            }
+
+            if (parameter.getSpecification().isSignature()) {
+                signatureIndex++;
+            }
+        }
+        return callMethod(receiver, method.getMethod(), bindings);
+    }
+
+    private CodeTree createTypeSafeReference(LocalVariable var, TypeMirror targetType) {
+        CodeTree valueReference = var.createReference();
+        TypeMirror sourceType = var.getTypeMirror();
+        if (targetType == null || sourceType == null) {
+            return valueReference;
+        }
+        if (needsCastTo(sourceType, targetType)) {
+            valueReference = TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference);
+        }
+        return valueReference;
+    }
+
+    private SpecializationGroup createSpecializationGroups() {
+        return SpecializationGroup.create(reachableSpecializations);
+    }
+
+    private CodeTree createSlowPathExecute(SpecializationData specialization, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        if (specialization.isFallback()) {
+            return builder.returnNull().build();
+        }
+
+        if (node.isFrameUsedByAnyGuard()) {
+            builder.tree(createTransferToInterpreterAndInvalidate());
+        }
+
+        // caches unbound to guards are invoked after all guards
+        for (CacheExpression cache : specialization.getCaches()) {
+            if (!specialization.isCacheBoundByGuard(cache)) {
+                initializeCache(builder, specialization, cache, currentValues);
+            }
+        }
+        boolean hasAssumptions = !specialization.getAssumptionExpressions().isEmpty();
+        if (hasAssumptions) {
+
+            for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
+                CodeTree assumptions = DSLExpressionGenerator.write(assumption.getExpression(), accessParent(null),
+                                castBoundTypes(bindExpressionValues(assumption.getExpression(), specialization, currentValues)));
+                String name = assumptionName(assumption);
+                // needs specialization index for assumption to make unique
+                String varName = name + specialization.getIndex();
+                TypeMirror type = assumption.getExpression().getResolvedType();
+                builder.declaration(type, varName, assumptions);
+                currentValues.set(name, new LocalVariable(type, varName, null, null));
+            }
+
+            builder.startIf();
+            String sep = "";
+            for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
+                LocalVariable assumptionVar = currentValues.get(assumptionName(assumption));
+                if (assumptionVar == null) {
+                    throw new AssertionError("assumption var not resolved");
+                }
+                builder.string(sep);
+                builder.startCall("isValid").tree(assumptionVar.createReference()).end();
+                sep = " && ";
+            }
+            builder.end();
+            builder.startBlock();
+        }
+
+        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();
+            }
+        }
+
+        CodeTree create = createCallCreateMethod(specialization, null, currentValues);
+
+        if (specialization.hasMultipleInstances()) {
+            builder.declaration(getType(SpecializationNode.class), "s", create);
+            DSLExpression limitExpression = specialization.getLimitExpression();
+            CodeTree limitExpressionTree;
+            if (limitExpression == null) {
+                limitExpressionTree = CodeTreeBuilder.singleString("3");
+            } else {
+                limitExpressionTree = DSLExpressionGenerator.write(limitExpression, accessParent(null), //
+                                castBoundTypes(bindExpressionValues(limitExpression, specialization, currentValues)));
+            }
+
+            builder.startIf().string("countSame(s) < ").tree(limitExpressionTree).end().startBlock();
+            builder.statement("return s");
+            builder.end();
+        } else {
+            builder.startReturn().tree(create).end();
+        }
+
+        if (hasAssumptions) {
+            builder.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 boolean hasFallthrough(SpecializationGroup group, TypeMirror forType, LocalContext currentValues, boolean fastPath, List<GuardExpression> ignoreGuards) {
+        for (TypeGuard guard : group.getTypeGuards()) {
+            if (currentValues.getValue(guard.getSignatureIndex()) == null) {
+                // not evaluated
+                return true;
+            }
+            LocalVariable value = currentValues.getValue(guard.getSignatureIndex());
+            if (needsCastTo(value.getTypeMirror(), guard.getType())) {
+                return true;
+            }
+        }
+
+        List<GuardExpression> guards = new ArrayList<>(group.getGuards());
+        List<GuardExpression> elseConnectable = group.findElseConnectableGuards();
+        guards.removeAll(elseConnectable);
+        if (ignoreGuards != null) {
+            guards.removeAll(ignoreGuards);
+        }
+        SpecializationData specialization = group.getSpecialization();
+        if (specialization != null && fastPath) {
+            for (ListIterator<GuardExpression> iterator = guards.listIterator(); iterator.hasNext();) {
+                GuardExpression guard = iterator.next();
+                if (!specialization.isDynamicParameterBound(guard.getExpression())) {
+                    iterator.remove();
+                }
+            }
+        }
+
+        if (!guards.isEmpty()) {
+            return true;
+        }
+
+        if (!fastPath && specialization != null && !specialization.getAssumptionExpressions().isEmpty()) {
+            return true;
+        }
+
+        if (!fastPath && specialization != null && mayBeExcluded(specialization)) {
+            return true;
+        }
+
+        if (!elseConnectable.isEmpty()) {
+            SpecializationGroup previous = group.getPrevious();
+            if (previous != null && hasFallthrough(previous, forType, currentValues, fastPath, previous.getGuards())) {
+                return true;
+            }
+        }
+
+        List<SpecializationGroup> groupChildren = group.getChildren();
+        if (!groupChildren.isEmpty()) {
+            return hasFallthrough(groupChildren.get(groupChildren.size() - 1), forType, currentValues, fastPath, ignoreGuards);
+        }
+
+        return false;
+    }
+
+    private Element createGetNext(CodeTypeElement type) {
+        if (!nextUsed) {
+            return null;
+        }
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), type.asType(), "getNext");
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startReturn().cast(type.asType(), CodeTreeBuilder.singleString("this.next")).end();
+        return method;
+    }
+
+    private Element createGetSuppliedChildrenMethod() {
+        ArrayType nodeArray = context.getEnvironment().getTypeUtils().getArrayType(getType(Node.class));
+
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), nodeArray, "getSuppliedChildren");
+        method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+
+        CodeTreeBuilder builder = method.createBuilder();
+        builder.startReturn().tree(createGetSuppliedChildren()).end();
+
+        return method;
+    }
+
+    private CodeTree createGetSuppliedChildren() {
+        ArrayType nodeArray = context.getEnvironment().getTypeUtils().getArrayType(getType(Node.class));
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.startNewArray(nodeArray, null);
+        for (int i = 0; i < node.getChildExecutions().size(); i++) {
+            NodeExecutionData execution = node.getChildExecutions().get(i);
+            if (execution.isShortCircuit()) {
+                builder.nullLiteral();
+            }
+            if (execution.getChild() == null) {
+                builder.nullLiteral();
+            } else {
+                builder.tree(accessParent(nodeFieldName(execution)));
+            }
+        }
+        builder.end();
+        return builder.build();
+    }
+
+    // create specialization
+
+    private CodeTree createCallCreateMethod(SpecializationData specialization, String rootName, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+
+        TypeMirror specializationType = specializationType(specialization);
+        if (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()) {
+                CodeVariableElement var = createImplicitProfileParameter(p.getSpecification().getExecution(), p.getType());
+                if (var != null) {
+                    LocalVariable variable = currentValues.get(p.getLocalName());
+                    if (variable == null) {
+                        throw new AssertionError("Could not bind cached value " + p.getLocalName() + ": " + currentValues);
+                    }
+                    builder.tree(variable.original().createReference());
+                }
+            }
+            for (CacheExpression cache : specialization.getCaches()) {
+                LocalVariable variable = currentValues.get(cache.getParameter().getLocalName());
+                if (variable == null) {
+                    throw new AssertionError("Could not bind cached value " + cache.getParameter().getLocalName() + ": " + currentValues);
+                }
+                builder.tree(variable.createReference());
+            }
+            for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
+                LocalVariable variable = currentValues.get(assumptionName(assumption));
+                if (variable == null) {
+                    throw new AssertionError("Could not bind assumption value " + assumption.getId() + ": " + currentValues);
+                }
+                builder.tree(variable.createReference());
+            }
+        }
+        builder.end();
+
+        return builder.build();
+    }
+
+    private Element createSpecializationCreateMethod(SpecializationData specialization, CodeExecutableElement constructor) {
+        if (!useLazyClassLoading()) {
+            return null;
+        }
+
+        CodeExecutableElement executable = CodeExecutableElement.clone(context.getEnvironment(), constructor);
+        executable.setReturnType(specializationType(null));
+        executable.setSimpleName(CodeNames.of("create"));
+        executable.getModifiers().add(STATIC);
+
+        CodeTreeBuilder builder = executable.createBuilder();
+        builder.startReturn().startNew(specializationType(specialization));
+        for (VariableElement parameter : executable.getParameters()) {
+            builder.string(parameter.getSimpleName().toString());
+        }
+        builder.end().end();
+        return executable;
+    }
+
+    private boolean useLazyClassLoading() {
+        return options.useLazyClassLoading() && !singleSpecializable;
+    }
+
+    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.getType());
+                if (implicitProfile != null) {
+                    LocalVariable var = LocalVariable.fromParameter(p).makeGeneric(context);
+
+                    String implicitFieldName = implicitProfile.getName();
+                    if (options.implicitCastOptimization().isDuplicateTail()) {
+                        constructor.addParameter(var.createParameter());
+                        CodeTree implicitType = TypeSystemCodeGenerator.implicitType(typeSystem, p.getType(), 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(typeSystem, p.getType(), var.createReference())).end();
+                    } else {
+                        throw new AssertionError();
+                    }
+                }
+            }
+            for (CacheExpression cache : specialization.getCaches()) {
+                String name = cache.getParameter().getLocalName();
+                TypeMirror type = cache.getParameter().getType();
+
+                if (ElementUtils.isAssignable(type, new ArrayCodeTypeMirror(getType(Node.class)))) {
+                    CodeVariableElement var = clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), type, name));
+                    var.addAnnotationMirror(new CodeAnnotationMirror(context.getDeclaredType(Children.class)));
+                } else if (ElementUtils.isAssignable(type, getType(Node.class))) {
+                    CodeVariableElement var = clazz.add(new CodeVariableElement(modifiers(PRIVATE), type, name));
+                    var.addAnnotationMirror(new CodeAnnotationMirror(context.getDeclaredType(Child.class)));
+                } else {
+                    clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), type, name));
+                }
+                constructor.addParameter(new CodeVariableElement(type, name));
+                builder.startStatement().string("this.").string(name).string(" = ").string(name).end();
+            }
+
+            for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
+                String name = assumptionName(assumption);
+                TypeMirror type = assumption.getExpression().getResolvedType();
+                CodeVariableElement field = clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), type, name));
+                field.addAnnotationMirror(new CodeAnnotationMirror(context.getDeclaredType(CompilationFinal.class)));
+                constructor.addParameter(new CodeVariableElement(type, name));
+                builder.startStatement().string("this.").string(name).string(" = ").string(name).end();
+            }
+        }
+
+        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 createThrowUnsupported(LocalContext currentValues) {
+        singleSpecializableUnsupportedUsed = true;
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.startThrow().startCall("unsupported");
+        currentValues.addReferencesTo(builder);
+        builder.end().end();
+        return builder.build();
+    }
+
+    private CodeTree createCallNext(CodeTreeBuilder parent, ExecutableTypeData currentType, ExecutableTypeData callType, LocalContext currentValues) {
+        if (singleSpecializable) {
+            return createThrowUnsupported(currentValues);
+        }
+        CodeTreeBuilder callBuilder = parent.create();
+        callBuilder.tree(createCallDelegateExecute(callBuilder, CodeTreeBuilder.singleString("getNext()"), currentValues, currentType, callType));
+        nextUsed = true;
+        return callBuilder.build();
+    }
+
+    private CodeTree createCallRemove(String reason, ExecutableTypeData forType, LocalContext currentValues) {
+        if (singleSpecializable) {
+            return createThrowUnsupported(currentValues);
+        }
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.startCall("remove");
+        builder.doubleQuote(reason);
+        currentValues.addReferencesTo(builder, FRAME_VALUE);
+        builder.end();
+        CodeTree call = builder.build();
+
+        builder = builder.create();
+        builder.startReturn();
+        builder.tree(expectOrCast(genericType, forType, call));
+        builder.end();
+        return builder.build();
+    }
+
+    private CodeTree createCallDelegate(String methodName, String reason, ExecutableTypeData forType, LocalContext currentValues) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.startCall(methodName);
+        if (reason != null) {
+            builder.doubleQuote(reason);
+        }
+        currentValues.addReferencesTo(builder, FRAME_VALUE);
+        builder.end();
+
+        CodeTree expectOrCast = expectOrCast(genericType, forType, builder.build());
+        return expectOrCast;
+    }
+
+    private CodeTree expectOrCast(TypeMirror sourceType, ExecutableTypeData targetType, CodeTree content) {
+        if (needsUnexpectedResultException(targetType)) {
+            return expect(sourceType, targetType.getReturnType(), content);
+        } else {
+            return cast(sourceType, targetType.getReturnType(), content);
+        }
+    }
+
+    private CodeTree cast(TypeMirror sourceType, TypeMirror targetType, CodeTree content) {
+        if (ElementUtils.needsCastTo(sourceType, targetType) && !isVoid(sourceType)) {
+            return TypeSystemCodeGenerator.cast(typeSystem, targetType, content);
+        } else {
+            return content;
+        }
+    }
+
+    private CodeTree expect(TypeMirror sourceType, TypeMirror forType, CodeTree tree) {
+        if (sourceType == null || ElementUtils.needsCastTo(sourceType, forType)) {
+            expectedTypes.add(forType);
+            return TypeSystemCodeGenerator.expect(typeSystem, forType, tree);
+        }
+        return tree;
+    }
+
+    private Set<ExecutableTypeData> findSpecializedExecutableTypes(NodeExecutionData execution, TypeMirror type) {
+        if (execution.getChild() == null) {
+            return Collections.emptySet();
+        }
+        ExecutableTypeData executableType = resolveExecutableType(execution.getChild(), type);
+        Set<ExecutableTypeData> executedTypes = new HashSet<>();
+        executedTypes.add(executableType);
+        if (typeSystem.hasImplicitSourceTypes(type)) {
+            executedTypes.addAll(resolveSpecializedExecutables(execution, typeSystem.lookupSourceTypes(type), options.implicitTypeBoxingOptimization()));
+        }
+        return executedTypes;
+    }
+
+    private ExecutableTypeData resolveExecutableType(NodeChildData child, TypeMirror type) {
+        int executeWithCount = child.getExecuteWith().size();
+        ExecutableTypeData executableType = child.getNodeData().findExecutableType(type, executeWithCount);
+        if (executableType == null) {
+            executableType = child.getNodeData().findAnyGenericExecutableType(context, executeWithCount);
+        }
+        return executableType;
+    }
+
+    private boolean hasChildUnexpectedResult(NodeExecutionData execution, TypeMirror type) {
+        for (ExecutableTypeData executableType : findSpecializedExecutableTypes(execution, type)) {
+            if (executableType != null && (executableType.hasUnexpectedValue(context) || needsCastTo(executableType.getReturnType(), type))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private Element createFastPathExecuteMethod(SpecializationData specialization, ExecutableTypeData executedType, List<ExecutableTypeData> allTypes) {
+        LocalContext currentLocals = LocalContext.load(this, executedType, Integer.MAX_VALUE);
+        CodeExecutableElement executable = createExecuteMethod(specialization, executedType, currentLocals, false, Integer.MAX_VALUE);
+        CodeTreeBuilder builder = executable.createBuilder();
+        if (specialization == null) {
+            if (executedType.getDelegatedTo() == null) {
+                executable.getModifiers().add(ABSTRACT);
+            }
+        } else {
+            executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
+        }
+        builder.tree(createFastPath(builder, specialization, executedType, allTypes, currentLocals));
+
+        return executable;
+    }
+
+    private static final String VARARGS_NAME = "args";
+
+    private CodeExecutableElement createExecuteMethod(SpecializationData specialization, ExecutableTypeData executedType, LocalContext currentLocals, boolean originalOverride, int varArgs) {
+        TypeMirror returnType = executedType.getReturnType();
+
+        if (specialization != null) {
+            currentLocals.loadFastPathState(specialization);
+        }
+
+        String methodName;
+        if (originalOverride && executedType.getMethod() != null) {
+            methodName = executedType.getMethod().getSimpleName().toString();
+        } else {
+            methodName = executedType.getUniqueName();
+        }
+
+        CodeExecutableElement executable;
+        if (originalOverride && executedType.getMethod() != null) {
+            executable = CodeExecutableElement.clone(context.getEnvironment(), executedType.getMethod());
+            executable.getAnnotationMirrors().clear();
+            executable.getModifiers().remove(ABSTRACT);
+            for (VariableElement var : executable.getParameters()) {
+                ((CodeVariableElement) var).getAnnotationMirrors().clear();
+            }
+            if (executedType.getFrameParameter() != null) {
+                ((CodeVariableElement) executable.getParameters().get(0)).setName(FRAME_VALUE);
+            }
+
+            if (executable.isVarArgs()) {
+                ((CodeVariableElement) executable.getParameters().get(executable.getParameters().size() - 1)).setName(VARARGS_NAME);
+            }
+
+            renameOriginalParameters(executedType, executable, currentLocals);
+        } else {
+            executable = currentLocals.createMethod(modifiers(PUBLIC), returnType, methodName, varArgs, FRAME_VALUE);
+        }
+        executable.getThrownTypes().clear();
+
+        if (needsUnexpectedResultException(executedType)) {
+            executable.getThrownTypes().add(context.getDeclaredType(UnexpectedResultException.class));
+        }
+
+        return executable;
+    }
+
+    private void renameOriginalParameters(ExecutableTypeData executedType, CodeExecutableElement executable, LocalContext currentLocals) {
+        // rename varargs parameter
+        int evaluatedIndex = 0;
+        for (int executionIndex = 0; executionIndex < node.getExecutionCount(); executionIndex++) {
+            NodeExecutionData execution = node.getChildExecutions().get(executionIndex);
+            if (execution.isShortCircuit()) {
+                if (evaluatedIndex < executedType.getEvaluatedCount()) {
+                    TypeMirror evaluatedType = executedType.getEvaluatedParameters().get(evaluatedIndex);
+                    LocalVariable shortCircuit = currentLocals.getShortCircuit(execution);
+                    if (shortCircuit != null) {
+                        currentLocals.setShortCircuitValue(execution, renameExecutableTypeParameter(executable, executedType, evaluatedIndex, evaluatedType, shortCircuit));
+                    }
+                    evaluatedIndex++;
+                }
+            }
+            if (evaluatedIndex < executedType.getEvaluatedCount()) {
+                TypeMirror evaluatedType = executedType.getEvaluatedParameters().get(evaluatedIndex);
+                LocalVariable value = currentLocals.getValue(execution);
+                if (value != null) {
+                    currentLocals.setValue(execution, renameExecutableTypeParameter(executable, executedType, evaluatedIndex, evaluatedType, value));
+                }
+                evaluatedIndex++;
+            }
+        }
+    }
+
+    private static LocalVariable renameExecutableTypeParameter(CodeExecutableElement method, ExecutableTypeData executedType, int evaluatedIndex, TypeMirror targetType, LocalVariable var) {
+        int parameterIndex = executedType.getParameterIndex(evaluatedIndex);
+        int varArgsIndex = executedType.getVarArgsIndex(parameterIndex);
+        LocalVariable returnVar = var;
+        if (varArgsIndex >= 0) {
+            returnVar = returnVar.accessWith(CodeTreeBuilder.singleString(VARARGS_NAME + "[" + varArgsIndex + "]"));
+        } else {
+            ((CodeVariableElement) method.getParameters().get(parameterIndex)).setName(returnVar.getName());
+        }
+        if (!isObject(targetType)) {
+            returnVar = returnVar.newType(targetType);
+        }
+        return returnVar;
+    }
+
+    private boolean needsUnexpectedResultException(ExecutableTypeData executedType) {
+        if (!executedType.hasUnexpectedValue(context)) {
+            return false;
+        }
+
+        SpecializationData polymorphicSpecialization = node.getPolymorphicSpecialization();
+        if (polymorphicSpecialization != null && isSubtypeBoxed(context, polymorphicSpecialization.getReturnType().getType(), executedType.getReturnType())) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private CodeTree createFastPath(CodeTreeBuilder parent, SpecializationData specialization, final ExecutableTypeData executableType, List<ExecutableTypeData> allTypes, LocalContext currentLocals) {
+        final CodeTreeBuilder builder = parent.create();
+        TypeMirror returnType = executableType.getReturnType();
+
+        ExecutableTypeData delegate = null;
+        if (specialization == null) {
+            delegate = executableType.getDelegatedTo();
+        }
+
+        if (delegate == null) {
+            delegate = findFastPathDelegate((specialization != null ? specialization.getReturnType().getType() : genericType), executableType, allTypes);
+        }
+
+        int delegateSignatureCount = delegate != null ? delegate.getSignatureParameters().size() : 0;
+        for (NodeExecutionData execution : node.getChildExecutions()) {
+            if (specialization == null && delegate != null && execution.getIndex() >= delegateSignatureCount) {
+                // we just evaluate children for the next delegate
+                continue;
+            } else if (specialization != null && delegate != null) {
+                // skip if already delegated
+                break;
+            }
+
+            LocalVariable var = currentLocals.getValue(execution);
+            if (var == null) {
+                TypeMirror targetType;
+                if (specialization == null) {
+                    targetType = node.getGenericType(execution);
+                } else {
+                    targetType = specialization.findParameterOrDie(execution).getType();
+                }
+                LocalVariable shortCircuit = resolveShortCircuit(specialization, execution, currentLocals);
+                var = currentLocals.createValue(execution, targetType).nextName();
+                builder.tree(createAssignExecuteChild(builder, execution, executableType, var, shortCircuit, currentLocals));
+                currentLocals.setValue(execution, var);
+            }
+        }
+
+        LocalContext originalValues = currentLocals.copy();
+        if (delegate != null) {
+            builder.tree(createCallDelegateExecute(builder, null, currentLocals, executableType, delegate));
+        } else if (specialization == null) {
+            // nothing to do. abstract anyway
+        } else if (specialization.isPolymorphic()) {
+            builder.tree(createCallNext(builder, executableType, node.getGenericExecutableType(executableType), currentLocals));
+        } else if (specialization.isUninitialized()) {
+            builder.startReturn().tree(createCallDelegate("uninitialized", null, executableType, currentLocals)).end();
+        } else {
+            SpecializationGroup group = SpecializationGroup.create(specialization);
+            SpecializationBody executionFactory = new SpecializationBody(true, true) {
+                @Override
+                public CodeTree createBody(SpecializationData s, LocalContext values) {
+                    return createFastPathExecute(builder, executableType, s, values);
+                }
+            };
+            builder.tree(createGuardAndCast(group, returnType, currentLocals, executionFactory));
+            if (hasFallthrough(group, returnType, originalValues, true, null) || group.getSpecialization().isFallback()) {
+                builder.tree(createCallNext(builder, executableType, node.getGenericExecutableType(executableType), originalValues));
+            }
+        }
+        return builder.build();
+    }
+
+    private CodeTree createCallDelegateExecute(final CodeTreeBuilder parent, CodeTree receiver, LocalContext currentLocals, ExecutableTypeData source, ExecutableTypeData delegate) {
+        CodeTreeBuilder callBuilder = parent.create();
+
+        if (singleSpecializable && delegate.getMethod() != null) {
+            callBuilder.startCall(receiver, delegate.getMethod().getSimpleName().toString());
+        } else {
+            callBuilder.startCall(receiver, delegate.getUniqueName());
+        }
+        callBuilder.trees(bindExecuteMethodParameters(null, delegate, currentLocals));
+        callBuilder.end();
+        CodeTree call = expectOrCast(delegate.getReturnType(), source, callBuilder.build());
+
+        CodeTreeBuilder returnBuilder = parent.create();
+        if (isVoid(source.getReturnType())) {
+            returnBuilder.statement(call);
+            returnBuilder.returnStatement();
+        } else if (isVoid(delegate.getReturnType())) {
+            returnBuilder.statement(call);
+            returnBuilder.returnDefault();
+        } else {
+            returnBuilder.startReturn().tree(call).end();
+        }
+
+        CodeTreeBuilder builder = parent.create();
+
+        if (!needsUnexpectedResultException(source) && needsUnexpectedResultException(delegate)) {
+            builder.startTryBlock();
+            builder.tree(returnBuilder.build());
+            builder.end().startCatchBlock(context.getType(UnexpectedResultException.class), "ex");
+            if (!isVoid(source.getReturnType())) {
+                builder.startReturn().tree(cast(context.getType(Object.class), source.getReturnType(), CodeTreeBuilder.singleString("ex.getResult()"))).end();
+            }
+            builder.end();
+        } else {
+            builder.tree(returnBuilder.build());
+        }
+        return builder.build();
+    }
+
+    private ExecutableTypeData findFastPathDelegate(TypeMirror targetType, ExecutableTypeData executableType, List<ExecutableTypeData> allTypes) {
+        if (typeEquals(executableType.getReturnType(), targetType)) {
+            // type matches look for even better delegates
+            for (ExecutableTypeData type : allTypes) {
+                if (typeEquals(type.getReturnType(), targetType) && executableType.sameParameters(type)) {
+                    if (type != executableType) {
+                        return type;
+                    }
+                }
+            }
+            return null;
+        } else {
+            for (ExecutableTypeData type : allTypes) {
+                if (typeEquals(type.getReturnType(), targetType) && executableType.sameParameters(type)) {
+                    return type;
+                }
+            }
+            int executableIndex = allTypes.indexOf(executableType);
+            int compareIndex = 0;
+            for (ExecutableTypeData type : allTypes) {
+                if (executableIndex != compareIndex && executableType.sameParameters(type)) {
+                    int result = ExecutableTypeData.compareType(context, type.getReturnType(), executableType.getReturnType());
+                    if (result < 0) {
+                        return type;
+                    } else if (result == 0 && executableIndex < compareIndex) {
+                        return type;
+                    }
+                }
+                compareIndex++;
+            }
+            return null;
+        }
+    }
+
+    private LocalVariable resolveShortCircuit(SpecializationData specialization, NodeExecutionData execution, LocalContext currentLocals) {
+        LocalVariable shortCircuit = null;
+        if (execution.isShortCircuit()) {
+            shortCircuit = currentLocals.getShortCircuit(execution);
+
+            if (shortCircuit == null) {
+                SpecializationData resolvedSpecialization = specialization;
+                if (specialization == null) {
+                    resolvedSpecialization = node.getGenericSpecialization();
+                }
+                ShortCircuitData shortCircuitData = resolvedSpecialization.getShortCircuits().get(calculateShortCircuitIndex(execution));
+                CodeTree access = callTemplateMethod(accessParent(null), shortCircuitData, currentLocals);
+                shortCircuit = currentLocals.createShortCircuitValue(execution).accessWith(access);
+            } else {
+                CodeTree access = shortCircuit.createReference();
+                shortCircuit = shortCircuit.nextName().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(CodeTreeBuilder parent, final ExecutableTypeData forType, SpecializationData specialization, LocalContext currentValues) {
+        CodeTreeBuilder builder = parent.create();
+        int ifCount = 0;
+        if (specialization.isFallback()) {
+            builder.startIf().startCall("guardFallback");
+            if (node.isFrameUsedByAnyGuard()) {
+                if (currentValues.get(FRAME_VALUE) != null) {
+                    builder.string(FRAME_VALUE);
+                } else {
+                    builder.nullLiteral();
+                }
+            }
+            currentValues.addReferencesTo(builder);
+
+            builder.end();
+            builder.end();
+            builder.startBlock();
+            ifCount++;
+        }
+        CodeTreeBuilder execute = builder.create();
+
+        if (!specialization.getAssumptionExpressions().isEmpty()) {
+            builder.startTryBlock();
+            for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
+                LocalVariable assumptionVar = currentValues.get(assumptionName(assumption));
+                if (assumptionVar == null) {
+                    throw new AssertionError("Could not resolve assumption var " + currentValues);
+                }
+                builder.startStatement().startCall("check").tree(assumptionVar.createReference()).end().end();
+            }
+            builder.end().startCatchBlock(getType(InvalidAssumptionException.class), "ae");
+            builder.startReturn();
+            List<String> assumptionIds = new ArrayList<>();
+            for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
+                assumptionIds.add(assumption.getId());
+            }
+            builder.tree(createCallDelegate("removeThis", String.format("Assumption %s invalidated", assumptionIds), forType, currentValues));
+            builder.end();
+            builder.end();
+        }
+
+        if (specialization.getMethod() == null) {
+            execute.startReturn();
+            execute.startCall("unsupported");
+            currentValues.addReferencesTo(execute, FRAME_VALUE);
+            execute.end();
+            execute.end();
+        } else {
+            boolean doReturn = !isVoid(specialization.getMethod().getReturnType());
+            if (doReturn) {
+                execute.startReturn();
+            } else {
+                execute.startStatement();
+            }
+            execute.tree(callTemplateMethod(accessParent(null), specialization, currentValues));
+            execute.end();
+            if (!doReturn) {
+                if (isVoid(forType.getReturnType())) {
+                    execute.returnStatement();
+                } else {
+                    execute.startReturn();
+                    execute.defaultValue(forType.getReturnType());
+                    execute.end();
+                }
+            }
+        }
+        builder.tree(createFastPathTryCatchRewriteException(specialization, forType, currentValues, execute.build()));
+        builder.end(ifCount);
+        return builder.build();
+    }
+
+    private CodeTree createGuardAndCast(SpecializationGroup group, TypeMirror forType, LocalContext currentValues, SpecializationBody execution) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+
+        Set<TypeGuard> castGuards;
+        if (execution.needsCastedValues()) {
+            castGuards = null; // cast all
+        } else {
+            castGuards = new HashSet<>();
+            for (TypeGuard castGuard : group.getTypeGuards()) {
+                if (isTypeGuardUsedInAnyGuardOrCacheBelow(group, currentValues, castGuard, execution.isFastPath())) {
+                    castGuards.add(castGuard);
+                }
+            }
+        }
+
+        SpecializationData specialization = group.getSpecialization();
+        CodeTree[] checkAndCast = createTypeCheckAndLocals(specialization, 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[] methodGuardAndAssertions = createMethodGuardCheck(guardExpressions, specialization, currentValues, execution.isFastPath());
+        CodeTree methodGuards = methodGuardAndAssertions[0];
+        CodeTree guardAssertions = methodGuardAndAssertions[1];
+
+        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++;
+        }
+        if (!guardAssertions.isEmpty()) {
+            builder.tree(guardAssertions);
+        }
+
+        boolean reachable = isReachableGroup(group, ifCount);
+        if (reachable) {
+            for (SpecializationGroup child : group.getChildren()) {
+                builder.tree(createGuardAndCast(child, forType, currentValues.copy(), execution));
+            }
+            if (specialization != null) {
+                builder.tree(execution.createBody(specialization, currentValues));
+            }
+        }
+        builder.end(ifCount);
+
+        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.getParent() == null || previous.getMaxSpecializationIndex() != previous.getParent().getMaxSpecializationIndex())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean isTypeGuardUsedInAnyGuardOrCacheBelow(SpecializationGroup group, LocalContext currentValues, TypeGuard typeGuard, boolean fastPath) {
+        String localName = currentValues.getValue(typeGuard.getSignatureIndex()).getName();
+
+        SpecializationData specialization = group.getSpecialization();
+        for (GuardExpression guard : group.getGuards()) {
+            if (isVariableBoundIn(specialization, guard.getExpression(), localName, currentValues)) {
+                return true;
+            }
+        }
+        if (!fastPath && specialization != null) {
+            for (CacheExpression cache : specialization.getCaches()) {
+                if (isVariableBoundIn(specialization, cache.getExpression(), localName, currentValues)) {
+                    return true;
+                }
+            }
+        }
+
+        for (SpecializationGroup child : group.getChildren()) {
+            if (isTypeGuardUsedInAnyGuardOrCacheBelow(child, currentValues, typeGuard, fastPath)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean isVariableBoundIn(SpecializationData specialization, DSLExpression expression, String localName, LocalContext currentValues) throws AssertionError {
+        Map<Variable, LocalVariable> boundValues = bindExpressionValues(expression, specialization, currentValues);
+        for (Variable var : expression.findBoundVariables()) {
+            LocalVariable target = boundValues.get(var);
+            if (target != null && localName.equals(target.getName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private CodeExecutableElement createExecuteChildMethod(NodeExecutionData execution, TypeMirror targetType) {
+        if (!usedExecuteChildMethods.contains(execution)) {
+            return null;
+        }
+
+        LocalContext locals = LocalContext.load(this, createSpecializationNodeSignature(0), Integer.MAX_VALUE);
+
+        CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), targetType, executeChildMethodName(execution, targetType), Integer.MAX_VALUE, FRAME_VALUE);
+        if (hasChildUnexpectedResult(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, locals.createValue(execution, targetType), locals, true);
+        if (executeChild.isSingleLine()) {
+            builder.statement(executeChild);
+        } else {
+            builder.tree(executeChild);
+        }
+        return method;
+    }
+
+    private CodeVariableElement createImplicitProfileParameter(NodeExecutionData execution, TypeMirror targetType) {
+        if (typeSystem.hasImplicitSourceTypes(targetType)) {
+            if (typeEquals(node.getGenericType(execution), targetType)) {
+                return null;
+            }
+
+            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(typeSystem, targetType), implicitNodeFieldName(execution));
+            }
+        }
+        return null;
+    }
+
+    private boolean isExecuteChildShared(NodeExecutionData execution, TypeMirror targetType) {
+        if (isVoid(targetType)) {
+            return false;
+        } else if (isObject(targetType)) {
+            return resolvePolymorphicExecutables(execution).size() >= 1;
+        } else {
+            if (!isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), targetType)) {
+                return false;
+            }
+            if (!typeSystem.hasImplicitSourceTypes(targetType)) {
+                return false;
+            }
+
+            int uses = 0;
+            for (SpecializationData specialization : node.getSpecializations()) {
+                List<Parameter> parameters = specialization.findByExecutionData(execution);
+                for (Parameter parameter : parameters) {
+                    if (targetType.equals(parameter.getType())) {
+                        uses++;
+                    }
+                }
+            }
+            if (uses > 1) {
+                return resolveSpecializedExecutables(execution, typeSystem.lookupSourceTypes(targetType), options.implicitTypeBoxingOptimization()).size() > 1;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private CodeTree createAssignExecuteChild(CodeTreeBuilder parent, NodeExecutionData execution, ExecutableTypeData type, LocalVariable targetValue, LocalVariable shortCircuit,
+                    LocalContext currentValues) {
+        CodeTreeBuilder builder = parent.create();
+        boolean hasUnexpected = hasChildUnexpectedResult(execution, targetValue.getTypeMirror());
+
+        CodeTree executeChild;
+        if (isExecuteChildShared(execution, targetValue.getTypeMirror())) {
+            executeChild = createCallSharedExecuteChild(execution, targetValue, currentValues);
+        } else {
+            executeChild = createExecuteChild(execution, targetValue, currentValues, false);
+        }
+
+        builder.tree(createTryExecuteChild(targetValue, executeChild, shortCircuit == null, hasUnexpected));
+
+        if (shortCircuit != null) {
+            currentValues.setShortCircuitValue(execution, shortCircuit.accessWith(null));
+        }
+
+        builder.end();
+        if (hasUnexpected) {
+            builder.startCatchBlock(getType(UnexpectedResultException.class), "ex");
+            LocalContext slowPathValues = currentValues.copy();
+            slowPathValues.setValue(execution, targetValue.makeGeneric(context).accessWith(CodeTreeBuilder.singleString("ex.getResult()")));
+
+            ExecutableTypeData delegateType = node.getGenericExecutableType(type);
+            boolean found = false;
+            for (NodeExecutionData otherExecution : node.getChildExecutions()) {
+                if (found) {
+                    LocalVariable childEvaluatedValue = slowPathValues.createValue(otherExecution, genericType);
+                    LocalVariable genericShortCircuit = resolveShortCircuit(null, otherExecution, slowPathValues);
+                    builder.tree(createAssignExecuteChild(builder, otherExecution, delegateType, childEvaluatedValue, genericShortCircuit, slowPathValues));
+                    slowPathValues.setValue(otherExecution, childEvaluatedValue);
+                } else {
+                    // skip forward already evaluated
+                    found = execution == otherExecution;
+                }
+            }
+            builder.tree(createCallNext(builder, type, delegateType, slowPathValues));
+            builder.end();
+        }
+
+        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.getTypeMirror())) {
+            throw new AssertionError("Execute child not shared with method but called.");
+        }
+        usedExecuteChildMethods.add(execution);
+
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        builder.tree(targetValue.createReference()).string(" = ");
+        builder.startCall(executeChildMethodName(execution, targetValue.getTypeMirror()));
+        if (currentValues.get(FRAME_VALUE) == null) {
+            builder.nullLiteral();
+        } else {
+            builder.string(FRAME_VALUE);
+        }
+
+        CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, targetValue.getTypeMirror());
+        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, LocalVariable target, LocalContext currentValues, boolean shared) {
+        final CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+
+        CodeTree assignment = createAssignmentStart(target, shared);
+
+        final Set<ExecutableTypeData> executableTypes = findSpecializedExecutableTypes(execution, target.getTypeMirror());
+        if (executableTypes.isEmpty()) {
+            throw new AssertionError(); // cannot execute child
+        } else if (executableTypes.size() == 1 && !typeSystem.hasImplicitSourceTypes(target.getTypeMirror())) {
+            ExecutableTypeData executableType = executableTypes.iterator().next();
+            if (isObject(target.getTypeMirror()) && executableType.getEvaluatedCount() == 0) {
+                return createPolymorphicExecuteChild(execution, target, currentValues, shared);
+            } else {
+                builder.tree(assignment);
+                builder.tree(createSingleExecute(execution, target, currentValues, executableType));
+            }
+        } else {
+            if (options.implicitCastOptimization().isNone()) {
+                throw new AssertionError("findSpecializedExecutableTypes is always 1 if implicit cast opt is disabled");
+            } else if (options.implicitCastOptimization().isDuplicateTail()) {
+                builder.tree(createExecuteChildDuplicateTail(builder, execution, assignment, target, currentValues));
+            } else if (options.implicitCastOptimization().isMergeCasts()) {
+                // TODO
+                throw new UnsupportedOperationException();
+            } else {
+                throw new AssertionError();
+            }
+        }
+        return builder.build();
+    }
+
+    private CodeTree createSingleExecute(NodeExecutionData execution, LocalVariable target, LocalContext currentValues, ExecutableTypeData executableType) {
+        CodeTree execute = callExecuteMethod(execution, executableType, currentValues);
+        return expect(executableType.getReturnType(), target.getTypeMirror(), execute);
+    }
+
+    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("At least one generic executable method must be available.");
+        }
+
+        List<ExecutableTypeData> specializedExecutables = resolvePolymorphicExecutables(execution);
+        Collections.sort(specializedExecutables, new Comparator<ExecutableTypeData>() {
+            public int compare(ExecutableTypeData o1, ExecutableTypeData o2) {
+                return compareType(o1.getReturnType(), o2.getReturnType());
+            }
+        });
+
+        CodeTree assignment = createAssignmentStart(target, shared);
+        CodeTree executeGeneric = createSingleExecute(execution, target, currentValues, genericExecutableType);
+
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        if (specializedExecutables.isEmpty()) {
+            builder.tree(assignment);
+            builder.tree(executeGeneric);
+        } else {
+            final CodeTreeBuilder polyChainBuilder = builder.create();
+            final String profileField = polymorphicTypeProfileFieldName(execution);
+            final String valueFieldName = "_value";
+            final String typeFieldName = "_type";
+
+            builder.declaration(getType(Class.class), profileField, accessParent(profileField));
+
+            boolean encounteredUnexpectedResult = false;
+            boolean hasSpecializedTypes = false;
+            for (ExecutableTypeData executableType : specializedExecutables) {
+                hasSpecializedTypes = polyChainBuilder.startIf(hasSpecializedTypes);
+                polyChainBuilder.string(profileField);
+                polyChainBuilder.string(" == ").typeLiteral(executableType.getReturnType());
+                polyChainBuilder.end();
+                polyChainBuilder.startBlock();
+                polyChainBuilder.startStatement();
+                polyChainBuilder.tree(assignment);
+                polyChainBuilder.tree(createSingleExecute(execution, target, currentValues, executableType)).end();
+                polyChainBuilder.end();
+                encounteredUnexpectedResult |= executableType.hasUnexpectedValue(context);
+            }
+
+            // else if null -> specialize
+            polyChainBuilder.startElseIf().string(profileField).string(" == null").end();
+            polyChainBuilder.startBlock();
+            polyChainBuilder.tree(createTransferToInterpreterAndInvalidate());
+            polyChainBuilder.declaration(context.getType(Class.class), typeFieldName, polyChainBuilder.create().typeLiteral(genericType).build());
+            polyChainBuilder.startTryBlock();
+            polyChainBuilder.declaration(genericExecutableType.getReturnType(), valueFieldName, executeGeneric);
+
+            hasSpecializedTypes = false;
+            for (ExecutableTypeData executableType : specializedExecutables) {
+                hasSpecializedTypes = polyChainBuilder.startIf(hasSpecializedTypes);
+                polyChainBuilder.tree(TypeSystemCodeGenerator.check(typeSystem, executableType.getReturnType(), CodeTreeBuilder.singleString(valueFieldName)));
+                polyChainBuilder.end();
+                polyChainBuilder.startBlock();
+                polyChainBuilder.startStatement().string(typeFieldName).string(" = ").typeLiteral(executableType.getReturnType()).end();
+                polyChainBuilder.end();
+            }
+            polyChainBuilder.startElseBlock();
+            polyChainBuilder.startStatement().string(typeFieldName).string(" = ").typeLiteral(genericType).end();
+            polyChainBuilder.end();
+            polyChainBuilder.startReturn().string(valueFieldName).end();
+
+            polyChainBuilder.end().startFinallyBlock();
+            polyChainBuilder.startStatement().tree(accessParent(profileField)).string(" = ").string(typeFieldName).end();
+            polyChainBuilder.end();
+            polyChainBuilder.end();
+
+            // else -> execute generic
+            polyChainBuilder.startElseBlock();
+            polyChainBuilder.startStatement().tree(assignment).tree(executeGeneric).end();
+            polyChainBuilder.end();
+
+            CodeTree executePolymorphic = polyChainBuilder.build();
+            if (encounteredUnexpectedResult) {
+                builder.startTryBlock();
+                builder.tree(executePolymorphic);
+                builder.end();
+                builder.startCatchBlock(getType(UnexpectedResultException.class), "ex");
+                builder.startStatement().tree(accessParent(profileField)).string(" = ").typeLiteral(genericType).end();
+                builder.startReturn().string("ex.getResult()").end();
+                builder.end();
+            } else {
+                builder.tree(executePolymorphic);
+            }
+        }
+        return builder.build();
+    }
+
+    private List<ExecutableTypeData> resolvePolymorphicExecutables(NodeExecutionData execution) {
+        if (singleSpecializable) {
+            return Collections.emptyList();
+        }
+        Set<TypeMirror> specializedTypes = new HashSet<>();
+        for (TypeMirror type : node.findSpecializedTypes(execution)) {
+            specializedTypes.addAll(typeSystem.lookupSourceTypes(type));
+        }
+        return resolveSpecializedExecutables(execution, specializedTypes, options.polymorphicTypeBoxingElimination());
+    }
+
+    private static CodeTree createAssignmentStart(LocalVariable target, boolean shared) {
+        CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
+        if (shared) {
+            builder.string("return ");
+        } else {
+            builder.string(target.getName()).string(" = ");
+        }
+        return builder.build();
+    }
+
+    private CodeTree createExecuteChildDuplicateTail(CodeTreeBuilder parent, NodeExecutionData execution, CodeTree assignment, LocalVariable target, LocalContext currentValues) {
+        CodeTreeBuilder builder = parent.create();
+        List<TypeMirror> sourceTypes = typeSystem.lookupSourceTypes(target.getTypeMirror());
+        String implicitClassFieldName = implicitClassFieldName(execution);
+        List<ExecutableTypeData> executableTypes = resolveSpecializedExecutables(execution, sourceTypes, options.implicitTypeBoxingOptimization());
+
+        boolean elseIf = false;
+        for (ExecutableTypeData executableType : executableTypes) {
+            elseIf = builder.startIf(elseIf);
+            builder.string(implicitClassFieldName).string(" == ").typeLiteral(executableType.getReturnType());
+            builder.end();
+            builder.startBlock();
+            builder.startStatement().tree(assignment);
+
+            CodeTree execute = callExecuteMethod(execution, executableType, currentValues);
+            ImplicitCastData cast = typeSystem.lookupCast(executableType.getReturnType(), target.getTypeMirror());
+            if (cast != null) {
+                execute = callMethod(null, cast.getMethod(), execute);
+            }
+            builder.tree(execute);
+            builder.end();
+            builder.end();
+        }
+
+        if (!executableTypes.isEmpty()) {
+            builder.startElseBlock();
+        }
+
+        LocalVariable genericValue = target.makeGeneric(context).nextName();
+        builder.tree(createAssignExecuteChild(builder, execution, node.getGenericExecutableType(null), genericValue, null, currentValues));
+        if (executableTypes.size() == sourceTypes.size()) {
+            builder.startThrow().startNew(getType(UnexpectedResultException.class)).tree(genericValue.createReference()).end().end();
+        } else {
+            builder.startStatement().tree(assignment);
+            builder.tree(TypeSystemCodeGenerator.implicitExpect(typeSystem, target.getTypeMirror(), genericValue.createReference(), implicitClassFieldName));
+            builder.end();
+        }
+
+        if (!executableTypes.isEmpty()) {
+            builder.end();
+        }
+        return builder.build();
+    }
+
+    private CodeTree createFastPathTryCatchRewriteException(SpecializationData specialization, ExecutableTypeData 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.tree(createCallRemove("threw rewrite exception", forType, currentValues));
+        builder.end();
+        return builder.build();
+    }
+
+    private CodeTree[] createMethodGuardCheck(List<GuardExpression> guardExpressions, SpecializationData specialization, LocalContext currentValues, boolean fastPath) {
+        CodeTreeBuilder expressionBuilder = CodeTreeBuilder.createBuilder();
+        CodeTreeBuilder assertionBuilder = CodeTreeBuilder.createBuilder();
+        String and = "";
+        for (GuardExpression guard : guardExpressions) {
+            DSLExpression expression = guard.getExpression();
+
+            Map<Variable, CodeTree> resolvedBindings = castBoundTypes(bindExpressionValues(expression, specialization, currentValues));
+            CodeTree expressionCode = DSLExpressionGenerator.write(expression, accessParent(null), resolvedBindings);
+
+            if (!specialization.isDynamicParameterBound(expression) && fastPath) {
+                /*
+                 * Guards where no dynamic parameters are bound can just be executed on the fast
+                 * path.
+                 */
+                assertionBuilder.startAssert().tree(expressionCode).end();
+            } else {
+                expressionBuilder.string(and);
+                expressionBuilder.tree(expressionCode);
+                and = " && ";
+            }
+        }
+        return new CodeTree[]{expressionBuilder.build(), assertionBuilder.build()};
+    }
+
+    private static Map<Variable, CodeTree> castBoundTypes(Map<Variable, LocalVariable> bindings) {
+        Map<Variable, CodeTree> resolvedBindings = new HashMap<>();
+        for (Variable variable : bindings.keySet()) {
+            LocalVariable localVariable = bindings.get(variable);
+            CodeTree resolved = localVariable.createReference();
+            TypeMirror sourceType = localVariable.getTypeMirror();
+            TypeMirror targetType = variable.getResolvedTargetType();
+            if (targetType == null) {
+                targetType = variable.getResolvedType();
+            }
+            if (!ElementUtils.isAssignable(sourceType, targetType)) {
+                resolved = CodeTreeBuilder.createBuilder().cast(targetType, resolved).build();
+            }
+            resolvedBindings.put(variable, resolved);
+        }
+        return resolvedBindings;
+    }
+
+    private static Map<Variable, LocalVariable> bindExpressionValues(DSLExpression expression, SpecializationData specialization, LocalContext currentValues) throws AssertionError {
+        Map<Variable, LocalVariable> bindings = new HashMap<>();
+
+        Set<Variable> boundVariables = expression.findBoundVariables();
+        if (specialization == null && !boundVariables.isEmpty()) {
+            throw new AssertionError("Cannot bind guard variable in non-specialization group. yet.");
+        }
+
+        // resolve bindings for local context
+        for (Variable variable : boundVariables) {
+            Parameter resolvedParameter = specialization.findByVariable(variable.getResolvedVariable());
+            if (resolvedParameter != null) {
+                LocalVariable localVariable;
+                if (resolvedParameter.getSpecification().isSignature()) {
+                    NodeExecutionData execution = resolvedParameter.getSpecification().getExecution();
+                    localVariable = currentValues.getValue(execution);
+                } else {
+                    localVariable = currentValues.get(resolvedParameter.getLocalName());
+                }
+                if (localVariable != null) {
+                    bindings.put(variable, localVariable);
+                }
+            }
+        }
+        return bindings;
+    }
+
+    private CodeTree[] createTypeCheckAndLocals(SpecializationData specialization, List<TypeGuard> typeGuards, Set<TypeGuard> castGuards, LocalContext currentValues,
+                    SpecializationBody specializationExecution) {
+        CodeTreeBuilder checksBuilder = CodeTreeBuilder.createBuilder();
+        CodeTreeBuilder localsBuilder = CodeTreeBuilder.createBuilder();
+        for (TypeGuard typeGuard : typeGuards) {
+            int signatureIndex = typeGuard.getSignatureIndex();
+            LocalVariable value = currentValues.getValue(signatureIndex);
+            TypeMirror targetType = typeGuard.getType();
+            if (!ElementUtils.needsCastTo(value.getTypeMirror(), 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 (!ElementUtils.isPrimitive(shortCircuit.getTypeMirror())) {
+                    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(typeSystem, targetType, value.createReference()));
+                castBuilder.tree(TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference));
+            } else {
+                ImplicitCastOptimization opt = options.implicitCastOptimization();
+                if (specializationExecution.isFastPath() && !opt.isNone()) {
+                    if (opt.isDuplicateTail()) {
+                        String typeHintField = implicitClassFieldName(execution);
+                        checkBuilder.tree(TypeSystemCodeGenerator.implicitCheck(typeSystem, targetType, valueReference, typeHintField));
+                        castBuilder.tree(TypeSystemCodeGenerator.implicitCast(typeSystem, 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(typeSystem, targetType, valueReference, null));
+                    castBuilder.tree(TypeSystemCodeGenerator.implicitCast(typeSystem, targetType, valueReference, null));
+                }
+            }
+
+            if (shortCircuit != null) {
+                checkBuilder.string(")");
+                castBuilder.string(" : ").defaultValue(targetType);
+            }
+
+            if (castGuards == null || castGuards.contains(typeGuard)) {
+                LocalVariable castVariable = currentValues.getValue(execution).nextName().newType(typeGuard.getType()).accessWith(null);
+                currentValues.setValue(execution, castVariable);
+                localsBuilder.tree(castVariable.createDeclaration(castBuilder.build()));
+            }
+
+            checksBuilder.tree(checkBuilder.build());
+        }
+
+        if (specialization != null && !specializationExecution.isFastPath()) {
+            for (CacheExpression cache : specialization.getCaches()) {
+                if (specialization.isCacheBoundByGuard(cache)) {
+                    initializeCache(localsBuilder, specialization, cache, currentValues);
+                }
+            }
+        }
+
+        return new CodeTree[]{checksBuilder.build(), localsBuilder.build()};
+    }
+
+    private void initializeCache(CodeTreeBuilder builder, SpecializationData specialization, CacheExpression cache, LocalContext currentValues) {
+        CodeTree initializer = DSLExpressionGenerator.write(cache.getExpression(), accessParent(null), castBoundTypes(bindExpressionValues(cache.getExpression(), specialization, currentValues)));
+        String name = cache.getParameter().getLocalName();
+        // multiple specializations might use the same name
+        String varName = name + specialization.getIndex();
+        TypeMirror type = cache.getParameter().getType();
+        builder.declaration(type, varName, initializer);
+        currentValues.set(name, new LocalVariable(type, varName, null, null));
+    }
+
+    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 void loadFastPathState(SpecializationData specialization) {
+            for (CacheExpression cache : specialization.getCaches()) {
+                Parameter cacheParameter = cache.getParameter();
+                String name = cacheParameter.getVariableElement().getSimpleName().toString();
+                set(cacheParameter.getLocalName(), new LocalVariable(cacheParameter.getType(), name, CodeTreeBuilder.singleString("this." + name), null));
+            }
+
+            for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
+                String name = assumptionName(assumption);
+                TypeMirror type = assumption.getExpression().getResolvedType();
+                set(name, new LocalVariable(type, name, CodeTreeBuilder.singleString("this." + name), null));
+            }
+        }
+
+        public CodeExecutableElement createMethod(Set<Modifier> modifiers, TypeMirror returnType, String name, int varArgsThreshold, String... optionalArguments) {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers, returnType, name);
+            addParametersTo(method, varArgsThreshold, optionalArguments);
+            return method;
+        }
+
+        public static LocalContext load(NodeGenFactory factory, ExecutableTypeData type, int varargsThreshold) {
+            LocalContext context = new LocalContext(factory);
+            context.loadEvaluatedValues(type, varargsThreshold);
+            return context;
+        }
+
+        private void loadEvaluatedValues(ExecutableTypeData executedType, int varargsThreshold) {
+            TypeMirror frame = executedType.getFrameParameter();
+            if (frame == null) {
+                removeValue(FRAME_VALUE);
+            } else {
+                set(FRAME_VALUE, new LocalVariable(frame, FRAME_VALUE, null, null));
+            }
+            for (NodeFieldData field : factory.node.getFields()) {
+                String fieldName = fieldValueName(field);
+                values.put(fieldName, new LocalVariable(field.getType(), fieldName, factory.accessParent(field.getName()), null));
+            }
+            boolean varargs = needsVarargs(false, varargsThreshold);
+            List<TypeMirror> evaluatedParameter = executedType.getEvaluatedParameters();
+            int evaluatedIndex = 0;
+            for (int executionIndex = 0; executionIndex < factory.node.getExecutionCount(); executionIndex++) {
+                NodeExecutionData execution = factory.node.getChildExecutions().get(executionIndex);
+                if (execution.isShortCircuit()) {
+                    if (evaluatedIndex < executedType.getEvaluatedCount()) {
+                        TypeMirror evaluatedType = evaluatedParameter.get(evaluatedIndex);
+                        LocalVariable shortCircuit = createShortCircuitValue(execution).newType(evaluatedType);
+                        if (varargs) {
+                            shortCircuit = shortCircuit.accessWith(createReadVarargs(evaluatedIndex));
+                        }
+                        values.put(shortCircuit.getName(), shortCircuit.makeOriginal());
+                        evaluatedIndex++;
+                    }
+                }
+                if (evaluatedIndex < executedType.getEvaluatedCount()) {
+                    TypeMirror evaluatedType = evaluatedParameter.get(evaluatedIndex);
+                    LocalVariable value = createValue(execution, evaluatedType);
+                    if (varargs) {
+                        value = value.accessWith(createReadVarargs(evaluatedIndex));
+                    }
+                    values.put(value.getName(), value.makeOriginal());
+                    evaluatedIndex++;
+                }
+            }
+        }
+
+        public static LocalContext load(NodeGenFactory factory) {
+            return load(factory, factory.createSpecializationNodeSignature(factory.node.getSignatureSize()), factory.varArgsThreshold);
+        }
+
+        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, TypeMirror type) {
+            return new LocalVariable(type, valueName(execution), null, null);
+        }
+
+        public LocalVariable createShortCircuitValue(NodeExecutionData execution) {
+            return new LocalVariable(factory.getType(boolean.class), shortCircuitName(execution), null, null);
+        }
+
+        private static String valueName(NodeExecutionData execution) {
+            return execution.getName() + "Value";
+        }
+
+        private static String shortCircuitName(NodeExecutionData execution) {
+            return "has" + ElementUtils.firstLetterUpperCase(valueName(execution));
+        }
+
+        public void set(String id, LocalVariable var) {
+            values.put(id, var);
+        }
+
+        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
+                List<NodeExecutionData> childExecutions = factory.node.getChildExecutions();
+                if (signatureIndex < childExecutions.size() && signatureIndex >= 0) {
+                    NodeExecutionData execution = childExecutions.get(signatureIndex);
+                    var = getValue(execution);
+                }
+            }
+            return var;
+        }
+
+        public LocalVariable getValue(NodeExecutionData execution) {
+            return get(valueName(execution));
+        }
+
+        public LocalVariable getValue(int signatureIndex) {
+            List<NodeExecutionData> childExecutions = factory.node.getChildExecutions();
+            if (signatureIndex < childExecutions.size()) {
+                return getValue(childExecutions.get(signatureIndex));
+            } else {
+                return null;
+            }
+        }
+
+        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 varArgsThreshold) {
+            int size = 0;
+            for (NodeExecutionData execution : factory.node.getChildExecutions()) {
+                if (requireLoaded && getValue(execution) == null) {
+                    continue;
+                }
+                if (execution.isShortCircuit()) {
+                    size += 2;
+                } else {
+                    size++;
+                }
+            }
+            return size >= varArgsThreshold;
+        }
+
+        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, int varArgsThreshold, String... optionalNames) {
+            for (String var : optionalNames) {
+                LocalVariable local = values.get(var);
+                if (local != null) {
+                    method.addParameter(local.createParameter());
+                }
+            }
+            if (needsVarargs(true, varArgsThreshold)) {
+                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));
+        }
+
+        @Override
+        public String toString() {
+            return "LocalContext [values=" + values + "]";
+        }
+
+    }
+
+    public static final class LocalVariable {
+
+        private final TypeMirror typeMirror;
+        private final CodeTree accessorTree;
+        private final String name;
+        private final LocalVariable previous;
+
+        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.getType(), name, null, null);
+        }
+
+        private LocalVariable(TypeMirror typeMirror, String name, CodeTree accessorTree, LocalVariable previous) {
+            Objects.requireNonNull(typeMirror);
+            this.typeMirror = typeMirror;
+            this.accessorTree = accessorTree;
+            this.name = name;
+            this.previous = previous;
+        }
+
+        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(TypeMirror newType) {
+            return new LocalVariable(newType, name, accessorTree, this);
+        }
+
+        public LocalVariable accessWith(CodeTree tree) {
+            return new LocalVariable(typeMirror, name, tree, this);
+        }
+
+        public LocalVariable nextName() {
+            return new LocalVariable(typeMirror, createNextName(name), accessorTree, this);
+        }
+
+        public LocalVariable makeOriginal() {
+            return new LocalVariable(typeMirror, name, accessorTree, null);
+        }
+
+        public LocalVariable original() {
+            LocalVariable variable = this;
+            while (variable.previous != null) {
+                variable = variable.previous;
+            }
+            return variable;
+        }
+
+        public LocalVariable makeGeneric(ProcessorContext context) {
+            return newType(context.getType(Object.class));
+        }
+
+        @Override
+        public String toString() {
+            return "Local[type = " + getTypeMirror() + ", name = " + name + ", accessWith = " + accessorTree + "]";
+        }
+
+    }
+
+    private abstract class SpecializationBody {
+
+        private final boolean fastPath;
+        private final boolean needsCastedValues;
+
+        public SpecializationBody(boolean fastPath, boolean needsCastedValues) {
+            this.fastPath = fastPath;
+            this.needsCastedValues = needsCastedValues;
+        }
+
+        public final boolean isFastPath() {
+            return fastPath;
+        }
+
+        public final boolean needsCastedValues() {
+            return needsCastedValues;
+        }
+
+        public abstract CodeTree createBody(SpecializationData specialization, LocalContext currentValues);
+
+    }
+
+}