changeset 9279:2a4b57f02fb4

Implemented basic support for assumptions for sourcecode generation.
author Christian Humer <christian.humer@gmail.com>
date Wed, 24 Apr 2013 17:44:15 +0200
parents a9cfbe03d9c4
children c62bf8be5caf
files graal/com.oracle.truffle.api.codegen.test/src/com/oracle/truffle/api/codegen/test/AssumptionsTest.java graal/com.oracle.truffle.api.codegen/src/com/oracle/truffle/api/codegen/NodeAssumptions.java graal/com.oracle.truffle.api.codegen/src/com/oracle/truffle/api/codegen/Specialization.java graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/TruffleTypes.java graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeCodeGenerator.java graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeData.java graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeParser.java graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/SpecializationData.java graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/SpecializationMethodParser.java
diffstat 9 files changed, 397 insertions(+), 102 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.codegen.test/src/com/oracle/truffle/api/codegen/test/AssumptionsTest.java	Wed Apr 24 17:44:15 2013 +0200
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.codegen.test;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.codegen.*;
+import com.oracle.truffle.api.codegen.test.AssumptionsTestFactory.DerivedAssumptionNodeFactory;
+import com.oracle.truffle.api.codegen.test.AssumptionsTestFactory.DerivedAssumptionRedeclaredNodeFactory;
+import com.oracle.truffle.api.codegen.test.AssumptionsTestFactory.MultipleAssumptionsNodeFactory;
+import com.oracle.truffle.api.codegen.test.AssumptionsTestFactory.SingleAssumptionNodeFactory;
+import com.oracle.truffle.api.codegen.test.TypeSystemTest.TestRootNode;
+import com.oracle.truffle.api.codegen.test.TypeSystemTest.ValueNode;
+
+public class AssumptionsTest {
+
+    @Test
+    public void testSingleAssumption() {
+        Assumption assumption = Truffle.getRuntime().createAssumption();
+        TestRootNode<?> root = TestHelper.create(SingleAssumptionNodeFactory.getInstance(), assumption);
+
+        Assert.assertEquals(42, TestHelper.executeWith(root));
+        assumption.invalidate();
+        Assert.assertEquals("42", TestHelper.executeWith(root));
+    }
+
+    @NodeAssumptions("assumption")
+    abstract static class SingleAssumptionNode extends ValueNode {
+
+        @Specialization(order = 0, assumptions = "assumption")
+        int doInt() {
+            return 42;
+        }
+
+        @Specialization
+        Object doObject() {
+            return "42";
+        }
+    }
+
+    @Test
+    public void testMultipleAssumption() {
+        Assumption assumption1 = Truffle.getRuntime().createAssumption();
+        Assumption assumption2 = Truffle.getRuntime().createAssumption();
+        TestRootNode<?> root = TestHelper.create(MultipleAssumptionsNodeFactory.getInstance(), assumption1, assumption2);
+
+        Assert.assertEquals(42, TestHelper.executeWith(root));
+        assumption2.invalidate();
+        Assert.assertEquals("42", TestHelper.executeWith(root));
+        assumption1.invalidate();
+        Assert.assertEquals("43", TestHelper.executeWith(root));
+    }
+
+    @NodeAssumptions({"assumption1", "assumption2"})
+    abstract static class MultipleAssumptionsNode extends ValueNode {
+
+        @Specialization(assumptions = {"assumption1", "assumption2"})
+        int doInt() {
+            return 42;
+        }
+
+        @Specialization(assumptions = "assumption1")
+        Object doObject() {
+            return "42";
+        }
+
+        @Generic
+        Object doGeneric() {
+            return "43";
+        }
+    }
+
+    @Test
+    public void testDerivedAssumption() {
+        Assumption additionalAssumption = Truffle.getRuntime().createAssumption();
+        Assumption assumption = Truffle.getRuntime().createAssumption();
+        TestRootNode<?> root = TestHelper.create(DerivedAssumptionNodeFactory.getInstance(), assumption, additionalAssumption);
+
+        Assert.assertEquals(42, TestHelper.executeWith(root));
+        assumption.invalidate();
+        Assert.assertEquals(43, TestHelper.executeWith(root));
+        additionalAssumption.invalidate();
+        Assert.assertEquals("42", TestHelper.executeWith(root));
+    }
+
+    @NodeAssumptions({"additionalAssumption"})
+    abstract static class DerivedAssumptionNode extends SingleAssumptionNode {
+
+        @Specialization(order = 1, assumptions = "additionalAssumption")
+        int doIntDerived() {
+            return 43;
+        }
+    }
+
+    @Test
+    public void testDerivedAssumptionRedeclared() {
+        Assumption additionalAssumption = Truffle.getRuntime().createAssumption();
+        Assumption assumption = Truffle.getRuntime().createAssumption();
+        TestRootNode<?> root = TestHelper.create(DerivedAssumptionRedeclaredNodeFactory.getInstance(), additionalAssumption, assumption);
+
+        Assert.assertEquals(42, TestHelper.executeWith(root));
+        assumption.invalidate();
+        Assert.assertEquals(43, TestHelper.executeWith(root));
+        additionalAssumption.invalidate();
+        Assert.assertEquals("42", TestHelper.executeWith(root));
+    }
+
+    @NodeAssumptions({"additionalAssumption", "assumption"})
+    abstract static class DerivedAssumptionRedeclaredNode extends SingleAssumptionNode {
+
+        @Specialization(order = 1, assumptions = "additionalAssumption")
+        int doIntDerived() {
+            return 43;
+        }
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.codegen/src/com/oracle/truffle/api/codegen/NodeAssumptions.java	Wed Apr 24 17:44:15 2013 +0200
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.codegen;
+
+import java.lang.annotation.*;
+
+/**
+ * Declares one or multiple assumptions for use inside a source code generation enabled node.
+ * Declared assumptions must be passed to the {@link NodeFactory#createNode(Object...)} method as
+ * parameters.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({ElementType.TYPE})
+public @interface NodeAssumptions {
+
+    String[] value();
+
+}
--- a/graal/com.oracle.truffle.api.codegen/src/com/oracle/truffle/api/codegen/Specialization.java	Tue Apr 23 21:03:47 2013 +0200
+++ b/graal/com.oracle.truffle.api.codegen/src/com/oracle/truffle/api/codegen/Specialization.java	Wed Apr 24 17:44:15 2013 +0200
@@ -36,4 +36,11 @@
 
     String[] guards() default {};
 
+    /**
+     * Defines the assumptions to check for this specialization. When the specialization method is
+     * invoked it is guaranteed that the assigned assumptions still hold. To declare assumptions use
+     * the {@link NodeAssumptions} annotation at class level.
+     */
+    String[] assumptions() default {};
+
 }
--- a/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/TruffleTypes.java	Tue Apr 23 21:03:47 2013 +0200
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/TruffleTypes.java	Wed Apr 24 17:44:15 2013 +0200
@@ -43,6 +43,8 @@
     private final TypeMirror nodeArray;
     private final TypeMirror unexpectedValueException;
     private final TypeMirror frame;
+    private final TypeMirror assumption;
+    private final TypeMirror invalidAssumption;
     private final DeclaredType childAnnotation;
     private final DeclaredType childrenAnnotation;
     private final TypeMirror compilerDirectives;
@@ -57,6 +59,8 @@
         childAnnotation = getRequired(context, Child.class);
         childrenAnnotation = getRequired(context, Children.class);
         compilerDirectives = getRequired(context, CompilerDirectives.class);
+        assumption = getRequired(context, Assumption.class);
+        invalidAssumption = getRequired(context, InvalidAssumptionException.class);
     }
 
     public boolean verify(ProcessorContext context, Element element, AnnotationMirror mirror) {
@@ -79,6 +83,14 @@
         return (DeclaredType) type;
     }
 
+    public TypeMirror getInvalidAssumption() {
+        return invalidAssumption;
+    }
+
+    public TypeMirror getAssumption() {
+        return assumption;
+    }
+
     public TypeMirror getCompilerDirectives() {
         return compilerDirectives;
     }
--- a/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeCodeGenerator.java	Tue Apr 23 21:03:47 2013 +0200
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeCodeGenerator.java	Wed Apr 24 17:44:15 2013 +0200
@@ -289,11 +289,11 @@
     }
 
     private CodeTree createGuardAndCast(CodeTreeBuilder parent, String conditionPrefix, SpecializationData sourceSpecialization, SpecializationData targetSpecialization, boolean castValues,
-                    CodeTree guardedStatements, CodeTree elseStatements) {
+                    CodeTree guardedStatements, CodeTree elseStatements, boolean emitAssumptions) {
 
         NodeData node = targetSpecialization.getNode();
         CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-        CodeTree implicitGuards = createImplicitGuards(parent, conditionPrefix, sourceSpecialization, targetSpecialization);
+        CodeTree implicitGuards = createImplicitGuards(parent, conditionPrefix, sourceSpecialization, targetSpecialization, emitAssumptions);
         CodeTree explicitGuards = createExplicitGuards(parent, implicitGuards == null ? conditionPrefix : null, sourceSpecialization, targetSpecialization);
 
         Set<String> valuesNeedsCast;
@@ -392,10 +392,26 @@
         return builder.getRoot();
     }
 
-    private CodeTree createImplicitGuards(CodeTreeBuilder parent, String conditionPrefix, SpecializationData valueSpecialization, SpecializationData guardedSpecialization) {
+    private CodeTree createImplicitGuards(CodeTreeBuilder parent, String conditionPrefix, SpecializationData valueSpecialization, SpecializationData guardedSpecialization, boolean emitAssumptions) {
         CodeTreeBuilder builder = new CodeTreeBuilder(parent);
         // Implict guards based on method signature
         String andOperator = conditionPrefix != null ? conditionPrefix + " && " : "";
+
+        if (emitAssumptions) {
+            boolean isStatic = parent.findMethod().getModifiers().contains(STATIC);
+
+            for (String assumption : guardedSpecialization.getAssumptions()) {
+                builder.string(andOperator);
+                if (isStatic) {
+                    builder.string(THIS_NODE_LOCAL_VAR_NAME);
+                } else {
+                    builder.string("this");
+                }
+                builder.string(".").string(assumption).string(".isValid()");
+                andOperator = " && ";
+            }
+        }
+
         for (ActualParameter guardedParam : guardedSpecialization.getParameters()) {
             NodeChildData field = guardedSpecialization.getNode().findChild(guardedParam.getSpecification().getName());
             if (field == null) {
@@ -528,6 +544,19 @@
         return constructors;
     }
 
+    private static ExecutableElement findCopyConstructor(TypeMirror type) {
+        for (ExecutableElement constructor : ElementFilter.constructorsIn(Utils.fromTypeMirror(type).getEnclosedElements())) {
+            if (constructor.getModifiers().contains(PRIVATE)) {
+                continue;
+            }
+            if (isCopyConstructor(constructor)) {
+                return constructor;
+            }
+        }
+
+        return null;
+    }
+
     private static boolean isCopyConstructor(ExecutableElement element) {
         if (element.getParameters().size() != 1) {
             return false;
@@ -578,6 +607,10 @@
                 }
             }
 
+            for (String assumption : node.getAssumptions()) {
+                clazz.add(createAssumptionField(assumption));
+            }
+
             createConstructors(node, clazz);
 
             if (node.getExtensionElements() != null) {
@@ -588,93 +621,88 @@
         }
 
         private void createConstructors(NodeData node, CodeTypeElement clazz) {
-            List<ExecutableElement> signatureConstructors = new ArrayList<>();
-            ExecutableElement copyConstructor = null;
-            List<? extends ExecutableElement> constructors = ElementFilter.constructorsIn(Utils.fromTypeMirror(node.getNodeType()).getEnclosedElements());
-            for (ExecutableElement constructor : constructors) {
-                if (constructor.getModifiers().contains(Modifier.PRIVATE) || constructor.getParameters().isEmpty()) {
-                    continue;
+            List<ExecutableElement> constructors = findUserConstructors(node.getNodeType());
+            if (constructors.isEmpty()) {
+                clazz.add(createUserConstructor(clazz, null));
+            } else {
+                for (ExecutableElement constructor : constructors) {
+                    clazz.add(createUserConstructor(clazz, constructor));
                 }
+            }
+            clazz.add(createCopyConstructor(clazz, findCopyConstructor(node.getNodeType())));
+        }
 
-                if (isCopyConstructor(constructor)) {
-                    assert copyConstructor == null;
-                    copyConstructor = createConstructor(clazz, constructor, true);
-                } else {
-                    signatureConstructors.add(createConstructor(clazz, constructor, false));
+        private CodeExecutableElement createUserConstructor(CodeTypeElement type, ExecutableElement superConstructor) {
+            CodeExecutableElement method = new CodeExecutableElement(null, type.getSimpleName().toString());
+            CodeTreeBuilder builder = method.createBuilder();
+
+            if (superConstructor != null) {
+                for (VariableElement param : superConstructor.getParameters()) {
+                    method.getParameters().add(CodeVariableElement.clone(param));
                 }
             }
 
-            if (copyConstructor == null && node.needsRewrites(getContext())) {
-                clazz.add(createConstructor(clazz, null, true));
-            } else if (copyConstructor != null) {
-                clazz.add(copyConstructor);
-            }
-
-            if (signatureConstructors.isEmpty() && !node.getChildren().isEmpty()) {
-                clazz.add(createConstructor(clazz, null, false));
-            } else {
-                clazz.getEnclosedElements().addAll(signatureConstructors);
-            }
-        }
-
-        private CodeExecutableElement createConstructor(CodeTypeElement type, ExecutableElement superConstructor, boolean copyConstructor) {
-            CodeExecutableElement method = new CodeExecutableElement(null, type.getSimpleName().toString());
-            CodeTreeBuilder builder = method.createBuilder();
-            if (!copyConstructor) {
-                if (superConstructor != null) {
-                    for (VariableElement param : superConstructor.getParameters()) {
-                        method.getParameters().add(CodeVariableElement.clone(param));
-                    }
-                }
-                for (NodeChildData child : getModel().getChildren()) {
-                    method.getParameters().add(new CodeVariableElement(child.getNodeType(), child.getName()));
-                }
-            } else {
-                if (!(superConstructor == null && getModel().getChildren().isEmpty())) {
-                    method.getParameters().add(new CodeVariableElement(type.asType(), "copy"));
-                }
+            for (VariableElement var : type.getFields()) {
+                method.getParameters().add(new CodeVariableElement(var.asType(), var.getSimpleName().toString()));
             }
 
             if (superConstructor != null) {
-                builder.startStatement();
-                builder.startSuperCall();
-                if (copyConstructor) {
-                    builder.string("copy");
-                } else {
-                    for (VariableElement param : superConstructor.getParameters()) {
-                        builder.string(param.getSimpleName().toString());
-                    }
+                builder.startStatement().startSuperCall();
+                for (VariableElement param : superConstructor.getParameters()) {
+                    builder.string(param.getSimpleName().toString());
                 }
-                builder.end();
-                builder.end();
+                builder.end().end();
             }
 
-            for (NodeChildData child : getModel().getChildren()) {
-
+            for (VariableElement var : type.getFields()) {
                 builder.startStatement();
-                builder.string("this.").string(child.getName()).string(" = ");
-
-                if (child.getCardinality() == Cardinality.MANY) {
-                    builder.startCall("adoptChildren");
+                String varName = var.getSimpleName().toString();
+                builder.string("this.").string(varName);
+                if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNode())) {
+                    builder.string(" = adoptChild(").string(varName).string(")");
+                } else if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNodeArray())) {
+                    builder.string(" = adoptChildren(").string(varName).string(")");
                 } else {
-                    builder.startCall("adoptChild");
+                    builder.string(" = ").string(varName);
                 }
+                builder.end();
+            }
+            return method;
+        }
 
-                builder.startGroup();
-                if (copyConstructor) {
-                    builder.string("copy.");
-                }
-                builder.string(child.getName());
-                builder.end();
-
-                builder.end();
-                builder.end();
-
+        private CodeExecutableElement createCopyConstructor(CodeTypeElement type, ExecutableElement superConstructor) {
+            CodeExecutableElement method = new CodeExecutableElement(null, type.getSimpleName().toString());
+            CodeTreeBuilder builder = method.createBuilder();
+            if (!(superConstructor == null && type.getFields().isEmpty())) {
+                method.getParameters().add(new CodeVariableElement(type.asType(), "copy"));
             }
 
+            if (superConstructor != null) {
+                builder.startStatement().startSuperCall().string("copy").end().end();
+            }
+
+            for (VariableElement var : type.getFields()) {
+                builder.startStatement();
+                String varName = var.getSimpleName().toString();
+                builder.string("this.").string(varName);
+                if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNode())) {
+                    builder.string(" = adoptChild(copy.").string(varName).string(")");
+                } else if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNodeArray())) {
+                    builder.string(" = adoptChildren(copy.").string(varName).string(")");
+                } else {
+                    builder.string(" = copy.").string(varName);
+                }
+                builder.end();
+            }
             return method;
         }
 
+        private CodeVariableElement createAssumptionField(String assumption) {
+            CodeVariableElement var = new CodeVariableElement(getContext().getTruffleTypes().getAssumption(), assumption);
+            var.getModifiers().add(Modifier.FINAL);
+            return var;
+        }
+
         private CodeVariableElement createChildField(NodeChildData child) {
             CodeVariableElement var = new CodeVariableElement(child.getNodeType(), child.getName());
             var.getModifiers().add(Modifier.PROTECTED);
@@ -728,11 +756,8 @@
 
                 createFactoryMethods(node, clazz, createVisibility);
 
-                if (node.getSpecializations().size() > 1) {
+                if (node.needsRewrites(context)) {
                     clazz.add(createCreateSpecializedMethod(node, createVisibility));
-                }
-
-                if (node.needsRewrites(context)) {
                     clazz.add(createSpecializeMethod(node));
                 }
 
@@ -912,7 +937,7 @@
 
         private CodeExecutableElement createCreateNodeSpecializedMethod(NodeData node) {
             CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), node.getNodeType(), "createNodeSpecialized");
-            CodeVariableElement nodeParam = new CodeVariableElement(node.getNodeType(), "thisNode");
+            CodeVariableElement nodeParam = new CodeVariableElement(node.getNodeType(), THIS_NODE_LOCAL_VAR_NAME);
             CodeVariableElement arguments = new CodeVariableElement(getContext().getType(Class.class), "types");
             method.addParameter(nodeParam);
             method.addParameter(arguments);
@@ -928,7 +953,9 @@
                 builder.startBlock();
 
                 builder.startReturn().startCall("createSpecialized");
-                builder.string("thisNode");
+                builder.startGroup();
+                builder.string(THIS_NODE_LOCAL_VAR_NAME);
+                builder.end();
                 builder.string("types[0]");
                 builder.end().end();
 
@@ -1085,10 +1112,16 @@
 
             CodeTreeBuilder body = method.createBuilder();
 
-            body.startStatement();
-            body.type(generatedNode.asType()).string(" ").string(THIS_NODE_LOCAL_VAR_NAME + "Cast");
-            body.string(" = ").string("(").type(generatedNode.asType()).string(") ").string(THIS_NODE_LOCAL_VAR_NAME);
-            body.end();
+            boolean hasCopyConstructor = findCopyConstructor(generatedNode.asType()) != null;
+
+            final String thisLocalVariableName = THIS_NODE_LOCAL_VAR_NAME + "Cast";
+
+            if (hasCopyConstructor) {
+                body.startStatement();
+                body.type(generatedNode.asType()).string(" ").string(thisLocalVariableName);
+                body.string(" = ").string("(").type(generatedNode.asType()).string(") ").string(THIS_NODE_LOCAL_VAR_NAME);
+                body.end();
+            }
 
             boolean first = true;
             for (TypeData type : node.getTypeSystem().getTypes()) {
@@ -1101,16 +1134,12 @@
                         body.startElseIf();
                     }
                     body.string("specializationClass == ").type(type.getBoxedType()).string(".class").end().startBlock();
-                    body.startReturn().startNew(nodeSpecializationClassName(specialization));
-                    body.string(THIS_NODE_LOCAL_VAR_NAME + "Cast");
-                    body.end().end(); // new, return
+                    body.tree(createReturnNewSpecialization(body, specialization, thisLocalVariableName, hasCopyConstructor));
 
                     body.end(); // if
                 }
             }
-            body.startReturn().startNew(nodeSpecializationClassName(node.getGenericSpecialization()));
-            body.string(THIS_NODE_LOCAL_VAR_NAME + "Cast");
-            body.end().end();
+            body.tree(createReturnNewSpecialization(body, node.getGenericSpecialization(), thisLocalVariableName, hasCopyConstructor));
             return method;
         }
 
@@ -1123,22 +1152,31 @@
             CodeTreeBuilder body = method.createBuilder();
             body.startStatement().string("boolean allowed = (minimumState == ").string(nodeSpecializationClassName(node.getSpecializations().get(0))).string(".class)").end();
 
+            boolean hasCopyConstructor = findCopyConstructor(generatedNode.asType()) != null;
+
             for (int i = 1; i < node.getSpecializations().size(); i++) {
                 SpecializationData specialization = node.getSpecializations().get(i);
                 body.startStatement().string("allowed = allowed || (minimumState == ").string(nodeSpecializationClassName(specialization)).string(".class)").end();
 
-                CodeTreeBuilder guarded = new CodeTreeBuilder(body);
-                guarded.startReturn().startNew(nodeSpecializationClassName(specialization));
-                guarded.string(THIS_NODE_LOCAL_VAR_NAME);
-                guarded.end().end();
+                CodeTree guarded = createReturnNewSpecialization(body, specialization, THIS_NODE_LOCAL_VAR_NAME, hasCopyConstructor);
 
-                body.tree(createGuardAndCast(body, "allowed", node.getGenericSpecialization(), specialization, false, guarded.getRoot(), null));
+                body.tree(createGuardAndCast(body, "allowed", node.getGenericSpecialization(), specialization, false, guarded, null, true));
             }
             body.startThrow().startNew(getContext().getType(IllegalArgumentException.class)).end().end();
 
             return method;
         }
 
+        private CodeTree createReturnNewSpecialization(CodeTreeBuilder parent, SpecializationData specialization, String thisLocalVariableName, boolean hasCopyConstructor) {
+            CodeTreeBuilder builder = new CodeTreeBuilder(parent);
+            builder.startReturn().startNew(nodeSpecializationClassName(specialization));
+            if (hasCopyConstructor) {
+                builder.string(thisLocalVariableName);
+            }
+            builder.end().end();
+            return builder.getRoot();
+        }
+
         private List<CodeExecutableElement> createGeneratedGenericMethod(NodeData node) {
             TypeMirror genericReturnType = node.getGenericSpecialization().getReturnType().getType();
             if (node.needsRewrites(context)) {
@@ -1155,7 +1193,7 @@
                     } else {
                         String methodName = generatedGenericMethodName(current);
                         CodeExecutableElement method = new CodeExecutableElement(modifiers(PRIVATE, STATIC), genericReturnType, methodName);
-                        method.addParameter(new CodeVariableElement(node.getNodeType(), THIS_NODE_LOCAL_VAR_NAME));
+                        method.addParameter(new CodeVariableElement(generatedNode.asType(), THIS_NODE_LOCAL_VAR_NAME));
                         addInternalValueParameters(method, node.getGenericSpecialization(), true);
 
                         emitGeneratedGenericSpecialization(method.createBuilder(), current, next);
@@ -1168,7 +1206,7 @@
                 return methods;
             } else {
                 CodeExecutableElement method = new CodeExecutableElement(modifiers(PRIVATE, STATIC), genericReturnType, generatedGenericMethodName(null));
-                method.addParameter(new CodeVariableElement(node.getNodeType(), THIS_NODE_LOCAL_VAR_NAME));
+                method.addParameter(new CodeVariableElement(generatedNode.asType(), THIS_NODE_LOCAL_VAR_NAME));
                 addInternalValueParameters(method, node.getGenericSpecialization(), true);
                 emitInvokeDoMethod(method.createBuilder(), node.getGenericSpecialization(), 0);
                 return Arrays.asList(method);
@@ -1188,7 +1226,7 @@
                 addInternalValueParameterNames(nextBuilder, next, next, null, true, true);
                 nextBuilder.end().end();
 
-                invokeMethod = createGuardAndCast(builder, null, current.getNode().getGenericSpecialization(), current, true, invokeMethod, nextBuilder.getRoot());
+                invokeMethod = createGuardAndCast(builder, null, current.getNode().getGenericSpecialization(), current, true, invokeMethod, nextBuilder.getRoot(), true);
             }
 
             builder.tree(invokeMethod);
@@ -1274,7 +1312,7 @@
                 }
             }
 
-            if (node.needsRewrites(getContext()) && !specialization.isGeneric() && !specialization.isUninitialized()) {
+            if (specialization.hasRewrite(getContext())) {
                 buildSpecializeAndExecute(clazz, specialization);
             }
         }
@@ -1469,7 +1507,7 @@
             if (next != null) {
                 returnSpecialized = createReturnSpecializeAndExecute(builder, executable, next, null);
             }
-            builder.tree(createGuardAndCast(builder, null, specialization, specialization, true, executeNode, returnSpecialized));
+            builder.tree(createGuardAndCast(builder, null, specialization, specialization, true, executeNode, returnSpecialized, false));
 
             return builder.getRoot();
         }
@@ -1507,10 +1545,16 @@
         private CodeTree createExecute(CodeTreeBuilder parent, ExecutableTypeData executable, SpecializationData specialization) {
             NodeData node = specialization.getNode();
             CodeTreeBuilder builder = new CodeTreeBuilder(parent);
-            if (!specialization.getExceptions().isEmpty()) {
+            if (!specialization.getExceptions().isEmpty() || !specialization.getAssumptions().isEmpty()) {
                 builder.startTryBlock();
             }
 
+            for (String assumption : specialization.getAssumptions()) {
+                builder.startStatement();
+                builder.string("this.").string(assumption).string(".check()");
+                builder.end();
+            }
+
             CodeTreeBuilder returnBuilder = new CodeTreeBuilder(parent);
             if (specialization.isUninitialized()) {
                 String genericMethodName = generatedGenericMethodName(null);
@@ -1559,6 +1603,12 @@
                 }
                 builder.end();
             }
+            if (!specialization.getAssumptions().isEmpty()) {
+                builder.end().startCatchBlock(getContext().getTruffleTypes().getInvalidAssumption(), "ex");
+                builder.tree(createReturnSpecializeAndExecute(parent, executable, specialization.findNextSpecialization(), null));
+                builder.end();
+            }
+
             return builder.getRoot();
         }
 
--- a/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeData.java	Tue Apr 23 21:03:47 2013 +0200
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeData.java	Wed Apr 24 17:44:15 2013 +0200
@@ -49,6 +49,7 @@
     private List<SpecializationListenerData> specializationListeners;
     private Map<Integer, List<ExecutableTypeData>> executableTypes;
     private List<ShortCircuitData> shortCircuits;
+    private List<String> assumptions;
 
     public NodeData(TypeElement type, String id) {
         super(type, null, null);
@@ -68,6 +69,7 @@
         this.shortCircuits = splitSource.shortCircuits;
         this.fields = splitSource.fields;
         this.children = splitSource.children;
+        this.assumptions = splitSource.assumptions;
     }
 
     public boolean isSplitByMethodName() {
@@ -143,6 +145,14 @@
         return getTemplateType().asType();
     }
 
+    void setAssumptions(List<String> assumptions) {
+        this.assumptions = assumptions;
+    }
+
+    public List<String> getAssumptions() {
+        return assumptions;
+    }
+
     public boolean needsFactory() {
         if (specializations == null) {
             return false;
@@ -241,6 +251,9 @@
     }
 
     public List<ExecutableTypeData> getExecutableTypes(int evaluatedCount) {
+        if (executableTypes == null) {
+            return Collections.emptyList();
+        }
         if (evaluatedCount == -1) {
             List<ExecutableTypeData> typeData = new ArrayList<>();
             for (int currentEvaluationCount : executableTypes.keySet()) {
@@ -304,7 +317,7 @@
                 break;
             }
         }
-        return needsRewrites;
+        return needsRewrites || getSpecializations().size() > 1;
     }
 
     public SpecializationData getGenericSpecialization() {
@@ -339,6 +352,7 @@
         dumpProperty(builder, indent, "fields", getChildren());
         dumpProperty(builder, indent, "executableTypes", getExecutableTypes());
         dumpProperty(builder, indent, "specializations", getSpecializations());
+        dumpProperty(builder, indent, "assumptions", getAssumptions());
         dumpProperty(builder, indent, "messages", collectMessages());
         if (getDeclaredNodes().size() > 0) {
             builder.append(String.format("\n%s  children = [", indent));
--- a/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeParser.java	Tue Apr 23 21:03:47 2013 +0200
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeParser.java	Wed Apr 24 17:44:15 2013 +0200
@@ -562,6 +562,22 @@
             splitByMethodName = Utils.getAnnotationValue(Boolean.class, nodeClass, "splitByMethodName");
         }
 
+        List<String> assumptionsList = new ArrayList<>();
+
+        for (int i = lookupTypes.size() - 1; i >= 0; i--) {
+            TypeElement type = lookupTypes.get(i);
+            AnnotationMirror assumptions = Utils.findAnnotationMirror(context.getEnvironment(), type, NodeAssumptions.class);
+            if (assumptions != null) {
+                List<String> assumptionStrings = Utils.getAnnotationValueList(String.class, assumptions, "value");
+                for (String string : assumptionStrings) {
+                    if (assumptionsList.contains(string)) {
+                        assumptionsList.remove(string);
+                    }
+                    assumptionsList.add(string);
+                }
+            }
+        }
+        nodeData.setAssumptions(new ArrayList<>(assumptionsList));
         nodeData.setNodeType(nodeType);
         nodeData.setSplitByMethodName(splitByMethodName);
         nodeData.setTypeSystem(typeSystem);
--- a/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/SpecializationData.java	Tue Apr 23 21:03:47 2013 +0200
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/SpecializationData.java	Wed Apr 24 17:44:15 2013 +0200
@@ -36,8 +36,9 @@
     private final boolean uninitialized;
     private final List<SpecializationThrowsData> exceptions;
     private List<String> guardDefinitions = Collections.emptyList();
-    private List<GuardData> guards;
+    private List<GuardData> guards = Collections.emptyList();
     private List<ShortCircuitData> shortCircuits;
+    private List<String> assumptions = Collections.emptyList();
     private boolean useSpecializationsForGeneric = true;
     private NodeData node;
 
@@ -59,7 +60,6 @@
         this.generic = generic;
         this.uninitialized = uninitialized;
         this.exceptions = Collections.emptyList();
-        this.guards = new ArrayList<>();
     }
 
     @Override
@@ -81,6 +81,9 @@
         if (!getGuards().isEmpty()) {
             return true;
         }
+        if (!getAssumptions().isEmpty()) {
+            return true;
+        }
         for (ActualParameter parameter : getParameters()) {
             NodeChildData field = getNode().findChild(parameter.getSpecification().getName());
             if (field == null) {
@@ -175,6 +178,14 @@
         return useSpecializationsForGeneric;
     }
 
+    public List<String> getAssumptions() {
+        return assumptions;
+    }
+
+    void setAssumptions(List<String> assumptions) {
+        this.assumptions = assumptions;
+    }
+
     public SpecializationData findNextSpecialization() {
         List<SpecializationData> specializations = node.getSpecializations();
         for (int i = 0; i < specializations.size() - 1; i++) {
--- a/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/SpecializationMethodParser.java	Tue Apr 23 21:03:47 2013 +0200
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/SpecializationMethodParser.java	Wed Apr 24 17:44:15 2013 +0200
@@ -53,7 +53,7 @@
         return Specialization.class;
     }
 
-    private static SpecializationData parseSpecialization(TemplateMethod method) {
+    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.");
@@ -82,7 +82,15 @@
         List<String> guardDefs = Utils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "guards");
         specialization.setGuardDefinitions(guardDefs);
 
+        List<String> assumptionDefs = Utils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "assumptions");
+        specialization.setAssumptions(assumptionDefs);
+
+        for (String assumption : assumptionDefs) {
+            if (!getNode().getAssumptions().contains(assumption)) {
+                specialization.addError("Undeclared assumption '%s' used. Use @NodeAssumptions to declare them.", assumption);
+            }
+        }
+
         return specialization;
     }
-
 }