changeset 16755:bd28da642eea

Truffle-DSL: Several new features implemented: Implementation of a new code generation layout which shares code between generated nodes. Declaration order of specializations is now used as specialization order. Specializations do no longer perform fallthrough on respecialization, they now always respecialize from the first specialization. Implemented support for contains relations between specializations. Improved reachability error messages. Preliminary support for @Implies.
author Christian Humer <christian.humer@gmail.com>
date Mon, 11 Aug 2014 15:53:05 +0200
parents 55fd5be68a52
children 5148aab962af
files graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/AbstractParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/AnnotationProcessor.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ProcessorContext.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleProcessor.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleTypes.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/Utils.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/codewriter/FixWarningsVisitor.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/codewriter/OrganizedImports.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/GeneratedTypeMirror.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationGroup.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationMethodParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/ClassElementFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/CodeElementFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/CompilationUnitFactory.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/MessageContainer.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/Template.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethod.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethodParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardExpression.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardParser.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemCodeGenerator.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java
diffstat 30 files changed, 1812 insertions(+), 1066 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/AbstractParser.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/AbstractParser.java	Mon Aug 11 15:53:05 2014 +0200
@@ -30,6 +30,7 @@
 import javax.tools.Diagnostic.Kind;
 
 import com.oracle.truffle.dsl.processor.template.*;
+import com.oracle.truffle.dsl.processor.template.MessageContainer.*;
 
 /**
  * THIS IS NOT PUBLIC API.
@@ -42,10 +43,10 @@
 
     protected final Log log;
 
-    public AbstractParser(ProcessorContext c) {
-        this.context = c;
-        this.processingEnv = c.getEnvironment();
-        this.log = c.getLog();
+    public AbstractParser() {
+        this.context = ProcessorContext.getInstance();
+        this.processingEnv = context.getEnvironment();
+        this.log = context.getLog();
     }
 
     public final M parse(RoundEnvironment env, Element element) {
@@ -65,7 +66,8 @@
                 return null;
             }
 
-            model.emitMessages(context, (TypeElement) element, log);
+            redirectMessages(new HashSet<MessageContainer>(), model, model);
+            model.emitMessages(context, log);
             return filterErrorElements(model);
         } catch (CompileErrorException e) {
             log.message(Kind.WARNING, element, null, null, "The truffle processor could not parse class due to error: %s", e.getMessage());
@@ -75,6 +77,51 @@
         }
     }
 
+    private void redirectMessages(Set<MessageContainer> visitedSinks, MessageContainer model, MessageContainer baseContainer) {
+        List<Message> messages = model.getMessages();
+        for (int i = messages.size() - 1; i >= 0; i--) {
+            Message message = messages.get(i);
+            if (!Utils.isEnclosedIn(baseContainer.getMessageElement(), message.getOriginalContainer().getMessageElement())) {
+                // redirect message
+                MessageContainer original = message.getOriginalContainer();
+                String text = wrapText(original.getMessageElement(), original.getMessageAnnotation(), message.getText());
+                Message redirectedMessage = new Message(null, baseContainer, text, message.getKind());
+                model.getMessages().remove(i);
+                baseContainer.getMessages().add(redirectedMessage);
+            }
+        }
+
+        for (MessageContainer childContainer : model) {
+            if (visitedSinks.contains(childContainer)) {
+                continue;
+            }
+            visitedSinks.add(childContainer);
+
+            MessageContainer newBase = baseContainer;
+            if (childContainer.getBaseContainer() != null) {
+                newBase = childContainer.getBaseContainer();
+            }
+            redirectMessages(visitedSinks, childContainer, newBase);
+        }
+    }
+
+    private static String wrapText(Element element, AnnotationMirror mirror, String text) {
+        StringBuilder b = new StringBuilder();
+        if (element != null) {
+            b.append("Element " + element.toString());
+        }
+        if (mirror != null) {
+            b.append(" at annotation @" + Utils.getSimpleName(mirror.getAnnotationType()));
+        }
+
+        if (b.length() > 0) {
+            b.append(" is erroneous: ").append(text);
+            return b.toString();
+        } else {
+            return text;
+        }
+    }
+
     protected M filterErrorElements(M model) {
         return model.hasErrors() ? null : model;
     }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/AnnotationProcessor.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/AnnotationProcessor.java	Mon Aug 11 15:53:05 2014 +0200
@@ -33,6 +33,7 @@
 import com.oracle.truffle.dsl.processor.ast.*;
 import com.oracle.truffle.dsl.processor.codewriter.*;
 import com.oracle.truffle.dsl.processor.compiler.*;
+import com.oracle.truffle.dsl.processor.node.*;
 import com.oracle.truffle.dsl.processor.template.*;
 
 /**
@@ -42,12 +43,10 @@
 
     private final AbstractParser<M> parser;
     private final CompilationUnitFactory<M> factory;
-    private final ProcessorContext context;
 
     private final Set<String> processedElements = new HashSet<>();
 
-    public AnnotationProcessor(ProcessorContext context, AbstractParser<M> parser, CompilationUnitFactory<M> factory) {
-        this.context = context;
+    public AnnotationProcessor(AbstractParser<M> parser, CompilationUnitFactory<M> factory) {
         this.parser = parser;
         this.factory = factory;
     }
@@ -56,13 +55,8 @@
         return parser;
     }
 
-    public ProcessorContext getContext() {
-        return context;
-    }
-
     @SuppressWarnings({"unchecked"})
     public void process(RoundEnvironment env, Element element, boolean callback) {
-
         // since it is not guaranteed to be called only once by the compiler
         // we check for already processed elements to avoid errors when writing files.
         if (!callback && element instanceof TypeElement) {
@@ -73,6 +67,7 @@
             processedElements.add(qualifiedName);
         }
 
+        ProcessorContext context = ProcessorContext.getInstance();
         TypeElement type = (TypeElement) element;
 
         M model = (M) context.getTemplate(type.asType(), false);
@@ -85,6 +80,7 @@
 
             if (model != null) {
                 CodeCompilationUnit unit = factory.process(null, model);
+                patchGeneratedTypes(unit);
                 unit.setGeneratorAnnotationMirror(model.getTemplateTypeAnnotation());
                 unit.setGeneratorElement(model.getTemplateType());
 
@@ -100,6 +96,55 @@
         }
     }
 
+    private static void patchGeneratedTypes(CodeCompilationUnit unit) {
+        final Map<String, CodeTypeElement> classes = new HashMap<>();
+
+        unit.accept(new CodeElementScanner<Void, Void>() {
+            @Override
+            public Void visitType(CodeTypeElement e, Void p) {
+                classes.put(e.getSimpleName().toString(), e);
+                return super.visitType(e, p);
+            }
+
+        }, null);
+
+        unit.accept(new CodeElementScanner<Void, Void>() {
+            @Override
+            public Void visitExecutable(CodeExecutableElement e, Void p) {
+                if (e.getReturnType() instanceof GeneratedTypeMirror) {
+                    e.setReturnType(patchType(e.getReturnType()));
+                }
+                for (VariableElement element : e.getParameters()) {
+                    if (element instanceof CodeVariableElement) {
+                        CodeVariableElement var = ((CodeVariableElement) element);
+                        if (var.getType() instanceof GeneratedTypeMirror) {
+                            var.setType(patchType(var.getType()));
+                        }
+                    }
+                }
+                return super.visitExecutable(e, p);
+            }
+
+            @Override
+            public void visitTree(CodeTree e, Void p) {
+                if (e.getType() instanceof GeneratedTypeMirror) {
+                    e.setType(patchType(e.asType()));
+                }
+            }
+
+            private TypeMirror patchType(TypeMirror typeMirror) {
+                assert typeMirror instanceof GeneratedTypeMirror;
+                GeneratedTypeMirror type = (GeneratedTypeMirror) typeMirror;
+                CodeTypeElement generatedType = classes.get(Utils.fromTypeMirror(type).getSimpleName().toString());
+                if (generatedType == null) {
+                    return type;
+                }
+                return generatedType.asType();
+            }
+        }, null);
+
+    }
+
     private static class CodeWriter extends AbstractCodeWriter {
 
         private final Element originalElement;
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ProcessorContext.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ProcessorContext.java	Mon Aug 11 15:53:05 2014 +0200
@@ -209,4 +209,14 @@
         }
         return type;
     }
+
+    private static final ThreadLocal<ProcessorContext> instance = new ThreadLocal<>();
+
+    public static void setThreadLocalInstance(ProcessorContext context) {
+        instance.set(context);
+    }
+
+    public static ProcessorContext getInstance() {
+        return instance.get();
+    }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleProcessor.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleProcessor.java	Mon Aug 11 15:53:05 2014 +0200
@@ -42,7 +42,6 @@
 @SupportedSourceVersion(SourceVersion.RELEASE_7)
 public class TruffleProcessor extends AbstractProcessor implements ProcessCallback {
 
-    private ProcessorContext context;
     private List<AnnotationProcessor<?>> generators;
 
     private RoundEnvironment round;
@@ -60,6 +59,7 @@
         // TODO run verifications that other annotations are not processed out of scope of the
         // operation or typelattice.
         try {
+            ProcessorContext.setThreadLocalInstance(new ProcessorContext(processingEnv, this));
             for (AnnotationProcessor<?> generator : getGenerators()) {
                 AbstractParser<?> parser = generator.getParser();
                 if (parser.getAnnotationType() != null) {
@@ -82,6 +82,7 @@
 
             }
         } finally {
+            ProcessorContext.setThreadLocalInstance(null);
             this.round = null;
         }
     }
@@ -96,7 +97,7 @@
 
     private static void handleThrowable(AnnotationProcessor<?> generator, Throwable t, Element e) {
         String message = "Uncaught error in " + generator.getClass().getSimpleName() + " while processing " + e;
-        generator.getContext().getEnvironment().getMessager().printMessage(Kind.ERROR, message + ": " + Utils.printException(t), e);
+        ProcessorContext.getInstance().getEnvironment().getMessager().printMessage(Kind.ERROR, message + ": " + Utils.printException(t), e);
     }
 
     @Override
@@ -127,19 +128,12 @@
     private List<AnnotationProcessor<?>> getGenerators() {
         if (generators == null && processingEnv != null) {
             generators = new ArrayList<>();
-            generators.add(new AnnotationProcessor<>(getContext(), new TypeSystemParser(getContext()), new TypeSystemCodeGenerator(getContext())));
-            generators.add(new AnnotationProcessor<>(getContext(), new NodeParser(getContext()), new NodeCodeGenerator(getContext())));
+            generators.add(new AnnotationProcessor<>(new TypeSystemParser(), new TypeSystemCodeGenerator()));
+            generators.add(new AnnotationProcessor<>(new NodeParser(), new NodeCodeGenerator()));
         }
         return generators;
     }
 
-    private ProcessorContext getContext() {
-        if (context == null) {
-            context = new ProcessorContext(processingEnv, this);
-        }
-        return context;
-    }
-
     @Override
     public synchronized void init(ProcessingEnvironment env) {
         this.processingEnv = env;
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleTypes.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/TruffleTypes.java	Mon Aug 11 15:53:05 2014 +0200
@@ -32,6 +32,7 @@
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.SlowPath;
 import com.oracle.truffle.api.dsl.*;
+import com.oracle.truffle.api.dsl.internal.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.nodes.Node.Child;
@@ -61,6 +62,13 @@
     private final DeclaredType sourceSection;
     private final DeclaredType truffleOptions;
     private final DeclaredType compilationFinal;
+    private final DeclaredType nodeUtil;
+    private final DeclaredType dslNode;
+    private final DeclaredType dslShare;
+    private final DeclaredType nodeFactory;
+    private final DeclaredType nodeFactoryBase;
+    private final DeclaredType dslMetadata;
+    private final DeclaredType implies;
     private final TypeElement expectError;
 
     private final List<String> errors = new ArrayList<>();
@@ -82,9 +90,40 @@
         sourceSection = getRequired(context, SourceSection.class);
         truffleOptions = getRequired(context, TruffleOptions.class);
         compilationFinal = getRequired(context, CompilationFinal.class);
+        nodeUtil = getRequired(context, NodeUtil.class);
+        dslNode = getRequired(context, DSLNode.class);
+        dslShare = getRequired(context, DSLShare.class);
+        nodeFactory = getRequired(context, NodeFactory.class);
+        nodeFactoryBase = getRequired(context, NodeFactoryBase.class);
+        dslMetadata = getRequired(context, DSLMetadata.class);
+        implies = getRequired(context, Implies.class);
         expectError = (TypeElement) getRequired(context, ExpectError.class).asElement();
     }
 
+    public DeclaredType getImplies() {
+        return implies;
+    }
+
+    public DeclaredType getDslMetadata() {
+        return dslMetadata;
+    }
+
+    public DeclaredType getNodeFactory() {
+        return nodeFactory;
+    }
+
+    public DeclaredType getNodeFactoryBase() {
+        return nodeFactoryBase;
+    }
+
+    public DeclaredType getDslNode() {
+        return dslNode;
+    }
+
+    public DeclaredType getDslShare() {
+        return dslShare;
+    }
+
     public DeclaredType getCompilationFinal() {
         return compilationFinal;
     }
@@ -172,4 +211,8 @@
     public DeclaredType getSourceSection() {
         return sourceSection;
     }
+
+    public DeclaredType getNodeUtil() {
+        return nodeUtil;
+    }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/Utils.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/Utils.java	Mon Aug 11 15:53:05 2014 +0200
@@ -50,14 +50,14 @@
         return null;
     }
 
-    public static boolean needsCastTo(ProcessorContext context, TypeMirror sourceType, TypeMirror targetType) {
+    public static boolean needsCastTo(TypeMirror sourceType, TypeMirror targetType) {
         if (typeEquals(sourceType, targetType)) {
             return false;
         } else if (isObject(targetType)) {
             return false;
         } else if (isVoid(targetType)) {
             return false;
-        } else if (isAssignable(context, sourceType, targetType)) {
+        } else if (isAssignable(sourceType, targetType)) {
             return false;
         }
         return true;
@@ -125,7 +125,7 @@
         return prev;
     }
 
-    public static TypeMirror getCommonSuperType(ProcessorContext context, TypeMirror type1, TypeMirror type2) {
+    private static TypeMirror getCommonSuperType(ProcessorContext context, TypeMirror type1, TypeMirror type2) {
         if (typeEquals(type1, type2)) {
             return type1;
         }
@@ -178,15 +178,24 @@
         }
     }
 
-    public static boolean isAssignable(ProcessorContext context, TypeMirror from, TypeMirror to) {
+    public static boolean isSubtype(TypeMirror type1, TypeMirror type2) {
+        if (type1 instanceof CodeTypeMirror && type2 instanceof CodeTypeMirror) {
+            throw new UnsupportedOperationException();
+        }
+        return ProcessorContext.getInstance().getEnvironment().getTypeUtils().isSubtype(type1, type2);
+    }
+
+    public static boolean isAssignable(TypeMirror from, TypeMirror to) {
+        ProcessorContext context = ProcessorContext.getInstance();
+
         if (!(from instanceof CodeTypeMirror) && !(to instanceof CodeTypeMirror)) {
             return context.getEnvironment().getTypeUtils().isAssignable(context.reloadType(from), context.reloadType(to));
         } else {
-            return isAssignableImpl(context, from, to);
+            return isAssignableImpl(from, to);
         }
     }
 
-    private static boolean isAssignableImpl(ProcessorContext context, TypeMirror from, TypeMirror to) {
+    private static boolean isAssignableImpl(TypeMirror from, TypeMirror to) {
         // JLS 5.1.1 identity conversion
         if (Utils.typeEquals(from, to)) {
             return true;
@@ -258,7 +267,7 @@
         }
 
         if (from instanceof ArrayType && to instanceof ArrayType) {
-            return isAssignable(context, ((ArrayType) from).getComponentType(), ((ArrayType) to).getComponentType());
+            return isAssignable(((ArrayType) from).getComponentType(), ((ArrayType) to).getComponentType());
         }
 
         if (from instanceof ArrayType || to instanceof ArrayType) {
@@ -461,11 +470,11 @@
     }
 
     public static boolean isVoid(TypeMirror mirror) {
-        return mirror.getKind() == TypeKind.VOID;
+        return mirror != null && mirror.getKind() == TypeKind.VOID;
     }
 
     public static boolean isPrimitive(TypeMirror mirror) {
-        return mirror.getKind().isPrimitive();
+        return mirror != null && mirror.getKind().isPrimitive();
     }
 
     public static boolean isPrimitiveOrVoid(TypeMirror mirror) {
@@ -495,6 +504,16 @@
         return null;
     }
 
+    public static boolean isEnclosedIn(Element enclosedIn, Element element) {
+        if (element == null) {
+            return false;
+        } else if (enclosedIn.equals(element)) {
+            return true;
+        } else {
+            return isEnclosedIn(enclosedIn, element.getEnclosingElement());
+        }
+    }
+
     public static TypeElement findRootEnclosingType(Element element) {
         List<Element> elements = getElementHierarchy(element);
 
@@ -767,10 +786,6 @@
 
     }
 
-    public static boolean getAnnotationValueBoolean(AnnotationMirror mirror, String name) {
-        return (Boolean) getAnnotationValue(mirror, name).getValue();
-    }
-
     public static String printException(Throwable e) {
         StringWriter string = new StringWriter();
         PrintWriter writer = new PrintWriter(string);
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/codewriter/FixWarningsVisitor.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/codewriter/FixWarningsVisitor.java	Mon Aug 11 15:53:05 2014 +0200
@@ -76,9 +76,6 @@
 
         symbolsUsed.clear();
         super.visitExecutable(e, p);
-        if (e.getBodyTree() == null && e.getBody() != null) {
-            computeSymbols(e.getBody());
-        }
 
         for (VariableElement parameter : e.getParameters()) {
             if (!symbolsUsed.contains(parameter.getSimpleName().toString())) {
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/codewriter/OrganizedImports.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/codewriter/OrganizedImports.java	Mon Aug 11 15:53:05 2014 +0200
@@ -104,20 +104,17 @@
     }
 
     public String createStaticFieldReference(Element enclosedElement, TypeMirror type, String fieldName) {
-        return createStaticReference(enclosedElement, type, fieldName, ambiguousStaticFields, declaredStaticFields);
+        return createStaticReference(enclosedElement, type, fieldName, ambiguousStaticFields);
     }
 
     public String createStaticMethodReference(Element enclosedElement, TypeMirror type, String methodName) {
-        return createStaticReference(enclosedElement, type, methodName, ambiguousStaticMethods, declaredStaticMethods);
+        return createStaticReference(enclosedElement, type, methodName, ambiguousStaticMethods);
     }
 
-    private String createStaticReference(Element enclosedElement, TypeMirror type, String name, Set<String> ambiguousSymbols, Set<String> declaredSymbols) {
+    private String createStaticReference(Element enclosedElement, TypeMirror type, String name, Set<String> ambiguousSymbols) {
         if (ambiguousSymbols.contains(name)) {
             // ambiguous import
             return createTypeReference(enclosedElement, type) + "." + name;
-        } else if (!declaredSymbols.contains(name)) {
-            // not imported at all
-            return createTypeReference(enclosedElement, type) + "." + name;
         } else {
             // import declared and not ambiguous
             return name;
@@ -135,7 +132,8 @@
     }
 
     private String createDeclaredTypeName(Element enclosedElement, DeclaredType type) {
-        String name = Utils.fixECJBinaryNameIssue(type.asElement().getSimpleName().toString());
+        String name;
+        name = Utils.fixECJBinaryNameIssue(type.asElement().getSimpleName().toString());
 
         if (needsImport(enclosedElement, type)) {
             TypeMirror usedByType = simpleNamesUsed.get(name);
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java	Mon Aug 11 15:53:05 2014 +0200
@@ -58,7 +58,7 @@
             baseType = foundChild.getOriginalType();
         }
 
-        MethodSpec spec = new MethodSpec(new InheritsParameterSpec(getContext(), "child", baseType));
+        MethodSpec spec = new MethodSpec(new InheritsParameterSpec("child", baseType));
         addDefaultFieldMethodSpec(spec);
         ParameterSpec childSpec = new ParameterSpec("castedChild", baseType);
         childSpec.setSignature(true);
@@ -97,18 +97,15 @@
 
     private static class InheritsParameterSpec extends ParameterSpec {
 
-        private final ProcessorContext context;
-
-        public InheritsParameterSpec(ProcessorContext context, String name, TypeMirror... allowedTypes) {
+        public InheritsParameterSpec(String name, TypeMirror... allowedTypes) {
             super(name, Arrays.asList(allowedTypes));
-            this.context = context;
         }
 
         @Override
         public boolean matches(TypeMirror actualType) {
             boolean found = false;
             for (TypeMirror specType : getAllowedTypes()) {
-                if (Utils.isAssignable(context, actualType, specType)) {
+                if (Utils.isAssignable(actualType, specType)) {
                     found = true;
                     break;
                 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/GeneratedTypeMirror.java	Mon Aug 11 15:53:05 2014 +0200
@@ -0,0 +1,79 @@
+package com.oracle.truffle.dsl.processor.node;
+
+import java.util.*;
+
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+
+import com.oracle.truffle.dsl.processor.ast.*;
+import com.oracle.truffle.dsl.processor.ast.CodeTypeMirror.*;
+
+public final class GeneratedTypeMirror extends DeclaredCodeTypeMirror {
+
+    public GeneratedTypeMirror(String packageName, String name) {
+        super(new GeneratedTypeElement(Collections.<Modifier> emptySet(), ElementKind.CLASS, new GeneratedPackageElement(packageName), name));
+    }
+
+    public static final class GeneratedPackageElement extends CodeElement<Element> implements PackageElement {
+
+        private final Name qualifiedName;
+        private final Name simpleName;
+
+        public GeneratedPackageElement(String qualifiedName) {
+            this.qualifiedName = CodeNames.of(qualifiedName);
+            int lastIndex = qualifiedName.lastIndexOf('.');
+            if (lastIndex == -1) {
+                simpleName = CodeNames.of("");
+            } else {
+                simpleName = CodeNames.of(qualifiedName.substring(lastIndex, qualifiedName.length()));
+            }
+        }
+
+        public TypeMirror asType() {
+            throw new UnsupportedOperationException();
+        }
+
+        public ElementKind getKind() {
+            return ElementKind.PACKAGE;
+        }
+
+        public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+            return v.visitPackage(this, p);
+        }
+
+        public Name getQualifiedName() {
+            return qualifiedName;
+        }
+
+        public Name getSimpleName() {
+            return simpleName;
+        }
+
+        public boolean isUnnamed() {
+            return simpleName.toString().equals("");
+        }
+
+        @Override
+        public int hashCode() {
+            return qualifiedName.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof PackageElement) {
+                return qualifiedName.equals(((PackageElement) obj).getQualifiedName());
+            }
+            return super.equals(obj);
+        }
+    }
+
+    public static final class GeneratedTypeElement extends CodeTypeElement {
+
+        public GeneratedTypeElement(Set<Modifier> modifiers, ElementKind kind, PackageElement packageElement, String simpleName) {
+            super(modifiers, kind, packageElement, simpleName);
+            setEnclosingElement(packageElement);
+        }
+
+    }
+
+}
\ No newline at end of file
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java	Mon Aug 11 15:53:05 2014 +0200
@@ -35,6 +35,7 @@
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.ast.*;
+import com.oracle.truffle.dsl.processor.ast.CodeTypeMirror.ArrayCodeTypeMirror;
 import com.oracle.truffle.dsl.processor.node.NodeChildData.Cardinality;
 import com.oracle.truffle.dsl.processor.node.SpecializationGroup.TypeGuard;
 import com.oracle.truffle.dsl.processor.template.*;
@@ -44,17 +45,20 @@
 
     private static final String THIS_NODE_LOCAL_VAR_NAME = "thisNode";
 
-    private static final String EXECUTE_GENERIC_NAME = "executeGeneric0";
-    private static final String EXECUTE_SPECIALIZE_NAME = "executeAndSpecialize0";
-    private static final String EXECUTE_POLYMORPHIC_NAME = "executePolymorphic0";
-
-    private static final String UPDATE_TYPES_NAME = "updateTypes";
-    private static final String COPY_WITH_CONSTRUCTOR_NAME = "copyWithConstructor";
-    private static final String CREATE_SPECIALIZATION_NAME = "createSpecialization";
-
-    public NodeCodeGenerator(ProcessorContext context) {
-        super(context);
-    }
+    private static final String EXECUTE_CHAINED = "executeChained0";
+    private static final String SPECIALIZE = "specialize0";
+    private static final String DSLSHARE_REWRITE = "rewrite";
+    private static final String DSLSHARE_FIND_ROOT = "findRoot";
+    private static final String DSLSHARE_REWRITE_TO_POLYMORHPIC = "rewriteToPolymorphic";
+    private static final String EXECUTE_UNINITIALIZED = "executeUninitialized0";
+    private static final String REWRITE = "rewrite0";
+    private static final String CREATE_INFO = "createInfo0";
+    private static final String CONTAINS_FALLBACK = "containsFallback";
+
+    private static final String FACTORY_METHOD_NAME = "create0";
+    private static final String EMPTY_CLASS_ARRAY = "EMPTY_CLASS_ARRAY";
+
+    private static final String METADATA_FIELD_NAME = "METADATA";
 
     private TypeMirror getUnexpectedValueException() {
         return getContext().getTruffleTypes().getUnexpectedValueException();
@@ -92,8 +96,8 @@
         return param.getLocalName() + "ImplicitType";
     }
 
-    private static String polymorphicTypeName(ActualParameter param) {
-        return param.getLocalName() + "PolymorphicType";
+    private static String polymorphicTypeName(NodeExecutionData param) {
+        return param.getName() + "PolymorphicType";
     }
 
     private static String valueName(ActualParameter param) {
@@ -146,7 +150,7 @@
         }
     }
 
-    private void addInternalValueParameterNames(CodeTreeBuilder builder, TemplateMethod source, TemplateMethod specialization, String unexpectedValueName, boolean forceFrame,
+    private static void addInternalValueParameterNames(CodeTreeBuilder builder, TemplateMethod source, TemplateMethod specialization, String unexpectedValueName, boolean forceFrame,
                     Map<String, String> customNames) {
         if (forceFrame && specialization.getSpecification().findParameterSpec("frame") != null) {
             builder.string("frameValue");
@@ -175,18 +179,18 @@
         }
     }
 
-    private String valueName(ActualParameter sourceParameter, ActualParameter targetParameter) {
+    private static String valueName(ActualParameter sourceParameter, ActualParameter targetParameter) {
         if (!sourceParameter.getSpecification().isSignature()) {
             return valueName(targetParameter);
         } else if (sourceParameter.getTypeSystemType() != null && targetParameter.getTypeSystemType() != null) {
-            if (sourceParameter.getTypeSystemType().needsCastTo(getContext(), targetParameter.getTypeSystemType())) {
+            if (sourceParameter.getTypeSystemType().needsCastTo(targetParameter.getTypeSystemType())) {
                 return castValueName(targetParameter);
             }
         }
         return valueName(targetParameter);
     }
 
-    private CodeTree createTemplateMethodCall(CodeTreeBuilder parent, CodeTree target, TemplateMethod sourceMethod, TemplateMethod targetMethod, String unexpectedValueName,
+    private static CodeTree createTemplateMethodCall(CodeTreeBuilder parent, CodeTree target, TemplateMethod sourceMethod, TemplateMethod targetMethod, String unexpectedValueName,
                     String... customSignatureValueNames) {
         CodeTreeBuilder builder = parent.create();
 
@@ -201,7 +205,7 @@
         NodeData node = (NodeData) targetMethod.getTemplate();
 
         if (target == null) {
-            boolean accessible = targetMethod.canBeAccessedByInstanceOf(getContext(), node.getNodeType());
+            boolean accessible = targetMethod.canBeAccessedByInstanceOf(node.getNodeType());
             if (accessible) {
                 if (builder.findMethod().getModifiers().contains(STATIC)) {
                     if (method.getModifiers().contains(STATIC)) {
@@ -276,7 +280,7 @@
                 builder.end();
             } else if (unexpectedValueName != null && targetParameter.getLocalName().equals(unexpectedValueName)) {
                 builder.cast(targetParameter.getType(), CodeTreeBuilder.singleString("ex.getResult()"));
-            } else if (!Utils.needsCastTo(getContext(), valueType, targetType)) {
+            } else if (!Utils.needsCastTo(valueType, targetType)) {
                 builder.startGroup();
                 builder.string(valueName(targetParameter));
                 builder.end();
@@ -297,9 +301,9 @@
         return name;
     }
 
-    private static CodeTree createCallTypeSystemMethod(ProcessorContext context, CodeTreeBuilder parent, NodeData node, String methodName, CodeTree... args) {
+    private static CodeTree createCallTypeSystemMethod(CodeTreeBuilder parent, NodeData node, String methodName, CodeTree... args) {
         CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-        startCallTypeSystemMethod(context, builder, node.getTypeSystem(), methodName);
+        startCallTypeSystemMethod(builder, node.getTypeSystem(), methodName);
         for (CodeTree arg : args) {
             builder.tree(arg);
         }
@@ -307,12 +311,10 @@
         return builder.getRoot();
     }
 
-    private static void startCallTypeSystemMethod(ProcessorContext context, CodeTreeBuilder body, TypeSystemData typeSystem, String methodName) {
-        VariableElement singleton = TypeSystemCodeGenerator.findSingleton(context, typeSystem);
-        assert singleton != null;
-
+    private static void startCallTypeSystemMethod(CodeTreeBuilder body, TypeSystemData typeSystem, String methodName) {
+        GeneratedTypeMirror typeMirror = new GeneratedTypeMirror(Utils.getPackageName(typeSystem.getTemplateType()), TypeSystemCodeGenerator.typeName(typeSystem));
         body.startGroup();
-        body.staticReference(singleton.getEnclosingElement().asType(), singleton.getSimpleName().toString());
+        body.staticReference(typeMirror, TypeSystemCodeGenerator.singletonName(typeSystem));
         body.string(".").startCall(methodName);
     }
 
@@ -363,18 +365,19 @@
                     nodes.nullLiteral();
                     arguments.string(valueName(parameter.getPreviousParameter()));
                 }
-                nodes.tree(createAccessChild(executionData, null));
+                nodes.tree(createAccessChild(executionData, "rootNode"));
                 arguments.string(valueName(parameter));
                 empty = false;
             }
         }
         nodes.end();
         arguments.end();
-
+        builder.startStatement().startStaticCall(context.getTruffleTypes().getCompilerDirectives(), "transferToInterpreter").end().end();
+
+        builder.declaration(baseClassName(getModel()), "rootNode", builder.create().startStaticCall(context.getTruffleTypes().getDslShare(), DSLSHARE_FIND_ROOT).string("this").end());
         builder.startThrow().startNew(getContext().getType(UnsupportedSpecializationException.class));
-        builder.string("this");
+        builder.string("rootNode");
         builder.startNewArray(getContext().getTruffleTypes().getNodeArray(), null);
-
         builder.tree(nodes.getRoot());
         builder.end();
         if (!empty) {
@@ -446,21 +449,21 @@
 
         Map<NodeData, List<TypeElement>> childTypes = new LinkedHashMap<>();
         for (NodeData nodeChild : node.getEnclosingNodes()) {
-            NodeCodeGenerator generator = new NodeCodeGenerator(getContext());
+            NodeCodeGenerator generator = new NodeCodeGenerator();
             childTypes.put(nodeChild, generator.process(null, nodeChild).getEnclosedElements());
         }
 
         if (node.needsFactory() || node.getNodeDeclaringChildren().size() > 0) {
-            NodeFactoryFactory factory = new NodeFactoryFactory(context, childTypes);
+            NodeFactoryFactory factory = new NodeFactoryFactory(childTypes);
             add(factory, node);
             factory.getElement().getEnclosedElements().addAll(casts);
         }
     }
 
-    protected CodeTree createCastType(TypeSystemData typeSystem, TypeData sourceType, TypeData targetType, boolean expect, CodeTree value) {
+    private static CodeTree createCastType(TypeSystemData typeSystem, TypeData sourceType, TypeData targetType, boolean expect, CodeTree value) {
         if (targetType == null) {
             return value;
-        } else if (sourceType != null && !sourceType.needsCastTo(getContext(), targetType)) {
+        } else if (sourceType != null && !sourceType.needsCastTo(targetType)) {
             return value;
         }
 
@@ -471,17 +474,17 @@
         } else {
             targetMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetType);
         }
-        startCallTypeSystemMethod(getContext(), builder, typeSystem, targetMethodName);
+        startCallTypeSystemMethod(builder, typeSystem, targetMethodName);
         builder.tree(value);
         builder.end().end();
         return builder.getRoot();
     }
 
-    protected CodeTree createExpectType(TypeSystemData typeSystem, TypeData sourceType, TypeData targetType, CodeTree expression) {
+    private static CodeTree createExpectType(TypeSystemData typeSystem, TypeData sourceType, TypeData targetType, CodeTree expression) {
         return createCastType(typeSystem, sourceType, targetType, true, expression);
     }
 
-    public CodeTree createDeoptimize(CodeTreeBuilder parent) {
+    private CodeTree createDeoptimize(CodeTreeBuilder parent) {
         CodeTreeBuilder builder = new CodeTreeBuilder(parent);
         builder.startStatement();
         builder.startStaticCall(getContext().getTruffleTypes().getCompilerDirectives(), "transferToInterpreterAndInvalidate").end();
@@ -494,20 +497,19 @@
         private final Map<NodeData, List<TypeElement>> childTypes;
         private CodeTypeElement generatedNode;
 
-        public NodeFactoryFactory(ProcessorContext context, Map<NodeData, List<TypeElement>> childElements) {
-            super(context);
+        public NodeFactoryFactory(Map<NodeData, List<TypeElement>> childElements) {
             this.childTypes = childElements;
         }
 
         @Override
         protected CodeTypeElement create(NodeData node) {
             Modifier visibility = Utils.getVisibility(node.getTemplateType().getModifiers());
+
             CodeTypeElement clazz = createClass(node, modifiers(), factoryClassName(node), null, false);
             if (visibility != null) {
                 clazz.getModifiers().add(visibility);
             }
             clazz.getModifiers().add(Modifier.FINAL);
-            clazz.add(createConstructorUsingFields(modifiers(PRIVATE), clazz));
             return clazz;
         }
 
@@ -517,47 +519,38 @@
 
             Modifier createVisibility = Utils.getVisibility(clazz.getModifiers());
 
-            CodeTypeElement polymorphicNode = null;
             if (node.needsFactory()) {
-                NodeBaseFactory factory = new NodeBaseFactory(context);
+                NodeBaseFactory factory = new NodeBaseFactory();
                 add(factory, node.getGenericSpecialization() == null ? node.getSpecializations().get(0) : node.getGenericSpecialization());
                 generatedNode = factory.getElement();
 
-                if (node.needsRewrites(context)) {
-                    clazz.add(createCreateGenericMethod(node, createVisibility));
-                }
-
                 createFactoryMethods(node, clazz, createVisibility);
 
                 for (SpecializationData specialization : node.getSpecializations()) {
-                    if (!specialization.isReachable()) {
+                    if (!specialization.isReachable() || specialization.isGeneric()) {
                         continue;
                     }
 
-                    if (specialization.isPolymorphic() && node.isPolymorphic()) {
-                        PolymorphicNodeFactory polymorphicFactory = new PolymorphicNodeFactory(getContext(), generatedNode);
+                    if (specialization.isPolymorphic() && node.isPolymorphic(context)) {
+                        PolymorphicNodeFactory polymorphicFactory = new PolymorphicNodeFactory(generatedNode);
                         add(polymorphicFactory, specialization);
-                        polymorphicNode = polymorphicFactory.getElement();
                         continue;
                     }
 
-                    add(new SpecializedNodeFactory(context, generatedNode), specialization);
+                    add(new SpecializedNodeFactory(generatedNode), specialization);
                 }
 
-                TypeMirror nodeFactory = Utils.getDeclaredType(Utils.fromTypeMirror(getContext().getType(NodeFactory.class)), node.getNodeType());
-                clazz.getImplements().add(nodeFactory);
+                TypeMirror nodeFactory = Utils.getDeclaredType(Utils.fromTypeMirror(getContext().getTruffleTypes().getNodeFactoryBase()), node.getNodeType());
+                clazz.setSuperClass(nodeFactory);
+                clazz.add(createNodeFactoryConstructor(node));
                 clazz.add(createCreateNodeMethod(node));
-                clazz.add(createGetNodeClassMethod(node));
-                clazz.add(createGetNodeSignaturesMethod());
-                clazz.add(createGetChildrenSignatureMethod(node));
+// clazz.add(createGetNodeClassMethod(node));
+// clazz.add(createGetNodeSignaturesMethod());
+// clazz.add(createGetChildrenSignatureMethod(node));
                 clazz.add(createGetInstanceMethod(node, createVisibility));
                 clazz.add(createInstanceConstant(node, clazz.asType()));
             }
 
-            if (polymorphicNode != null) {
-                patchParameterType(clazz, UPDATE_TYPES_NAME, generatedNode.asType(), polymorphicNode.asType());
-            }
-
             for (NodeData childNode : childTypes.keySet()) {
                 if (childNode.getTemplateType().getModifiers().contains(Modifier.PRIVATE)) {
                     continue;
@@ -584,81 +577,52 @@
 
         }
 
-        private void patchParameterType(CodeTypeElement enclosingClass, String methodName, TypeMirror originalType, TypeMirror newType) {
-            for (TypeElement enclosedType : ElementFilter.typesIn(enclosingClass.getEnclosedElements())) {
-                CodeTypeElement type = (CodeTypeElement) enclosedType;
-                ExecutableElement method = type.getMethod(methodName);
-                for (VariableElement v : method.getParameters()) {
-                    CodeVariableElement var = (CodeVariableElement) v;
-                    if (Utils.typeEquals(var.getType(), originalType)) {
-                        var.setType(newType);
-                    }
+        private Element createNodeFactoryConstructor(NodeData node) {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PRIVATE), null, factoryClassName(node));
+            CodeTreeBuilder builder = method.createBuilder();
+            builder.startStatement();
+            builder.startCall("super");
+
+            // node type
+            builder.typeLiteral(node.getNodeType());
+
+            // execution signature
+            builder.startGroup();
+            if (node.getChildExecutions().isEmpty()) {
+                builder.staticReference(context.getTruffleTypes().getDslMetadata(), EMPTY_CLASS_ARRAY);
+            } else {
+                builder.startNewArray(new ArrayCodeTypeMirror(context.getType(Class.class)), null);
+                for (NodeExecutionData execution : node.getChildExecutions()) {
+                    builder.typeLiteral(execution.getNodeType());
                 }
+                builder.end();
             }
-        }
-
-        private CodeExecutableElement createGetNodeClassMethod(NodeData node) {
-            TypeMirror returnType = Utils.getDeclaredType(Utils.fromTypeMirror(getContext().getType(Class.class)), node.getNodeType());
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getNodeClass");
-            CodeTreeBuilder builder = method.createBuilder();
-            builder.startReturn().typeLiteral(node.getNodeType()).end();
-            return method;
-        }
-
-        private CodeExecutableElement createGetNodeSignaturesMethod() {
-            TypeElement listType = Utils.fromTypeMirror(getContext().getType(List.class));
-            TypeMirror classType = getContext().getType(Class.class);
-            TypeMirror returnType = Utils.getDeclaredType(listType, Utils.getDeclaredType(listType, classType));
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getNodeSignatures");
-            CodeTreeBuilder builder = method.createBuilder();
-            builder.startReturn();
-            builder.startStaticCall(getContext().getType(Arrays.class), "asList");
+            builder.end();
+
+            // node signatures
+            builder.startGroup();
+            builder.startNewArray(new ArrayCodeTypeMirror(new ArrayCodeTypeMirror(context.getType(Class.class))), null);
             List<ExecutableElement> constructors = findUserConstructors(generatedNode.asType());
             for (ExecutableElement constructor : constructors) {
-                builder.tree(createAsList(builder, Utils.asTypeMirrors(constructor.getParameters()), classType));
+                builder.startGroup();
+                if (constructor.getParameters().isEmpty()) {
+                    builder.staticReference(context.getTruffleTypes().getDslMetadata(), EMPTY_CLASS_ARRAY);
+                } else {
+                    builder.startNewArray(new ArrayCodeTypeMirror(context.getType(Class.class)), null);
+                    for (VariableElement var : constructor.getParameters()) {
+                        builder.typeLiteral(var.asType());
+                    }
+                    builder.end();
+                }
+                builder.end();
             }
             builder.end();
             builder.end();
+
+            builder.end().end().end();
             return method;
         }
 
-        private CodeExecutableElement createGetChildrenSignatureMethod(NodeData node) {
-            Types types = getContext().getEnvironment().getTypeUtils();
-            TypeElement listType = Utils.fromTypeMirror(getContext().getType(List.class));
-            TypeMirror classType = getContext().getType(Class.class);
-            TypeMirror nodeType = getContext().getTruffleTypes().getNode();
-            TypeMirror wildcardNodeType = types.getWildcardType(nodeType, null);
-            classType = Utils.getDeclaredType(Utils.fromTypeMirror(classType), wildcardNodeType);
-            TypeMirror returnType = Utils.getDeclaredType(listType, classType);
-
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getExecutionSignature");
-            CodeTreeBuilder builder = method.createBuilder();
-
-            List<TypeMirror> signatureTypes = new ArrayList<>();
-            assert !node.getSpecializations().isEmpty();
-            SpecializationData data = node.getSpecializations().get(0);
-
-            for (ActualParameter parameter : data.getSignatureParameters()) {
-                signatureTypes.add(parameter.getSpecification().getExecution().getNodeType());
-            }
-
-            builder.startReturn().tree(createAsList(builder, signatureTypes, classType)).end();
-            return method;
-        }
-
-        private CodeTree createAsList(CodeTreeBuilder parent, List<TypeMirror> types, TypeMirror elementClass) {
-            CodeTreeBuilder builder = parent.create();
-            builder.startGroup();
-            builder.type(getContext().getType(Arrays.class));
-            builder.string(".<").type(elementClass).string(">");
-            builder.startCall("asList");
-            for (TypeMirror typeMirror : types) {
-                builder.typeLiteral(typeMirror);
-            }
-            builder.end().end();
-            return builder.getRoot();
-        }
-
         private CodeExecutableElement createCreateNodeMethod(NodeData node) {
             CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), node.getNodeType(), "createNode");
             CodeVariableElement arguments = new CodeVariableElement(getContext().getType(Object.class), "arguments");
@@ -836,7 +800,7 @@
             if (node.getSpecializations().isEmpty()) {
                 body.nullLiteral();
             } else {
-                body.startCall(nodeSpecializationClassName(node.getSpecializations().get(0)), CREATE_SPECIALIZATION_NAME);
+                body.startCall(nodeSpecializationClassName(node.getSpecializations().get(0)), FACTORY_METHOD_NAME);
                 for (VariableElement var : method.getParameters()) {
                     body.string(var.getSimpleName().toString());
                 }
@@ -846,43 +810,15 @@
             return method;
         }
 
-        private CodeExecutableElement createCreateGenericMethod(NodeData node, Modifier visibility) {
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(), node.getNodeType(), "createGeneric");
-            if (visibility != null) {
-                method.getModifiers().add(visibility);
-            }
-            method.getModifiers().add(Modifier.STATIC);
-            method.addParameter(new CodeVariableElement(node.getNodeType(), THIS_NODE_LOCAL_VAR_NAME));
-
-            CodeTreeBuilder body = method.createBuilder();
-
-            SpecializationData found = null;
-            List<SpecializationData> specializations = node.getSpecializations();
-            for (int i = 0; i < specializations.size(); i++) {
-                if (specializations.get(i).isReachable()) {
-                    found = specializations.get(i);
-                }
-            }
-
-            if (found == null) {
-                body.startThrow().startNew(getContext().getType(UnsupportedOperationException.class)).end().end();
-            } else {
-                body.startReturn().startCall(nodeSpecializationClassName(found), CREATE_SPECIALIZATION_NAME).startGroup().string(THIS_NODE_LOCAL_VAR_NAME).end().end().end();
-            }
-            return method;
-        }
     }
 
     private class NodeBaseFactory extends ClassElementFactory<SpecializationData> {
 
-        public NodeBaseFactory(ProcessorContext context) {
-            super(context);
-        }
-
         @Override
         protected CodeTypeElement create(SpecializationData specialization) {
             NodeData node = specialization.getNode();
             CodeTypeElement clazz = createClass(node, modifiers(PRIVATE, ABSTRACT, STATIC), baseClassName(node), node.getNodeType(), false);
+            clazz.getImplements().add(context.getTruffleTypes().getDslNode());
 
             for (NodeChildData child : node.getChildren()) {
                 clazz.add(createChildField(child));
@@ -928,7 +864,7 @@
             SpecializationGroup rootGroup = createSpecializationGroups(node);
 
             if (node.needsRewrites(context)) {
-                if (node.isPolymorphic()) {
+                if (node.isPolymorphic(context)) {
 
                     CodeVariableElement var = new CodeVariableElement(modifiers(PROTECTED), clazz.asType(), "next0");
                     var.getAnnotationMirrors().add(new CodeAnnotationMirror(getContext().getTruffleTypes().getChildAnnotation()));
@@ -937,43 +873,106 @@
                     CodeExecutableElement genericCachedExecute = createCachedExecute(node, node.getPolymorphicSpecialization());
                     clazz.add(genericCachedExecute);
 
-                    getElement().add(createUpdateTypes(clazz.asType()));
                 }
 
                 for (CodeExecutableElement method : createImplicitChildrenAccessors()) {
                     clazz.add(method);
                 }
-                clazz.add(createGenericExecuteAndSpecialize(node, rootGroup));
                 clazz.add(createInfoMessage(node));
+                clazz.add(createMonomorphicRewrite());
+                clazz.add(createCreateSpecializationMethod(node, rootGroup));
             }
 
-            if (needsInvokeCopyConstructorMethod()) {
-                clazz.add(createCopy(clazz.asType(), null));
+            clazz.add(createAdoptChildren0());
+            clazz.add(createGetMetadata0(true));
+            clazz.add(createUpdateTypes0());
+            clazz.add(createGetNext());
+        }
+
+        private Element createGetNext() {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), context.getType(Node.class), "getNext0");
+            CodeTreeBuilder builder = method.createBuilder();
+            NodeData node = getModel().getNode();
+
+            if (node.isPolymorphic(context)) {
+                builder.startReturn().string("next0").end();
+            } else {
+                builder.returnNull();
             }
 
-            if (node.getGenericSpecialization() != null && node.getGenericSpecialization().isReachable()) {
-                clazz.add(createGenericExecute(node, rootGroup));
-            }
+            return method;
         }
 
-        protected boolean needsInvokeCopyConstructorMethod() {
-            return getModel().getNode().isPolymorphic();
+        protected final CodeExecutableElement createUpdateTypes0() {
+            ArrayType classArray = new ArrayCodeTypeMirror(context.getType(Class.class));
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), context.getType(void.class), "updateTypes0");
+            method.getParameters().add(new CodeVariableElement(classArray, "types"));
+
+            if (getModel().isPolymorphic()) {
+                CodeTreeBuilder builder = method.createBuilder();
+
+                int index = 0;
+                for (NodeExecutionData execution : getModel().getNode().getChildExecutions()) {
+                    String fieldName = polymorphicTypeName(execution);
+
+                    builder.startStatement();
+                    builder.string(fieldName).string(" = ").string("types[").string(String.valueOf(index)).string("]");
+                    builder.end();
+                    index++;
+                }
+            }
+
+            return method;
         }
 
-        protected CodeExecutableElement createCopy(TypeMirror baseType, SpecializationData specialization) {
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), baseType, COPY_WITH_CONSTRUCTOR_NAME);
-            if (specialization == null) {
-                method.getModifiers().add(ABSTRACT);
+        protected final CodeExecutableElement createGetMetadata0(boolean empty) {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), context.getTruffleTypes().getDslMetadata(), "getMetadata0");
+            if (empty) {
+                method.createBuilder().startReturn().staticReference(context.getTruffleTypes().getDslMetadata(), "NONE").end();
             } else {
-                CodeTreeBuilder builder = method.createBuilder();
-                builder.startReturn();
-                builder.startNew(getElement().asType());
-                builder.string("this");
-                for (ActualParameter param : getImplicitTypeParameters(specialization)) {
-                    builder.string(implicitTypeName(param));
+                method.createBuilder().startReturn().string(METADATA_FIELD_NAME).end();
+            }
+            return method;
+        }
+
+        private CodeExecutableElement createAdoptChildren0() {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), context.getType(void.class), "adoptChildren0");
+            method.getParameters().add(new CodeVariableElement(context.getTruffleTypes().getNode(), "other"));
+            method.getParameters().add(new CodeVariableElement(context.getTruffleTypes().getNode(), "newNext"));
+            NodeData node = getModel().getNode();
+            CodeTreeBuilder builder = method.createBuilder();
+            List<NodeExecutionData> executions = node.getChildExecutions();
+
+            if (executions.size() > 0) {
+                builder.startIf().string("other == null").end().startBlock();
+                for (NodeExecutionData execution : executions) {
+                    builder.startStatement().tree(createAccessChild(execution, "this")).string(" = null").end();
                 }
-                builder.end().end();
+                builder.end().startElseBlock();
+
+                String access;
+                if (executions.size() > 1) {
+                    builder.declaration(baseClassName(node), "otherCast", builder.create().cast(baseClassName(node)).string("other"));
+                    access = "otherCast";
+                } else {
+                    assert executions.size() == 1;
+                    access = "((" + baseClassName(node) + ") other)";
+                }
+                for (NodeExecutionData execution : executions) {
+                    builder.startStatement().tree(createAccessChild(execution, "this")).string(" = ").tree(createAccessChild(execution, access)).end();
+                }
+
+                builder.end();
             }
+
+            if (getModel().getNode().isPolymorphic(context)) {
+                builder.startIf().string("newNext == null").end().startBlock();
+                builder.statement("this.next0 = null");
+                builder.end().startElseBlock();
+                builder.statement("this.next0 = (" + baseClassName(getModel().getNode()) + ") newNext");
+                builder.end();
+            }
+
             return method;
         }
 
@@ -1035,7 +1034,7 @@
         }
 
         private Element createInfoMessage(NodeData node) {
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, STATIC), getContext().getType(String.class), "createInfo0");
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, STATIC), getContext().getType(String.class), CREATE_INFO);
             method.addParameter(new CodeVariableElement(getContext().getType(String.class), "message"));
             addInternalValueParameters(method, node.getGenericSpecialization(), false, false);
 
@@ -1091,7 +1090,7 @@
         }
 
         private CodeExecutableElement createCachedExecute(NodeData node, SpecializationData polymorph) {
-            CodeExecutableElement cachedExecute = new CodeExecutableElement(modifiers(PROTECTED, ABSTRACT), polymorph.getReturnType().getType(), EXECUTE_POLYMORPHIC_NAME);
+            CodeExecutableElement cachedExecute = new CodeExecutableElement(modifiers(PROTECTED, ABSTRACT), polymorph.getReturnType().getType(), EXECUTE_CHAINED);
             addInternalValueParameters(cachedExecute, polymorph, true, false);
 
             ExecutableTypeData sourceExecutableType = node.findExecutableType(polymorph.getReturnType().getTypeSystemType(), 0);
@@ -1120,7 +1119,8 @@
                 }
             }
             if (node.needsRewrites(getContext())) {
-                clazz.add(createCopyConstructor(clazz, findCopyConstructor(node.getNodeType()), sourceSectionConstructor));
+                ExecutableElement copyConstructor = findCopyConstructor(node.getNodeType());
+                clazz.add(createCopyConstructor(clazz, copyConstructor, sourceSectionConstructor));
             }
         }
 
@@ -1145,6 +1145,9 @@
             }
 
             for (VariableElement var : type.getFields()) {
+                if (var.getModifiers().contains(STATIC)) {
+                    continue;
+                }
                 NodeChildData child = node.findChild(var.getSimpleName().toString());
 
                 if (child != null) {
@@ -1187,16 +1190,17 @@
             }
 
             for (VariableElement var : type.getFields()) {
-                builder.startStatement();
+                if (var.getModifiers().contains(STATIC) || !var.getModifiers().contains(FINAL)) {
+                    continue;
+                }
                 final String varName = var.getSimpleName().toString();
                 final TypeMirror varType = var.asType();
-
-                String copyAccess = "copy." + varName;
-                if (Utils.isAssignable(getContext(), varType, getContext().getTruffleTypes().getNodeArray())) {
-                    copyAccess += ".clone()";
+                if (Utils.isAssignable(varType, getContext().getTruffleTypes().getNodeArray())) {
+                    CodeTree size = builder.create().string("copy.", varName, ".length").getRoot();
+                    builder.startStatement().string("this.").string(varName).string(" = ").startNewArray((ArrayType) varType, size).end().end();
+                } else {
+                    builder.startStatement().string("this.", varName, " = copy.", varName).end();
                 }
-                CodeTree init = CodeTreeBuilder.singleString(copyAccess);
-                builder.startStatement().string("this.").string(varName).string(" = ").tree(init).end();
             }
 
             return method;
@@ -1225,54 +1229,6 @@
             return var;
         }
 
-        private CodeExecutableElement createGenericExecuteAndSpecialize(final NodeData node, SpecializationGroup rootGroup) {
-            TypeMirror genericReturnType = node.getGenericSpecialization().getReturnType().getType();
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), genericReturnType, EXECUTE_SPECIALIZE_NAME);
-            method.addParameter(new CodeVariableElement(getContext().getType(int.class), "minimumState"));
-            addInternalValueParameters(method, node.getGenericSpecialization(), true, false);
-            method.addParameter(new CodeVariableElement(getContext().getType(String.class), "reason"));
-
-            CodeTreeBuilder builder = method.createBuilder();
-            builder.startStatement();
-            builder.startStaticCall(getContext().getTruffleTypes().getCompilerAsserts(), "neverPartOfCompilation").end();
-            builder.end();
-
-            String currentNode = "this";
-            for (SpecializationData specialization : node.getSpecializations()) {
-                if (!specialization.getExceptions().isEmpty()) {
-                    currentNode = "current";
-                    builder.declaration(baseClassName(node), currentNode, "this");
-                    break;
-                }
-            }
-
-            builder.startStatement().string("String message = ").startCall("createInfo0").string("reason");
-            addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, false, null);
-            builder.end().end();
-
-            final String currentNodeVar = currentNode;
-            builder.tree(createExecuteTree(builder, node.getGenericSpecialization(), rootGroup, true, new CodeBlock<SpecializationData>() {
-
-                public CodeTree create(CodeTreeBuilder b, SpecializationData current) {
-                    return createGenericInvokeAndSpecialize(b, node.getGenericSpecialization(), current, currentNodeVar);
-                }
-            }, null, false, true, false));
-
-            boolean firstUnreachable = true;
-            for (SpecializationData current : node.getSpecializations()) {
-                if (current.isReachable()) {
-                    continue;
-                }
-                if (firstUnreachable) {
-                    emitEncounteredSynthetic(builder, current);
-                    firstUnreachable = false;
-                }
-            }
-            emitUnreachableSpecializations(builder, node);
-
-            return method;
-        }
-
         private SpecializationGroup createSpecializationGroups(final NodeData node) {
             List<SpecializationData> specializations = node.getSpecializations();
             List<SpecializationData> filteredSpecializations = new ArrayList<>();
@@ -1286,28 +1242,175 @@
             return SpecializationGroup.create(filteredSpecializations);
         }
 
-        private CodeExecutableElement createGenericExecute(NodeData node, SpecializationGroup group) {
-            TypeMirror genericReturnType = node.getGenericSpecialization().getReturnType().getType();
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), genericReturnType, EXECUTE_GENERIC_NAME);
-
+        protected final CodeExecutableElement createExecuteUninitialized() {
+            NodeData node = getModel().getNode();
+            SpecializationData generic = node.getGenericSpecialization();
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED), generic.getReturnType().getType(), EXECUTE_UNINITIALIZED);
+            addInternalValueParameters(method, generic, true, false);
+            CodeTreeBuilder builder = method.createBuilder();
+
+            CodeTreeBuilder createSpecializationCall = builder.create();
+            createSpecializationCall.startCall(SPECIALIZE);
+            addInternalValueParameterNames(createSpecializationCall, generic, generic, null, node.needsFrame(getContext()), null);
+            createSpecializationCall.end();
+            builder.declaration(baseClassName(node), "newNode", createSpecializationCall);
+
+            if (generic.isReachable()) {
+                builder.startIf().string("newNode == null").end().startBlock();
+
+                builder.startIf().startStaticCall(context.getTruffleTypes().getCompilerDirectives(), "inInterpreter").end().end().startBlock();
+                builder.statement("containsFallback = true");
+                builder.end();
+                builder.tree(createGenericInvoke(builder, generic, generic));
+                builder.end();
+                builder.startElseBlock();
+                builder.startStatement().startStaticCall(context.getTruffleTypes().getCompilerDirectives(), "transferToInterpreterAndInvalidate").end().end();
+            }
+
+            builder.startReturn();
+            builder.startStaticCall(context.getTruffleTypes().getDslShare(), "rewriteUninitialized").string("this").string("newNode").end();
+            builder.string(".").startCall(EXECUTE_CHAINED);
+            addInternalValueParameterNames(builder, generic, generic, null, true, null);
+            builder.end();
+            builder.end();
+
+            if (generic.isReachable()) {
+                builder.end();
+            }
+
+            return method;
+        }
+
+        private CodeTree createInfoCall(CodeTreeBuilder parent, SpecializationData specialization, String reason) {
+            CodeTreeBuilder builder = parent.create();
+            builder.startCall(CREATE_INFO).string(reason);
+            addInternalValueParameterNames(builder, specialization, specialization, null, false, null);
+            builder.end();
+            return builder.getRoot();
+        }
+
+        private CodeExecutableElement createMonomorphicRewrite() {
+            NodeData node = getModel().getNode();
+
+            SpecializationData generic = node.getGenericSpecialization();
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), generic.getReturnType().getType(), REWRITE);
+            addInternalValueParameters(method, generic, true, false);
+            method.addParameter(new CodeVariableElement(getContext().getType(String.class), "reason"));
+
+            CodeTreeBuilder builder = method.createBuilder();
+
+            builder.startStatement().startStaticCall(context.getTruffleTypes().getCompilerAsserts(), "neverPartOfCompilation").end().end();
+            String baseClassName = baseClassName(getModel().getNode());
+            CodeTreeBuilder createSpecializationCall = builder.create();
+            createSpecializationCall.startCall(SPECIALIZE);
+            addInternalValueParameterNames(createSpecializationCall, generic, generic, null, node.needsFrame(getContext()), null);
+            createSpecializationCall.end();
+            builder.declaration(baseClassName, "newNode", createSpecializationCall);
+
+            builder.startIf().string("newNode == null").end().startBlock();
+            builder.startStatement();
+            String uninitializedName = nodeSpecializationClassName(node.getUninitializedSpecialization());
+            builder.string("newNode = ").startNew(uninitializedName).string("this").end();
+            builder.end();
+            if (node.isFallbackReachable()) {
+                builder.startStatement().string("((", uninitializedName, ") newNode).containsFallback = true").end();
+            }
+            builder.end();
+
+            builder.startStatement();
+            builder.type(getContext().getType(String.class)).string(" message = ").tree(createInfoCall(builder, generic, "reason"));
+            builder.end();
+
+            builder.declaration(baseClassName, "returnNode",
+                            builder.create().startStaticCall(context.getTruffleTypes().getDslShare(), DSLSHARE_REWRITE).string("this").string("newNode").string("message").end().getRoot());
+            builder.startIf().string("returnNode == null").end().startBlock();
+            builder.tree(createRewritePolymorphic(builder, node, "this"));
+            builder.end();
+
+            builder.startReturn();
+            builder.startCall("returnNode", EXECUTE_CHAINED);
+            addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, true, null);
+            builder.end();
+            builder.end();
+
+            return method;
+        }
+
+        private CodeTree createRewritePolymorphic(CodeTreeBuilder parent, NodeData node, String currentNode) {
+            String polyClassName = nodePolymorphicClassName(node);
+            CodeTreeBuilder builder = parent.create();
+
+            builder.startStatement().string("returnNode = ");
+            builder.startStaticCall(context.getTruffleTypes().getDslShare(), DSLSHARE_REWRITE_TO_POLYMORHPIC);
+            builder.string("this");
+            builder.tree(builder.create().startNew(nodeSpecializationClassName(node.getUninitializedSpecialization())).string(currentNode).end().getRoot());
+            builder.tree(builder.create().startNew(polyClassName).string(currentNode).end().getRoot());
+            builder.startGroup().cast(baseClassName(node)).startCall("copy").end().end();
+            builder.string("newNode");
+            builder.string("message");
+            builder.end();
+            builder.end();
+
+            return builder.getRoot();
+        }
+
+        private CodeExecutableElement createCreateSpecializationMethod(NodeData node, SpecializationGroup group) {
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), new GeneratedTypeMirror(Utils.getPackageName(node.getTemplateType()), baseClassName(node)),
+                            SPECIALIZE);
             if (!node.needsFrame(getContext())) {
                 method.getAnnotationMirrors().add(new CodeAnnotationMirror(getContext().getTruffleTypes().getSlowPath()));
             }
+
             addInternalValueParameters(method, node.getGenericSpecialization(), node.needsFrame(getContext()), false);
             final CodeTreeBuilder builder = method.createBuilder();
-
-            builder.tree(createExecuteTree(builder, node.getGenericSpecialization(), group, false, new CodeBlock<SpecializationData>() {
+            builder.tree(createExecuteTree(builder, node.getGenericSpecialization(), group, new CodeBlock<SpecializationData>() {
 
                 public CodeTree create(CodeTreeBuilder b, SpecializationData current) {
-                    return createGenericInvoke(builder, current.getNode().getGenericSpecialization(), current);
+                    return createCreateSpecializationMethodBody0(builder, current);
                 }
-            }, null, false, true, false));
+            }, null, false, true, false, true));
 
             emitUnreachableSpecializations(builder, node);
 
             return method;
         }
 
+        protected CodeTree createCreateSpecializationMethodBody0(CodeTreeBuilder parent, SpecializationData current) {
+            CodeTreeBuilder builder = new CodeTreeBuilder(parent);
+            if (current.isGeneric()) {
+                builder.startReturn().nullLiteral().end();
+            } else {
+                String className = nodeSpecializationClassName(current);
+                if (!current.getExcludedBy().isEmpty()) {
+                    builder.startIf().string("!").startStaticCall(context.getTruffleTypes().getDslShare(), "isExcluded");
+                    builder.string("this").string(nodeSpecializationClassName(current), ".", METADATA_FIELD_NAME).end().end();
+                    builder.startBlock();
+                }
+
+                if (current.getNode().getGenericSpecialization().isReachable()) {
+                    builder.startStatement().startStaticCall(context.getTruffleTypes().getCompilerDirectives(), "transferToInterpreterAndInvalidate").end().end();
+                }
+                builder.startReturn();
+                builder.cast(baseClassName(getModel().getNode()));
+                builder.startGroup().startCall(className, FACTORY_METHOD_NAME).string("this");
+                for (ActualParameter param : current.getSignatureParameters()) {
+                    NodeChildData child = param.getSpecification().getExecution().getChild();
+                    List<TypeData> types = child.getNodeData().getTypeSystem().lookupSourceTypes(param.getTypeSystemType());
+                    if (types.size() > 1) {
+                        builder.string(implicitTypeName(param));
+                    }
+                }
+                builder.end().end();
+                builder.end();
+
+                if (!current.getExcludedBy().isEmpty()) {
+                    builder.end();
+                }
+            }
+            return builder.getRoot();
+
+        }
+
         private void emitUnreachableSpecializations(final CodeTreeBuilder builder, NodeData node) {
             for (SpecializationData current : node.getSpecializations()) {
                 if (current.isReachable()) {
@@ -1317,9 +1420,9 @@
             }
         }
 
-        protected CodeTree createExecuteTree(CodeTreeBuilder outerParent, final SpecializationData source, final SpecializationGroup group, final boolean checkMinimumState,
-                        final CodeBlock<SpecializationData> guardedblock, final CodeTree elseBlock, boolean forceElse, final boolean emitAssumptions, final boolean typedCasts) {
-            return guard(outerParent, source, group, checkMinimumState, new CodeBlock<Integer>() {
+        protected CodeTree createExecuteTree(CodeTreeBuilder outerParent, final SpecializationData source, final SpecializationGroup group, final CodeBlock<SpecializationData> guardedblock,
+                        final CodeTree elseBlock, boolean forceElse, final boolean emitAssumptions, final boolean typedCasts, final boolean castForGuardsOnly) {
+            return guard(outerParent, source, group, new CodeBlock<Integer>() {
 
                 public CodeTree create(CodeTreeBuilder parent, Integer ifCount) {
                     CodeTreeBuilder builder = parent.create();
@@ -1331,22 +1434,22 @@
 
                     } else {
                         for (SpecializationGroup childGroup : group.getChildren()) {
-                            builder.tree(createExecuteTree(builder, source, childGroup, checkMinimumState, guardedblock, null, false, emitAssumptions, typedCasts));
+                            builder.tree(createExecuteTree(builder, source, childGroup, guardedblock, null, false, emitAssumptions, typedCasts, castForGuardsOnly));
                         }
                     }
 
                     return builder.getRoot();
                 }
-            }, elseBlock, forceElse, emitAssumptions, typedCasts);
+            }, elseBlock, forceElse, emitAssumptions, typedCasts, castForGuardsOnly);
         }
 
-        private CodeTree guard(CodeTreeBuilder parent, SpecializationData source, SpecializationGroup group, boolean checkMinimumState, CodeBlock<Integer> bodyBlock, CodeTree elseBlock,
-                        boolean forceElse, boolean emitAssumptions, boolean typedCasts) {
+        private CodeTree guard(CodeTreeBuilder parent, SpecializationData source, SpecializationGroup group, CodeBlock<Integer> bodyBlock, CodeTree elseBlock, boolean forceElse,
+                        boolean emitAssumptions, boolean typedCasts, boolean castForGuardsOnly) {
             CodeTreeBuilder builder = parent.create();
 
-            int ifCount = emitGuards(builder, source, group, checkMinimumState, emitAssumptions, typedCasts);
-
-            if (isReachableGroup(group, ifCount, checkMinimumState)) {
+            int ifCount = emitGuards(builder, source, group, emitAssumptions, typedCasts, castForGuardsOnly);
+
+            if (isReachableGroup(group, ifCount)) {
                 builder.tree(bodyBlock.create(builder, ifCount));
             }
 
@@ -1361,12 +1464,12 @@
             return builder.getRoot();
         }
 
-        private boolean isReachableGroup(SpecializationGroup group, int ifCount, boolean checkMinimumState) {
+        private boolean isReachableGroup(SpecializationGroup group, int ifCount) {
             if (ifCount != 0) {
                 return true;
             }
             SpecializationGroup previous = group.getPreviousGroup();
-            if (previous == null || previous.findElseConnectableGuards(checkMinimumState).isEmpty()) {
+            if (previous == null || previous.findElseConnectableGuards().isEmpty()) {
                 return true;
             }
 
@@ -1374,7 +1477,7 @@
              * Hacky else case. In this case the specialization is not reachable due to previous
              * else branch. This is only true if the minimum state is not checked.
              */
-            if (previous.getGuards().size() == 1 && previous.getTypeGuards().isEmpty() && previous.getAssumptions().isEmpty() && !checkMinimumState &&
+            if (previous.getGuards().size() == 1 && previous.getTypeGuards().isEmpty() && previous.getAssumptions().isEmpty() &&
                             (previous.getParent() == null || previous.getMaxSpecializationIndex() != previous.getParent().getMaxSpecializationIndex())) {
                 return false;
             }
@@ -1382,7 +1485,7 @@
             return true;
         }
 
-        private int emitGuards(CodeTreeBuilder builder, SpecializationData source, SpecializationGroup group, boolean checkMinimumState, boolean emitAssumptions, boolean typedCasts) {
+        private int emitGuards(CodeTreeBuilder builder, SpecializationData source, SpecializationGroup group, boolean emitAssumptions, boolean typedCasts, boolean castForGuardsOnly) {
             NodeData node = source.getNode();
 
             CodeTreeBuilder guardsBuilder = builder.create();
@@ -1392,17 +1495,6 @@
             String guardsAnd = "";
             String guardsCastAnd = "";
 
-            boolean minimumState = checkMinimumState;
-            if (minimumState) {
-                int groupMaxIndex = group.getUncheckedSpecializationIndex();
-
-                if (groupMaxIndex > -1) {
-                    guardsBuilder.string(guardsAnd);
-                    guardsBuilder.string("minimumState < " + groupMaxIndex);
-                    guardsAnd = " && ";
-                }
-            }
-
             if (emitAssumptions) {
                 for (String assumption : group.getAssumptions()) {
                     guardsBuilder.string(guardsAnd);
@@ -1436,14 +1528,30 @@
                     guardsAnd = " && ";
                 }
 
-                CodeTree cast = createCast(castBuilder, execution, valueParam, typeGuard.getType(), checkMinimumState, typedCasts);
-                if (cast != null) {
-                    castBuilder.tree(cast);
+                CodeTree implicitGetType = null;
+                if (castForGuardsOnly) {
+                    implicitGetType = createGetImplicitType(builder, execution, valueParam, typeGuard.getType());
+                }
+
+                boolean performCast = true;
+                if (castForGuardsOnly) {
+                    // if cast for guards we just cast if the type guard is used inside a guard.
+                    performCast = group.isTypeGuardUsedInAnyGuardBelow(context, source, typeGuard);
+                }
+
+                if (performCast) {
+                    CodeTree cast = createCast(castBuilder, execution, valueParam, typeGuard.getType(), typedCasts);
+                    if (cast != null) {
+                        castBuilder.tree(cast);
+                    }
+                }
+                if (implicitGetType != null) {
+                    castBuilder.tree(implicitGetType);
                 }
             }
-            List<GuardData> elseGuards = group.findElseConnectableGuards(checkMinimumState);
-
-            for (GuardData guard : group.getGuards()) {
+            List<GuardExpression> elseGuards = group.findElseConnectableGuards();
+
+            for (GuardExpression guard : group.getGuards()) {
                 if (elseGuards.contains(guard)) {
                     continue;
                 }
@@ -1463,7 +1571,7 @@
             return ifCount;
         }
 
-        private int startGuardIf(CodeTreeBuilder builder, CodeTreeBuilder conditionBuilder, int ifCount, List<GuardData> elseGuard) {
+        private int startGuardIf(CodeTreeBuilder builder, CodeTreeBuilder conditionBuilder, int ifCount, List<GuardExpression> elseGuard) {
             int newIfCount = ifCount;
 
             if (!conditionBuilder.isEmpty()) {
@@ -1482,9 +1590,9 @@
             return newIfCount;
         }
 
-        private boolean needsTypeGuard(SpecializationData source, SpecializationGroup group, GuardData guard) {
+        private boolean needsTypeGuard(SpecializationData source, SpecializationGroup group, GuardExpression guard) {
             int signatureIndex = 0;
-            for (ActualParameter parameter : guard.getParameters()) {
+            for (ActualParameter parameter : guard.getResolvedGuard().getParameters()) {
                 if (!parameter.getSpecification().isSignature()) {
                     continue;
                 }
@@ -1498,7 +1606,7 @@
                         sourceParameter = source.getNode().getGenericSpecialization().findParameter(parameter.getLocalName());
                     }
 
-                    if (Utils.needsCastTo(getContext(), sourceParameter.getType(), requiredType.getPrimitiveType())) {
+                    if (Utils.needsCastTo(sourceParameter.getType(), requiredType.getPrimitiveType())) {
                         return true;
                     }
                 }
@@ -1515,7 +1623,7 @@
 
             TypeData sourceType = source.getTypeSystemType();
 
-            if (!sourceType.needsCastTo(getContext(), targetType)) {
+            if (!sourceType.needsCastTo(targetType)) {
                 return null;
             }
 
@@ -1541,7 +1649,7 @@
                 castMethodName = TypeSystemCodeGenerator.isTypeMethodName(targetType);
             }
 
-            startCallTypeSystemMethod(getContext(), builder, node.getTypeSystem(), castMethodName);
+            startCallTypeSystemMethod(builder, node.getTypeSystem(), castMethodName);
             builder.string(valueName(source));
             if (castTypeName != null) {
                 builder.string(castTypeName);
@@ -1558,11 +1666,11 @@
         }
 
         // TODO merge redundancies with #createTypeGuard
-        private CodeTree createCast(CodeTreeBuilder parent, NodeExecutionData execution, ActualParameter source, TypeData targetType, boolean checkMinimumState, boolean typedCasts) {
+        private CodeTree createCast(CodeTreeBuilder parent, NodeExecutionData execution, ActualParameter source, TypeData targetType, boolean typedCasts) {
             NodeData node = execution.getChild().getNodeData();
             TypeData sourceType = source.getTypeSystemType();
 
-            if (!sourceType.needsCastTo(getContext(), targetType)) {
+            if (!sourceType.needsCastTo(targetType)) {
                 return null;
             }
 
@@ -1591,25 +1699,39 @@
                 args.add(CodeTreeBuilder.singleString(castTypeName));
             }
 
-            CodeTree value = createCallTypeSystemMethod(context, parent, node, castMethodName, args.toArray(new CodeTree[0]));
+            CodeTree cast = createCallTypeSystemMethod(parent, node, castMethodName, args.toArray(new CodeTree[0]));
 
             CodeTreeBuilder builder = parent.create();
-            builder.tree(createLazyAssignment(parent, castValueName(source), targetType.getPrimitiveType(), condition, value));
-            if (checkMinimumState && types.size() > 1) {
-                CodeTree castType = createCallTypeSystemMethod(context, parent, node, TypeSystemCodeGenerator.getImplicitClass(targetType), CodeTreeBuilder.singleString(valueName(source)));
-                builder.tree(createLazyAssignment(builder, implicitTypeName(source), getContext().getType(Class.class), condition, castType));
-            }
+            builder.tree(createLazyAssignment(parent, castValueName(source), targetType.getPrimitiveType(), condition, cast));
 
             return builder.getRoot();
         }
 
-        private CodeTree createMethodGuard(CodeTreeBuilder parent, String prefix, SpecializationData source, GuardData guard) {
+        private CodeTree createGetImplicitType(CodeTreeBuilder parent, NodeExecutionData execution, ActualParameter source, TypeData targetType) {
+            CodeTree condition = null;
+            if (execution.isShortCircuit()) {
+                ActualParameter shortCircuit = source.getPreviousParameter();
+                assert shortCircuit != null;
+                condition = CodeTreeBuilder.singleString(valueName(shortCircuit));
+            }
+
+            CodeTreeBuilder builder = parent.create();
+            List<TypeData> types = getModel().getNode().getTypeSystem().lookupSourceTypes(targetType);
+            if (types.size() > 1) {
+                CodeTree castType = createCallTypeSystemMethod(parent, execution.getChild().getNodeData(), TypeSystemCodeGenerator.getImplicitClass(targetType),
+                                CodeTreeBuilder.singleString(valueName(source)));
+                builder.tree(createLazyAssignment(builder, implicitTypeName(source), getContext().getType(Class.class), condition, castType));
+            }
+            return builder.getRoot();
+        }
+
+        private CodeTree createMethodGuard(CodeTreeBuilder parent, String prefix, SpecializationData source, GuardExpression guard) {
             CodeTreeBuilder builder = parent.create();
             builder.string(prefix);
             if (guard.isNegated()) {
                 builder.string("!");
             }
-            builder.tree(createTemplateMethodCall(builder, null, source, guard, null));
+            builder.tree(createTemplateMethodCall(builder, null, source, guard.getResolvedGuard(), null));
             return builder.getRoot();
         }
 
@@ -1625,67 +1747,6 @@
             return encloseThrowsWithFallThrough(current, builder.getRoot());
         }
 
-        protected CodeTree createGenericInvokeAndSpecialize(CodeTreeBuilder parent, SpecializationData source, SpecializationData current, String currentNodeVar) {
-            CodeTreeBuilder builder = parent.create();
-            CodeTreeBuilder prefix = parent.create();
-
-            NodeData node = current.getNode();
-
-            if (current.isGeneric() && node.isPolymorphic()) {
-                builder.startIf().string(currentNodeVar).string(".next0 == null && minimumState > 0").end().startBlock();
-                builder.tree(createRewritePolymorphic(builder, node, currentNodeVar));
-                builder.end();
-                builder.startElseBlock();
-                builder.tree(createRewriteGeneric(builder, source, current, currentNodeVar));
-                builder.end();
-            } else {
-                if (current.getExceptions().isEmpty()) {
-                    builder.tree(createGenericInvoke(builder, source, current, createReplaceCall(builder, current, currentNodeVar, currentNodeVar, null), null));
-                } else {
-                    builder.startStatement().string(currentNodeVar).string(" = ").tree(createReplaceCall(builder, current, currentNodeVar, currentNodeVar, null)).end();
-                    builder.tree(createGenericInvoke(builder, source, current, null, CodeTreeBuilder.singleString(currentNodeVar)));
-                }
-            }
-            CodeTreeBuilder root = parent.create();
-            root.tree(prefix.getRoot());
-            root.tree(encloseThrowsWithFallThrough(current, builder.getRoot()));
-            return root.getRoot();
-        }
-
-        private CodeTree createRewriteGeneric(CodeTreeBuilder parent, SpecializationData source, SpecializationData current, String currentNode) {
-            NodeData node = current.getNode();
-
-            CodeTreeBuilder builder = parent.create();
-            builder.declaration(getContext().getTruffleTypes().getNode(), "root", currentNode);
-            builder.startIf().string(currentNode).string(".next0 != null").end().startBlock();
-            /*
-             * Communicates to the caller of executeAndSpecialize that it was rewritten to generic.
-             * Its important that this is used instead of the currentNode since the caller is this.
-             * CurrentNode may not be this anymore at this place.
-             */
-            builder.statement("this.next0 = null");
-            builder.tree(createFindRoot(builder, node, false));
-            builder.end();
-            builder.end();
-            builder.tree(createGenericInvoke(builder, source, current, createReplaceCall(builder, current, "root", "(" + baseClassName(node) + ") root", null), null));
-            return builder.getRoot();
-        }
-
-        protected CodeTree createFindRoot(CodeTreeBuilder parent, NodeData node, boolean countDepth) {
-            CodeTreeBuilder builder = parent.create();
-            builder.startDoBlock();
-            builder.startAssert().string("root != null").string(" : ").doubleQuote("No polymorphic parent node.").end();
-            builder.startStatement().string("root = ").string("root.getParent()").end();
-            if (countDepth) {
-                builder.statement("depth++");
-            }
-            builder.end();
-            builder.startDoWhile();
-            builder.string("!").startParantheses().instanceOf("root", nodePolymorphicClassName(node)).end();
-            builder.end();
-            return builder.getRoot();
-        }
-
         private CodeTree encloseThrowsWithFallThrough(SpecializationData current, CodeTree tree) {
             if (current.getExceptions().isEmpty()) {
                 return tree;
@@ -1696,88 +1757,10 @@
             builder.tree(tree);
             for (SpecializationThrowsData exception : current.getExceptions()) {
                 builder.end().startCatchBlock(exception.getJavaClass(), "rewriteEx");
-                builder.string("// fall through").newLine();
-            }
-            builder.end();
-
-            return builder.getRoot();
-        }
-
-        protected CodeTree createGenericInvoke(CodeTreeBuilder parent, SpecializationData source, SpecializationData current, CodeTree replaceCall, CodeTree replaceVar) {
-            assert replaceCall == null || replaceVar == null;
-            CodeTreeBuilder builder = parent.create();
-            CodeTree replace = replaceVar;
-            if (replace == null) {
-                replace = replaceCall;
-            }
-            if (current.isGeneric()) {
-                builder.startReturn().tree(replace).string(".").startCall(EXECUTE_GENERIC_NAME);
-                addInternalValueParameterNames(builder, source, current, null, current.getNode().needsFrame(getContext()), null);
-                builder.end().end();
-            } else if (current.getMethod() == null) {
-                if (replaceCall != null) {
-                    builder.statement(replaceCall);
-                }
-                emitEncounteredSynthetic(builder, current);
-            } else if (!current.canBeAccessedByInstanceOf(getContext(), source.getNode().getNodeType())) {
-                if (replaceCall != null) {
-                    builder.statement(replaceCall);
-                }
-                builder.startReturn().tree(createTemplateMethodCall(parent, null, source, current, null)).end();
-            } else {
-                replace.add(new CodeTree(CodeTreeKind.STRING, null, "."));
-                builder.startReturn().tree(createTemplateMethodCall(parent, replace, source, current, null)).end();
+                builder.tree(createDeoptimize(builder));
+                builder.tree(createCallRewriteMonomorphic(builder, false, current.getNode().getGenericSpecialization().getReturnType().getTypeSystemType(), current, null,
+                                "Thrown " + Utils.getSimpleName(exception.getJavaClass())));
             }
-            return builder.getRoot();
-        }
-
-        protected CodeTree createReplaceCall(CodeTreeBuilder builder, SpecializationData current, String target, String source, String message) {
-            String className = nodeSpecializationClassName(current);
-            CodeTreeBuilder replaceCall = builder.create();
-            if (target != null) {
-                replaceCall.startCall(target, "replace");
-            } else {
-                replaceCall.startCall("replace");
-            }
-            replaceCall.startGroup().cast(baseClassName(current.getNode())).startCall(className, CREATE_SPECIALIZATION_NAME).string(source);
-            for (ActualParameter param : current.getSignatureParameters()) {
-                NodeChildData child = param.getSpecification().getExecution().getChild();
-                List<TypeData> types = child.getNodeData().getTypeSystem().lookupSourceTypes(param.getTypeSystemType());
-                if (types.size() > 1) {
-                    replaceCall.string(implicitTypeName(param));
-                }
-            }
-            replaceCall.end().end();
-
-            if (message == null) {
-                replaceCall.string("message");
-            } else {
-                replaceCall.doubleQuote(message);
-            }
-            replaceCall.end();
-            return replaceCall.getRoot();
-        }
-
-        private CodeTree createRewritePolymorphic(CodeTreeBuilder parent, NodeData node, String currentNode) {
-            String polyClassName = nodePolymorphicClassName(node);
-            String uninitializedName = nodeSpecializationClassName(node.getUninitializedSpecialization());
-            CodeTreeBuilder builder = parent.create();
-
-            builder.declaration(getElement().asType(), "currentCopy", currentNode + "." + COPY_WITH_CONSTRUCTOR_NAME + "()");
-            for (ActualParameter param : getModel().getSignatureParameters()) {
-                NodeExecutionData execution = param.getSpecification().getExecution();
-                builder.startStatement().tree(createAccessChild(execution, "currentCopy")).string(" = ").nullLiteral().end();
-            }
-            builder.startStatement().string("currentCopy.next0 = ").startNew(uninitializedName).string("currentCopy").end().end();
-
-            builder.declaration(polyClassName, "polymorphic", builder.create().startNew(polyClassName).string(currentNode).end());
-            builder.startStatement().string("polymorphic.next0 = ").string("currentCopy").end();
-            builder.startStatement().startCall(currentNode, "replace").string("polymorphic").string("message").end().end();
-
-            builder.startReturn();
-            builder.startCall("currentCopy.next0", EXECUTE_POLYMORPHIC_NAME);
-            addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, true, null);
-            builder.end();
             builder.end();
 
             return builder.getRoot();
@@ -1788,7 +1771,6 @@
             CodeTreeBuilder builder = new CodeTreeBuilder(parent);
             NodeData node = specialization.getNode();
 
-            ExecutableTypeData castedType = node.findExecutableType(type, 0);
             TypeData primaryType = castExecutable.getType();
 
             boolean needsTry = castExecutable.hasUnexpectedValue(getContext());
@@ -1809,6 +1791,7 @@
             }
 
             builder.tree(createExecuteChildren(builder, executable, specialization, executeParameters, null));
+            boolean hasUnexpected = executable.hasUnexpectedValue(getContext());
 
             CodeTree primaryExecuteCall = createTemplateMethodCall(builder, null, executable, castExecutable, null, executeParameterNames);
             if (needsTry) {
@@ -1831,14 +1814,15 @@
                     builder.string("// ignore").newLine();
                 } else {
                     builder.startReturn();
-                    builder.tree(createExpectExecutableType(node, specialization.getNode().getTypeSystem().getGenericTypeData(), castedType, CodeTreeBuilder.singleString("ex.getResult()")));
+                    builder.tree(createExpectExecutableType(node, specialization.getNode().getTypeSystem().getGenericTypeData(), hasUnexpected, executable.getType(),
+                                    CodeTreeBuilder.singleString("ex.getResult()")));
                     builder.end();
                 }
                 builder.end();
 
                 if (!returnVoid) {
                     builder.startReturn();
-                    builder.tree(createExpectExecutableType(node, castExecutable.getReturnType().getTypeSystemType(), executable, CodeTreeBuilder.singleString("value")));
+                    builder.tree(createExpectExecutableType(node, castExecutable.getReturnType().getTypeSystemType(), hasUnexpected, executable.getType(), CodeTreeBuilder.singleString("value")));
                     builder.end();
                 }
             } else {
@@ -1846,7 +1830,7 @@
                     builder.statement(primaryExecuteCall);
                 } else {
                     builder.startReturn();
-                    builder.tree(createExpectExecutableType(node, castExecutable.getReturnType().getTypeSystemType(), executable, primaryExecuteCall));
+                    builder.tree(createExpectExecutableType(node, castExecutable.getReturnType().getTypeSystemType(), hasUnexpected, executable.getType(), primaryExecuteCall));
                     builder.end();
                 }
             }
@@ -1854,9 +1838,8 @@
             return builder.getRoot();
         }
 
-        protected CodeTree createExpectExecutableType(NodeData node, TypeData sourceType, ExecutableTypeData castedType, CodeTree value) {
-            boolean hasUnexpected = castedType.hasUnexpectedValue(getContext());
-            return createCastType(node.getTypeSystem(), sourceType, castedType.getType(), hasUnexpected, value);
+        protected CodeTree createExpectExecutableType(NodeData node, TypeData sourceType, boolean hasUnexpected, TypeData exepctedType, CodeTree value) {
+            return createCastType(node.getTypeSystem(), sourceType, exepctedType, hasUnexpected, value);
         }
 
         protected CodeTree createExecuteChildren(CodeTreeBuilder parent, ExecutableTypeData sourceExecutable, SpecializationData specialization, List<ActualParameter> targetParameters,
@@ -1911,7 +1894,7 @@
 
                     ActualParameter sourceParameter = sourceExecutable.findParameter(targetParameter.getLocalName());
                     TypeData sourceType = sourceParameter != null ? sourceParameter.getTypeSystemType() : null;
-                    builder.string(polymorphicTypeName(targetParameter)).string(" == ").typeLiteral(possiblePolymoprhicType.getPrimitiveType());
+                    builder.string(polymorphicTypeName(targetParameter.getSpecification().getExecution())).string(" == ").typeLiteral(possiblePolymoprhicType.getPrimitiveType());
                     builder.end().startBlock();
                     builder.startStatement();
                     builder.tree(createExecuteChildExpression(parent, execution, sourceType, new ActualParameter(targetParameter, possiblePolymoprhicType), unexpectedParameter, null));
@@ -2121,7 +2104,7 @@
                 return expression;
             }
             CodeTreeBuilder builder = parent.create();
-            startCallTypeSystemMethod(getContext(), builder, typeSystem, cast.getMethodName());
+            startCallTypeSystemMethod(builder, typeSystem, cast.getMethodName());
             builder.tree(expression);
             builder.end().end();
             return builder.getRoot();
@@ -2178,12 +2161,12 @@
 
                 ImplicitCastData cast = getModel().getNode().getTypeSystem().lookupCast(implicitSourceType, targetType);
                 if (cast != null) {
-                    if (cast.getSourceType().needsCastTo(getContext(), targetType)) {
+                    if (cast.getSourceType().needsCastTo(targetType)) {
                         return true;
                     }
                 }
 
-                if (sourceType.needsCastTo(getContext(), targetType)) {
+                if (sourceType.needsCastTo(targetType)) {
                     return true;
                 }
             }
@@ -2219,7 +2202,7 @@
             if (specialization.isPolymorphic()) {
                 builder.tree(createReturnOptimizeTypes(builder, currentExecutable, specialization, param));
             } else {
-                builder.tree(createReturnExecuteAndSpecialize(builder, currentExecutable, specialization, param,
+                builder.tree(createCallRewriteMonomorphic(builder, currentExecutable.hasUnexpectedValue(context), currentExecutable.getType(), specialization, param,
                                 "Expected " + param.getLocalName() + " instanceof " + Utils.getSimpleName(param.getType())));
             }
             builder.end(); // catch block
@@ -2232,18 +2215,18 @@
             SpecializationData polymorphic = node.getPolymorphicSpecialization();
 
             CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-            builder.startStatement().string(polymorphicTypeName(param)).string(" = ").typeLiteral(getContext().getType(Object.class)).end();
+            builder.startStatement().string(polymorphicTypeName(param.getSpecification().getExecution())).string(" = ").typeLiteral(getContext().getType(Object.class)).end();
 
             builder.startReturn();
 
             CodeTreeBuilder execute = new CodeTreeBuilder(builder);
-            execute.startCall("next0", EXECUTE_POLYMORPHIC_NAME);
+            execute.startCall("next0", EXECUTE_CHAINED);
             addInternalValueParameterNames(execute, specialization, polymorphic, param.getLocalName(), true, null);
             execute.end();
 
             TypeData sourceType = polymorphic.getReturnType().getTypeSystemType();
 
-            builder.tree(createExpectExecutableType(node, sourceType, currentExecutable, execute.getRoot()));
+            builder.tree(createExpectExecutableType(node, sourceType, currentExecutable.hasUnexpectedValue(context), currentExecutable.getType(), execute.getRoot()));
 
             builder.end();
             return builder.getRoot();
@@ -2290,8 +2273,8 @@
 
                         CodeTree value = CodeTreeBuilder.singleString(localName);
 
-                        if (sourceType.needsCastTo(getContext(), targetType)) {
-                            value = createCallTypeSystemMethod(getContext(), builder, getModel().getNode(), TypeSystemCodeGenerator.asTypeMethodName(targetType), value);
+                        if (sourceType.needsCastTo(targetType)) {
+                            value = createCallTypeSystemMethod(builder, getModel().getNode(), TypeSystemCodeGenerator.asTypeMethodName(targetType), value);
                         }
                         builder.tree(value);
                     } else {
@@ -2350,12 +2333,11 @@
             return builder.getRoot();
         }
 
-        protected CodeTree createReturnExecuteAndSpecialize(CodeTreeBuilder parent, ExecutableTypeData executable, SpecializationData current, ActualParameter exceptionParam, String reason) {
+        protected CodeTree createCallRewriteMonomorphic(CodeTreeBuilder parent, boolean hasUnexpected, TypeData returnType, SpecializationData current, ActualParameter exceptionParam, String reason) {
             NodeData node = current.getNode();
             SpecializationData generic = node.getGenericSpecialization();
             CodeTreeBuilder specializeCall = new CodeTreeBuilder(parent);
-            specializeCall.startCall(EXECUTE_SPECIALIZE_NAME);
-            specializeCall.string(String.valueOf(node.getSpecializations().indexOf(current)));
+            specializeCall.startCall(REWRITE);
             addInternalValueParameterNames(specializeCall, generic, node.getGenericSpecialization(), exceptionParam != null ? exceptionParam.getLocalName() : null, true, null);
             specializeCall.doubleQuote(reason);
             specializeCall.end().end();
@@ -2363,49 +2345,18 @@
             CodeTreeBuilder builder = new CodeTreeBuilder(parent);
 
             builder.startReturn();
-            builder.tree(createExpectExecutableType(node, generic.getReturnType().getTypeSystemType(), executable, specializeCall.getRoot()));
+            builder.tree(createExpectExecutableType(node, generic.getReturnType().getTypeSystemType(), hasUnexpected, returnType, specializeCall.getRoot()));
             builder.end();
 
             return builder.getRoot();
         }
 
-        protected final CodeExecutableElement createUpdateTypes(TypeMirror polymorphicType) {
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED), getContext().getType(void.class), UPDATE_TYPES_NAME);
-            method.getParameters().add(new CodeVariableElement(polymorphicType, "polymorphic"));
-            CodeTreeBuilder builder = method.createBuilder();
-
-            if (getModel().isPolymorphic()) {
-                builder.startStatement();
-                builder.startCall("next0", "updateTypes").string("polymorphic").end();
-                builder.end();
-            } else if (getModel().isSpecialized()) {
-                for (ActualParameter parameter : getModel().getParameters()) {
-                    if (!parameter.getSpecification().isSignature()) {
-                        continue;
-                    }
-                    if (lookupPolymorphicTargetTypes(parameter).size() <= 1) {
-                        continue;
-                    }
-                    builder.startStatement();
-                    builder.startCall("polymorphic", createUpdateTypeName(parameter));
-                    builder.typeLiteral(parameter.getType());
-                    builder.end().end();
-                }
-
-                builder.startStatement().startCall("super", UPDATE_TYPES_NAME).string("polymorphic").end().end();
-            }
-            return method;
-        }
-
-        protected String createUpdateTypeName(ActualParameter parameter) {
-            return "update" + Utils.firstLetterUpperCase(parameter.getLocalName()) + "Type";
-        }
     }
 
     private class PolymorphicNodeFactory extends SpecializedNodeFactory {
 
-        public PolymorphicNodeFactory(ProcessorContext context, CodeTypeElement nodeGen) {
-            super(context, nodeGen);
+        public PolymorphicNodeFactory(CodeTypeElement nodeGen) {
+            super(nodeGen);
         }
 
         @Override
@@ -2432,7 +2383,12 @@
                     assert parameter != null;
                     types.add(parameter.getTypeSystemType());
                 }
-                CodeVariableElement var = new CodeVariableElement(modifiers(PRIVATE), getContext().getType(Class.class), polymorphicTypeName(polymorphParameter));
+
+            }
+
+            for (NodeExecutionData execution : getModel().getNode().getChildExecutions()) {
+                String fieldName = polymorphicTypeName(execution);
+                CodeVariableElement var = new CodeVariableElement(modifiers(PRIVATE), getContext().getType(Class.class), fieldName);
                 var.getAnnotationMirrors().add(new CodeAnnotationMirror(getContext().getTruffleTypes().getCompilationFinal()));
                 clazz.add(var);
             }
@@ -2447,50 +2403,17 @@
             createConstructors(clazz);
             createExecuteMethods(specialization);
 
-            getElement().add(createUpdateTypes(nodeGen.asType()));
-
-            for (ActualParameter parameter : specialization.getParameters()) {
-                if (!parameter.getSpecification().isSignature()) {
-                    continue;
-                }
-                if (lookupPolymorphicTargetTypes(parameter).size() <= 1) {
-                    continue;
-                }
-                getElement().add(createUpdateType(parameter));
-            }
-
-            if (needsInvokeCopyConstructorMethod()) {
-                clazz.add(createCopy(nodeGen.asType(), specialization));
-            }
-
+            clazz.add(createUpdateTypes0());
             createCachedExecuteMethods(specialization);
         }
 
-        private ExecutableElement createUpdateType(ActualParameter parameter) {
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED), getContext().getType(void.class), createUpdateTypeName(parameter));
-            method.getParameters().add(new CodeVariableElement(getContext().getType(Class.class), "type"));
-            CodeTreeBuilder builder = method.createBuilder();
-
-            String fieldName = polymorphicTypeName(parameter);
-            builder.startIf().string(fieldName).isNull().end().startBlock();
-            builder.startStatement().string(fieldName).string(" = ").string("type").end();
-            builder.end();
-            builder.startElseIf().string(fieldName).string(" != ").string("type").end();
-            builder.startBlock();
-            builder.startStatement().string(fieldName).string(" = ").typeLiteral(getContext().getType(Object.class)).end();
-            builder.end();
-
-            return method;
-        }
-
     }
 
     private class SpecializedNodeFactory extends NodeBaseFactory {
 
         protected final CodeTypeElement nodeGen;
 
-        public SpecializedNodeFactory(ProcessorContext context, CodeTypeElement nodeGen) {
-            super(context);
+        public SpecializedNodeFactory(CodeTypeElement nodeGen) {
             this.nodeGen = nodeGen;
         }
 
@@ -2503,6 +2426,11 @@
             }
             CodeTypeElement clazz = createClass(node, modifiers(PRIVATE, STATIC, FINAL), nodeSpecializationClassName(specialization), baseType, false);
 
+            if (specialization.isSpecialized() || specialization.isUninitialized()) {
+                clazz.add(createGetMetadata0(false));
+                clazz.add(createMetadataLiteral());
+            }
+
             NodeCost cost;
             if (specialization.isGeneric()) {
                 cost = NodeCost.MEGAMORPHIC;
@@ -2517,9 +2445,93 @@
             }
             clazz.getAnnotationMirrors().add(createNodeInfo(node, cost));
 
+            if (specialization.isUninitialized() && node.getGenericSpecialization().isReachable()) {
+                clazz.add(createUninitializedGetCostOverride());
+            }
+
             return clazz;
         }
 
+        private Element createUninitializedGetCostOverride() {
+            TypeMirror returnType = context.getTruffleTypes().getNodeCost();
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getCost");
+            CodeTreeBuilder builder = method.createBuilder();
+            builder.startIf().string(CONTAINS_FALLBACK).end().startBlock();
+            builder.startReturn().staticReference(returnType, "MONOMORPHIC").end();
+            builder.end();
+            builder.startReturn().string("super.getCost()").end();
+            return method;
+        }
+
+        private CodeVariableElement createMetadataLiteral() {
+            CodeVariableElement includes = new CodeVariableElement(modifiers(PRIVATE, STATIC, FINAL), context.getTruffleTypes().getDslMetadata(), METADATA_FIELD_NAME);
+
+            CodeTreeBuilder builder = includes.createInitBuilder();
+
+            SpecializationData specialization = getModel();
+            NodeData node = specialization.getNode();
+
+            Set<SpecializationData> contains = specialization.getContains();
+            if (specialization.isUninitialized()) {
+                contains = new HashSet<>();
+
+                SpecializationData polymorphic = node.getPolymorphicSpecialization();
+                if (polymorphic != null) {
+                    contains.addAll(polymorphic.getContains());
+                }
+                SpecializationData generic = node.getGenericSpecialization();
+                if (generic != null) {
+                    contains.addAll(generic.getContains());
+                }
+            }
+
+            builder.startNew(context.getTruffleTypes().getDslMetadata());
+            builder.startGroup().string(nodeSpecializationClassName(getModel()), ".class").end();
+            builder.tree(createSpecializationListLiteral(builder, contains));
+            builder.tree(createSpecializationListLiteral(builder, getModel().getExcludedBy()));
+            builder.tree(createSpecializationTypeLiteral(builder, SpecializationData.getSignatureTypes(getModel())));
+            builder.string("0").string("0");
+            builder.end();
+            return includes;
+        }
+
+        private CodeTree createSpecializationTypeLiteral(CodeTreeBuilder parent, List<TypeMirror> list) {
+            ArrayType classArray = new ArrayCodeTypeMirror(context.getType(Class.class));
+            CodeTreeBuilder builder = parent.create();
+
+            if (list.isEmpty()) {
+                builder.staticReference(context.getTruffleTypes().getDslMetadata(), EMPTY_CLASS_ARRAY);
+            } else {
+                builder.startNewArray(classArray, null);
+                for (TypeMirror type : list) {
+                    builder.typeLiteral(type);
+                }
+                builder.end();
+            }
+
+            return builder.getRoot();
+        }
+
+        private CodeTree createSpecializationListLiteral(CodeTreeBuilder parent, Set<SpecializationData> list) {
+            ArrayType classArray = new ArrayCodeTypeMirror(context.getType(Class.class));
+            CodeTreeBuilder builder = parent.create();
+
+            if (list.isEmpty()) {
+                builder.staticReference(context.getTruffleTypes().getDslMetadata(), EMPTY_CLASS_ARRAY);
+            } else {
+                builder.startNewArray(classArray, null);
+                for (SpecializationData specialization : list) {
+                    if (specialization.isGeneric() || specialization.isPolymorphic()) {
+                        specialization = getModel().getNode().getUninitializedSpecialization();
+                    }
+                    builder.startGroup().string(nodeSpecializationClassName(specialization)).string(".class").end();
+                }
+                builder.end();
+            }
+
+            return builder.getRoot();
+        }
+
         protected CodeAnnotationMirror createNodeInfo(NodeData node, NodeCost cost) {
             String shortName = node.getShortName();
             CodeAnnotationMirror nodeInfoMirror = new CodeAnnotationMirror(getContext().getTruffleTypes().getNodeInfoAnnotation());
@@ -2541,11 +2553,14 @@
 
             createExecuteMethods(specialization);
             createCachedExecuteMethods(specialization);
-            if (specialization.getNode().isPolymorphic()) {
-                getElement().add(createUpdateTypes(nodeGen.asType()));
-            }
-            if (needsInvokeCopyConstructorMethod()) {
-                clazz.add(createCopy(nodeGen.asType(), specialization));
+
+            if (specialization.isUninitialized()) {
+                if (specialization.getNode().isFallbackReachable()) {
+                    CodeVariableElement var = new CodeVariableElement(modifiers(Modifier.PRIVATE), context.getType(boolean.class), CONTAINS_FALLBACK);
+                    var.addAnnotationMirror(new CodeAnnotationMirror(context.getTruffleTypes().getCompilationFinal()));
+                    clazz.add(var);
+                }
+                clazz.add(createExecuteUninitialized());
             }
 
             if (!specialization.isUninitialized() && specialization.getNode().needsRewrites(context)) {
@@ -2568,7 +2583,7 @@
             for (ExecutableElement constructor : ElementFilter.constructorsIn(superTypeElement.getEnclosedElements())) {
                 if (specialization.isUninitialized()) {
                     // ignore copy constructors for uninitialized if not polymorphic
-                    if (isCopyConstructor(constructor) && !node.isPolymorphic()) {
+                    if (isCopyConstructor(constructor) && !node.isPolymorphic(context)) {
                         continue;
                     }
                 } else if (node.getUninitializedSpecialization() != null) {
@@ -2586,11 +2601,6 @@
                 CodeTreeBuilder builder = superConstructor.createBuilder();
                 builder.tree(body);
 
-                if (node.isPolymorphic()) {
-                    if (specialization.isSpecialized() || specialization.isPolymorphic()) {
-                        builder.statement("this.next0 = copy.next0");
-                    }
-                }
                 if (superConstructor != null) {
                     for (ActualParameter param : getImplicitTypeParameters(getModel())) {
                         clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), getContext().getType(Class.class), implicitTypeName(param)));
@@ -2628,90 +2638,49 @@
 
         protected void createCachedExecuteMethods(SpecializationData specialization) {
             NodeData node = specialization.getNode();
-            if (!node.isPolymorphic()) {
+            if (!node.isPolymorphic(context)) {
                 return;
             }
 
             CodeTypeElement clazz = getElement();
 
             final SpecializationData polymorphic = node.getPolymorphicSpecialization();
-            ExecutableElement executeCached = nodeGen.getMethod(EXECUTE_POLYMORPHIC_NAME);
+            ExecutableElement executeCached = nodeGen.getMethod(EXECUTE_CHAINED);
             CodeExecutableElement executeMethod = CodeExecutableElement.clone(getContext().getEnvironment(), executeCached);
             executeMethod.getModifiers().remove(Modifier.ABSTRACT);
             CodeTreeBuilder builder = executeMethod.createBuilder();
 
-            if (specialization.isGeneric() || specialization.isPolymorphic()) {
-                builder.startThrow().startNew(getContext().getType(AssertionError.class));
-                builder.doubleQuote("Should not be reached.");
+            if (specialization.isPolymorphic()) {
+                builder.startReturn().startCall("this.next0", EXECUTE_CHAINED);
+                addInternalValueParameterNames(builder, polymorphic, polymorphic, null, true, null);
                 builder.end().end();
             } else if (specialization.isUninitialized()) {
-                builder.tree(createAppendPolymorphic(builder, specialization));
+                if (node.getGenericSpecialization().isReachable()) {
+                    builder.startIf().string("!containsFallback").end().startBlock();
+                    builder.startStatement().startStaticCall(context.getTruffleTypes().getCompilerDirectives(), "transferToInterpreterAndInvalidate").end().end();
+                    builder.end();
+                } else {
+                    builder.startStatement().startStaticCall(context.getTruffleTypes().getCompilerDirectives(), "transferToInterpreterAndInvalidate").end().end();
+                }
+                builder.startReturn().startCall("this", EXECUTE_UNINITIALIZED);
+                addInternalValueParameterNames(builder, polymorphic, polymorphic, null, true, null);
+                builder.end().end();
             } else {
                 CodeTreeBuilder elseBuilder = new CodeTreeBuilder(builder);
-                elseBuilder.startReturn().startCall("this.next0", EXECUTE_POLYMORPHIC_NAME);
+                elseBuilder.startReturn().startCall("this.next0", EXECUTE_CHAINED);
                 addInternalValueParameterNames(elseBuilder, polymorphic, polymorphic, null, true, null);
                 elseBuilder.end().end();
 
-                boolean forceElse = specialization.getExceptions().size() > 0;
-                builder.tree(createExecuteTree(builder, polymorphic, SpecializationGroup.create(specialization), false, new CodeBlock<SpecializationData>() {
+                builder.tree(createExecuteTree(builder, polymorphic, SpecializationGroup.create(specialization), new CodeBlock<SpecializationData>() {
 
                     public CodeTree create(CodeTreeBuilder b, SpecializationData current) {
                         return createGenericInvoke(b, polymorphic, current);
                     }
-                }, elseBuilder.getRoot(), forceElse, true, true));
+                }, elseBuilder.getRoot(), false, true, true, false));
             }
             clazz.add(executeMethod);
         }
 
-        private CodeTree createAppendPolymorphic(CodeTreeBuilder parent, SpecializationData specialization) {
-            NodeData node = specialization.getNode();
-
-            CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-            builder.tree(createDeoptimize(builder));
-
-            builder.declaration(getContext().getTruffleTypes().getNode(), "root", "this");
-            builder.declaration(getContext().getType(int.class), "depth", "0");
-            builder.tree(createFindRoot(builder, node, true));
-            builder.newLine();
-
-            builder.startIf().string("depth > ").string(String.valueOf(node.getPolymorphicDepth())).end();
-            builder.startBlock();
-            String message = "Polymorphic limit reached (" + node.getPolymorphicDepth() + ")";
-            String castRoot = "(" + baseClassName(node) + ") root";
-            builder.tree(createGenericInvoke(builder, node.getPolymorphicSpecialization(), node.getGenericSpecialization(),
-                            createReplaceCall(builder, node.getGenericSpecialization(), "root", castRoot, message), null));
-            builder.end();
-
-            builder.startElseBlock();
-            builder.startStatement().string("next0 = ");
-            builder.startNew(nodeSpecializationClassName(node.getUninitializedSpecialization())).string("this").end();
-            builder.end();
-
-            CodeTreeBuilder specializeCall = new CodeTreeBuilder(builder);
-            specializeCall.startCall(EXECUTE_SPECIALIZE_NAME);
-            specializeCall.string("0");
-            addInternalValueParameterNames(specializeCall, specialization, node.getGenericSpecialization(), null, true, null);
-            specializeCall.startGroup().doubleQuote("Uninitialized polymorphic (").string(" + depth + ").doubleQuote("/" + node.getPolymorphicDepth() + ")").end();
-            specializeCall.end().end();
-
-            builder.declaration(node.getGenericSpecialization().getReturnType().getType(), "result", specializeCall.getRoot());
-
-            CodeTree root = builder.create().cast(nodePolymorphicClassName(node)).string("root").getRoot();
-            builder.startIf().string("this.next0 != null").end().startBlock();
-            builder.startStatement().string("(").tree(root).string(").").startCall(UPDATE_TYPES_NAME).tree(root).end().end();
-            builder.end();
-
-            if (Utils.isVoid(builder.findMethod().getReturnType())) {
-                builder.returnStatement();
-            } else {
-                builder.startReturn().string("result").end();
-            }
-
-            builder.end();
-
-            return builder.getRoot();
-        }
-
         private CodeTree createExecuteBody(CodeTreeBuilder parent, SpecializationData specialization, ExecutableTypeData execType) {
             CodeTreeBuilder builder = new CodeTreeBuilder(parent);
 
@@ -2732,6 +2701,11 @@
         private CodeExecutableElement createExecutableTypeOverride(ExecutableTypeData execType, boolean evaluated) {
             CodeExecutableElement method = CodeExecutableElement.clone(getContext().getEnvironment(), execType.getMethod());
 
+            method.getAnnotationMirrors().clear();
+            for (VariableElement variable : method.getParameters()) {
+                variable.getAnnotationMirrors().clear();
+            }
+
             CodeTreeBuilder builder = method.createBuilder();
             int i = 0;
             int signatureIndex = -1;
@@ -2840,16 +2814,17 @@
             if (specialization.findNextSpecialization() != null) {
                 CodeTreeBuilder returnBuilder = new CodeTreeBuilder(builder);
                 returnBuilder.tree(createDeoptimize(builder));
-                returnBuilder.tree(createReturnExecuteAndSpecialize(builder, executable, specialization, null, "One of guards " + specialization.getGuardDefinitions() + " failed"));
+                returnBuilder.tree(createCallRewriteMonomorphic(builder, executable.hasUnexpectedValue(context), executable.getType(), specialization, null,
+                                "One of guards " + specialization.getGuards() + " failed"));
                 returnSpecialized = returnBuilder.getRoot();
             }
 
-            builder.tree(createExecuteTree(builder, specialization, SpecializationGroup.create(specialization), false, new CodeBlock<SpecializationData>() {
+            builder.tree(createExecuteTree(builder, specialization, SpecializationGroup.create(specialization), new CodeBlock<SpecializationData>() {
 
                 public CodeTree create(CodeTreeBuilder b, SpecializationData current) {
                     return createExecute(b, executable, specialization);
                 }
-            }, returnSpecialized, false, false, false));
+            }, returnSpecialized, false, false, false, false));
 
             return builder.getRoot();
         }
@@ -2869,21 +2844,15 @@
 
             CodeTreeBuilder returnBuilder = new CodeTreeBuilder(parent);
             if (specialization.isPolymorphic()) {
-                returnBuilder.startCall("next0", EXECUTE_POLYMORPHIC_NAME);
+                returnBuilder.startCall("next0", EXECUTE_CHAINED);
                 addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, null);
                 returnBuilder.end();
             } else if (specialization.isUninitialized()) {
-                returnBuilder.startCall("super", EXECUTE_SPECIALIZE_NAME);
-                returnBuilder.string("0");
+                returnBuilder.startCall(EXECUTE_UNINITIALIZED);
                 addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, null);
-                returnBuilder.doubleQuote("Uninitialized monomorphic");
                 returnBuilder.end();
             } else if (specialization.getMethod() == null && !node.needsRewrites(context)) {
                 emitEncounteredSynthetic(builder, specialization);
-            } else if (specialization.isGeneric()) {
-                returnBuilder.startCall("super", EXECUTE_GENERIC_NAME);
-                addInternalValueParameterNames(returnBuilder, specialization, specialization, null, node.needsFrame(getContext()), null);
-                returnBuilder.end();
             } else {
                 returnBuilder.tree(createTemplateMethodCall(returnBuilder, null, specialization, specialization, null));
             }
@@ -2895,12 +2864,12 @@
                 builder.startReturn();
                 if (targetType == null || sourceType == null) {
                     builder.tree(returnBuilder.getRoot());
-                } else if (sourceType.needsCastTo(getContext(), targetType)) {
+                } else if (sourceType.needsCastTo(targetType)) {
                     String castMethodName = TypeSystemCodeGenerator.expectTypeMethodName(targetType);
                     if (!executable.hasUnexpectedValue(context)) {
                         castMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetType);
                     }
-                    builder.tree(createCallTypeSystemMethod(context, parent, node, castMethodName, returnBuilder.getRoot()));
+                    builder.tree(createCallTypeSystemMethod(parent, node, castMethodName, returnBuilder.getRoot()));
                 } else {
                     builder.tree(returnBuilder.getRoot());
                 }
@@ -2911,13 +2880,14 @@
                 for (SpecializationThrowsData exception : specialization.getExceptions()) {
                     builder.end().startCatchBlock(exception.getJavaClass(), "ex");
                     builder.tree(createDeoptimize(builder));
-                    builder.tree(createReturnExecuteAndSpecialize(parent, executable, specialization, null, "Thrown " + Utils.getSimpleName(exception.getJavaClass())));
+                    builder.tree(createCallRewriteMonomorphic(parent, executable.hasUnexpectedValue(context), executable.getType(), specialization, null,
+                                    "Thrown " + Utils.getSimpleName(exception.getJavaClass())));
                 }
                 builder.end();
             }
             if (!specialization.getAssumptions().isEmpty()) {
                 builder.end().startCatchBlock(getContext().getTruffleTypes().getInvalidAssumption(), "ex");
-                builder.tree(createReturnExecuteAndSpecialize(parent, executable, specialization, null, "Assumption failed"));
+                builder.tree(createCallRewriteMonomorphic(parent, executable.hasUnexpectedValue(context), executable.getType(), specialization, null, "Assumption failed"));
                 builder.end();
             }
 
@@ -2926,14 +2896,12 @@
 
         protected CodeExecutableElement createCopyConstructorFactoryMethod(TypeMirror baseType, SpecializationData specialization) {
             List<ActualParameter> implicitTypeParams = getImplicitTypeParameters(specialization);
-            CodeVariableElement[] parameters = new CodeVariableElement[implicitTypeParams.size() + 1];
-            int i = 0;
             String baseName = "current";
-            parameters[i++] = new CodeVariableElement(specialization.getNode().getNodeType(), baseName);
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(STATIC), specialization.getNode().getNodeType(), FACTORY_METHOD_NAME);
+            method.addParameter(new CodeVariableElement(specialization.getNode().getNodeType(), baseName));
             for (ActualParameter implicitTypeParam : implicitTypeParams) {
-                parameters[i++] = new CodeVariableElement(getContext().getType(Class.class), implicitTypeName(implicitTypeParam));
+                method.addParameter(new CodeVariableElement(getContext().getType(Class.class), implicitTypeName(implicitTypeParam)));
             }
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(STATIC), specialization.getNode().getNodeType(), CREATE_SPECIALIZATION_NAME, parameters);
             CodeTreeBuilder builder = method.createBuilder();
             builder.startReturn();
             builder.startNew(getElement().asType());
@@ -2947,7 +2915,7 @@
 
         protected CodeExecutableElement createConstructorFactoryMethod(SpecializationData specialization, ExecutableElement constructor) {
             List<? extends VariableElement> parameters = constructor.getParameters();
-            CodeExecutableElement method = new CodeExecutableElement(modifiers(STATIC), specialization.getNode().getNodeType(), CREATE_SPECIALIZATION_NAME,
+            CodeExecutableElement method = new CodeExecutableElement(modifiers(STATIC), specialization.getNode().getNodeType(), FACTORY_METHOD_NAME,
                             parameters.toArray(new CodeVariableElement[parameters.size()]));
             CodeTreeBuilder builder = method.createBuilder();
             builder.startReturn();
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeData.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeData.java	Mon Aug 11 15:53:05 2014 +0200
@@ -54,11 +54,9 @@
 
     private final NodeExecutionData thisExecution;
 
-    private int polymorphicDepth = -1;
-
-    public NodeData(TypeElement type, String shortName, TypeSystemData typeSystem, List<NodeChildData> children, List<NodeExecutionData> executions, List<NodeFieldData> fields,
-                    List<String> assumptions, int polymorphicDepth) {
-        super(type, null, null);
+    public NodeData(ProcessorContext context, TypeElement type, String shortName, TypeSystemData typeSystem, List<NodeChildData> children, List<NodeExecutionData> executions,
+                    List<NodeFieldData> fields, List<String> assumptions) {
+        super(context, type, null, null);
         this.nodeId = type.getSimpleName().toString();
         this.shortName = shortName;
         this.typeSystem = typeSystem;
@@ -66,7 +64,6 @@
         this.children = children;
         this.childExecutions = executions;
         this.assumptions = assumptions;
-        this.polymorphicDepth = polymorphicDepth;
         this.thisExecution = new NodeExecutionData(new NodeChildData(null, null, "this", getNodeType(), getNodeType(), null, Cardinality.ONE), -1, false);
         this.thisExecution.getChild().setNode(this);
         if (children != null) {
@@ -76,14 +73,22 @@
         }
     }
 
-    public NodeData(TypeElement type) {
-        this(type, null, null, null, null, null, null, -1);
+    public NodeData(ProcessorContext context, TypeElement type) {
+        this(context, type, null, null, null, null, null, null);
     }
 
     public NodeExecutionData getThisExecution() {
         return thisExecution;
     }
 
+    public boolean isFallbackReachable() {
+        SpecializationData generic = getGenericSpecialization();
+        if (generic != null) {
+            return generic.isReachable();
+        }
+        return false;
+    }
+
     public void addEnclosedNode(NodeData node) {
         this.enclosingNodes.add(node);
         node.declaringNode = this;
@@ -121,16 +126,8 @@
         return false;
     }
 
-    public int getPolymorphicDepth() {
-        return polymorphicDepth;
-    }
-
-    public boolean isPolymorphic() {
-        return polymorphicDepth > 1;
-    }
-
-    public void setPolymorphicDepth(int polymorphicDepth) {
-        this.polymorphicDepth = polymorphicDepth;
+    public boolean isPolymorphic(ProcessorContext context) {
+        return needsRewrites(context);
     }
 
     public List<CreateCastData> getCasts() {
@@ -404,7 +401,6 @@
         dumpProperty(builder, indent, "fields", getChildren());
         dumpProperty(builder, indent, "executableTypes", getExecutableTypes());
         dumpProperty(builder, indent, "specializations", getSpecializations());
-        dumpProperty(builder, indent, "polymorphicDepth", getPolymorphicDepth());
         dumpProperty(builder, indent, "assumptions", getAssumptions());
         dumpProperty(builder, indent, "casts", getCasts());
         dumpProperty(builder, indent, "messages", collectMessages());
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java	Mon Aug 11 15:53:05 2014 +0200
@@ -33,6 +33,7 @@
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.dsl.processor.*;
+import com.oracle.truffle.dsl.processor.compiler.*;
 import com.oracle.truffle.dsl.processor.node.NodeChildData.Cardinality;
 import com.oracle.truffle.dsl.processor.node.SpecializationData.SpecializationKind;
 import com.oracle.truffle.dsl.processor.template.*;
@@ -46,10 +47,6 @@
 
     private Map<String, NodeData> parsedNodes;
 
-    public NodeParser(ProcessorContext c) {
-        super(c);
-    }
-
     @Override
     protected NodeData parse(Element element, AnnotationMirror mirror) {
         NodeData node = null;
@@ -115,7 +112,7 @@
 
         NodeData node = parseNode(rootType);
         if (node == null && !enclosedNodes.isEmpty()) {
-            node = new NodeData(rootType);
+            node = new NodeData(context, rootType);
         }
 
         if (node != null) {
@@ -138,11 +135,10 @@
         }
 
         List<TypeElement> lookupTypes = collectSuperClasses(new ArrayList<TypeElement>(), templateType);
-        if (!Utils.isAssignable(context, templateType.asType(), context.getTruffleTypes().getNode())) {
+        if (!Utils.isAssignable(templateType.asType(), context.getTruffleTypes().getNode())) {
             return null;
         }
-
-        List<? extends Element> elements = context.getEnvironment().getElementUtils().getAllMembers(templateType);
+        List<? extends Element> elements = CompilerFactory.getCompiler(templateType).getAllMembersInDeclarationOrder(context.getEnvironment(), templateType);
 
         NodeData node = parseNodeData(templateType, elements, lookupTypes);
         if (node.hasErrors()) {
@@ -165,7 +161,6 @@
         initializeShortCircuits(node); // requires specializations and polymorphic specializations
 
         verifyVisibilities(node);
-        verifySpecializationOrder(node);
         verifyMissingAbstractMethods(node, elements);
         verifyConstructors(node);
         verifyNamingConvention(node.getShortCircuits(), "needs");
@@ -176,7 +171,7 @@
     private NodeData parseNodeData(TypeElement templateType, List<? extends Element> elements, List<TypeElement> typeHierarchy) {
         AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class);
         if (typeSystemMirror == null) {
-            NodeData nodeData = new NodeData(templateType);
+            NodeData nodeData = new NodeData(context, templateType);
             nodeData.addError("No @%s annotation found in type hierarchy of %s.", TypeSystemReference.class.getSimpleName(), Utils.getQualifiedName(templateType));
             return nodeData;
         }
@@ -184,24 +179,11 @@
         TypeMirror typeSystemType = Utils.getAnnotationValue(TypeMirror.class, typeSystemMirror, "value");
         final TypeSystemData typeSystem = (TypeSystemData) context.getTemplate(typeSystemType, true);
         if (typeSystem == null) {
-            NodeData nodeData = new NodeData(templateType);
+            NodeData nodeData = new NodeData(context, templateType);
             nodeData.addError("The used type system '%s' is invalid or not a Node.", Utils.getQualifiedName(typeSystemType));
             return nodeData;
         }
 
-        AnnotationMirror polymorphicMirror = findFirstAnnotation(typeHierarchy, PolymorphicLimit.class);
-        int polymorphicLimit = -1;
-        if (polymorphicMirror != null) {
-            AnnotationValue limitValue = Utils.getAnnotationValue(polymorphicMirror, "value");
-            int customPolymorphicLimit = Utils.getAnnotationValue(Integer.class, polymorphicMirror, "value");
-            if (customPolymorphicLimit < 1) {
-                NodeData nodeData = new NodeData(templateType);
-                nodeData.addError(limitValue, "Invalid polymorphic limit %s.", polymorphicLimit);
-                return nodeData;
-            }
-            polymorphicLimit = customPolymorphicLimit;
-        }
-
         List<String> assumptionsList = new ArrayList<>();
         for (int i = typeHierarchy.size() - 1; i >= 0; i--) {
             TypeElement type = typeHierarchy.get(i);
@@ -223,10 +205,10 @@
         }
 
         List<NodeFieldData> fields = parseFields(typeHierarchy, elements);
-        List<NodeChildData> children = parseChildren(elements, typeHierarchy);
+        List<NodeChildData> children = parseChildren(typeHierarchy, elements);
         List<NodeExecutionData> executions = parseExecutions(children, elements);
 
-        NodeData nodeData = new NodeData(templateType, shortName, typeSystem, children, executions, fields, assumptionsList, polymorphicLimit);
+        NodeData nodeData = new NodeData(context, templateType, shortName, typeSystem, children, executions, fields, assumptionsList);
         nodeData.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements)));
 
         parsedNodes.put(Utils.getQualifiedName(templateType), nodeData);
@@ -278,7 +260,7 @@
         return fields;
     }
 
-    private List<NodeChildData> parseChildren(List<? extends Element> elements, final List<TypeElement> typeHierarchy) {
+    private List<NodeChildData> parseChildren(final List<TypeElement> typeHierarchy, List<? extends Element> elements) {
         Set<String> shortCircuits = new HashSet<>();
         for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
             AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, method, ShortCircuit.class);
@@ -306,7 +288,7 @@
             AnnotationMirror nodeChildrenMirror = Utils.findAnnotationMirror(processingEnv, type, NodeChildren.class);
 
             TypeMirror nodeClassType = type.getSuperclass();
-            if (!Utils.isAssignable(context, nodeClassType, context.getTruffleTypes().getNode())) {
+            if (!Utils.isAssignable(nodeClassType, context.getTruffleTypes().getNode())) {
                 nodeClassType = null;
             }
 
@@ -530,24 +512,18 @@
             return;
         }
 
-        for (SpecializationData specialization : node.getSpecializations()) {
-            if (!specialization.isSpecialized()) {
-                continue;
-            }
-            initializeGuards(elements, specialization);
-        }
-
+        initializeGuards(elements, node);
         initializeGeneric(node);
         initializeUninitialized(node);
+        initializeOrder(node);
         initializePolymorphism(node); // requires specializations
-        Collections.sort(node.getSpecializations());
         initializeReachability(node);
+        initializeContains(node);
 
-        // reduce polymorphicness if generic is not reachable
-        if (node.getGenericSpecialization() != null && !node.getGenericSpecialization().isReachable()) {
-            node.setPolymorphicDepth(1);
-            node.getSpecializations().remove(node.getPolymorphicSpecialization());
+        if (!node.hasErrors()) {
+            initializeExceptions(node);
         }
+        resolveContains(node);
 
         List<SpecializationData> needsId = new ArrayList<>();
         for (SpecializationData specialization : node.getSpecializations()) {
@@ -572,24 +548,195 @@
 
     }
 
-    private void initializeReachability(final NodeData node) {
-        SpecializationData prev = null;
-        boolean reachable = true;
-        for (SpecializationData specialization : node.getSpecializations()) {
-            if (specialization.isUninitialized() || specialization.isPolymorphic()) {
-                specialization.setReachable(true);
+    private static void initializeOrder(NodeData node) {
+        List<SpecializationData> specializations = node.getSpecializations();
+        Collections.sort(specializations);
+
+        for (SpecializationData specialization : specializations) {
+            String searchName = specialization.getInsertBeforeName();
+            if (searchName == null || specialization.getMethod() == null) {
+                continue;
+            }
+            SpecializationData found = lookupSpecialization(node, searchName);
+            if (found == null || found.getMethod() == null) {
+                AnnotationValue value = Utils.getAnnotationValue(specialization.getMarkerAnnotation(), "insertBefore");
+                specialization.addError(value, "The referenced specialization '%s' could not be found.", searchName);
+                continue;
+            }
+
+            ExecutableElement currentMethod = specialization.getMethod();
+            ExecutableElement insertBeforeMethod = found.getMethod();
+
+            TypeMirror currentEnclosedType = currentMethod.getEnclosingElement().asType();
+            TypeMirror insertBeforeEnclosedType = insertBeforeMethod.getEnclosingElement().asType();
+
+            if (Utils.typeEquals(currentEnclosedType, insertBeforeEnclosedType) || !Utils.isSubtype(currentEnclosedType, insertBeforeEnclosedType)) {
+                AnnotationValue value = Utils.getAnnotationValue(specialization.getMarkerAnnotation(), "insertBefore");
+                specialization.addError(value, "Specializations can only be inserted before specializations in superclasses.", searchName);
+                continue;
+            }
+
+            specialization.setInsertBefore(found);
+        }
+
+        int endIndex = specializations.size() - 1;
+        for (int i = endIndex; i >= 0; i--) {
+            SpecializationData specialization = specializations.get(i);
+            if (specialization.isGeneric() || specialization.isPolymorphic()) {
+                endIndex--;
+                continue;
+            }
+
+            SpecializationData insertBefore = specialization.getInsertBefore();
+            if (insertBefore != null) {
+                int insertIndex = specializations.indexOf(insertBefore);
+                if (insertIndex < i) {
+                    List<SpecializationData> range = new ArrayList<>(specializations.subList(i, endIndex + 1));
+                    specializations.removeAll(range);
+                    specializations.addAll(insertIndex, range);
+                }
+            }
+        }
+
+        for (int i = 0; i < specializations.size(); i++) {
+            specializations.get(i).setIndex(i);
+        }
+    }
+
+    private static void initializeExceptions(NodeData node) {
+        List<SpecializationData> specializations = node.getSpecializations();
+        for (int i = 0; i < specializations.size(); i++) {
+            SpecializationData cur = specializations.get(i);
+            if (cur.getExceptions().isEmpty()) {
+                continue;
+            }
+            SpecializationData next = i + 1 < specializations.size() ? specializations.get(i + 1) : null;
+
+            if (!cur.isContainedBy(next)) {
+                // error should be able to contain
+                next.addError("This specialiation is not a valid exceptional rewrite target for %s. To fix this make %s compatible to %s or remove the exceptional rewrite.",
+                                cur.createReferenceName(), next.createReferenceName(), cur.createReferenceName());
+                continue;
+            }
+            if (!next.getContains().contains(cur)) {
+                next.getContains().add(cur);
+                // TODO resolve transitive contains
+            }
+        }
+
+        for (SpecializationData cur : specializations) {
+            if (cur.getExceptions().isEmpty()) {
                 continue;
             }
-            if (prev != null && prev.equalsGuards(specialization) && prev.getExceptions().isEmpty()) {
-                specialization.addError("%s is not reachable.", specialization.isGeneric() ? "Generic" : "Specialization");
-            } else if (!reachable && specialization.getMethod() != null) {
-                specialization.addError("%s is not reachable.", specialization.isGeneric() ? "Generic" : "Specialization");
+            for (SpecializationData child : specializations) {
+                if (child != null && child != cur && child.getContains().contains(cur)) {
+                    cur.getExcludedBy().add(child);
+                }
+            }
+        }
+    }
+
+    private static void initializeContains(NodeData node) {
+        for (SpecializationData specialization : node.getSpecializations()) {
+            Set<SpecializationData> resolvedSpecializations = specialization.getContains();
+            resolvedSpecializations.clear();
+            Set<String> includeNames = specialization.getContainsNames();
+            for (String includeName : includeNames) {
+                // TODO reduce complexity of this lookup.
+                SpecializationData foundSpecialization = lookupSpecialization(node, includeName);
+
+                if (foundSpecialization == null) {
+                    AnnotationValue value = Utils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains");
+                    specialization.addError(value, "The referenced specialization '%s' could not be found.", includeName);
+                } else {
+                    if (!foundSpecialization.isContainedBy(specialization)) {
+                        AnnotationValue value = Utils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains");
+                        if (foundSpecialization.compareTo(specialization) > 0) {
+                            specialization.addError(value, "The contained specialization '%s' must be declared before the containing specialization.", includeName);
+                        } else {
+                            specialization.addError(value,
+                                            "The contained specialization '%s' is not fully compatible. The contained specialization must be strictly more generic than the containing one.",
+                                            includeName);
+                        }
+
+                    }
+                    resolvedSpecializations.add(foundSpecialization);
+                }
+            }
+        }
+    }
+
+    private void resolveContains(NodeData node) {
+        // flatten transitive includes
+        for (SpecializationData specialization : node.getSpecializations()) {
+            if (specialization.getContains().isEmpty()) {
+                continue;
             }
-            specialization.setReachable(reachable);
-            if (!specialization.hasRewrite(context)) {
-                reachable = false;
+            Set<SpecializationData> foundSpecializations = new HashSet<>();
+            collectIncludes(specialization, foundSpecializations, new HashSet<SpecializationData>());
+            specialization.getContains().addAll(foundSpecializations);
+        }
+    }
+
+    private static SpecializationData lookupSpecialization(NodeData node, String includeName) {
+        SpecializationData foundSpecialization = null;
+        for (SpecializationData searchSpecialization : node.getSpecializations()) {
+            if (searchSpecialization.getMethodName().equals(includeName)) {
+                foundSpecialization = searchSpecialization;
+                break;
             }
-            prev = specialization;
+        }
+        return foundSpecialization;
+    }
+
+    private void collectIncludes(SpecializationData specialization, Set<SpecializationData> found, Set<SpecializationData> visited) {
+        if (visited.contains(specialization)) {
+            // circle found
+            specialization.addError("Circular contained specialization '%s' found.", specialization.createReferenceName());
+            return;
+        }
+        visited.add(specialization);
+
+        for (SpecializationData included : specialization.getContains()) {
+            collectIncludes(included, found, new HashSet<>(visited));
+            found.add(included);
+        }
+    }
+
+    private static void initializeReachability(final NodeData node) {
+        List<SpecializationData> specializations = node.getSpecializations();
+        for (int i = specializations.size() - 1; i >= 0; i--) {
+            SpecializationData current = specializations.get(i);
+            if (current.isPolymorphic()) {
+                current.setReachable(true);
+                continue;
+            }
+
+            List<SpecializationData> shadowedBy = null;
+            for (int j = i - 1; j >= 0; j--) {
+                SpecializationData prev = specializations.get(j);
+                if (prev.isPolymorphic()) {
+                    continue;
+                }
+                if (!current.isReachableAfter(prev)) {
+                    if (shadowedBy == null) {
+                        shadowedBy = new ArrayList<>();
+                    }
+                    shadowedBy.add(prev);
+                }
+            }
+
+            if (shadowedBy != null) {
+                StringBuilder name = new StringBuilder();
+                String sep = "";
+                for (SpecializationData shadowSpecialization : shadowedBy) {
+                    name.append(sep);
+                    name.append(shadowSpecialization.createReferenceName());
+                    sep = ", ";
+                }
+                current.addError("%s is not reachable. It is shadowed by %s.", current.isGeneric() ? "Generic" : "Specialization", name);
+            }
+            current.setReachable(shadowedBy == null);
         }
     }
 
@@ -705,29 +852,59 @@
         return signatures;
     }
 
-    private void initializeGuards(List<? extends Element> elements, SpecializationData specialization) {
-        if (specialization.getGuardDefinitions().isEmpty()) {
-            specialization.setGuards(Collections.<GuardData> emptyList());
-            return;
-        }
-
-        List<GuardData> foundGuards = new ArrayList<>();
-        List<ExecutableElement> methods = ElementFilter.methodsIn(elements);
-        for (String guardDefinition : specialization.getGuardDefinitions()) {
-            GuardParser parser = new GuardParser(context, specialization, guardDefinition);
-            List<GuardData> guards = parser.parse(methods);
-            if (!guards.isEmpty()) {
-                foundGuards.add(guards.get(0));
-            } else {
-                // error no guard found
-                MethodSpec spec = parser.createSpecification(specialization.getMethod(), null);
-                spec.applyTypeDefinitions("types");
-                specialization.addError("Guard with method name '%s' not found. Expected signature: %n%s", guardDefinition, spec.toSignatureString("guard"));
+    private void initializeGuards(List<? extends Element> elements, NodeData node) {
+        Map<String, List<GuardData>> guards = new HashMap<>();
+        for (SpecializationData specialization : node.getSpecializations()) {
+            for (GuardExpression exp : specialization.getGuards()) {
+                guards.put(exp.getGuardName(), null);
             }
         }
 
-        specialization.setGuards(foundGuards);
+        GuardParser parser = new GuardParser(context, node, null, guards.keySet());
+        List<GuardData> resolvedGuards = parser.parse(elements);
+        for (GuardData guard : resolvedGuards) {
+            List<GuardData> groupedGuards = guards.get(guard.getMethodName());
+            if (groupedGuards == null) {
+                groupedGuards = new ArrayList<>();
+                guards.put(guard.getMethodName(), groupedGuards);
+            }
+            groupedGuards.add(guard);
+        }
+
+        for (SpecializationData specialization : node.getSpecializations()) {
+            for (GuardExpression exp : specialization.getGuards()) {
+                resolveGuardExpression(node, specialization, guards, exp);
+            }
+        }
+    }
 
+    private void resolveGuardExpression(NodeData node, TemplateMethod source, Map<String, List<GuardData>> guards, GuardExpression expression) {
+        List<GuardData> availableGuards = guards.get(expression.getGuardName());
+        if (availableGuards == null) {
+            source.addError("No compatible guard with method name '%s' found. Please note that all signature types of the method guard must be declared in the type system.", expression.getGuardName());
+            return;
+        }
+        List<ExecutableElement> guardMethods = new ArrayList<>();
+        for (GuardData guard : availableGuards) {
+            guardMethods.add(guard.getMethod());
+        }
+        GuardParser parser = new GuardParser(context, node, source, new HashSet<>(Arrays.asList(expression.getGuardName())));
+        List<GuardData> matchingGuards = parser.parse(guardMethods);
+        if (!matchingGuards.isEmpty()) {
+            GuardData guard = matchingGuards.get(0);
+            // use the shared instance of the guard data
+            for (GuardData guardData : availableGuards) {
+                if (guardData.getMethod() == guard.getMethod()) {
+                    expression.setGuard(guardData);
+                    return;
+                }
+            }
+            throw new AssertionError("Should not reach here.");
+        } else {
+            MethodSpec spec = parser.createSpecification(source.getMethod(), source.getMarkerAnnotation());
+            spec.applyTypeDefinitions("types");
+            source.addError("No guard with name '%s' matched the required signature. Expected signature: %n%s", expression.getGuardName(), spec.toSignatureString("guard"));
+        }
     }
 
     private void initializeGeneric(final NodeData node) {
@@ -774,7 +951,7 @@
         }
 
         TypeMirror returnType = createGenericType(specification.getReturnType(), node.getSpecializations(), 0);
-        SpecializationData generic = parser.create("Generic", null, null, returnType, parameterTypes);
+        SpecializationData generic = parser.create("Generic", TemplateMethod.NO_NATURAL_ORDER, null, null, returnType, parameterTypes);
         if (generic == null) {
             throw new RuntimeException("Unable to create generic signature for node " + node.getNodeId() + " with " + parameterTypes + ". Specification " + specification + ".");
         }
@@ -831,16 +1008,14 @@
                 generic.replaceParameter(parameter.getLocalName(), new ActualParameter(parameter, node.getTypeSystem().getGenericTypeData()));
             }
         }
-        TemplateMethod uninializedMethod = new TemplateMethod("Uninitialized", node, generic.getSpecification(), null, null, generic.getReturnType(), generic.getParameters());
+        TemplateMethod uninializedMethod = new TemplateMethod("Uninitialized", -1, node, generic.getSpecification(), null, null, generic.getReturnType(), generic.getParameters());
         // should not use messages from generic specialization
         uninializedMethod.getMessages().clear();
         node.getSpecializations().add(new SpecializationData(node, uninializedMethod, SpecializationKind.UNINITIALIZED));
     }
 
     private void initializePolymorphism(NodeData node) {
-        initializePolymorphicDepth(node);
-
-        if (!node.needsRewrites(context) || !node.isPolymorphic()) {
+        if (!node.needsRewrites(context)) {
             return;
         }
 
@@ -879,27 +1054,6 @@
         node.getSpecializations().add(polymorphic);
     }
 
-    private static void initializePolymorphicDepth(final NodeData node) {
-        int polymorphicCombinations = 0;
-        for (SpecializationData specialization : node.getSpecializations()) {
-            if (specialization.isGeneric()) {
-                continue;
-            }
-
-            int combinations = 1;
-            for (ActualParameter parameter : specialization.getSignatureParameters()) {
-                combinations *= node.getTypeSystem().lookupSourceTypes(parameter.getTypeSystemType()).size();
-            }
-            polymorphicCombinations += combinations;
-        }
-
-        // initialize polymorphic depth
-        if (node.getPolymorphicDepth() < 0) {
-            node.setPolymorphicDepth(polymorphicCombinations - 1);
-        }
-
-    }
-
     private void initializeShortCircuits(NodeData node) {
         Map<String, List<ShortCircuitData>> groupedShortCircuits = groupShortCircuits(node.getShortCircuits());
 
@@ -1046,34 +1200,6 @@
         }
     }
 
-    private static void verifySpecializationOrder(NodeData node) {
-        List<SpecializationData> specializations = node.getSpecializations();
-        for (int i = 0; i < specializations.size(); i++) {
-            SpecializationData m1 = specializations.get(i);
-            for (int j = i + 1; j < specializations.size(); j++) {
-                SpecializationData m2 = specializations.get(j);
-                int inferredOrder = m1.compareBySignature(m2);
-
-                if (m1.getOrder() != Specialization.DEFAULT_ORDER && m2.getOrder() != Specialization.DEFAULT_ORDER) {
-                    int specOrder = m1.getOrder() - m2.getOrder();
-                    if (specOrder == 0) {
-                        m1.addError("Order value %d used multiple times", m1.getOrder());
-                        m2.addError("Order value %d used multiple times", m1.getOrder());
-                        return;
-                    } else if ((specOrder < 0 && inferredOrder > 0) || (specOrder > 0 && inferredOrder < 0)) {
-                        m1.addError("Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
-                        m2.addError("Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
-                        return;
-                    }
-                } else if (inferredOrder == 0) {
-                    SpecializationData m = (m1.getOrder() == Specialization.DEFAULT_ORDER ? m1 : m2);
-                    m.addError("Cannot calculate a consistent order for this specialization. Define the order attribute to resolve this.");
-                    return;
-                }
-            }
-        }
-    }
-
     private static void verifyMissingAbstractMethods(NodeData nodeData, List<? extends Element> originalElements) {
         if (!nodeData.needsFactory()) {
             // missing abstract methods only needs to be implemented
@@ -1205,7 +1331,7 @@
         }
 
         for (ExecutableElement method : ElementFilter.methodsIn(elements)) {
-            if (method.getSimpleName().toString().equals(methodName) && method.getParameters().size() == 0 && Utils.isAssignable(context, type, method.getReturnType())) {
+            if (method.getSimpleName().toString().equals(methodName) && method.getParameters().size() == 0 && Utils.isAssignable(type, method.getReturnType())) {
                 return method;
             }
         }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationData.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationData.java	Mon Aug 11 15:53:05 2014 +0200
@@ -24,14 +24,11 @@
 
 import java.util.*;
 
-import javax.lang.model.type.*;
-
-import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.template.*;
 import com.oracle.truffle.dsl.processor.typesystem.*;
 
-public class SpecializationData extends TemplateMethod {
+public final class SpecializationData extends TemplateMethod {
 
     public enum SpecializationKind {
         UNINITIALIZED,
@@ -41,29 +38,61 @@
     }
 
     private final NodeData node;
-    private final int order;
     private final SpecializationKind kind;
     private final List<SpecializationThrowsData> exceptions;
-    private List<String> guardDefinitions = Collections.emptyList();
-    private List<GuardData> guards = Collections.emptyList();
+    private List<GuardExpression> guards = Collections.emptyList();
     private List<ShortCircuitData> shortCircuits;
     private List<String> assumptions = Collections.emptyList();
+    private final Set<SpecializationData> contains = new TreeSet<>();
+    private final Set<String> containsNames = new TreeSet<>();
+    private final Set<SpecializationData> excludedBy = new TreeSet<>();
+    private String insertBeforeName;
+    private SpecializationData insertBefore;
     private boolean reachable;
+    private int index;
 
-    public SpecializationData(NodeData node, TemplateMethod template, SpecializationKind kind, int order, List<SpecializationThrowsData> exceptions) {
+    public SpecializationData(NodeData node, TemplateMethod template, SpecializationKind kind, List<SpecializationThrowsData> exceptions) {
         super(template);
         this.node = node;
-        this.order = order;
         this.kind = kind;
         this.exceptions = exceptions;
+        this.index = template.getNaturalOrder();
 
         for (SpecializationThrowsData exception : exceptions) {
             exception.setSpecialization(this);
         }
     }
 
+    public void setInsertBefore(SpecializationData insertBefore) {
+        this.insertBefore = insertBefore;
+    }
+
+    public void setInsertBeforeName(String insertBeforeName) {
+        this.insertBeforeName = insertBeforeName;
+    }
+
+    public SpecializationData getInsertBefore() {
+        return insertBefore;
+    }
+
+    public String getInsertBeforeName() {
+        return insertBeforeName;
+    }
+
+    public Set<String> getContainsNames() {
+        return containsNames;
+    }
+
     public SpecializationData(NodeData node, TemplateMethod template, SpecializationKind kind) {
-        this(node, template, kind, Specialization.DEFAULT_ORDER, new ArrayList<SpecializationThrowsData>());
+        this(node, template, kind, new ArrayList<SpecializationThrowsData>());
+    }
+
+    public Set<SpecializationData> getContains() {
+        return contains;
+    }
+
+    public Set<SpecializationData> getExcludedBy() {
+        return excludedBy;
     }
 
     public void setReachable(boolean reachable) {
@@ -85,28 +114,15 @@
             sinks.addAll(exceptions);
         }
         if (guards != null) {
-            sinks.addAll(guards);
+            for (GuardExpression guard : guards) {
+                if (guard.isResolved()) {
+                    sinks.add(guard.getResolvedGuard());
+                }
+            }
         }
         return sinks;
     }
 
-    public boolean isGenericSpecialization(ProcessorContext context) {
-        if (isGeneric()) {
-            return true;
-        }
-        if (hasRewrite(context)) {
-            return false;
-        }
-
-        for (ActualParameter parameter : getSignatureParameters()) {
-            ActualParameter genericParameter = getNode().getGenericSpecialization().findParameter(parameter.getLocalName());
-            if (!parameter.getTypeSystemType().equals(genericParameter.getTypeSystemType())) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     public boolean hasRewrite(ProcessorContext context) {
         if (!getExceptions().isEmpty()) {
             return true;
@@ -122,7 +138,7 @@
             if (type.hasUnexpectedValue(context)) {
                 return true;
             }
-            if (type.getReturnType().getTypeSystemType().needsCastTo(context, parameter.getTypeSystemType())) {
+            if (type.getReturnType().getTypeSystemType().needsCastTo(parameter.getTypeSystemType())) {
                 return true;
             }
 
@@ -131,46 +147,246 @@
     }
 
     @Override
-    public int compareBySignature(TemplateMethod other) {
+    public int compareTo(TemplateMethod other) {
         if (this == other) {
             return 0;
         } else if (!(other instanceof SpecializationData)) {
-            return super.compareBySignature(other);
+            return super.compareTo(other);
         }
-
         SpecializationData m2 = (SpecializationData) other;
-
         int kindOrder = kind.compareTo(m2.kind);
         if (kindOrder != 0) {
             return kindOrder;
         }
-        if (getOrder() != Specialization.DEFAULT_ORDER && m2.getOrder() != Specialization.DEFAULT_ORDER) {
-            return getOrder() - m2.getOrder();
+
+        int compare = 0;
+        int order1 = index;
+        int order2 = m2.index;
+        if (order1 != NO_NATURAL_ORDER && order2 != NO_NATURAL_ORDER) {
+            compare = Integer.compare(order1, order2);
+            if (compare != 0) {
+                return compare;
+            }
+        }
+
+        return super.compareTo(other);
+    }
+
+    public void setIndex(int order) {
+        this.index = order;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public int compareByConcreteness(SpecializationData m2) {
+        int kindOrder = kind.compareTo(m2.kind);
+        if (kindOrder != 0) {
+            return kindOrder;
         }
 
         if (getTemplate() != m2.getTemplate()) {
             throw new UnsupportedOperationException("Cannot compare two specializations with different templates.");
         }
+        boolean intersects = intersects(m2);
+        int result = 0;
+        if (intersects) {
+            if (this.contains(m2)) {
+                return 1;
+            } else if (m2.contains(this)) {
+                return -1;
+            }
+        }
 
-        return super.compareBySignature(m2);
+        result = compareBySignature(m2);
+        if (result != 0) {
+            return result;
+        }
+
+        result = compareGuards(getGuards(), m2.getGuards());
+        if (result != 0) {
+            return result;
+        }
+
+        result = compareAssumptions(getAssumptions(), m2.getAssumptions());
+        if (result != 0) {
+            return result;
+        }
+
+        result = compareParameter(node.getTypeSystem(), getReturnType().getType(), m2.getReturnType().getType());
+        if (result != 0) {
+            return result;
+        }
+
+        result = m2.getExceptions().size() - getExceptions().size();
+        if (result != 0) {
+            return result;
+        }
+
+        return result;
+    }
+
+    public boolean contains(SpecializationData other) {
+        return getContains().contains(other);
+    }
+
+    private int compareAssumptions(List<String> assumptions1, List<String> assumptions2) {
+        Iterator<String> iterator1 = assumptions1.iterator();
+        Iterator<String> iterator2 = assumptions2.iterator();
+        while (iterator1.hasNext() && iterator2.hasNext()) {
+            String a1 = iterator1.next();
+            String a2 = iterator2.next();
+
+            int index1 = getNode().getAssumptions().indexOf(a1);
+            int index2 = getNode().getAssumptions().indexOf(a2);
+            int result = index1 - index2;
+            if (result != 0) {
+                return result;
+            }
+        }
+        if (iterator1.hasNext()) {
+            return -1;
+        } else if (iterator2.hasNext()) {
+            return 1;
+        }
+        return 0;
+    }
+
+    public boolean isContainedBy(SpecializationData next) {
+        if (compareTo(next) > 0) {
+            // must be declared after the current specialization
+            return false;
+        }
+
+        Iterator<ActualParameter> currentSignature = getSignatureParameters().iterator();
+        Iterator<ActualParameter> nextSignature = next.getSignatureParameters().iterator();
+
+        while (currentSignature.hasNext() && nextSignature.hasNext()) {
+            TypeData currentType = currentSignature.next().getTypeSystemType();
+            TypeData prevType = nextSignature.next().getTypeSystemType();
+
+            if (!currentType.isImplicitSubtypeOf(prevType)) {
+                return false;
+            }
+        }
+
+        for (String nextAssumption : next.getAssumptions()) {
+            if (!getAssumptions().contains(nextAssumption)) {
+                return false;
+            }
+        }
+
+        Iterator<GuardExpression> nextGuards = next.getGuards().iterator();
+        while (nextGuards.hasNext()) {
+            GuardExpression nextGuard = nextGuards.next();
+            boolean implied = false;
+            for (GuardExpression currentGuard : getGuards()) {
+                if (currentGuard.implies(nextGuard)) {
+                    implied = true;
+                    break;
+                }
+            }
+            if (!implied) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public boolean intersects(SpecializationData other) {
+        return intersectsTypeGuards(other) || intersectsMethodGuards(other);
+    }
+
+    private boolean intersectsTypeGuards(SpecializationData other) {
+        final TypeSystemData typeSystem = getTemplate().getTypeSystem();
+        if (typeSystem != other.getTemplate().getTypeSystem()) {
+            throw new IllegalStateException("Cannot compare two methods with different type systems.");
+        }
+
+        Iterator<ActualParameter> signature1 = getSignatureParameters().iterator();
+        Iterator<ActualParameter> signature2 = other.getSignatureParameters().iterator();
+        while (signature1.hasNext() && signature2.hasNext()) {
+            TypeData parameter1 = signature1.next().getTypeSystemType();
+            TypeData parameter2 = signature2.next().getTypeSystemType();
+            if (parameter1 == null || parameter2 == null) {
+                continue;
+            }
+            if (!parameter1.intersects(parameter2)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean intersectsMethodGuards(SpecializationData other) {
+        for (GuardExpression guard1 : getGuards()) {
+            for (GuardExpression guard2 : other.getGuards()) {
+                if (guard1.impliesNot(guard2) || guard2.impliesNot(guard1)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private static int compareGuards(List<GuardExpression> guards1, List<GuardExpression> guards2) {
+        Iterator<GuardExpression> signature1 = guards1.iterator();
+        Iterator<GuardExpression> signature2 = guards2.iterator();
+        boolean allSame = true;
+        while (signature1.hasNext() && signature2.hasNext()) {
+            GuardExpression guard1 = signature1.next();
+            GuardExpression guard2 = signature2.next();
+            boolean g1impliesg2 = guard1.implies(guard2);
+            boolean g2impliesg1 = guard2.implies(guard1);
+            if (g1impliesg2 && g2impliesg1) {
+                continue;
+            } else if (g1impliesg2) {
+                return -1;
+            } else if (g2impliesg1) {
+                return 1;
+            } else {
+                allSame = false;
+            }
+        }
+
+        if (allSame) {
+            if (signature1.hasNext()) {
+                return -1;
+            } else if (signature2.hasNext()) {
+                return 1;
+            }
+        }
+
+        return 0;
+    }
+
+    public String createReferenceName() {
+        StringBuilder b = new StringBuilder();
+
+        b.append(getMethodName());
+        b.append("(");
+
+        String sep = "";
+        for (ActualParameter parameter : getParameters()) {
+            b.append(sep);
+            b.append(Utils.getSimpleName(parameter.getType()));
+            sep = ", ";
+        }
+
+        b.append(")");
+        return b.toString();
     }
 
     public NodeData getNode() {
         return node;
     }
 
-    public void setGuards(List<GuardData> guards) {
+    public void setGuards(List<GuardExpression> guards) {
         this.guards = guards;
     }
 
-    public void setGuardDefinitions(List<String> guardDefinitions) {
-        this.guardDefinitions = guardDefinitions;
-    }
-
-    public int getOrder() {
-        return order;
-    }
-
     public boolean isSpecialized() {
         return kind == SpecializationKind.SPECIALIZED;
     }
@@ -187,11 +403,7 @@
         return exceptions;
     }
 
-    public List<String> getGuardDefinitions() {
-        return guardDefinitions;
-    }
-
-    public List<GuardData> getGuards() {
+    public List<GuardExpression> getGuards() {
         return guards;
     }
 
@@ -211,16 +423,6 @@
         this.assumptions = assumptions;
     }
 
-    public SpecializationData findPreviousSpecialization() {
-        List<SpecializationData> specializations = node.getSpecializations();
-        for (int i = 0; i < specializations.size() - 1; i++) {
-            if (specializations.get(i) == this && i > 0) {
-                return specializations.get(i - 1);
-            }
-        }
-        return null;
-    }
-
     public SpecializationData findNextSpecialization() {
         List<SpecializationData> specializations = node.getSpecializations();
         for (int i = 0; i < specializations.size() - 1; i++) {
@@ -231,24 +433,11 @@
         return null;
     }
 
-    public boolean hasDynamicGuards() {
-        return !getGuards().isEmpty();
-    }
-
     @Override
     public String toString() {
         return String.format("%s [id = %s, method = %s, guards = %s, signature = %s]", getClass().getSimpleName(), getId(), getMethod(), getGuards(), getTypeSignature());
     }
 
-    public void forceFrame(TypeMirror frameType) {
-        if (getParameters().isEmpty() || !Utils.typeEquals(getParameters().get(0).getType(), frameType)) {
-            ParameterSpec frameSpec = getSpecification().findParameterSpec("frame");
-            if (frameSpec != null) {
-                getParameters().add(0, new ActualParameter(frameSpec, frameType, -1, -1));
-            }
-        }
-    }
-
     public boolean equalsGuards(SpecializationData specialization) {
         if (assumptions.equals(specialization.getAssumptions()) && guards.equals(specialization.getGuards()) && getTypeSignature().equalsParameters(specialization.getTypeSignature())) {
             return true;
@@ -265,4 +454,43 @@
         return false;
     }
 
+    public boolean isReachableAfter(SpecializationData prev) {
+        if (!prev.isSpecialized()) {
+            return true;
+        }
+
+        if (!prev.getExceptions().isEmpty()) {
+            return true;
+        }
+
+        Iterator<ActualParameter> currentSignature = getSignatureParameters().iterator();
+        Iterator<ActualParameter> prevSignature = prev.getSignatureParameters().iterator();
+
+        while (currentSignature.hasNext() && prevSignature.hasNext()) {
+            TypeData currentType = currentSignature.next().getTypeSystemType();
+            TypeData prevType = prevSignature.next().getTypeSystemType();
+
+            if (!currentType.isImplicitSubtypeOf(prevType)) {
+                return true;
+            }
+        }
+
+        for (String prevAssumption : prev.getAssumptions()) {
+            if (!getAssumptions().contains(prevAssumption)) {
+                return true;
+            }
+        }
+
+        Iterator<GuardExpression> prevGuards = prev.getGuards().iterator();
+        Iterator<GuardExpression> currentGuards = getGuards().iterator();
+        while (prevGuards.hasNext()) {
+            GuardExpression prevGuard = prevGuards.next();
+            GuardExpression currentGuard = currentGuards.hasNext() ? currentGuards.next() : null;
+            if (currentGuard == null || !currentGuard.implies(prevGuard)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationGroup.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationGroup.java	Mon Aug 11 15:53:05 2014 +0200
@@ -39,7 +39,7 @@
 
     private final List<String> assumptions;
     private final List<TypeGuard> typeGuards;
-    private final List<GuardData> guards;
+    private final List<GuardExpression> guards;
 
     private final NodeData node;
     private final SpecializationData specialization;
@@ -62,7 +62,7 @@
         this.guards.addAll(data.getGuards());
     }
 
-    public SpecializationGroup(List<SpecializationGroup> children, List<String> assumptionMatches, List<TypeGuard> typeGuardsMatches, List<GuardData> guardMatches) {
+    public SpecializationGroup(List<SpecializationGroup> children, List<String> assumptionMatches, List<TypeGuard> typeGuardsMatches, List<GuardExpression> guardMatches) {
         assert !children.isEmpty() : "children must not be empty";
         this.assumptions = assumptionMatches;
         this.typeGuards = typeGuardsMatches;
@@ -90,14 +90,7 @@
         return null;
     }
 
-    public List<GuardData> findElseConnectableGuards(boolean minimumStateCheck) {
-        if (minimumStateCheck) {
-            /*
-             * TODO investigate further if we really cannot else connect guards if minimum state is
-             * required
-             */
-            return Collections.emptyList();
-        }
+    public List<GuardExpression> findElseConnectableGuards() {
         if (!getTypeGuards().isEmpty() || !getAssumptions().isEmpty()) {
             return Collections.emptyList();
         }
@@ -106,9 +99,9 @@
             return Collections.emptyList();
         }
 
-        List<GuardData> elseConnectableGuards = new ArrayList<>();
+        List<GuardExpression> elseConnectableGuards = new ArrayList<>();
         int guardIndex = 0;
-        while (guardIndex < getGuards().size() && findNegatedGuardInPrevious(getGuards().get(guardIndex), minimumStateCheck) != null) {
+        while (guardIndex < getGuards().size() && findNegatedGuardInPrevious(getGuards().get(guardIndex)) != null) {
             elseConnectableGuards.add(getGuards().get(guardIndex));
             guardIndex++;
         }
@@ -116,12 +109,12 @@
         return elseConnectableGuards;
     }
 
-    private GuardData findNegatedGuardInPrevious(GuardData guard, boolean minimumStateCheck) {
+    private GuardExpression findNegatedGuardInPrevious(GuardExpression guard) {
         SpecializationGroup previous = this.getPreviousGroup();
         if (previous == null) {
             return null;
         }
-        List<GuardData> elseConnectedGuards = previous.findElseConnectableGuards(minimumStateCheck);
+        List<GuardExpression> elseConnectedGuards = previous.findElseConnectableGuards();
 
         if (previous == null || previous.getGuards().size() != elseConnectedGuards.size() + 1) {
             return null;
@@ -132,8 +125,8 @@
             return guard;
         }
 
-        GuardData previousGuard = previous.getGuards().get(elseConnectedGuards.size());
-        if (guard.getMethod().equals(previousGuard.getMethod()) && guard.isNegated() != previousGuard.isNegated()) {
+        GuardExpression previousGuard = previous.getGuards().get(elseConnectedGuards.size());
+        if (guard.getResolvedGuard().getMethod().equals(previousGuard.getResolvedGuard().getMethod()) && guard.isNegated() != previousGuard.isNegated()) {
             return guard;
         }
         return null;
@@ -161,7 +154,7 @@
         return typeGuards;
     }
 
-    public List<GuardData> getGuards() {
+    public List<GuardExpression> getGuards() {
         return guards;
     }
 
@@ -183,7 +176,7 @@
 
         List<String> assumptionMatches = new ArrayList<>();
         List<TypeGuard> typeGuardsMatches = new ArrayList<>();
-        List<GuardData> guardMatches = new ArrayList<>();
+        List<GuardExpression> guardMatches = new ArrayList<>();
 
         SpecializationGroup first = groups.get(0);
         List<SpecializationGroup> others = groups.subList(1, groups.size());
@@ -208,7 +201,7 @@
             typeGuardsMatches.add(typeGuard);
         }
 
-        outer: for (GuardData guard : first.guards) {
+        outer: for (GuardExpression guard : first.guards) {
             for (SpecializationGroup other : others) {
                 if (!other.guards.contains(guard)) {
                     // we must break here. One guard may depend on the other.
@@ -219,11 +212,11 @@
         }
 
         // check for guards for required type casts
-        for (Iterator<GuardData> iterator = guardMatches.iterator(); iterator.hasNext();) {
-            GuardData guardMatch = iterator.next();
+        for (Iterator<GuardExpression> iterator = guardMatches.iterator(); iterator.hasNext();) {
+            GuardExpression guardMatch = iterator.next();
 
             int signatureIndex = 0;
-            for (ActualParameter parameter : guardMatch.getParameters()) {
+            for (ActualParameter parameter : guardMatch.getResolvedGuard().getParameters()) {
                 signatureIndex++;
                 if (!parameter.getSpecification().isSignature()) {
                     continue;
@@ -297,7 +290,7 @@
         for (SpecializationData specialization : specializations) {
             groups.add(new SpecializationGroup(specialization));
         }
-        return new SpecializationGroup(createCombinationalGroups(groups), Collections.<String> emptyList(), Collections.<TypeGuard> emptyList(), Collections.<GuardData> emptyList());
+        return new SpecializationGroup(createCombinationalGroups(groups), Collections.<String> emptyList(), Collections.<TypeGuard> emptyList(), Collections.<GuardExpression> emptyList());
     }
 
     @Override
@@ -429,4 +422,27 @@
             return type;
         }
     }
+
+    public boolean isTypeGuardUsedInAnyGuardBelow(ProcessorContext context, SpecializationData source, TypeGuard typeGuard) {
+
+        for (GuardExpression guard : guards) {
+            ActualParameter guardParameter = guard.getResolvedGuard().getSignatureParameter(typeGuard.getSignatureIndex());
+            if (guardParameter == null) {
+                // guardParameters are optional
+                continue;
+            }
+            ActualParameter sourceParameter = source.getSignatureParameter(typeGuard.getSignatureIndex());
+            if (sourceParameter.getTypeSystemType().needsCastTo(guardParameter.getType())) {
+                return true;
+            }
+        }
+
+        for (SpecializationGroup group : getChildren()) {
+            if (group.isTypeGuardUsedInAnyGuardBelow(context, source, typeGuard)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationMethodParser.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/SpecializationMethodParser.java	Mon Aug 11 15:53:05 2014 +0200
@@ -32,6 +32,7 @@
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.node.SpecializationData.SpecializationKind;
 import com.oracle.truffle.dsl.processor.template.*;
+import com.oracle.truffle.dsl.processor.typesystem.*;
 
 public class SpecializationMethodParser extends NodeMethodParser<SpecializationData> {
 
@@ -55,11 +56,6 @@
     }
 
     private SpecializationData parseSpecialization(TemplateMethod method) {
-        int order = Utils.getAnnotationValue(Integer.class, method.getMarkerAnnotation(), "order");
-        if (order < 0 && order != Specialization.DEFAULT_ORDER) {
-            method.addError("Invalid order attribute %d. The value must be >= 0 or the default value.");
-        }
-
         AnnotationValue rewriteValue = Utils.getAnnotationValue(method.getMarkerAnnotation(), "rewriteOn");
         List<TypeMirror> exceptionTypes = Utils.getAnnotationValueList(TypeMirror.class, method.getMarkerAnnotation(), "rewriteOn");
         List<SpecializationThrowsData> exceptionData = new ArrayList<>();
@@ -78,9 +74,34 @@
                 return Utils.compareByTypeHierarchy(o1.getJavaClass(), o2.getJavaClass());
             }
         });
-        SpecializationData specialization = new SpecializationData(getNode(), method, SpecializationKind.SPECIALIZED, order, exceptionData);
+        SpecializationData specialization = new SpecializationData(getNode(), method, SpecializationKind.SPECIALIZED, exceptionData);
+
+        String insertBeforeName = Utils.getAnnotationValue(String.class, method.getMarkerAnnotation(), "insertBefore");
+        if (!insertBeforeName.equals("")) {
+            specialization.setInsertBeforeName(insertBeforeName);
+        }
+
         List<String> guardDefs = Utils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "guards");
-        specialization.setGuardDefinitions(guardDefs);
+        List<GuardExpression> guardExpressions = new ArrayList<>();
+        for (String guardDef : guardDefs) {
+            guardExpressions.add(new GuardExpression(guardDef));
+        }
+        specialization.setGuards(guardExpressions);
+
+        List<String> containsDefs = Utils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "contains");
+        Set<String> containsNames = specialization.getContainsNames();
+        containsNames.clear();
+        if (containsDefs != null) {
+            for (String include : containsDefs) {
+                if (!containsNames.contains(include)) {
+                    specialization.getContainsNames().add(include);
+                } else {
+                    AnnotationValue value = Utils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains");
+                    specialization.addError(value, "Duplicate contains declaration '%s'.", include);
+                }
+            }
+
+        }
 
         List<String> assumptionDefs = Utils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "assumptions");
         specialization.setAssumptions(assumptionDefs);
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/ClassElementFactory.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/ClassElementFactory.java	Mon Aug 11 15:53:05 2014 +0200
@@ -32,15 +32,10 @@
 import javax.lang.model.util.*;
 
 import com.oracle.truffle.api.dsl.*;
-import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.ast.*;
 
 public abstract class ClassElementFactory<M> extends CodeElementFactory<M> {
 
-    public ClassElementFactory(ProcessorContext context) {
-        super(context);
-    }
-
     @Override
     protected abstract CodeTypeElement create(M m);
 
@@ -75,7 +70,7 @@
             builder.string("this.");
             builder.string(fieldName);
             builder.string(" = ");
-            if (isAssignable(getContext(), field.asType(), getContext().getTruffleTypes().getNode())) {
+            if (isAssignable(field.asType(), getContext().getTruffleTypes().getNode())) {
                 builder.string("adoptChild(").string(fieldName).string(")");
             } else {
                 builder.string(fieldName);
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/CodeElementFactory.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/CodeElementFactory.java	Mon Aug 11 15:53:05 2014 +0200
@@ -34,8 +34,8 @@
 
     private CodeElement<? super Element> element;
 
-    public CodeElementFactory(ProcessorContext context) {
-        this.context = context;
+    public CodeElementFactory() {
+        this.context = ProcessorContext.getInstance();
     }
 
     protected abstract CodeElement<?> create(M m);
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/CompilationUnitFactory.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/CompilationUnitFactory.java	Mon Aug 11 15:53:05 2014 +0200
@@ -22,15 +22,10 @@
  */
 package com.oracle.truffle.dsl.processor.template;
 
-import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.ast.*;
 
 public abstract class CompilationUnitFactory<M> extends CodeElementFactory<M> {
 
-    public CompilationUnitFactory(ProcessorContext context) {
-        super(context);
-    }
-
     @Override
     public final CodeCompilationUnit create(M m) {
         return new CodeCompilationUnit();
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/MessageContainer.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/MessageContainer.java	Mon Aug 11 15:53:05 2014 +0200
@@ -29,7 +29,7 @@
 
 import com.oracle.truffle.dsl.processor.*;
 
-public abstract class MessageContainer {
+public abstract class MessageContainer implements Iterable<MessageContainer> {
 
     private final List<Message> messages = new ArrayList<>();
 
@@ -37,6 +37,10 @@
         getMessages().add(new Message(null, this, String.format(text, params), Kind.WARNING));
     }
 
+    public final void addWarning(AnnotationValue value, String text, Object... params) {
+        getMessages().add(new Message(value, this, String.format(text, params), Kind.WARNING));
+    }
+
     public final void addError(String text, Object... params) {
         addError(null, text, params);
     }
@@ -51,11 +55,19 @@
 
     public abstract Element getMessageElement();
 
-    public final void emitMessages(ProcessorContext context, TypeElement baseElement, Log log) {
-        emitMessagesImpl(context, baseElement, log, new HashSet<MessageContainer>(), null);
+    public MessageContainer getBaseContainer() {
+        return null;
     }
 
-    private void emitMessagesImpl(ProcessorContext context, TypeElement baseElement, Log log, Set<MessageContainer> visitedSinks, List<Message> verifiedMessages) {
+    public Iterator<MessageContainer> iterator() {
+        return findChildContainers().iterator();
+    }
+
+    public final void emitMessages(ProcessorContext context, Log log) {
+        emitMessagesImpl(context, log, new HashSet<MessageContainer>(), null);
+    }
+
+    private void emitMessagesImpl(ProcessorContext context, Log log, Set<MessageContainer> visitedSinks, List<Message> verifiedMessages) {
         List<Message> childMessages;
         if (verifiedMessages == null) {
             childMessages = collectMessagesWithElementChildren(new HashSet<MessageContainer>(), getMessageElement());
@@ -65,7 +77,7 @@
         verifyExpectedMessages(context, log, childMessages);
 
         for (int i = getMessages().size() - 1; i >= 0; i--) {
-            emitDefault(context, baseElement, log, getMessages().get(i));
+            emitDefault(context, log, getMessages().get(i));
         }
 
         for (MessageContainer sink : findChildContainers()) {
@@ -75,9 +87,9 @@
 
             visitedSinks.add(sink);
             if (sink.getMessageElement() == this.getMessageElement()) {
-                sink.emitMessagesImpl(context, baseElement, log, visitedSinks, childMessages);
+                sink.emitMessagesImpl(context, log, visitedSinks, childMessages);
             } else {
-                sink.emitMessagesImpl(context, baseElement, log, visitedSinks, null);
+                sink.emitMessagesImpl(context, log, visitedSinks, null);
             }
         }
     }
@@ -115,7 +127,7 @@
         }
     }
 
-    private void emitDefault(ProcessorContext context, TypeElement baseType, Log log, Message message) {
+    private void emitDefault(ProcessorContext context, Log log, Message message) {
         Kind kind = message.getKind();
 
         Element messageElement = getMessageElement();
@@ -127,17 +139,6 @@
 
         String text = message.getText();
 
-        TypeElement rootEnclosing = Utils.findRootEnclosingType(getMessageElement());
-        TypeElement baseEnclosing = Utils.findRootEnclosingType(baseType);
-        if (rootEnclosing == null || !Utils.typeEquals(baseEnclosing.asType(), rootEnclosing.asType())) {
-            // redirect message
-            MessageContainer original = message.getOriginalContainer();
-            messageElement = baseType;
-            messageAnnotation = null;
-            messageValue = null;
-            text = wrapText(original.getMessageElement(), original.getMessageAnnotation(), message.getText());
-        }
-
         TypeElement expectError = context.getTruffleTypes().getExpectError();
         if (expectError != null) {
             AnnotationMirror mirror = Utils.findAnnotationMirror(messageElement.getAnnotationMirrors(), expectError);
@@ -165,23 +166,6 @@
         log.message(kind, messageElement, messageAnnotation, messageValue, text);
     }
 
-    private static String wrapText(Element element, AnnotationMirror mirror, String text) {
-        StringBuilder b = new StringBuilder();
-        if (element != null) {
-            b.append("Element " + element.toString());
-        }
-        if (mirror != null) {
-            b.append(" at annotation @" + Utils.getSimpleName(mirror.getAnnotationType()));
-        }
-
-        if (b.length() > 0) {
-            b.append(" is erroneous: ").append(text);
-            return b.toString();
-        } else {
-            return text;
-        }
-    }
-
     public AnnotationMirror getMessageAnnotation() {
         return null;
     }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/Template.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/Template.java	Mon Aug 11 15:53:05 2014 +0200
@@ -31,16 +31,27 @@
 
 public abstract class Template extends MessageContainer {
 
+    private final ProcessorContext context;
     private final TypeElement templateType;
     private final String templateMethodName;
     private final AnnotationMirror annotation;
 
-    public Template(TypeElement templateType, String templateMethodName, AnnotationMirror annotation) {
+    public Template(ProcessorContext context, TypeElement templateType, String templateMethodName, AnnotationMirror annotation) {
+        this.context = context;
         this.templateType = templateType;
         this.templateMethodName = templateMethodName;
         this.annotation = annotation;
     }
 
+    public ProcessorContext getContext() {
+        return context;
+    }
+
+    @Override
+    public MessageContainer getBaseContainer() {
+        return this;
+    }
+
     public abstract TypeSystemData getTypeSystem();
 
     @Override
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethod.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethod.java	Mon Aug 11 15:53:05 2014 +0200
@@ -36,18 +36,22 @@
  */
 public class TemplateMethod extends MessageContainer implements Comparable<TemplateMethod> {
 
+    public static final int NO_NATURAL_ORDER = -1;
+
     private String id;
     private final Template template;
+    private final int naturalOrder;
     private final MethodSpec specification;
     private final ExecutableElement method;
     private final AnnotationMirror markerAnnotation;
     private ActualParameter returnType;
     private List<ActualParameter> parameters;
 
-    public TemplateMethod(String id, Template template, MethodSpec specification, ExecutableElement method, AnnotationMirror markerAnnotation, ActualParameter returnType,
+    public TemplateMethod(String id, int naturalOrder, Template template, MethodSpec specification, ExecutableElement method, AnnotationMirror markerAnnotation, ActualParameter returnType,
                     List<ActualParameter> parameters) {
         this.template = template;
         this.specification = specification;
+        this.naturalOrder = naturalOrder;
         this.method = method;
         this.markerAnnotation = markerAnnotation;
         this.returnType = returnType;
@@ -60,13 +64,17 @@
         this.id = id;
     }
 
+    public int getNaturalOrder() {
+        return naturalOrder;
+    }
+
     public TemplateMethod(TemplateMethod method) {
-        this(method.id, method.template, method.specification, method.method, method.markerAnnotation, method.returnType, method.parameters);
+        this(method.id, method.naturalOrder, method.template, method.specification, method.method, method.markerAnnotation, method.returnType, method.parameters);
         getMessages().addAll(method.getMessages());
     }
 
     public TemplateMethod(TemplateMethod method, ExecutableElement executable) {
-        this(method.id, method.template, method.specification, executable, method.markerAnnotation, method.returnType, method.parameters);
+        this(method.id, method.naturalOrder, method.template, method.specification, executable, method.markerAnnotation, method.returnType, method.parameters);
         getMessages().addAll(method.getMessages());
     }
 
@@ -174,9 +182,9 @@
         return Collections.unmodifiableList(allParameters);
     }
 
-    public boolean canBeAccessedByInstanceOf(ProcessorContext context, TypeMirror type) {
+    public boolean canBeAccessedByInstanceOf(TypeMirror type) {
         TypeMirror methodType = Utils.findNearestEnclosingType(getMethod()).asType();
-        return Utils.isAssignable(context, type, methodType) || Utils.isAssignable(context, methodType, type);
+        return Utils.isAssignable(type, methodType) || Utils.isAssignable(methodType, type);
     }
 
     public ExecutableElement getMethod() {
@@ -306,31 +314,27 @@
 
         List<TypeMirror> signature1 = getSignatureTypes(this);
         List<TypeMirror> signature2 = getSignatureTypes(compareMethod);
-        if (signature1.size() != signature2.size()) {
-            return signature2.size() - signature1.size();
-        }
 
         int result = 0;
-        for (int i = 1; i < signature1.size(); i++) {
-            TypeMirror t1 = signature1.get(i);
-            TypeMirror t2 = signature2.get(i);
-
-            int typeResult = compareParameter(typeSystem, t1, t2);
-            if (result == 0) {
-                result = typeResult;
-            } else if (typeResult != 0 && Math.signum(result) != Math.signum(typeResult)) {
-                // We cannot define an order.
-                return 0;
+        for (int i = 0; i < Math.max(signature1.size(), signature2.size()); i++) {
+            TypeMirror t1 = i < signature1.size() ? signature1.get(i) : null;
+            TypeMirror t2 = i < signature2.size() ? signature2.get(i) : null;
+            result = compareParameter(typeSystem, t1, t2);
+            if (result != 0) {
+                break;
             }
         }
-        if (result == 0 && signature1.size() > 0) {
-            result = compareParameter(typeSystem, signature1.get(0), signature2.get(0));
-        }
 
         return result;
     }
 
-    private static int compareParameter(TypeSystemData data, TypeMirror signature1, TypeMirror signature2) {
+    protected static int compareParameter(TypeSystemData data, TypeMirror signature1, TypeMirror signature2) {
+        if (signature1 == null) {
+            return 1;
+        } else if (signature2 == null) {
+            return -1;
+        }
+
         if (Utils.typeEquals(signature1, signature2)) {
             return 0;
         }
@@ -341,6 +345,7 @@
             return index1 - index2;
         }
 
+        // TODO this version if subclass of should be improved.
         if (signature1.getKind() == TypeKind.DECLARED && signature2.getKind() == TypeKind.DECLARED) {
             TypeElement element1 = Utils.fromTypeMirror(signature1);
             TypeElement element2 = Utils.fromTypeMirror(signature2);
@@ -356,7 +361,6 @@
 
     public static List<TypeMirror> getSignatureTypes(TemplateMethod method) {
         List<TypeMirror> types = new ArrayList<>();
-        types.add(method.getReturnType().getType());
         for (ActualParameter param : method.getSignatureParameters()) {
             types.add(param.getType());
         }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethodParser.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/template/TemplateMethodParser.java	Mon Aug 11 15:53:05 2014 +0200
@@ -97,6 +97,7 @@
 
         List<E> parsedMethods = new ArrayList<>();
         boolean valid = true;
+        int naturalOrder = 0;
         for (ExecutableElement method : methods) {
             if (!isParsable(method)) {
                 continue;
@@ -108,7 +109,7 @@
                 mirror = Utils.findAnnotationMirror(getContext().getEnvironment(), method, annotationType);
             }
 
-            E parsedMethod = parse(method, mirror);
+            E parsedMethod = parse(naturalOrder, method, mirror);
 
             if (method.getModifiers().contains(Modifier.PRIVATE) && emitErrors) {
                 parsedMethod.addError("Method annotated with @%s must not be private.", getAnnotationType().getSimpleName());
@@ -122,6 +123,7 @@
             } else {
                 valid = false;
             }
+            naturalOrder++;
         }
         Collections.sort(parsedMethods);
 
@@ -131,7 +133,7 @@
         return parsedMethods;
     }
 
-    private E parse(ExecutableElement method, AnnotationMirror annotation) {
+    private E parse(int naturalOrder, ExecutableElement method, AnnotationMirror annotation) {
         MethodSpec methodSpecification = createSpecification(method, annotation);
         if (methodSpecification == null) {
             return null;
@@ -146,15 +148,15 @@
             parameterTypes.add(var.asType());
         }
 
-        return parseImpl(methodSpecification, id, method, annotation, returnType, parameterTypes);
+        return parseImpl(methodSpecification, naturalOrder, id, method, annotation, returnType, parameterTypes);
     }
 
-    private E parseImpl(MethodSpec methodSpecification, String id, ExecutableElement method, AnnotationMirror annotation, TypeMirror returnType, List<TypeMirror> parameterTypes) {
+    private E parseImpl(MethodSpec methodSpecification, int naturalOrder, String id, ExecutableElement method, AnnotationMirror annotation, TypeMirror returnType, List<TypeMirror> parameterTypes) {
         ParameterSpec returnTypeSpec = methodSpecification.getReturnType();
         ActualParameter returnTypeMirror = matchParameter(returnTypeSpec, returnType, template, -1, -1);
         if (returnTypeMirror == null) {
             if (emitErrors) {
-                E invalidMethod = create(new TemplateMethod(id, template, methodSpecification, method, annotation, returnTypeMirror, Collections.<ActualParameter> emptyList()), true);
+                E invalidMethod = create(new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, Collections.<ActualParameter> emptyList()), true);
                 String expectedReturnType = returnTypeSpec.toSignatureString(true);
                 String actualReturnType = Utils.getSimpleName(returnType);
 
@@ -170,7 +172,7 @@
         List<ActualParameter> parameters = parseParameters(methodSpecification, parameterTypes, isUseVarArgs() && method != null ? method.isVarArgs() : false);
         if (parameters == null) {
             if (isEmitErrors() && method != null) {
-                E invalidMethod = create(new TemplateMethod(id, template, methodSpecification, method, annotation, returnTypeMirror, Collections.<ActualParameter> emptyList()), true);
+                E invalidMethod = create(new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, Collections.<ActualParameter> emptyList()), true);
                 String message = String.format("Method signature %s does not match to the expected signature: \n%s", createActualSignature(method),
                                 methodSpecification.toSignatureString(method.getSimpleName().toString()));
                 invalidMethod.addError(message);
@@ -180,7 +182,7 @@
             }
         }
 
-        return create(new TemplateMethod(id, template, methodSpecification, method, annotation, returnTypeMirror, parameters), false);
+        return create(new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, parameters), false);
     }
 
     private static String createActualSignature(ExecutableElement method) {
@@ -349,7 +351,7 @@
         }
     }
 
-    public final E create(String id, ExecutableElement methodMetadata, AnnotationMirror mirror, TypeMirror returnType, List<TypeMirror> parameterTypes) {
-        return parseImpl(createSpecification(methodMetadata, mirror), id, methodMetadata, mirror, returnType, parameterTypes);
+    public final E create(String id, int naturalOrder, ExecutableElement methodMetadata, AnnotationMirror mirror, TypeMirror returnType, List<TypeMirror> parameterTypes) {
+        return parseImpl(createSpecification(methodMetadata, mirror), naturalOrder, id, methodMetadata, mirror, returnType, parameterTypes);
     }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardData.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardData.java	Mon Aug 11 15:53:05 2014 +0200
@@ -22,29 +22,28 @@
  */
 package com.oracle.truffle.dsl.processor.typesystem;
 
-import com.oracle.truffle.dsl.processor.node.*;
+import java.util.*;
+
 import com.oracle.truffle.dsl.processor.template.*;
 
 public class GuardData extends TemplateMethod {
 
-    private final SpecializationData specialization;
-    private final boolean negated;
+    private List<GuardExpression> impliesExpressions;
 
-    public GuardData(TemplateMethod method, SpecializationData specialization, boolean negated) {
+    public GuardData(TemplateMethod method, List<GuardExpression> impliesExpressions) {
         super(method);
-        this.negated = negated;
-        this.specialization = specialization;
+        this.impliesExpressions = impliesExpressions;
     }
 
-    public boolean isNegated() {
-        return negated;
+    public List<GuardExpression> getImpliesExpressions() {
+        return impliesExpressions;
     }
 
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof GuardData) {
             GuardData other = (GuardData) obj;
-            return getMethod().equals(other.getMethod()) && negated == other.negated;
+            return getMethod().equals(other.getMethod());
         }
         return false;
     }
@@ -54,13 +53,4 @@
         return getMethod().hashCode();
     }
 
-    public SpecializationData getSpecialization() {
-        return specialization;
-    }
-
-    @Override
-    public String toString() {
-        return (negated ? "!" : "") + getMethodName() + getParameters().toString();
-    }
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardExpression.java	Mon Aug 11 15:53:05 2014 +0200
@@ -0,0 +1,143 @@
+package com.oracle.truffle.dsl.processor.typesystem;
+
+import java.util.*;
+
+public final class GuardExpression {
+
+    private GuardData resolvedGuard;
+
+    private final String guardName;
+    private final boolean negated;
+
+    public GuardExpression(String expression) {
+        if (expression.startsWith("!")) {
+            guardName = expression.substring(1, expression.length());
+            negated = true;
+        } else {
+            guardName = expression;
+            negated = false;
+        }
+    }
+
+    public boolean isResolved() {
+        return resolvedGuard != null;
+    }
+
+    public String getGuardName() {
+        return guardName;
+    }
+
+    public void setGuard(GuardData guard) {
+        this.resolvedGuard = guard;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof GuardExpression) {
+            GuardExpression other = (GuardExpression) obj;
+            if (isResolved() && other.isResolved()) {
+                return resolvedGuard.equals(other.resolvedGuard) && negated == other.negated;
+            } else {
+                return guardName.equals(other.guardName) && negated == other.negated;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(guardName, negated, resolvedGuard);
+    }
+
+    public int compareConcreteness(GuardExpression other) {
+        if (other == null) {
+            return -1;
+        } else if (this == other) {
+            return 0;
+        }
+
+        /*
+         * Positive and negated guard are always disjunct. So we can choose the positive to be
+         * first.
+         */
+        if (getGuardName().equals(other.getGuardName())) {
+            if (negated == !other.negated) {
+                if (negated) {
+                    return 1;
+                } else {
+                    return -1;
+                }
+            }
+        }
+
+        /*
+         * Very simple version of the implies annotation implementation.
+         */
+        if (isResolved() && other.isResolved()) {
+            if (impliesNot(other)) {
+                return 1;
+            } else if (other.impliesNot(this)) {
+                return -1;
+            }
+        }
+        return 0;
+    }
+
+    public final boolean implies(GuardExpression other) {
+        if (other == this) {
+            return true;
+        }
+        if (getGuardName().equals(other.getGuardName())) {
+            if (isNegated() == other.isNegated()) {
+                return true;
+            }
+        }
+
+        if (isResolved() && other.isResolved()) {
+            for (GuardExpression implies : getResolvedGuard().getImpliesExpressions()) {
+                if (implies.getGuardName().equals(other.getGuardName())) {
+                    if (implies.isNegated() == other.isNegated()) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    public final boolean impliesNot(GuardExpression other) {
+        if (other == this) {
+            return false;
+        }
+        if (getGuardName().equals(other.getGuardName())) {
+            if (isNegated() != other.isNegated()) {
+                return true;
+            }
+        }
+
+        if (isResolved() && other.isResolved()) {
+            for (GuardExpression implies : getResolvedGuard().getImpliesExpressions()) {
+                if (implies.getGuardName().equals(other.getGuardName())) {
+                    if (implies.isNegated() != other.isNegated()) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return (negated ? "!" : "") + guardName;
+    }
+
+    public boolean isNegated() {
+        return negated;
+    }
+
+    public GuardData getResolvedGuard() {
+        return resolvedGuard;
+    }
+
+}
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardParser.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/GuardParser.java	Mon Aug 11 15:53:05 2014 +0200
@@ -23,29 +23,25 @@
 package com.oracle.truffle.dsl.processor.typesystem;
 
 import java.lang.annotation.*;
+import java.util.*;
 
 import javax.lang.model.element.*;
+import javax.lang.model.type.*;
 
+import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.dsl.processor.*;
 import com.oracle.truffle.dsl.processor.node.*;
 import com.oracle.truffle.dsl.processor.template.*;
 
 public class GuardParser extends NodeMethodParser<GuardData> {
 
-    private final SpecializationData specialization;
-    private final String guardName;
-    private final boolean negated;
+    private final Set<String> guardNames;
+    private final TemplateMethod compatibleSource;
 
-    public GuardParser(ProcessorContext context, SpecializationData specialization, String guardDefinition) {
-        super(context, specialization.getNode());
-        this.specialization = specialization;
-        if (guardDefinition.startsWith("!")) {
-            this.guardName = guardDefinition.substring(1, guardDefinition.length());
-            this.negated = true;
-        } else {
-            this.guardName = guardDefinition;
-            this.negated = false;
-        }
+    public GuardParser(ProcessorContext context, NodeData node, TemplateMethod compatibleSource, Set<String> guardNames) {
+        super(context, node);
+        this.guardNames = guardNames;
+        this.compatibleSource = compatibleSource;
         setEmitErrors(false);
         setParseNullOnError(false);
     }
@@ -59,13 +55,21 @@
     public MethodSpec createSpecification(ExecutableElement method, AnnotationMirror mirror) {
         MethodSpec spec = createDefaultMethodSpec(method, mirror, true, null);
         spec.setIgnoreAdditionalSpecifications(true);
-        spec.getRequired().clear();
+        if (compatibleSource != null) {
+            spec.getRequired().clear();
+            for (ActualParameter parameter : compatibleSource.getRequiredParameters()) {
+                spec.addRequired(new ParameterSpec(parameter.getSpecification(), Utils.getAssignableTypes(getContext(), parameter.getType())));
+            }
+        }
+        return spec;
+    }
 
-        for (ActualParameter parameter : specialization.getRequiredParameters()) {
-            spec.addRequired(new ParameterSpec(parameter.getSpecification(), Utils.getAssignableTypes(getContext(), parameter.getType())));
-        }
-
-        return spec;
+    @Override
+    protected List<TypeMirror> nodeTypeMirrors(NodeData nodeData) {
+        Set<TypeMirror> typeMirrors = new LinkedHashSet<>();
+        typeMirrors.addAll(nodeData.getTypeSystem().getPrimitiveTypeMirrors());
+        typeMirrors.addAll(nodeData.getTypeSystem().getBoxedTypeMirrors());
+        return new ArrayList<>(typeMirrors);
     }
 
     @Override
@@ -75,12 +79,21 @@
 
     @Override
     public boolean isParsable(ExecutableElement method) {
-        return method.getSimpleName().toString().equals(guardName);
+        return guardNames.contains(method.getSimpleName().toString());
     }
 
     @Override
     public GuardData create(TemplateMethod method, boolean invalid) {
-        return new GuardData(method, specialization, negated);
+        Implies impliesAnnotation = method.getMethod().getAnnotation(Implies.class);
+        String[] impliesExpressions = new String[0];
+        if (impliesAnnotation != null) {
+            impliesExpressions = impliesAnnotation.value();
+        }
+        List<GuardExpression> guardExpressions = new ArrayList<>();
+        for (String string : impliesExpressions) {
+            guardExpressions.add(new GuardExpression(string));
+        }
+        return new GuardData(method, guardExpressions);
     }
 
     @Override
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeData.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeData.java	Mon Aug 11 15:53:05 2014 +0200
@@ -111,6 +111,20 @@
     }
 
     @Override
+    public int hashCode() {
+        return Objects.hash(index, primitiveType);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof TypeData)) {
+            return false;
+        }
+        TypeData otherType = (TypeData) obj;
+        return index == otherType.index && Utils.typeEquals(primitiveType, otherType.primitiveType);
+    }
+
+    @Override
     public String toString() {
         return getClass().getSimpleName() + "[" + Utils.getSimpleName(primitiveType) + "]";
     }
@@ -119,12 +133,39 @@
         return Utils.typeEquals(boxedType, actualTypeData.boxedType);
     }
 
-    public boolean needsCastTo(ProcessorContext context, TypeData targetType) {
-        return Utils.needsCastTo(context, getPrimitiveType(), targetType.getPrimitiveType());
+    public boolean needsCastTo(TypeData targetType) {
+        return Utils.needsCastTo(getPrimitiveType(), targetType.getPrimitiveType());
+    }
+
+    public boolean needsCastTo(TypeMirror targetType) {
+        return Utils.needsCastTo(getPrimitiveType(), targetType);
     }
 
     public boolean isPrimitive() {
         return Utils.isPrimitive(getPrimitiveType());
     }
 
+    public boolean isImplicitSubtypeOf(TypeData other) {
+        List<ImplicitCastData> casts = other.getTypeSystem().lookupByTargetType(other);
+        for (ImplicitCastData cast : casts) {
+            if (isSubtypeOf(cast.getSourceType())) {
+                return true;
+            }
+        }
+        return isSubtypeOf(other);
+    }
+
+    public boolean isSubtypeOf(TypeData other) {
+        return Utils.isSubtype(boxedType, other.boxedType);
+    }
+
+    public boolean intersects(TypeData type) {
+        if (this.equals(type)) {
+            return true;
+        }
+        if (type.isGeneric() || isGeneric()) {
+            return true;
+        }
+        return isSubtypeOf(type) || type.isSubtypeOf(this);
+    }
 }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemCodeGenerator.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemCodeGenerator.java	Mon Aug 11 15:53:05 2014 +0200
@@ -36,10 +36,6 @@
 
 public class TypeSystemCodeGenerator extends CompilationUnitFactory<TypeSystemData> {
 
-    public TypeSystemCodeGenerator(ProcessorContext context) {
-        super(context);
-    }
-
     public static String isTypeMethodName(TypeData type) {
         return "is" + Utils.getTypeId(type.getBoxedType());
     }
@@ -64,28 +60,33 @@
         return "expect" + Utils.getTypeId(type.getBoxedType());
     }
 
+    public static String typeName(TypeSystemData typeSystem) {
+        String name = getSimpleName(typeSystem.getTemplateType());
+        return name + "Gen";
+    }
+
+    public static String singletonName(TypeSystemData type) {
+        return createConstantName(getSimpleName(type.getTemplateType().asType()));
+    }
+
     /**
      * Finds the generated singleton field for a TypeSytemData instance. TypeSystemCodeGenerator
      * must be applied to the TypeSystemData model before use.
      */
     public static VariableElement findSingleton(ProcessorContext context, TypeSystemData typeSystem) {
-        TypeMirror type = context.findGeneratedClassBySimpleName(TypeClassFactory.typeName(typeSystem), typeSystem);
-        return Utils.findDeclaredField(type, TypeClassFactory.singletonName(typeSystem.getTemplateType().asType()));
+        TypeMirror type = context.findGeneratedClassBySimpleName(typeName(typeSystem), typeSystem);
+        return Utils.findDeclaredField(type, singletonName(typeSystem));
     }
 
     @Override
     protected void createChildren(TypeSystemData m) {
-        add(new TypeClassFactory(context), m);
+        add(new TypeClassFactory(), m);
     }
 
     protected static class TypeClassFactory extends ClassElementFactory<TypeSystemData> {
 
         private static final String LOCAL_VALUE = "value";
 
-        public TypeClassFactory(ProcessorContext context) {
-            super(context);
-        }
-
         @Override
         public CodeTypeElement create(TypeSystemData typeSystem) {
             String name = typeName(typeSystem);
@@ -127,17 +128,8 @@
             return new ArrayList<>(sourceTypes);
         }
 
-        private static String typeName(TypeSystemData typeSystem) {
-            String name = getSimpleName(typeSystem.getTemplateType());
-            return name + "Gen";
-        }
-
-        private static String singletonName(TypeMirror type) {
-            return createConstantName(getSimpleName(type));
-        }
-
         private CodeVariableElement createSingleton(CodeTypeElement clazz) {
-            CodeVariableElement field = new CodeVariableElement(modifiers(PUBLIC, STATIC, FINAL), clazz.asType(), singletonName(getModel().getTemplateType().asType()));
+            CodeVariableElement field = new CodeVariableElement(modifiers(PUBLIC, STATIC, FINAL), clazz.asType(), singletonName(getModel()));
             field.createInitBuilder().startNew(clazz.asType()).end();
             return field;
         }
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java	Mon Aug 11 15:53:05 2014 +0200
@@ -43,8 +43,8 @@
     private TypeMirror genericType;
     private TypeData voidType;
 
-    public TypeSystemData(TypeElement templateType, AnnotationMirror annotation) {
-        super(templateType, null, annotation);
+    public TypeSystemData(ProcessorContext context, TypeElement templateType, AnnotationMirror annotation) {
+        super(context, templateType, null, annotation);
     }
 
     @Override
--- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java	Mon Aug 11 15:53:05 2014 +0200
+++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java	Mon Aug 11 15:53:05 2014 +0200
@@ -39,10 +39,6 @@
 
     public static final List<Class<? extends Annotation>> ANNOTATIONS = Arrays.asList(TypeSystem.class, ExpectError.class);
 
-    public TypeSystemParser(ProcessorContext c) {
-        super(c);
-    }
-
     @Override
     public Class<? extends Annotation> getAnnotationType() {
         return TypeSystem.class;
@@ -52,7 +48,7 @@
     protected TypeSystemData parse(Element element, AnnotationMirror mirror) {
         TypeElement templateType = (TypeElement) element;
         AnnotationMirror templateTypeAnnotation = mirror;
-        TypeSystemData typeSystem = new TypeSystemData(templateType, templateTypeAnnotation);
+        TypeSystemData typeSystem = new TypeSystemData(context, templateType, templateTypeAnnotation);
 
         // annotation type on class path!?
         TypeElement annotationTypeElement = processingEnv.getElementUtils().getTypeElement(getAnnotationType().getCanonicalName());